"""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
# 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()
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):
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])
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
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<<self.PRIO_BITS)-1
+ class Spec: pass
+ spec = Spec()
+ spec.addr_wid = 30
+ spec.mask_wid = 4
+ spec.reg_wid = 32
+ self.bus = Record(make_wb_layout(spec))
+
+ self.int_level_i = Signal(SRC_NUM)
+ self.icp_out = ICS2ICP("icp_o")
+
+ def prio_pack(self, pri8):
+ return pri8[:self.PRIO_BITS]
+
+ def prio_unpack(self, pri):
+ return Mux(pri == self.pri_masked, 0xff, pri[:self.PRIO_BITS])
+
+ # A more favored than b ?
+ def a_mf_b(self, a, b):
+ #report "a_mf_b a=" & to_hstring(a) &
+ # " b=" & to_hstring(b) &
+ # " r=" & boolean'image(a < b);
+ return a < b;
+
+ def elaborate(self, platform):
+ m = Module()
+ comb, sync = m.d.comb, m.d.sync
+
+ xives = Array([Xive("xive%d" % i, self.PRIO_BITS, self.pri_masked)
+ for i in range(self.SRC_NUM)])
+
+ wb_valid = Signal()
+ reg_idx = Signal(log2_int(self.SRC_NUM))
+ icp_out_next = ICS2ICP("icp_r")
+ int_level_l = Signal(self.SRC_NUM)
+
+ # 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
+
+ reg_is_xive = Signal()
+ reg_is_config = Signal()
+ reg_is_debug = Signal()
+
+ assert self.SRC_NUM == 16, "Fixup address decode with log2"
+
+ comb += reg_is_xive.eq(self.bus.adr[11])
+ comb += reg_is_config.eq(self.bus.adr[0:12] == 0x0)
+ comb += reg_is_debug.eq(self.bus.adr[0:12] == 0x4)
+
+ # Register index XX FIXME: figure out bits from SRC_NUM
+ comb += reg_idx.eq(self.bus.adr[2:6])
+
+ # Latch interrupt inputs for timing
+ sync += int_level_l.eq(self.int_level_i)
+
+ # We don't stall. Acks are sent by the read machine one cycle
+ # after a request, but we can handle one access per cycle.
+ comb += wb_valid.eq(self.bus.cyc & self.bus.stb)
+
+ # Big read mux. This could be replaced by a slower state
+ # machine iterating registers instead if timing gets tight.
+ be_out = Signal(32)
+ comb += be_out.eq(0)
+
+ # XIVE reg
+ with m.If(reg_is_xive):
+ pri_i = self.prio_unpack(xives[reg_idx].pri)
+ ibit = Signal()
+ comb += ibit.eq(int_level_l.bit_select(reg_idx, 1))
+ comb += be_out.eq(Cat(pri_i, # bits 0..7
+ Const(0, 20), # 8-27
+ 0, # 28
+ ibit, # 29
+ 0, # 30
+ ibit)) # 31
+ # Config reg
+ with m.Elif(reg_is_config):
+ comb += be_out.eq(Cat(Const(self.SRC_NUM, 24), # 0-23
+ Const(self.PRIO_BITS, 4), # 24-27
+ Const(0, 4))) # 28-31
+ # Debug reg
+ with m.Elif(reg_is_debug):
+ comb += be_out.eq(Cat(icp_out_next.pri, # 0-7
+ Const(0, 20), # 8-27
+ icp_out_next.src)) # 28-31
+
+ sync += self.bus.dat_r.eq(bswap(be_out))
+ sync += self.bus.ack.eq(wb_valid)
+
+ # Register write machine
+ be_in = Signal(32)
# 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 : 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():
#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()