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
21 # * https://bugs.libre-soc.org/show_bug.cgi?id=407
23 from nmigen
import Elaboratable
, Module
, Signal
, Cat
, Const
, Record
, Array
, Mux
24 from nmutil
.iocontrol
import RecordObject
25 from nmigen
.utils
import log2_int
26 from nmigen
.cli
import rtlil
27 from soc
.minerva
.wishbone
import make_wb_layout
28 from nmutil
.util
import wrap
29 from soc
.bus
.test
.wb_rw
import wb_read
, wb_write
34 from nmigen
.sim
.cxxsim
import Simulator
, Settle
36 from nmigen
.back
.pysim
import Simulator
, Settle
40 class ICS2ICP(RecordObject
):
42 # Level interrupts only, ICS just keeps prsenting the
43 # highest priority interrupt. Once handling edge, something
44 # smarter involving handshake & reject support will be needed
46 def __init__(self
, name
):
47 super().__init
__(name
=name
)
48 self
.src
= Signal(4, reset_less
=True)
49 self
.pri
= Signal(8, reset_less
=True)
51 # hardwire the hardware IRQ priority
52 HW_PRIORITY
= Const(0x80, 8)
54 # 8 bit offsets for each presentation - all addresses are in "words"
55 XIRR_POLL
= 0x00 # 0x000
61 class RegInternal(RecordObject
):
62 def __init__(self
, name
=None):
63 super().__init
__(name
=name
)
64 self
.xisr
= Signal(24)
66 self
.mfrr
= Signal(8, reset
=0xff) # mask everything on reset
68 self
.wb_rd_data
= Signal(32)
69 self
.wb_ack
= Signal(1)
73 return Cat(v
[24:32], v
[16:24], v
[8:16], v
[0:8])
76 class XICS_ICP(Elaboratable
):
84 self
.bus
= Record(make_wb_layout(spec
, cti
=False), name
="icp_wb")
85 self
.ics_i
= ICS2ICP("ics_i")
86 self
.core_irq_o
= Signal()
88 def elaborate(self
, platform
):
90 comb
, sync
= m
.d
.comb
, m
.d
.sync
93 r_next
= RegInternal()
96 # We delay core_irq_out by a cycle to help with timing
97 sync
+= self
.core_irq_o
.eq(r
.irq
)
99 comb
+= self
.bus
.ack
.eq(r
.wb_ack
& self
.bus
.cyc
)
100 with m
.If(self
.bus
.ack
):
101 comb
+= self
.bus
.dat_r
.eq(r
.wb_rd_data
)
104 xirr_accept_rd
= Signal()
109 pending_priority
= Signal(8)
112 comb
+= v
.eq(r
) # start from the register (r)
113 comb
+= v
.wb_ack
.eq(0)
115 comb
+= xirr_accept_rd
.eq(0)
117 comb
+= be_in
.eq(bswap(self
.bus
.dat_w
))
120 with m
.If(self
.bus
.cyc
& self
.bus
.stb
):
121 comb
+= v
.wb_ack
.eq(1) # always ack
122 with m
.If(self
.bus
.we
): # write
123 # writes to both XIRR are the same
124 with m
.Switch(self
.bus
.adr
[:6]):
125 with m
.Case(XIRR_POLL
):
126 # report "ICP XIRR_POLL write";
127 comb
+= v
.cppr
.eq(be_in
[24:32])
129 comb
+= v
.cppr
.eq(be_in
[24:32])
130 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
131 #report "ICP XIRR write word (EOI) :" & \
134 with m
.Elif(self
.bus
.sel
== 0x1): # 1 byte
135 #report "ICP XIRR write byte (CPPR):" & \
136 #to_hstring(be_in(31 downto 24));
139 #report "ICP XIRR UNSUPPORTED write ! sel=" & \
140 # to_hstring(self.bus.sel);
143 comb
+= v
.mfrr
.eq(be_in
[24:32])
144 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
145 # report "ICP MFRR write word:" & to_hstring(be_in);
147 with m
.Elif(self
.bus
.sel
== 0x1): # 1 byte
148 # report "ICP MFRR write byte:" & \
149 # to_hstring(be_in(31 downto 24));
152 # report "ICP MFRR UNSUPPORTED write ! sel=" & \
153 # to_hstring(self.bus.sel);
156 with m
.Else(): # read
158 with m
.Switch(self
.bus
.adr
[:6]):
159 with m
.Case(XIRR_POLL
):
160 # report "ICP XIRR_POLL read";
161 comb
+= be_out
.eq(Cat(r
.xisr
, r
.cppr
))
163 # report "ICP XIRR read";
164 comb
+= be_out
.eq(Cat(r
.xisr
, r
.cppr
))
165 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
166 comb
+= xirr_accept_rd
.eq(1)
168 # report "ICP MFRR read";
169 comb
+= be_out
[24:32].eq(r
.mfrr
)
171 comb
+= pending_priority
.eq(0xff)
172 comb
+= v
.xisr
.eq(0x0)
173 comb
+= v
.irq
.eq(0x0)
176 with m
.If(self
.ics_i
.pri
!= 0xff):
177 comb
+= v
.xisr
.eq(Cat(self
.ics_i
.src
, Const(0x00001, 20)))
178 comb
+= pending_priority
.eq(self
.ics_i
.pri
)
181 with m
.If(r
.mfrr
< pending_priority
):
182 comb
+= v
.xisr
.eq(Const(0x2, 24)) # special XICS MFRR IRQ src num
183 comb
+= min_pri
.eq(r
.mfrr
)
185 comb
+= min_pri
.eq(pending_priority
)
187 # Accept the interrupt
188 with m
.If(xirr_accept_rd
):
189 #report "XICS: ICP ACCEPT" &
190 # " cppr:" & to_hstring(r.cppr) &
191 # " xisr:" & to_hstring(r.xisr) &
192 # " mfrr:" & to_hstring(r.mfrr);
193 comb
+= v
.cppr
.eq(min_pri
)
195 comb
+= v
.wb_rd_data
.eq(bswap(be_out
))
197 # check if the core needs an interrupt notification (or clearing)
198 with m
.If(min_pri
< v
.cppr
):
212 for field
in self
.bus
.fields
.values():
214 yield from self
.ics_i
215 yield self
.core_irq_o
221 class Xive(RecordObject
):
222 def __init__(self
, name
, wid
, rst
):
223 super().__init
__(name
=name
)
224 self
.pri
= Signal(wid
, reset
=rst
)
228 class XICS_ICS(Elaboratable
):
229 def __init__(self
, SRC_NUM
=16, PRIO_BITS
=8):
230 self
.SRC_NUM
= SRC_NUM
231 self
.PRIO_BITS
= PRIO_BITS
232 self
.pri_masked
= (1<<self
.PRIO_BITS
)-1
238 self
.bus
= Record(make_wb_layout(spec
, cti
=False), name
="ics_wb")
240 self
.int_level_i
= Signal(SRC_NUM
)
241 self
.icp_o
= ICS2ICP("icp_o")
243 def prio_pack(self
, pri8
):
244 return pri8
[:self
.PRIO_BITS
]
246 def prio_unpack(self
, pri
):
247 return Mux(pri
== self
.pri_masked
, Const(0xff, 8), pri
[:self
.PRIO_BITS
])
249 # A more favored than b ?
250 def a_mf_b(self
, a
, b
):
251 #report "a_mf_b a=" & to_hstring(a) &
252 # " b=" & to_hstring(b) &
253 # " r=" & boolean'image(a < b);
256 def elaborate(self
, platform
):
258 comb
, sync
= m
.d
.comb
, m
.d
.sync
260 xives
= Array([Xive("xive%d" % i
, self
.PRIO_BITS
, self
.pri_masked
)
261 for i
in range(self
.SRC_NUM
)])
264 reg_idx
= Signal(log2_int(self
.SRC_NUM
))
265 icp_o_next
= ICS2ICP("icp_r")
266 int_level_l
= Signal(self
.SRC_NUM
)
270 # 4 : Debug/diagnostics
274 # Config register format:
276 # 23.. 0 : Interrupt base (hard wired to 16)
277 # 27.. 24 : #prio bits (1..8)
279 # XIVE register format:
281 # 31 : input bit (reflects interrupt input)
283 # 29 : P (mirrors input for now)
284 # 28 : Q (not implemented in this version)
286 # 19 .. 8 : target (not implemented in this version)
289 reg_is_xive
= Signal()
290 reg_is_config
= Signal()
291 reg_is_debug
= Signal()
293 assert self
.SRC_NUM
== 16, "Fixup address decode with log2"
295 comb
+= reg_is_xive
.eq(self
.bus
.adr
[9])
296 comb
+= reg_is_config
.eq(self
.bus
.adr
[0:10] == 0x0)
297 comb
+= reg_is_debug
.eq(self
.bus
.adr
[0:10] == 0x4)
299 # Register index XX FIXME: figure out bits from SRC_NUM
300 comb
+= reg_idx
.eq(self
.bus
.adr
[:4])
302 # Latch interrupt inputs for timing
303 sync
+= int_level_l
.eq(self
.int_level_i
)
305 # We don't stall. Acks are sent by the read machine one cycle
306 # after a request, but we can handle one access per cycle.
307 comb
+= wb_valid
.eq(self
.bus
.cyc
& self
.bus
.stb
)
309 # Big read mux. This could be replaced by a slower state
310 # machine iterating registers instead if timing gets tight.
315 with m
.If(reg_is_xive
):
316 pri_i
= self
.prio_unpack(xives
[reg_idx
].pri
)
318 comb
+= ibit
.eq(int_level_l
.bit_select(reg_idx
, 1))
319 comb
+= be_out
.eq(Cat(pri_i
, # bits 0..7
326 with m
.Elif(reg_is_config
):
327 comb
+= be_out
.eq(Cat(Const(self
.SRC_NUM
, 24), # 0-23
328 Const(self
.PRIO_BITS
, 4), # 24-27
329 Const(0, 4))) # 28-31
331 with m
.Elif(reg_is_debug
):
332 comb
+= be_out
.eq(Cat(icp_o_next
.pri
, # 0-7
334 icp_o_next
.src
)) # 28-31
336 sync
+= self
.bus
.dat_r
.eq(bswap(be_out
))
337 sync
+= self
.bus
.ack
.eq(wb_valid
)
339 # Register write machine
342 comb
+= be_in
.eq(bswap(self
.bus
.dat_w
))
344 with m
.If(wb_valid
& self
.bus
.we
):
345 with m
.If(reg_is_xive
):
346 # TODO: When adding support for other bits, make sure to
347 # properly implement self.bus.sel to allow partial writes.
348 sync
+= xives
[reg_idx
].pri
.eq(self
.prio_pack(be_in
[:8]))
349 #report "ICS irq " & integer'image(reg_idx) &
350 # " set to:" & to_hstring(be_in(7 downto 0));
353 # generate interrupt. This is a simple combinational process,
354 # potentially wasteful in HW for large number of interrupts.
356 # could be replaced with iterative state machines and a message
357 # system between ICSs' (plural) and ICP incl. reject etc...
359 sync
+= self
.icp_o
.eq(icp_o_next
)
361 max_idx
= Signal(log2_int(self
.SRC_NUM
))
362 max_pri
= Signal(self
.PRIO_BITS
)
364 # XXX FIXME: Use a tree (or examine each bit in turn)
365 comb
+= max_pri
.eq(self
.pri_masked
)
366 comb
+= max_idx
.eq(0)
367 for i
in range(self
.SRC_NUM
):
368 cur_idx
= Signal(log2_int(self
.SRC_NUM
), name
="cur_idx%d" % i
)
369 cur_pri
= Signal(self
.PRIO_BITS
, name
="cur_pri%d" % i
)
370 comb
+= cur_pri
.eq(max_pri
)
371 comb
+= cur_idx
.eq(max_idx
)
372 with m
.If(int_level_l
[i
] & self
.a_mf_b(xives
[i
].pri
, max_pri
)):
373 comb
+= cur_pri
.eq(xives
[i
].pri
)
374 comb
+= cur_idx
.eq(i
)
377 with m
.If(max_pri
!= self
.pri_masked
):
378 #report "MFI: " & integer'image(max_idx) &
379 #" pri=" & to_hstring(prio_unpack(max_pri));
381 comb
+= icp_o_next
.src
.eq(max_idx
)
382 comb
+= icp_o_next
.pri
.eq(self
.prio_unpack(max_pri
))
387 for field
in self
.bus
.fields
.values():
389 yield self
.int_level_i
390 yield from self
.icp_o
.ports()
397 def sim_xics_icp(dut
):
400 data
= yield from wb_read(dut
.bus
, MFRR
)
401 print ("mfrr", hex(data
), bin(data
))
402 assert (yield dut
.core_irq_o
) == 0
406 # read wb XIRR (8-bit)
407 data
= yield from wb_read(dut
.bus
, XIRR
, False)
408 print ("xirr", hex(data
), bin(data
))
409 assert (yield dut
.core_irq_o
) == 0
413 # read wb XIRR (32-bit)
414 data
= yield from wb_read(dut
.bus
, XIRR
)
415 print ("xirr", hex(data
), bin(data
))
416 assert (yield dut
.core_irq_o
) == 0
421 data
= yield from wb_read(dut
.bus
, XIRR_POLL
)
422 print ("xirr poll", hex(data
), bin(data
))
423 assert (yield dut
.core_irq_o
) == 0
426 # set dut src/pri to something, anything
428 yield dut
.ics_i
.src
.eq(9)
429 yield dut
.ics_i
.pri
.eq(0x1e)
432 data
= yield from wb_read(dut
.bus
, MFRR
)
433 print ("mfrr", hex(data
), bin(data
))
434 assert (yield dut
.core_irq_o
) == 0
438 # read wb XIRR (8-bit)
439 data
= yield from wb_read(dut
.bus
, XIRR
, False)
440 print ("xirr", hex(data
), bin(data
))
441 assert (yield dut
.core_irq_o
) == 0
445 # read wb XIRR (32-bit)
446 data
= yield from wb_read(dut
.bus
, XIRR
)
447 print ("xirr", hex(data
), bin(data
))
448 assert (yield dut
.core_irq_o
) == 0
453 data
= yield from wb_read(dut
.bus
, XIRR_POLL
)
454 print ("xirr poll", hex(data
), bin(data
))
455 assert (yield dut
.core_irq_o
) == 0
457 ######################
460 yield from wb_write(dut
.bus
, XIRR
, data
)
461 print ("xirr written", hex(data
), bin(data
))
463 assert (yield dut
.core_irq_o
) == 1 # ok *now* it should be set
466 data
= yield from wb_read(dut
.bus
, XIRR_POLL
, False)
467 print ("xirr poll", hex(data
), bin(data
))
468 assert (yield dut
.core_irq_o
) == 1 # should not clear
470 # read wb XIRR (8-bit)
471 data
= yield from wb_read(dut
.bus
, XIRR
, False)
472 print ("xirr", hex(data
), bin(data
))
473 assert (yield dut
.core_irq_o
) == 1 # should not clear
475 # read wb XIRR (32-bit)
476 data
= yield from wb_read(dut
.bus
, XIRR
)
477 print ("xirr", hex(data
), bin(data
))
479 assert (yield dut
.core_irq_o
) == 0
485 return int.from_bytes(x
.to_bytes(4, byteorder
='little'),
486 byteorder
='big', signed
=False)
488 def get_field(x
, wid
, shift
):
490 return x
& ((1<<wid
)-1)
493 def sim_xics(icp
, ics
):
496 data
= yield from wb_read(ics
.bus
, 0)
497 print ("config", hex(data
), bin(data
))
499 base
= get_field(data
, 24, 0)
500 pri
= get_field(data
, 8, 24)
501 print (" base", hex(base
))
502 print (" pri", hex(pri
))
510 data
= yield from wb_read(ics
.bus
, 0x800//4)
511 print ("xive0", hex(data
), bin(data
))
513 irq
= get_field(data
, 1, 31)
514 rsvd
= get_field(data
, 1, 30)
515 p
= get_field(data
, 1, 29)
516 q
= get_field(data
, 1, 28)
517 rsvd2
= get_field(data
, 8, 20)
518 target
= get_field(data
, 12, 8)
519 prio
= get_field(data
, 8, 0)
520 print(" irq", hex(irq
))
521 print(" rsvd", hex(rsvd
))
524 print(" rsvd2", hex(rsvd2
))
525 print(" target", hex(target
))
526 print(" prio", hex(prio
))
527 assert irq
== 0 # not active
530 assert target
== 0 # not implemented
536 # raise XIVE 1 (just for fun)
537 yield ics
.int_level_i
.eq(1<<1)
539 yield # wait for interrupt to propagate through from ics to icp...
542 data
= yield from wb_read(ics
.bus
, 0x804//4)
543 print ("xive1", hex(data
), bin(data
))
545 irq
= get_field(data
, 1, 31)
546 rsvd
= get_field(data
, 1, 30)
547 p
= get_field(data
, 1, 29)
548 q
= get_field(data
, 1, 28)
549 rsvd2
= get_field(data
, 8, 20)
550 target
= get_field(data
, 12, 8)
551 prio
= get_field(data
, 8, 0)
552 print(" irq", hex(irq
))
553 print(" rsvd", hex(rsvd
))
556 print(" rsvd2", hex(rsvd2
))
557 print(" target", hex(target
))
558 print(" prio", hex(prio
))
559 assert irq
== 1 # active!
562 assert target
== 0 # not implemented
568 # check that after setting IRQ 2 core is still 0 because priority is 0xff
569 assert (yield icp
.core_irq_o
) == 0
572 # set XIVE1 priority to 0xf0
574 yield from wb_write(ics
.bus
, 0x804//4, data
)
575 print ("XIVE1 priority written", hex(data
), bin(data
))
577 ######################
580 yield from wb_write(icp
.bus
, XIRR
, data
)
581 print ("xirr written", hex(data
), bin(data
))
583 assert (yield icp
.core_irq_o
) == 1 # ok *now* it should be set
585 # read wb XIRR (32-bit)
586 data
= yield from wb_read(icp
.bus
, XIRR
)
587 print ("xirr", hex(data
), bin(data
))
589 cppr
= get_field(data
, 8, 24)
590 xisr
= get_field(data
, 24, 0)
591 print(" cppr", hex(cppr
))
592 print(" xisr", hex(xisr
))
594 assert (yield icp
.core_irq_o
) == 0
603 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
604 with
open("test_xics_icp.il", "w") as f
:
608 m
.submodules
.xics_icp
= dut
613 sim
.add_sync_process(wrap(sim_xics_icp(dut
)))
614 sim_writer
= sim
.write_vcd('test_xics_icp.vcd')
621 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
622 with
open("test_xics_ics.il", "w") as f
:
625 #run_simulation(dut, ldst_sim(dut), vcd_name='test_ldst_regspec.vcd')
630 m
.submodules
.icp
= icp
= XICS_ICP()
631 m
.submodules
.ics
= ics
= XICS_ICS()
632 m
.d
.comb
+= icp
.ics_i
.eq(ics
.icp_o
)
634 vl
= rtlil
.convert(m
, ports
=icp
.ports()+ics
.ports())
635 with
open("test_xics.il", "w") as f
:
641 sim
.add_sync_process(wrap(sim_xics(icp
, ics
)))
642 sim_writer
= sim
.write_vcd('test_xics.vcd')
647 if __name__
== '__main__':