|
|
|
--
|
|
|
|
-- This is a simple XICS compliant interrupt controller. This is a
|
|
|
|
-- Presenter (ICP) and Source (ICS) in two small units directly
|
|
|
|
-- connected to each other with no routing layer.
|
|
|
|
--
|
|
|
|
-- 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
|
|
|
|
-- 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_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;
|
|
|
|
|
|
|
|
ics_in : in ics_to_icp_t;
|
|
|
|
core_irq_out : out std_ulogic
|
|
|
|
);
|
|
|
|
end xics_icp;
|
|
|
|
|
|
|
|
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);
|
|
|
|
mfrr : std_ulogic_vector(7 downto 0);
|
|
|
|
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 => x"ff", -- mask everything on reset
|
|
|
|
irq => '0',
|
|
|
|
others => (others => '0'));
|
|
|
|
|
|
|
|
signal r, r_next : reg_internal_t;
|
|
|
|
|
|
|
|
-- 8 bit offsets for each presentation
|
|
|
|
constant XIRR_POLL : std_ulogic_vector(7 downto 0) := x"00";
|
|
|
|
constant XIRR : std_ulogic_vector(7 downto 0) := x"04";
|
|
|
|
constant RESV0 : std_ulogic_vector(7 downto 0) := x"08";
|
|
|
|
constant MFRR : std_ulogic_vector(7 downto 0) := x"0c";
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
|
|
|
regs : process(clk)
|
|
|
|
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
|
|
|
|
|
|
|
|
comb : process(all)
|
|
|
|
variable v : reg_internal_t;
|
|
|
|
variable xirr_accept_rd : std_ulogic;
|
|
|
|
|
|
|
|
function bswap(vec : in std_ulogic_vector(31 downto 0)) return std_ulogic_vector is
|
|
|
|
variable rout : std_ulogic_vector(31 downto 0);
|
|
|
|
begin
|
|
|
|
rout( 7 downto 0) := vec(31 downto 24);
|
|
|
|
rout(15 downto 8) := vec(23 downto 16);
|
|
|
|
rout(23 downto 16) := vec(15 downto 8);
|
|
|
|
rout(31 downto 24) := vec( 7 downto 0);
|
|
|
|
return rout;
|
|
|
|
end function;
|
|
|
|
|
|
|
|
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';
|
|
|
|
|
|
|
|
be_in := bswap(wb_in.dat);
|
|
|
|
be_out := (others => '0');
|
|
|
|
|
|
|
|
if wb_in.cyc = '1' and wb_in.stb = '1' then
|
|
|
|
v.wb_ack := '1'; -- always ack
|
|
|
|
if wb_in.we = '1' then -- write
|
|
|
|
-- writes to both XIRR are the same
|
|
|
|
case wb_in.adr(5 downto 0) & "00" is
|
|
|
|
when XIRR_POLL =>
|
|
|
|
report "ICP XIRR_POLL write";
|
|
|
|
v.cppr := be_in(31 downto 24);
|
|
|
|
when XIRR =>
|
|
|
|
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);
|
|
|
|
elsif wb_in.sel = x"1" then -- 1 byte
|
|
|
|
report "ICP XIRR write byte (CPPR):" & to_hstring(be_in(31 downto 24));
|
|
|
|
else
|
|
|
|
report "ICP XIRR UNSUPPORTED write ! sel=" & to_hstring(wb_in.sel);
|
|
|
|
end if;
|
|
|
|
when MFRR =>
|
|
|
|
v.mfrr := be_in(31 downto 24);
|
|
|
|
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
|
|
|
|
report "ICP MFRR write byte:" & to_hstring(be_in(31 downto 24));
|
|
|
|
else
|
|
|
|
report "ICP MFRR UNSUPPORTED write ! sel=" & to_hstring(wb_in.sel);
|
|
|
|
end if;
|
|
|
|
when others =>
|
|
|
|
end case;
|
|
|
|
|
|
|
|
else -- read
|
|
|
|
|
|
|
|
case wb_in.adr(5 downto 0) & "00" is
|
|
|
|
when XIRR_POLL =>
|
|
|
|
report "ICP XIRR_POLL read";
|
|
|
|
be_out := r.cppr & r.xisr;
|
|
|
|
when XIRR =>
|
|
|
|
report "ICP XIRR read";
|
|
|
|
be_out := r.cppr & r.xisr;
|
|
|
|
if wb_in.sel = x"f" then
|
|
|
|
xirr_accept_rd := '1';
|
|
|
|
end if;
|
|
|
|
when MFRR =>
|
|
|
|
report "ICP MFRR read";
|
|
|
|
be_out(31 downto 24) := r.mfrr;
|
|
|
|
when others =>
|
|
|
|
end case;
|
|
|
|
end if;
|
|
|
|
end if;
|
|
|
|
|
|
|
|
pending_priority := x"ff";
|
|
|
|
v.xisr := x"000000";
|
|
|
|
v.irq := '0';
|
|
|
|
|
|
|
|
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: 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 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;
|
|
|
|
end if;
|
|
|
|
|
|
|
|
r_next <= v;
|
|
|
|
|
|
|
|
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.utils.all;
|
|
|
|
use work.wishbone_types.all;
|
|
|
|
use work.helpers.all;
|
|
|
|
|
|
|
|
entity xics_ics is
|
|
|
|
generic (
|
|
|
|
SRC_NUM : integer range 1 to 256 := 16;
|
|
|
|
PRIO_BITS : integer range 1 to 8 := 3
|
|
|
|
);
|
|
|
|
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
|
|
|
|
|
|
|
|
constant SRC_NUM_BITS : natural := log2(SRC_NUM);
|
|
|
|
|
|
|
|
subtype pri_t is std_ulogic_vector(PRIO_BITS-1 downto 0);
|
|
|
|
type xive_t is record
|
|
|
|
pri : pri_t;
|
|
|
|
end record;
|
|
|
|
constant pri_masked : pri_t := (others => '1');
|
|
|
|
|
|
|
|
subtype pri_vector_t is std_ulogic_vector(2**PRIO_BITS - 1 downto 0);
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
function get_config return std_ulogic_vector is
|
|
|
|
variable r: std_ulogic_vector(31 downto 0);
|
|
|
|
begin
|
|
|
|
r := (others => '0');
|
|
|
|
r(23 downto 0) := std_ulogic_vector(to_unsigned(SRC_NUM, 24));
|
|
|
|
r(27 downto 24) := std_ulogic_vector(to_unsigned(PRIO_BITS, 4));
|
|
|
|
return r;
|
|
|
|
end function;
|
|
|
|
|
|
|
|
function prio_pack(pri8: std_ulogic_vector(7 downto 0)) return pri_t is
|
|
|
|
variable masked : std_ulogic_vector(7 downto 0);
|
|
|
|
begin
|
|
|
|
masked := x"00";
|
|
|
|
masked(PRIO_BITS - 1 downto 0) := (others => '1');
|
|
|
|
if unsigned(pri8) >= unsigned(masked) then
|
|
|
|
return pri_masked;
|
|
|
|
else
|
|
|
|
return pri8(PRIO_BITS-1 downto 0);
|
|
|
|
end if;
|
|
|
|
end function;
|
|
|
|
|
|
|
|
function prio_unpack(pri: pri_t) return std_ulogic_vector is
|
|
|
|
variable r : std_ulogic_vector(7 downto 0);
|
|
|
|
begin
|
|
|
|
if pri = pri_masked then
|
|
|
|
r := x"ff";
|
|
|
|
else
|
|
|
|
r := (others => '0');
|
|
|
|
r(PRIO_BITS-1 downto 0) := pri;
|
|
|
|
end if;
|
|
|
|
return r;
|
|
|
|
end function;
|
|
|
|
|
|
|
|
function prio_decode(pri: pri_t) return pri_vector_t is
|
|
|
|
variable v: pri_vector_t;
|
|
|
|
begin
|
|
|
|
v := (others => '0');
|
|
|
|
v(to_integer(unsigned(pri))) := '1';
|
|
|
|
return v;
|
|
|
|
end function;
|
|
|
|
|
|
|
|
-- Assumes nbits <= 6; v is 2^nbits wide
|
|
|
|
function priority_encoder(v: std_ulogic_vector; nbits: natural) return std_ulogic_vector is
|
|
|
|
variable h: std_ulogic_vector(2**nbits - 1 downto 0);
|
|
|
|
variable p: std_ulogic_vector(5 downto 0);
|
|
|
|
begin
|
|
|
|
-- Set the lowest-priority (highest-numbered) bit
|
|
|
|
h := v;
|
|
|
|
h(2**nbits - 1) := '1';
|
|
|
|
p := count_right_zeroes(h);
|
|
|
|
return p(nbits - 1 downto 0);
|
|
|
|
end function;
|
|
|
|
|
|
|
|
-- Register map
|
|
|
|
-- 0 : Config
|
|
|
|
-- 4 : Debug/diagnostics
|
|
|
|
-- 800 : XIVE0
|
|
|
|
-- 804 : XIVE1 ...
|
|
|
|
--
|
|
|
|
-- Config register format:
|
|
|
|
--
|
|
|
|
-- 23.. 0 : Interrupt base (hard wired to 16)
|
|
|
|
-- 27.. 24 : #prio bits (1..8)
|
|
|
|
--
|
|
|
|
-- 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(9);
|
|
|
|
reg_is_config <= '1' when wb_in.adr(9 downto 0) = 10x"000" else '0';
|
|
|
|
reg_is_debug <= '1' when wb_in.adr(9 downto 0) = 10x"001" else '0';
|
|
|
|
|
|
|
|
-- Register index XX FIXME: figure out bits from SRC_NUM
|
|
|
|
reg_idx <= to_integer(unsigned(wb_in.adr(3 downto 0)));
|
|
|
|
|
|
|
|
-- 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" &
|
|
|
|
prio_unpack(xives(reg_idx).pri);
|
|
|
|
elsif reg_is_config = '1' then
|
|
|
|
be_out := get_config;
|
|
|
|
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
|
|
|
|
if rising_edge(clk) then
|
|
|
|
if rst = '1' then
|
|
|
|
for i in 0 to SRC_NUM - 1 loop
|
|
|
|
xives(i) <= (pri => pri_masked);
|
|
|
|
end loop;
|
|
|
|
elsif wb_valid = '1' and wb_in.we = '1' then
|
|
|
|
-- Byteswapped input
|
|
|
|
be_in := bswap(wb_in.dat);
|
|
|
|
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 <= prio_pack(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 wasteful 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 : std_ulogic_vector(SRC_NUM_BITS - 1 downto 0);
|
|
|
|
variable max_pri : pri_t;
|
|
|
|
variable pending_pri : pri_vector_t;
|
|
|
|
variable pending_at_pri : std_ulogic_vector(SRC_NUM - 1 downto 0);
|
|
|
|
begin
|
|
|
|
-- Work out the most-favoured (lowest) priority of the pending interrupts
|
|
|
|
pending_pri := (others => '0');
|
|
|
|
for i in 0 to SRC_NUM - 1 loop
|
|
|
|
if int_level_l(i) = '1' then
|
|
|
|
pending_pri := pending_pri or prio_decode(xives(i).pri);
|
|
|
|
end if;
|
|
|
|
end loop;
|
|
|
|
max_pri := priority_encoder(pending_pri, PRIO_BITS);
|
|
|
|
|
|
|
|
-- Work out which interrupts are pending at that priority
|
|
|
|
pending_at_pri := (others => '0');
|
|
|
|
for i in 0 to SRC_NUM - 1 loop
|
|
|
|
if int_level_l(i) = '1' and xives(i).pri = max_pri then
|
|
|
|
pending_at_pri(i) := '1';
|
|
|
|
end if;
|
|
|
|
end loop;
|
|
|
|
max_idx := priority_encoder(pending_at_pri, SRC_NUM_BITS);
|
|
|
|
|
|
|
|
if max_pri /= pri_masked then
|
|
|
|
report "MFI: " & integer'image(to_integer(unsigned(max_idx))) & " pri=" & to_hstring(prio_unpack(max_pri));
|
|
|
|
end if;
|
|
|
|
icp_out_next.src <= max_idx;
|
|
|
|
icp_out_next.pri <= prio_unpack(max_pri);
|
|
|
|
end process;
|
|
|
|
|
|
|
|
end architecture rtl;
|