1 """Microwatt xics.vhdl converted to nmigen
3 # This is a simple XICS compliant interrupt controller. This is a
4 # Presenter (ICP) and Source (ICS) in two small units directly
5 # connected to each other with no routing layer.
7 # The sources have a configurable IRQ priority set a set of ICS
8 # registers in the source units.
10 # The source ids start at 16 for int_level_in(0) and go up from
11 # there (ie int_level_in(1) is source id 17). XXX Make a generic
13 # The presentation layer will pick an interupt that is more
14 # favourable than the current CPPR and present it via the XISR and
15 # send an interrpt to the processor (via e_out). This may not be the
16 # highest priority interrupt currently presented (which is allowed
20 from nmigen
import Elaboratable
, Module
, Signal
, Cat
, Const
, Record
21 from nmutil
.iocontrol
import RecordObject
22 from nmigen
.cli
import rtlil
23 from soc
.minerva
.wishbone
import make_wb_layout
26 class ICS2ICP(RecordObject
):
28 # Level interrupts only, ICS just keeps prsenting the
29 # highest priority interrupt. Once handling edge, something
30 # smarter involving handshake & reject support will be needed
32 def __init__(self
, name
):
33 super().__init
__(name
=name
)
34 self
.src
= Signal(4, reset_less
=True)
35 self
.pri
= Signal(8, reset_less
=True)
37 # hardwire the hardware IRQ priority
38 HW_PRIORITY
= Const(0x80, 8)
40 # 8 bit offsets for each presentation
47 class RegInternal(RecordObject
):
48 def __init__(self
, name
=None):
49 super().__init
__(name
=name
)
50 self
.xisr
= Signal(24)
52 self
.mfrr
= Signal(8, reset
=0xff) # mask everything on reset
54 self
.wb_rd_data
= Signal(32)
55 self
.wb_ack
= Signal(1)
59 return Cat(v
[24:32], v
[16:24], v
[8:16], v
[0:8])
62 class XICS_ICP(Elaboratable
):
70 self
.bus
= Record(make_wb_layout(spec
))
71 self
.ics_i
= ICS2ICP("ics_i")
72 self
.core_irq_o
= Signal()
74 def elaborate(self
, platform
):
76 comb
, sync
= m
.d
.comb
, m
.d
.sync
79 r_next
= RegInternal()
82 # We delay core_irq_out by a cycle to help with timing
83 sync
+= self
.core_irq_o
.eq(r
.irq
)
85 comb
+= self
.bus
.dat_w
.eq(r
.wb_rd_data
)
86 comb
+= self
.bus
.ack
.eq(r
.wb_ack
)
89 xirr_accept_rd
= Signal()
94 pending_priority
= Signal(8)
97 comb
+= v
.wb_ack
.eq(0)
99 comb
+= xirr_accept_rd
.eq(0)
101 comb
+= be_in
.eq(bswap(self
.bus
.dat_r
))
104 with m
.If(self
.bus
.cyc
& self
.bus
.stb
):
105 comb
+= v
.wb_ack
.eq(1) # always ack
106 with m
.If(self
.bus
.we
): # write
107 # writes to both XIRR are the same
108 with m
.Switch( self
.bus
.adr
[:8]):
109 with m
.Case(XIRR_POLL
):
110 # report "ICP XIRR_POLL write";
111 comb
+= v
.cppr
.eq(be_in
[24:32])
113 comb
+= v
.cppr
.eq(be_in
[24:32])
114 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
115 #report "ICP XIRR write word (EOI) :" & \
118 with m
.Elif(self
.bus
.sel
== 0x1): # 1 byte
119 #report "ICP XIRR write byte (CPPR):" & \
120 #to_hstring(be_in(31 downto 24));
123 #report "ICP XIRR UNSUPPORTED write ! sel=" & \
124 # to_hstring(wb_in.sel);
127 comb
+= v
.mfrr
.eq(be_in
[24:32])
128 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
129 # report "ICP MFRR write word:" & to_hstring(be_in);
131 with m
.Elif(self
.bus
.sel
== 0x1): # 1 byte
132 # report "ICP MFRR write byte:" & \
133 # to_hstring(be_in(31 downto 24));
136 # report "ICP MFRR UNSUPPORTED write ! sel=" & \
137 # to_hstring(wb_in.sel);
140 with m
.Else(): # read
142 with m
.Switch( self
.bus
.adr
[:8]):
143 with m
.Case(XIRR_POLL
):
144 # report "ICP XIRR_POLL read";
145 comb
+= be_out
.eq(r
.xisr
& r
.cppr
)
147 # report "ICP XIRR read";
148 comb
+= be_out
.eq(Cat(r
.xisr
, r
.cppr
))
149 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
150 comb
+= xirr_accept_rd
.eq(1)
152 # report "ICP MFRR read";
153 comb
+= be_out
.eq(r
.mfrr
)
155 comb
+= pending_priority
.eq(0xff)
156 comb
+= v
.xisr
.eq(0x0)
157 comb
+= v
.irq
.eq(0x0)
159 with m
.If(self
.ics_i
.pri
!= 0xff):
160 comb
+= v
.xisr
.eq(Cat(self
.ics_i
.src
, Const(0x00001)))
161 comb
+= pending_priority
.eq(self
.ics_i
.pri
)
164 with m
.If(r
.mfrr
< pending_priority
):
165 # special XICS MFRR IRQ source number
166 comb
+= v
.xisr
.eq(Const(0x000002))
167 comb
+= pending_priority
.eq(r
.mfrr
)
169 # Accept the interrupt
170 with m
.If(xirr_accept_rd
):
171 #report "XICS: ICP ACCEPT" &
172 # " cppr:" & to_hstring(r.cppr) &
173 # " xisr:" & to_hstring(r.xisr) &
174 # " mfrr:" & to_hstring(r.mfrr);
175 comb
+= v
.cppr
.eq(pending_priority
)
177 comb
+= v
.wb_rd_data
.eq(bswap(be_out
))
179 with m
.If(pending_priority
< v
.cppr
):
193 for field
in self
.bus
.fields
.values():
195 yield from self
.ics_i
196 yield self
.core_irq_o
203 end architecture behaviour;
206 use ieee.std_logic_1164.all;
207 use ieee.numeric_std.all;
211 use work.wishbone_types.all;
215 SRC_NUM : integer range 1 to 256 := 16;
216 PRIO_BITS : integer range 1 to 8 := 8
222 wb_in : in wb_io_master_out;
223 wb_out : out wb_io_slave_out;
225 int_level_in : in std_ulogic_vector(SRC_NUM - 1 downto 0);
226 icp_out : out ics_to_icp_t
230 architecture rtl of xics_ics is
232 subtype pri_t is std_ulogic_vector(PRIO_BITS-1 downto 0);
233 type xive_t is record
236 constant pri_masked : pri_t := (others => '1');
238 type xive_array_t is array(0 to SRC_NUM-1) of xive_t;
239 signal xives : xive_array_t;
241 signal wb_valid : std_ulogic;
242 signal reg_idx : integer range 0 to SRC_NUM - 1;
243 signal icp_out_next : ics_to_icp_t;
244 signal int_level_l : std_ulogic_vector(SRC_NUM - 1 downto 0);
246 function bswap(v : in std_ulogic_vector(31 downto 0)) return std_ulogic_vector is
247 variable r : std_ulogic_vector(31 downto 0);
249 r( 7 downto 0) := v(31 downto 24);
250 r(15 downto 8) := v(23 downto 16);
251 r(23 downto 16) := v(15 downto 8);
252 r(31 downto 24) := v( 7 downto 0);
256 function get_config return std_ulogic_vector is
257 variable r: std_ulogic_vector(31 downto 0);
259 r := (others => '0');
260 r(23 downto 0) := std_ulogic_vector(to_unsigned(SRC_NUM, 24));
261 r(27 downto 24) := std_ulogic_vector(to_unsigned(PRIO_BITS, 4));
265 function prio_pack(pri8: std_ulogic_vector(7 downto 0)) return pri_t is
267 return pri8(PRIO_BITS-1 downto 0);
270 function prio_unpack(pri: pri_t) return std_ulogic_vector is
271 variable r : std_ulogic_vector(7 downto 0);
273 if pri = pri_masked then
276 r := (others => '0');
277 r(PRIO_BITS-1 downto 0) := pri;
285 # 4 : Debug/diagnostics
289 # Config register format:
291 # 23.. 0 : Interrupt base (hard wired to 16)
292 # 27.. 24 : #prio bits (1..8)
294 # XIVE register format:
296 # 31 : input bit (reflects interrupt input)
298 # 29 : P (mirrors input for now)
299 # 28 : Q (not implemented in this version)
301 # 19 .. 8 : target (not implemented in this version)
304 signal reg_is_xive : std_ulogic;
305 signal reg_is_config : std_ulogic;
306 signal reg_is_debug : std_ulogic;
310 assert SRC_NUM = 16 report "Fixup address decode with log2";
312 reg_is_xive <= wb_in.adr(11);
313 reg_is_config <= '1' when wb_in.adr(11 downto 0) = x"000" else '0';
314 reg_is_debug <= '1' when wb_in.adr(11 downto 0) = x"004" else '0';
316 # Register index XX FIXME: figure out bits from SRC_NUM
317 reg_idx <= to_integer(unsigned(wb_in.adr(5 downto 2)));
319 # Latch interrupt inputs for timing
320 int_latch: process(clk)
322 if rising_edge(clk) then
323 int_level_l <= int_level_in;
327 # We don't stall. Acks are sent by the read machine one cycle
328 # after a request, but we can handle one access per cycle.
330 wb_valid <= wb_in.cyc and wb_in.stb;
332 # Big read mux. This could be replaced by a slower state
333 # machine iterating registers instead if timing gets tight.
334 reg_read: process(clk)
335 variable be_out : std_ulogic_vector(31 downto 0);
337 if rising_edge(clk) then
338 be_out := (others => '0');
340 if reg_is_xive = '1' then
341 be_out := int_level_l(reg_idx) &
343 int_level_l(reg_idx) &
346 prio_unpack(xives(reg_idx).pri);
347 elsif reg_is_config = '1' then
348 be_out := get_config;
349 elsif reg_is_debug = '1' then
350 be_out := x"00000" & icp_out_next.src & icp_out_next.pri;
352 wb_out.dat <= bswap(be_out);
353 wb_out.ack <= wb_valid;
357 # Register write machine
358 reg_write: process(clk)
359 variable be_in : std_ulogic_vector(31 downto 0);
362 be_in := bswap(wb_in.dat);
364 if rising_edge(clk) then
366 for i in 0 to SRC_NUM - 1 loop
367 xives(i) <= (pri => pri_masked);
369 elsif wb_valid = '1' and wb_in.we = '1' then
371 # TODO: When adding support for other bits, make sure to
372 # properly implement wb_in.sel to allow partial writes.
373 xives(reg_idx).pri <= prio_pack(be_in(7 downto 0));
374 report "ICS irq " & integer'image(reg_idx) &
375 " set to:" & to_hstring(be_in(7 downto 0));
381 # generate interrupt. This is a simple combinational process,
382 # potentially wasteful in HW for large number of interrupts.
384 # could be replaced with iterative state machines and a message
385 # system between ICSs' (plural) and ICP incl. reject etc...
387 irq_gen_sync: process(clk)
389 if rising_edge(clk) then
390 icp_out <= icp_out_next;
394 irq_gen: process(all)
395 variable max_idx : integer range 0 to SRC_NUM-1;
396 variable max_pri : pri_t;
398 # A more favored than b ?
399 function a_mf_b(a: pri_t; b: pri_t) return boolean is
400 variable a_i : unsigned(PRIO_BITS-1 downto 0);
401 variable b_i : unsigned(PRIO_BITS-1 downto 0);
405 report "a_mf_b a=" & to_hstring(a) &
406 " b=" & to_hstring(b) &
407 " r=" & boolean'image(a < b);
411 # XXX FIXME: Use a tree
412 max_pri := pri_masked;
414 for i in 0 to SRC_NUM - 1 loop
415 if int_level_l(i) = '1' and a_mf_b(xives(i).pri, max_pri) then
416 max_pri := xives(i).pri;
420 if max_pri /= pri_masked then
421 report "MFI: " & integer'image(max_idx) & " pri=" & to_hstring(prio_unpack(max_pri));
423 icp_out_next.src <= std_ulogic_vector(to_unsigned(max_idx, 4));
424 icp_out_next.pri <= prio_unpack(max_pri);
427 end architecture rtl;
433 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
434 with
open("test_xics_icp.il", "w") as f
:
437 #run_simulation(dut, ldst_sim(dut), vcd_name='test_ldst_regspec.vcd')
440 if __name__
== '__main__':