@ -1,11 +1,13 @@
--
-- This is a simple XICS compliant interrupt controller. This is a
-- Presenter (ICP) and Source (ICS) in a single unit with no routing
-- layer.
-- Presenter (ICP) and Source (ICS) in two small units directly
-- connected to each other 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 sources have a configurable IRQ priority set a set of ICS
-- registers in the source units.
--
-- The source ids start at 16 for int_level_in(0) and go up from
-- there (ie int_level_in(1) is source id 17). XXX Make a generic
--
-- The presentation layer will pick an interupt that is more
-- favourable than the current CPPR and present it via the XISR and
@ -22,38 +24,31 @@ library work;
use work.common.all;
use work.wishbone_types.all;
entity xics is
generic (
LEVEL_NUM : positive := 16
);
entity xics_icp is
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);
wb_in : in wb_io_master_out;
wb_out : out wb_io_slave_out;
ics_in : in ics_to_icp_t;
core_irq_out : out std_ulogic
);
end xics;
end xics_icp;
architecture behaviour of xics is
architecture behaviour of xics_icp 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"ff", -- no IPI on reset
mfrr => x"ff", -- mask everything on reset
irq => '0',
others => (others => '0'));
@ -74,18 +69,19 @@ begin
begin
if rising_edge(clk) then
r <= r_next;
-- We delay core_irq_out by a cycle to help with timing
core_irq_out <= r.irq;
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;
function bswap(v : in std_ulogic_vector(31 downto 0)) return std_ulogic_vector is
variable r : std_ulogic_vector(31 downto 0);
@ -99,13 +95,14 @@ begin
variable be_in : std_ulogic_vector(31 downto 0);
variable be_out : std_ulogic_vector(31 downto 0);
variable pending_priority : std_ulogic_vector(7 downto 0);
begin
v := r;
v.wb_ack := '0';
xirr_accept_rd := '0';
irq_eoi := '0';
be_in := bswap(wb_in.dat);
be_out := (others => '0');
@ -122,7 +119,6 @@ begin
v.cppr := be_in(31 downto 24);
if wb_in.sel = x"f" then -- 4 byte
report "ICP XIRR write word (EOI) :" & to_hstring(be_in);
irq_eoi := '1';
elsif wb_in.sel = x"1" then -- 1 byte
report "ICP XIRR write byte (CPPR):" & to_hstring(be_in(31 downto 24));
else
@ -130,7 +126,6 @@ begin
end if;
when MFRR =>
v.mfrr := be_in(31 downto 24);
v.mfrr_pending := '1';
if wb_in.sel = x"f" then -- 4 bytes
report "ICP MFRR write word:" & to_hstring(be_in);
elsif wb_in.sel = x"1" then -- 1 byte
@ -161,49 +156,40 @@ begin
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;
pending_priority := x"ff";
v.xisr := x"000000";
v.irq := '0';
-- 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;
if ics_in.pri /= x"ff" then
v.xisr := x"00001" & ics_in.src;
pending_priority := ics_in.pri;
end if;
-- Check MFRR
if unsigned(r.mfrr) < unsigned(pending_priority) then --
v.xisr := x"000002"; -- special XICS MFRR IRQ source number
pending_priority := r.mfrr;
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;
report "XICS: ICP ACCEPT" &
" cppr:" & to_hstring(r.cppr) &
" xisr:" & to_hstring(r.xisr) &
" mfrr:" & to_hstring(r.mfrr);
v.cppr := pending_priority;
end if;
v.wb_rd_data := bswap(be_out);
if irq_eoi = '1' then
v.irq := '0';
end if;
if unsigned(pending_priority) < unsigned(v.cppr) then
if r.irq = '0' then
report "IRQ set";
end if;
v.irq := '1';
elsif r.irq = '1' then
report "IRQ clr";
end if;
if rst = '1' then
v := reg_internal_init;
@ -214,3 +200,192 @@ begin
end process;
end architecture behaviour;
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_ics is
generic (
SRC_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(SRC_NUM - 1 downto 0);
icp_out : out ics_to_icp_t
);
end xics_ics;
architecture rtl of xics_ics is
subtype pri_t is std_ulogic_vector(7 downto 0);
type xive_t is record
pri : pri_t;
end record;
type xive_array_t is array(0 to SRC_NUM-1) of xive_t;
signal xives : xive_array_t;
signal wb_valid : std_ulogic;
signal reg_idx : integer range 0 to SRC_NUM - 1;
signal icp_out_next : ics_to_icp_t;
signal int_level_l : std_ulogic_vector(SRC_NUM - 1 downto 0);
function bswap(v : in std_ulogic_vector(31 downto 0)) return std_ulogic_vector is
variable r : std_ulogic_vector(31 downto 0);
begin
r( 7 downto 0) := v(31 downto 24);
r(15 downto 8) := v(23 downto 16);
r(23 downto 16) := v(15 downto 8);
r(31 downto 24) := v( 7 downto 0);
return r;
end function;
-- Register map
-- 0 : Config (currently hard wired base irq#)
-- 4 : Debug/diagnostics
-- 800 : XIVE0
-- 804 : XIVE1 ...
--
-- Config register format:
--
-- 23.. 0 : Interrupt base (hard wired to 16)
--
-- XIVE register format:
--
-- 31 : input bit (reflects interrupt input)
-- 30 : reserved
-- 29 : P (mirrors input for now)
-- 28 : Q (not implemented in this version)
-- 30 .. : reserved
-- 19 .. 8 : target (not implemented in this version)
-- 7 .. 0 : prio/mask
signal reg_is_xive : std_ulogic;
signal reg_is_config : std_ulogic;
signal reg_is_debug : std_ulogic;
begin
assert SRC_NUM = 16 report "Fixup address decode with log2";
reg_is_xive <= wb_in.adr(11);
reg_is_config <= '1' when wb_in.adr(11 downto 0) = x"000" else '0';
reg_is_debug <= '1' when wb_in.adr(11 downto 0) = x"004" else '0';
-- Register index XX FIXME: figure out bits from SRC_NUM
reg_idx <= to_integer(unsigned(wb_in.adr(5 downto 2)));
-- Latch interrupt inputs for timing
int_latch: process(clk)
begin
if rising_edge(clk) then
int_level_l <= int_level_in;
end if;
end process;
-- We don't stall. Acks are sent by the read machine one cycle
-- after a request, but we can handle one access per cycle.
wb_out.stall <= '0';
wb_valid <= wb_in.cyc and wb_in.stb;
-- Big read mux. This could be replaced by a slower state
-- machine iterating registers instead if timing gets tight.
reg_read: process(clk)
variable be_out : std_ulogic_vector(31 downto 0);
begin
if rising_edge(clk) then
be_out := (others => '0');
if reg_is_xive = '1' then
be_out := int_level_l(reg_idx) &
'0' &
int_level_l(reg_idx) &
'0' &
x"00000" &
xives(reg_idx).pri;
elsif reg_is_config = '1' then
be_out := std_ulogic_vector(to_unsigned(SRC_NUM, 32));
elsif reg_is_debug = '1' then
be_out := x"00000" & icp_out_next.src & icp_out_next.pri;
end if;
wb_out.dat <= bswap(be_out);
wb_out.ack <= wb_valid;
end if;
end process;
-- Register write machine
reg_write: process(clk)
variable be_in : std_ulogic_vector(31 downto 0);
begin
-- Byteswapped input
be_in := bswap(wb_in.dat);
if rising_edge(clk) then
if rst = '1' then
for i in 0 to SRC_NUM - 1 loop
xives(i) <= (pri => x"ff");
end loop;
elsif wb_valid = '1' and wb_in.we = '1' then
if reg_is_xive then
-- TODO: When adding support for other bits, make sure to
-- properly implement wb_in.sel to allow partial writes.
xives(reg_idx).pri <= be_in(7 downto 0);
report "ICS irq " & integer'image(reg_idx) & " set to:" & to_hstring(be_in(7 downto 0));
end if;
end if;
end if;
end process;
-- generate interrupt. This is a simple combinational process,
-- potentially wasteul in HW for large number of interrupts.
--
-- could be replaced with iterative state machines and a message
-- system between ICSs' (plural) and ICP incl. reject etc...
--
irq_gen_sync: process(clk)
begin
if rising_edge(clk) then
icp_out <= icp_out_next;
end if;
end process;
irq_gen: process(all)
variable max_idx : integer range 0 to SRC_NUM-1;
variable max_pri : pri_t;
-- A more favored than b ?
function a_mf_b(a: pri_t; b: pri_t) return boolean is
variable a_i : integer range 0 to 255;
variable b_i : integer range 0 to 255;
begin
a_i := to_integer(unsigned(a));
b_i := to_integer(unsigned(b));
return a < b;
end function;
begin
-- XXX FIXME: Use a tree
max_pri := x"ff";
max_idx := 0;
for i in 0 to SRC_NUM - 1 loop
if int_level_l(i) = '1' and a_mf_b(xives(i).pri, max_pri) then
max_pri := xives(i).pri;
max_idx := i;
end if;
end loop;
if max_pri /= x"ff" then
report "MFI: " & integer'image(max_idx) & " pri=" & to_hstring(max_pri);
end if;
icp_out_next.src <= std_ulogic_vector(to_unsigned(max_idx, 4));
icp_out_next.pri <= max_pri;
end process;
end architecture rtl;