From 46a368d998f225d2b07c0c9c2a2a4e14a48a39fc Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Mon, 27 Jul 2020 22:36:55 +0100 Subject: [PATCH] add 2nd part of XICS interrupt interface --- src/soc/interrupts/xics.py | 421 ++++++++++++++++--------------------- 1 file changed, 186 insertions(+), 235 deletions(-) diff --git a/src/soc/interrupts/xics.py b/src/soc/interrupts/xics.py index b67cea7b..e95fb613 100644 --- a/src/soc/interrupts/xics.py +++ b/src/soc/interrupts/xics.py @@ -1,24 +1,25 @@ """Microwatt xics.vhdl converted to nmigen --- +# # 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) --- +# """ -from nmigen import Elaboratable, Module, Signal, Cat, Const, Record +from nmigen import Elaboratable, Module, Signal, Cat, Const, Record, Array, Mux from nmutil.iocontrol import RecordObject +from nmigen.utils import log2_int from nmigen.cli import rtlil from soc.minerva.wishbone import make_wb_layout @@ -82,7 +83,7 @@ class XICS_ICP(Elaboratable): # We delay core_irq_out by a cycle to help with timing sync += self.core_irq_o.eq(r.irq) - comb += self.bus.dat_w.eq(r.wb_rd_data) + comb += self.bus.dat_r.eq(r.wb_rd_data) comb += self.bus.ack.eq(r.wb_ack) v = RegInternal() @@ -98,7 +99,7 @@ class XICS_ICP(Elaboratable): comb += xirr_accept_rd.eq(0) - comb += be_in.eq(bswap(self.bus.dat_r)) + comb += be_in.eq(bswap(self.bus.dat_w)) comb += be_out.eq(0) with m.If(self.bus.cyc & self.bus.stb): @@ -121,7 +122,7 @@ class XICS_ICP(Elaboratable): pass with m.Else(): #report "ICP XIRR UNSUPPORTED write ! sel=" & \ - # to_hstring(wb_in.sel); + # to_hstring(self.bus.sel); pass with m.Case(MFRR ): comb += v.mfrr.eq(be_in[24:32]) @@ -134,7 +135,7 @@ class XICS_ICP(Elaboratable): pass with m.Else(): # report "ICP MFRR UNSUPPORTED write ! sel=" & \ - # to_hstring(wb_in.sel); + # to_hstring(self.bus.sel); pass with m.Else(): # read @@ -199,233 +200,173 @@ class XICS_ICP(Elaboratable): return list(self) -""" -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 : integer range 1 to 256 := 16; - PRIO_BITS : integer range 1 to 8 := 8 - ); - 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(PRIO_BITS-1 downto 0); - type xive_t is record - pri : pri_t; - end record; - constant pri_masked : pri_t := (others => '1'); - - 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 - begin - return pri8(PRIO_BITS-1 downto 0); - 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; - - -# 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(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" & - 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 +class Xive(RecordObject): + def __init__(self, name, wid, rst): + super().__init__(name=name) + self.pri = Signal(wid, reset=rst) + + + +class XICS_ICS(Elaboratable): + def __init__(self, SRC_NUM=16, PRIO_BITS=8): + self.SRC_NUM = SRC_NUM + self.PRIO_BITS = PRIO_BITS + self.pri_masked = (1< 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 : 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 : unsigned(PRIO_BITS-1 downto 0); - variable b_i : unsigned(PRIO_BITS-1 downto 0); - begin - a_i := unsigned(a); - b_i := unsigned(b); - report "a_mf_b a=" & to_hstring(a) & - " b=" & to_hstring(b) & - " r=" & boolean'image(a < b); - return a_i < b_i; - end function; - begin + comb += be_in.eq(bswap(self.bus.dat_w)) + + with m.If(wb_valid & self.bus.we): + with m.If(reg_is_xive): + # TODO: When adding support for other bits, make sure to + # properly implement self.bus.sel to allow partial writes. + sync += xives[reg_idx].pri.eq(self.prio_pack(be_in[:8])) + #report "ICS irq " & integer'image(reg_idx) & + # " set to:" & to_hstring(be_in(7 downto 0)); + + # 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... + # + sync += self.icp_out.eq(icp_out_next) + + max_idx = Signal(log2_int(self.SRC_NUM)) + max_pri = Signal(self.PRIO_BITS) + # XXX FIXME: Use a tree - max_pri := pri_masked; - 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 /= pri_masked then - report "MFI: " & integer'image(max_idx) & " pri=" & to_hstring(prio_unpack(max_pri)); - end if; - icp_out_next.src <= std_ulogic_vector(to_unsigned(max_idx, 4)); - icp_out_next.pri <= prio_unpack(max_pri); - end process; - -end architecture rtl; -""" + comb += max_pri.eq(self.pri_masked) + comb += max_idx.eq(0) + for i in range(self.SRC_NUM): + with m.If(int_level_l[i] & self.a_mf_b(xives[i].pri, max_pri)): + comb += max_pri.eq(xives[i].pri) + comb += max_idx.eq(i) + with m.If(max_pri != self.pri_masked): + #report "MFI: " & integer'image(max_idx) & + #" pri=" & to_hstring(prio_unpack(max_pri)); + pass + comb += icp_out_next.src.eq(max_idx) + comb += icp_out_next.pri.eq(self.prio_unpack(max_pri)) + + return m + + def __iter__(self): + for field in self.bus.fields.values(): + yield field + yield self.int_level_i + yield from self.icp_out.ports() + + def ports(self): + return list(self) + def test_xics_icp(): @@ -436,7 +377,17 @@ def test_xics_icp(): #run_simulation(dut, ldst_sim(dut), vcd_name='test_ldst_regspec.vcd') +def test_xics_ics(): + + dut = XICS_ICS() + vl = rtlil.convert(dut, ports=dut.ports()) + with open("test_xics_ics.il", "w") as f: + f.write(vl) + + #run_simulation(dut, ldst_sim(dut), vcd_name='test_ldst_regspec.vcd') + if __name__ == '__main__': test_xics_icp() + test_xics_ics() -- 2.30.2