1
0
mirror of https://github.com/antonblanchard/microwatt.git synced 2026-03-06 11:03:25 +00:00
Files
antonblanchard.microwatt/xics.vhdl
Paul Mackerras 0aa898c7a6 xics: Rework the irq_gen process
At present, the loop in the irq_gen process generates a chain of
comparators and other logic to work out the source number and priority
of the most-favoured (lowest priority number) pending interrupt.
This replaces that chain with (1) logic to generate an array of bits,
one per priority, indicating whether any interrupt is pending at that
priority, (2) a priority encoder to select the most favoured priority
with an interrupt pending, (3) logic to generate an array of bits, one
per source, indicating whether an interrupt is pending at the priority
calculated in step 2, and (4) a priority encoder to work out the
lowest numbered source that has an interrupt pending at the selected
priority.  This reduces LUT utilization.

The priority encoder function implemented here uses the optimized
count-leading-zeroes logic from helpers.vhdl.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
2022-02-21 13:29:51 +11:00

454 lines
15 KiB
VHDL

--
-- 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(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;
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 pri8 >= 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
-- 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 => pri_masked);
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 <= 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;