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
, Array
, Mux
21 from nmutil
.iocontrol
import RecordObject
22 from nmigen
.utils
import log2_int
23 from nmigen
.cli
import rtlil
24 from soc
.minerva
.wishbone
import make_wb_layout
25 from nmutil
.util
import wrap
26 from soc
.bus
.test
.wb_rw
import wb_read
, wb_write
31 from nmigen
.sim
.cxxsim
import Simulator
, Settle
33 from nmigen
.back
.pysim
import Simulator
, Settle
37 class ICS2ICP(RecordObject
):
39 # Level interrupts only, ICS just keeps prsenting the
40 # highest priority interrupt. Once handling edge, something
41 # smarter involving handshake & reject support will be needed
43 def __init__(self
, name
):
44 super().__init
__(name
=name
)
45 self
.src
= Signal(4, reset_less
=True)
46 self
.pri
= Signal(8, reset_less
=True)
48 # hardwire the hardware IRQ priority
49 HW_PRIORITY
= Const(0x80, 8)
51 # 8 bit offsets for each presentation - all addresses are in "words"
52 XIRR_POLL
= 0x00 # 0x000
58 class RegInternal(RecordObject
):
59 def __init__(self
, name
=None):
60 super().__init
__(name
=name
)
61 self
.xisr
= Signal(24)
63 self
.mfrr
= Signal(8, reset
=0xff) # mask everything on reset
65 self
.wb_rd_data
= Signal(32)
66 self
.wb_ack
= Signal(1)
70 return Cat(v
[24:32], v
[16:24], v
[8:16], v
[0:8])
73 class XICS_ICP(Elaboratable
):
81 self
.bus
= Record(make_wb_layout(spec
, cti
=False), name
="icp_wb")
82 self
.ics_i
= ICS2ICP("ics_i")
83 self
.core_irq_o
= Signal()
85 def elaborate(self
, platform
):
87 comb
, sync
= m
.d
.comb
, m
.d
.sync
90 r_next
= RegInternal()
93 # We delay core_irq_out by a cycle to help with timing
94 sync
+= self
.core_irq_o
.eq(r
.irq
)
96 comb
+= self
.bus
.ack
.eq(r
.wb_ack
& self
.bus
.cyc
)
97 with m
.If(self
.bus
.ack
):
98 comb
+= self
.bus
.dat_r
.eq(r
.wb_rd_data
)
101 xirr_accept_rd
= Signal()
106 pending_priority
= Signal(8)
109 comb
+= v
.eq(r
) # start from the register (r)
110 comb
+= v
.wb_ack
.eq(0)
112 comb
+= xirr_accept_rd
.eq(0)
114 comb
+= be_in
.eq(bswap(self
.bus
.dat_w
))
117 with m
.If(self
.bus
.cyc
& self
.bus
.stb
):
118 comb
+= v
.wb_ack
.eq(1) # always ack
119 with m
.If(self
.bus
.we
): # write
120 # writes to both XIRR are the same
121 with m
.Switch(self
.bus
.adr
[:6]):
122 with m
.Case(XIRR_POLL
):
123 # report "ICP XIRR_POLL write";
124 comb
+= v
.cppr
.eq(be_in
[24:32])
126 comb
+= v
.cppr
.eq(be_in
[24:32])
127 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
128 #report "ICP XIRR write word (EOI) :" & \
131 with m
.Elif(self
.bus
.sel
== 0x1): # 1 byte
132 #report "ICP XIRR write byte (CPPR):" & \
133 #to_hstring(be_in(31 downto 24));
136 #report "ICP XIRR UNSUPPORTED write ! sel=" & \
137 # to_hstring(self.bus.sel);
140 comb
+= v
.mfrr
.eq(be_in
[24:32])
141 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
142 # report "ICP MFRR write word:" & to_hstring(be_in);
144 with m
.Elif(self
.bus
.sel
== 0x1): # 1 byte
145 # report "ICP MFRR write byte:" & \
146 # to_hstring(be_in(31 downto 24));
149 # report "ICP MFRR UNSUPPORTED write ! sel=" & \
150 # to_hstring(self.bus.sel);
153 with m
.Else(): # read
155 with m
.Switch(self
.bus
.adr
[:6]):
156 with m
.Case(XIRR_POLL
):
157 # report "ICP XIRR_POLL read";
158 comb
+= be_out
.eq(Cat(r
.xisr
, r
.cppr
))
160 # report "ICP XIRR read";
161 comb
+= be_out
.eq(Cat(r
.xisr
, r
.cppr
))
162 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
163 comb
+= xirr_accept_rd
.eq(1)
165 # report "ICP MFRR read";
166 comb
+= be_out
[24:32].eq(r
.mfrr
)
168 comb
+= pending_priority
.eq(0xff)
169 comb
+= v
.xisr
.eq(0x0)
170 comb
+= v
.irq
.eq(0x0)
173 with m
.If(self
.ics_i
.pri
!= 0xff):
174 comb
+= v
.xisr
.eq(Cat(self
.ics_i
.src
, Const(0x00001, 20)))
175 comb
+= pending_priority
.eq(self
.ics_i
.pri
)
178 with m
.If(r
.mfrr
< pending_priority
):
179 comb
+= v
.xisr
.eq(Const(0x2, 24)) # special XICS MFRR IRQ src num
180 comb
+= min_pri
.eq(r
.mfrr
)
182 comb
+= min_pri
.eq(pending_priority
)
184 # Accept the interrupt
185 with m
.If(xirr_accept_rd
):
186 #report "XICS: ICP ACCEPT" &
187 # " cppr:" & to_hstring(r.cppr) &
188 # " xisr:" & to_hstring(r.xisr) &
189 # " mfrr:" & to_hstring(r.mfrr);
190 comb
+= v
.cppr
.eq(min_pri
)
192 comb
+= v
.wb_rd_data
.eq(bswap(be_out
))
194 # check if the core needs an interrupt notification (or clearing)
195 with m
.If(min_pri
< v
.cppr
):
209 for field
in self
.bus
.fields
.values():
211 yield from self
.ics_i
212 yield self
.core_irq_o
218 class Xive(RecordObject
):
219 def __init__(self
, name
, wid
, rst
):
220 super().__init
__(name
=name
)
221 self
.pri
= Signal(wid
, reset
=rst
)
225 class XICS_ICS(Elaboratable
):
226 def __init__(self
, SRC_NUM
=16, PRIO_BITS
=8):
227 self
.SRC_NUM
= SRC_NUM
228 self
.PRIO_BITS
= PRIO_BITS
229 self
.pri_masked
= (1<<self
.PRIO_BITS
)-1
235 self
.bus
= Record(make_wb_layout(spec
, cti
=False), name
="ics_wb")
237 self
.int_level_i
= Signal(SRC_NUM
)
238 self
.icp_o
= ICS2ICP("icp_o")
240 def prio_pack(self
, pri8
):
241 return pri8
[:self
.PRIO_BITS
]
243 def prio_unpack(self
, pri
):
244 return Mux(pri
== self
.pri_masked
, Const(0xff, 8), pri
[:self
.PRIO_BITS
])
246 # A more favored than b ?
247 def a_mf_b(self
, a
, b
):
248 #report "a_mf_b a=" & to_hstring(a) &
249 # " b=" & to_hstring(b) &
250 # " r=" & boolean'image(a < b);
253 def elaborate(self
, platform
):
255 comb
, sync
= m
.d
.comb
, m
.d
.sync
257 xives
= Array([Xive("xive%d" % i
, self
.PRIO_BITS
, self
.pri_masked
)
258 for i
in range(self
.SRC_NUM
)])
261 reg_idx
= Signal(log2_int(self
.SRC_NUM
))
262 icp_o_next
= ICS2ICP("icp_r")
263 int_level_l
= Signal(self
.SRC_NUM
)
267 # 4 : Debug/diagnostics
271 # Config register format:
273 # 23.. 0 : Interrupt base (hard wired to 16)
274 # 27.. 24 : #prio bits (1..8)
276 # XIVE register format:
278 # 31 : input bit (reflects interrupt input)
280 # 29 : P (mirrors input for now)
281 # 28 : Q (not implemented in this version)
283 # 19 .. 8 : target (not implemented in this version)
286 reg_is_xive
= Signal()
287 reg_is_config
= Signal()
288 reg_is_debug
= Signal()
290 assert self
.SRC_NUM
== 16, "Fixup address decode with log2"
292 comb
+= reg_is_xive
.eq(self
.bus
.adr
[9])
293 comb
+= reg_is_config
.eq(self
.bus
.adr
[0:10] == 0x0)
294 comb
+= reg_is_debug
.eq(self
.bus
.adr
[0:10] == 0x4)
296 # Register index XX FIXME: figure out bits from SRC_NUM
297 comb
+= reg_idx
.eq(self
.bus
.adr
[:4])
299 # Latch interrupt inputs for timing
300 sync
+= int_level_l
.eq(self
.int_level_i
)
302 # We don't stall. Acks are sent by the read machine one cycle
303 # after a request, but we can handle one access per cycle.
304 comb
+= wb_valid
.eq(self
.bus
.cyc
& self
.bus
.stb
)
306 # Big read mux. This could be replaced by a slower state
307 # machine iterating registers instead if timing gets tight.
312 with m
.If(reg_is_xive
):
313 pri_i
= self
.prio_unpack(xives
[reg_idx
].pri
)
315 comb
+= ibit
.eq(int_level_l
.bit_select(reg_idx
, 1))
316 comb
+= be_out
.eq(Cat(pri_i
, # bits 0..7
323 with m
.Elif(reg_is_config
):
324 comb
+= be_out
.eq(Cat(Const(self
.SRC_NUM
, 24), # 0-23
325 Const(self
.PRIO_BITS
, 4), # 24-27
326 Const(0, 4))) # 28-31
328 with m
.Elif(reg_is_debug
):
329 comb
+= be_out
.eq(Cat(icp_o_next
.pri
, # 0-7
331 icp_o_next
.src
)) # 28-31
333 sync
+= self
.bus
.dat_r
.eq(bswap(be_out
))
334 sync
+= self
.bus
.ack
.eq(wb_valid
)
336 # Register write machine
339 comb
+= be_in
.eq(bswap(self
.bus
.dat_w
))
341 with m
.If(wb_valid
& self
.bus
.we
):
342 with m
.If(reg_is_xive
):
343 # TODO: When adding support for other bits, make sure to
344 # properly implement self.bus.sel to allow partial writes.
345 sync
+= xives
[reg_idx
].pri
.eq(self
.prio_pack(be_in
[:8]))
346 #report "ICS irq " & integer'image(reg_idx) &
347 # " set to:" & to_hstring(be_in(7 downto 0));
350 # generate interrupt. This is a simple combinational process,
351 # potentially wasteful in HW for large number of interrupts.
353 # could be replaced with iterative state machines and a message
354 # system between ICSs' (plural) and ICP incl. reject etc...
356 sync
+= self
.icp_o
.eq(icp_o_next
)
358 max_idx
= Signal(log2_int(self
.SRC_NUM
))
359 max_pri
= Signal(self
.PRIO_BITS
)
361 # XXX FIXME: Use a tree (or examine each bit in turn)
362 comb
+= max_pri
.eq(self
.pri_masked
)
363 comb
+= max_idx
.eq(0)
364 for i
in range(self
.SRC_NUM
):
365 cur_idx
= Signal(log2_int(self
.SRC_NUM
), name
="cur_idx%d" % i
)
366 cur_pri
= Signal(self
.PRIO_BITS
, name
="cur_pri%d" % i
)
367 comb
+= cur_pri
.eq(max_pri
)
368 comb
+= cur_idx
.eq(max_idx
)
369 with m
.If(int_level_l
[i
] & self
.a_mf_b(xives
[i
].pri
, max_pri
)):
370 comb
+= cur_pri
.eq(xives
[i
].pri
)
371 comb
+= cur_idx
.eq(i
)
374 with m
.If(max_pri
!= self
.pri_masked
):
375 #report "MFI: " & integer'image(max_idx) &
376 #" pri=" & to_hstring(prio_unpack(max_pri));
378 comb
+= icp_o_next
.src
.eq(max_idx
)
379 comb
+= icp_o_next
.pri
.eq(self
.prio_unpack(max_pri
))
384 for field
in self
.bus
.fields
.values():
386 yield self
.int_level_i
387 yield from self
.icp_o
.ports()
394 def sim_xics_icp(dut
):
397 data
= yield from wb_read(dut
.bus
, MFRR
)
398 print ("mfrr", hex(data
), bin(data
))
399 assert (yield dut
.core_irq_o
) == 0
403 # read wb XIRR (8-bit)
404 data
= yield from wb_read(dut
.bus
, XIRR
, False)
405 print ("xirr", hex(data
), bin(data
))
406 assert (yield dut
.core_irq_o
) == 0
410 # read wb XIRR (32-bit)
411 data
= yield from wb_read(dut
.bus
, XIRR
)
412 print ("xirr", hex(data
), bin(data
))
413 assert (yield dut
.core_irq_o
) == 0
418 data
= yield from wb_read(dut
.bus
, XIRR_POLL
)
419 print ("xirr poll", hex(data
), bin(data
))
420 assert (yield dut
.core_irq_o
) == 0
423 # set dut src/pri to something, anything
425 yield dut
.ics_i
.src
.eq(9)
426 yield dut
.ics_i
.pri
.eq(0x1e)
429 data
= yield from wb_read(dut
.bus
, MFRR
)
430 print ("mfrr", hex(data
), bin(data
))
431 assert (yield dut
.core_irq_o
) == 0
435 # read wb XIRR (8-bit)
436 data
= yield from wb_read(dut
.bus
, XIRR
, False)
437 print ("xirr", hex(data
), bin(data
))
438 assert (yield dut
.core_irq_o
) == 0
442 # read wb XIRR (32-bit)
443 data
= yield from wb_read(dut
.bus
, XIRR
)
444 print ("xirr", hex(data
), bin(data
))
445 assert (yield dut
.core_irq_o
) == 0
450 data
= yield from wb_read(dut
.bus
, XIRR_POLL
)
451 print ("xirr poll", hex(data
), bin(data
))
452 assert (yield dut
.core_irq_o
) == 0
454 ######################
457 yield from wb_write(dut
.bus
, XIRR
, data
)
458 print ("xirr written", hex(data
), bin(data
))
460 assert (yield dut
.core_irq_o
) == 1 # ok *now* it should be set
463 data
= yield from wb_read(dut
.bus
, XIRR_POLL
, False)
464 print ("xirr poll", hex(data
), bin(data
))
465 assert (yield dut
.core_irq_o
) == 1 # should not clear
467 # read wb XIRR (8-bit)
468 data
= yield from wb_read(dut
.bus
, XIRR
, False)
469 print ("xirr", hex(data
), bin(data
))
470 assert (yield dut
.core_irq_o
) == 1 # should not clear
472 # read wb XIRR (32-bit)
473 data
= yield from wb_read(dut
.bus
, XIRR
)
474 print ("xirr", hex(data
), bin(data
))
476 assert (yield dut
.core_irq_o
) == 0
482 return int.from_bytes(x
.to_bytes(4, byteorder
='little'),
483 byteorder
='big', signed
=False)
485 def get_field(x
, wid
, shift
):
487 return x
& ((1<<wid
)-1)
490 def sim_xics(icp
, ics
):
493 data
= yield from wb_read(ics
.bus
, 0)
494 print ("config", hex(data
), bin(data
))
496 base
= get_field(data
, 24, 0)
497 pri
= get_field(data
, 8, 24)
498 print (" base", hex(base
))
499 print (" pri", hex(pri
))
507 data
= yield from wb_read(ics
.bus
, 0x800//4)
508 print ("xive0", hex(data
), bin(data
))
510 irq
= get_field(data
, 1, 31)
511 rsvd
= get_field(data
, 1, 30)
512 p
= get_field(data
, 1, 29)
513 q
= get_field(data
, 1, 28)
514 rsvd2
= get_field(data
, 8, 20)
515 target
= get_field(data
, 12, 8)
516 prio
= get_field(data
, 8, 0)
517 print(" irq", hex(irq
))
518 print(" rsvd", hex(rsvd
))
521 print(" rsvd2", hex(rsvd2
))
522 print(" target", hex(target
))
523 print(" prio", hex(prio
))
524 assert irq
== 0 # not active
527 assert target
== 0 # not implemented
533 # raise XIVE 1 (just for fun)
534 yield ics
.int_level_i
.eq(1<<1)
536 yield # wait for interrupt to propagate through from ics to icp...
539 data
= yield from wb_read(ics
.bus
, 0x804//4)
540 print ("xive1", hex(data
), bin(data
))
542 irq
= get_field(data
, 1, 31)
543 rsvd
= get_field(data
, 1, 30)
544 p
= get_field(data
, 1, 29)
545 q
= get_field(data
, 1, 28)
546 rsvd2
= get_field(data
, 8, 20)
547 target
= get_field(data
, 12, 8)
548 prio
= get_field(data
, 8, 0)
549 print(" irq", hex(irq
))
550 print(" rsvd", hex(rsvd
))
553 print(" rsvd2", hex(rsvd2
))
554 print(" target", hex(target
))
555 print(" prio", hex(prio
))
556 assert irq
== 1 # active!
559 assert target
== 0 # not implemented
565 # check that after setting IRQ 2 core is still 0 because priority is 0xff
566 assert (yield icp
.core_irq_o
) == 0
569 # set XIVE1 priority to 0xf0
571 yield from wb_write(ics
.bus
, 0x804//4, data
)
572 print ("XIVE1 priority written", hex(data
), bin(data
))
574 ######################
577 yield from wb_write(icp
.bus
, XIRR
, data
)
578 print ("xirr written", hex(data
), bin(data
))
580 assert (yield icp
.core_irq_o
) == 1 # ok *now* it should be set
582 # read wb XIRR (32-bit)
583 data
= yield from wb_read(icp
.bus
, XIRR
)
584 print ("xirr", hex(data
), bin(data
))
586 cppr
= get_field(data
, 8, 24)
587 xisr
= get_field(data
, 24, 0)
588 print(" cppr", hex(cppr
))
589 print(" xisr", hex(xisr
))
591 assert (yield icp
.core_irq_o
) == 0
600 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
601 with
open("test_xics_icp.il", "w") as f
:
605 m
.submodules
.xics_icp
= dut
610 sim
.add_sync_process(wrap(sim_xics_icp(dut
)))
611 sim_writer
= sim
.write_vcd('test_xics_icp.vcd')
618 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
619 with
open("test_xics_ics.il", "w") as f
:
622 #run_simulation(dut, ldst_sim(dut), vcd_name='test_ldst_regspec.vcd')
627 m
.submodules
.icp
= icp
= XICS_ICP()
628 m
.submodules
.ics
= ics
= XICS_ICS()
629 m
.d
.comb
+= icp
.ics_i
.eq(ics
.icp_o
)
631 vl
= rtlil
.convert(m
, ports
=icp
.ports()+ics
.ports())
632 with
open("test_xics.il", "w") as f
:
638 sim
.add_sync_process(wrap(sim_xics(icp
, ics
)))
639 sim_writer
= sim
.write_vcd('test_xics.vcd')
644 if __name__
== '__main__':