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
), 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
.dat_r
.eq(r
.wb_rd_data
)
97 comb
+= self
.bus
.ack
.eq(r
.wb_ack
)
100 xirr_accept_rd
= Signal()
105 pending_priority
= Signal(8)
108 comb
+= v
.eq(r
) # start from the register (r)
109 comb
+= v
.wb_ack
.eq(0)
111 comb
+= xirr_accept_rd
.eq(0)
113 comb
+= be_in
.eq(bswap(self
.bus
.dat_w
))
116 with m
.If(self
.bus
.cyc
& self
.bus
.stb
):
117 comb
+= v
.wb_ack
.eq(1) # always ack
118 with m
.If(self
.bus
.we
): # write
119 # writes to both XIRR are the same
120 with m
.Switch(self
.bus
.adr
[:8]):
121 with m
.Case(XIRR_POLL
):
122 # report "ICP XIRR_POLL write";
123 comb
+= v
.cppr
.eq(be_in
[24:32])
125 comb
+= v
.cppr
.eq(be_in
[24:32])
126 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
127 #report "ICP XIRR write word (EOI) :" & \
130 with m
.Elif(self
.bus
.sel
== 0x1): # 1 byte
131 #report "ICP XIRR write byte (CPPR):" & \
132 #to_hstring(be_in(31 downto 24));
135 #report "ICP XIRR UNSUPPORTED write ! sel=" & \
136 # to_hstring(self.bus.sel);
139 comb
+= v
.mfrr
.eq(be_in
[24:32])
140 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
141 # report "ICP MFRR write word:" & to_hstring(be_in);
143 with m
.Elif(self
.bus
.sel
== 0x1): # 1 byte
144 # report "ICP MFRR write byte:" & \
145 # to_hstring(be_in(31 downto 24));
148 # report "ICP MFRR UNSUPPORTED write ! sel=" & \
149 # to_hstring(self.bus.sel);
152 with m
.Else(): # read
154 with m
.Switch(self
.bus
.adr
[:8]):
155 with m
.Case(XIRR_POLL
):
156 # report "ICP XIRR_POLL read";
157 comb
+= be_out
.eq(r
.xisr
& r
.cppr
)
159 # report "ICP XIRR read";
160 comb
+= be_out
.eq(Cat(r
.xisr
, r
.cppr
))
161 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
162 comb
+= xirr_accept_rd
.eq(1)
164 # report "ICP MFRR read";
165 comb
+= be_out
.eq(r
.mfrr
)
167 comb
+= pending_priority
.eq(0xff)
168 comb
+= v
.xisr
.eq(0x0)
169 comb
+= v
.irq
.eq(0x0)
172 with m
.If(self
.ics_i
.pri
!= 0xff):
173 comb
+= v
.xisr
.eq(Cat(self
.ics_i
.src
, Const(0x00001, 20)))
174 comb
+= pending_priority
.eq(self
.ics_i
.pri
)
177 with m
.If(r
.mfrr
< pending_priority
):
178 comb
+= v
.xisr
.eq(Const(0x2)) # special XICS MFRR IRQ source number
179 comb
+= min_pri
.eq(r
.mfrr
)
181 comb
+= min_pri
.eq(pending_priority
)
183 # Accept the interrupt
184 with m
.If(xirr_accept_rd
):
185 #report "XICS: ICP ACCEPT" &
186 # " cppr:" & to_hstring(r.cppr) &
187 # " xisr:" & to_hstring(r.xisr) &
188 # " mfrr:" & to_hstring(r.mfrr);
189 comb
+= v
.cppr
.eq(min_pri
)
191 comb
+= v
.wb_rd_data
.eq(bswap(be_out
))
193 # check if the core needs an interrupt notification (or clearing)
194 comb
+= v
.irq
.eq(min_pri
< v
.cppr
)
208 for field
in self
.bus
.fields
.values():
210 yield from self
.ics_i
211 yield self
.core_irq_o
217 class Xive(RecordObject
):
218 def __init__(self
, name
, wid
, rst
):
219 super().__init
__(name
=name
)
220 self
.pri
= Signal(wid
, reset
=rst
)
224 class XICS_ICS(Elaboratable
):
225 def __init__(self
, SRC_NUM
=16, PRIO_BITS
=8):
226 self
.SRC_NUM
= SRC_NUM
227 self
.PRIO_BITS
= PRIO_BITS
228 self
.pri_masked
= (1<<self
.PRIO_BITS
)-1
234 self
.bus
= Record(make_wb_layout(spec
), name
="ics_wb")
236 self
.int_level_i
= Signal(SRC_NUM
)
237 self
.icp_o
= ICS2ICP("icp_o")
239 def prio_pack(self
, pri8
):
240 return pri8
[:self
.PRIO_BITS
]
242 def prio_unpack(self
, pri
):
243 return Mux(pri
== self
.pri_masked
, Const(0xff, 8), pri
[:self
.PRIO_BITS
])
245 # A more favored than b ?
246 def a_mf_b(self
, a
, b
):
247 #report "a_mf_b a=" & to_hstring(a) &
248 # " b=" & to_hstring(b) &
249 # " r=" & boolean'image(a < b);
252 def elaborate(self
, platform
):
254 comb
, sync
= m
.d
.comb
, m
.d
.sync
256 xives
= Array([Xive("xive%d" % i
, self
.PRIO_BITS
, self
.pri_masked
)
257 for i
in range(self
.SRC_NUM
)])
260 reg_idx
= Signal(log2_int(self
.SRC_NUM
))
261 icp_o_next
= ICS2ICP("icp_r")
262 int_level_l
= Signal(self
.SRC_NUM
)
266 # 4 : Debug/diagnostics
270 # Config register format:
272 # 23.. 0 : Interrupt base (hard wired to 16)
273 # 27.. 24 : #prio bits (1..8)
275 # XIVE register format:
277 # 31 : input bit (reflects interrupt input)
279 # 29 : P (mirrors input for now)
280 # 28 : Q (not implemented in this version)
282 # 19 .. 8 : target (not implemented in this version)
285 reg_is_xive
= Signal()
286 reg_is_config
= Signal()
287 reg_is_debug
= Signal()
289 assert self
.SRC_NUM
== 16, "Fixup address decode with log2"
291 comb
+= reg_is_xive
.eq(self
.bus
.adr
[9])
292 comb
+= reg_is_config
.eq(self
.bus
.adr
[0:10] == 0x0)
293 comb
+= reg_is_debug
.eq(self
.bus
.adr
[0:10] == 0x4)
295 # Register index XX FIXME: figure out bits from SRC_NUM
296 comb
+= reg_idx
.eq(self
.bus
.adr
[:4])
298 # Latch interrupt inputs for timing
299 sync
+= int_level_l
.eq(self
.int_level_i
)
301 # We don't stall. Acks are sent by the read machine one cycle
302 # after a request, but we can handle one access per cycle.
303 comb
+= wb_valid
.eq(self
.bus
.cyc
& self
.bus
.stb
)
305 # Big read mux. This could be replaced by a slower state
306 # machine iterating registers instead if timing gets tight.
311 with m
.If(reg_is_xive
):
312 pri_i
= self
.prio_unpack(xives
[reg_idx
].pri
)
314 comb
+= ibit
.eq(int_level_l
.bit_select(reg_idx
, 1))
315 comb
+= be_out
.eq(Cat(pri_i
, # bits 0..7
322 with m
.Elif(reg_is_config
):
323 comb
+= be_out
.eq(Cat(Const(self
.SRC_NUM
, 24), # 0-23
324 Const(self
.PRIO_BITS
, 4), # 24-27
325 Const(0, 4))) # 28-31
327 with m
.Elif(reg_is_debug
):
328 comb
+= be_out
.eq(Cat(icp_o_next
.pri
, # 0-7
330 icp_o_next
.src
)) # 28-31
332 sync
+= self
.bus
.dat_r
.eq(bswap(be_out
))
333 sync
+= self
.bus
.ack
.eq(wb_valid
)
335 # Register write machine
338 comb
+= be_in
.eq(bswap(self
.bus
.dat_w
))
340 with m
.If(wb_valid
& self
.bus
.we
):
341 with m
.If(reg_is_xive
):
342 # TODO: When adding support for other bits, make sure to
343 # properly implement self.bus.sel to allow partial writes.
344 sync
+= xives
[reg_idx
].pri
.eq(self
.prio_pack(be_in
[:8]))
345 #report "ICS irq " & integer'image(reg_idx) &
346 # " set to:" & to_hstring(be_in(7 downto 0));
349 # generate interrupt. This is a simple combinational process,
350 # potentially wasteful in HW for large number of interrupts.
352 # could be replaced with iterative state machines and a message
353 # system between ICSs' (plural) and ICP incl. reject etc...
355 sync
+= self
.icp_o
.eq(icp_o_next
)
357 max_idx
= Signal(log2_int(self
.SRC_NUM
))
358 max_pri
= Signal(self
.PRIO_BITS
)
360 # XXX FIXME: Use a tree (or examine each bit in turn)
361 comb
+= max_pri
.eq(self
.pri_masked
)
362 comb
+= max_idx
.eq(0)
363 for i
in range(self
.SRC_NUM
):
364 cur_idx
= Signal(log2_int(self
.SRC_NUM
), name
="cur_idx%d" % i
)
365 cur_pri
= Signal(self
.PRIO_BITS
, name
="cur_pri%d" % i
)
366 comb
+= cur_pri
.eq(max_pri
)
367 comb
+= cur_idx
.eq(max_idx
)
368 with m
.If(int_level_l
[i
] & self
.a_mf_b(xives
[i
].pri
, max_pri
)):
369 comb
+= cur_pri
.eq(xives
[i
].pri
)
370 comb
+= cur_idx
.eq(i
)
373 with m
.If(max_pri
!= self
.pri_masked
):
374 #report "MFI: " & integer'image(max_idx) &
375 #" pri=" & to_hstring(prio_unpack(max_pri));
377 comb
+= icp_o_next
.src
.eq(max_idx
)
378 comb
+= icp_o_next
.pri
.eq(self
.prio_unpack(max_pri
))
383 for field
in self
.bus
.fields
.values():
385 yield self
.int_level_i
386 yield from self
.icp_o
.ports()
393 def sim_xics_icp(dut
):
396 data
= yield from wb_read(dut
.bus
, MFRR
)
397 print ("mfrr", hex(data
), bin(data
))
398 assert (yield dut
.core_irq_o
) == 0
402 # read wb XIRR (8-bit)
403 data
= yield from wb_read(dut
.bus
, XIRR
, False)
404 print ("xirr", hex(data
), bin(data
))
405 assert (yield dut
.core_irq_o
) == 0
409 # read wb XIRR (32-bit)
410 data
= yield from wb_read(dut
.bus
, XIRR
)
411 print ("xirr", hex(data
), bin(data
))
412 assert (yield dut
.core_irq_o
) == 0
417 data
= yield from wb_read(dut
.bus
, XIRR_POLL
)
418 print ("xirr poll", hex(data
), bin(data
))
419 assert (yield dut
.core_irq_o
) == 0
422 # set dut src/pri to something, anything
424 yield dut
.ics_i
.src
.eq(9)
425 yield dut
.ics_i
.pri
.eq(0x1e)
428 data
= yield from wb_read(dut
.bus
, MFRR
)
429 print ("mfrr", hex(data
), bin(data
))
430 assert (yield dut
.core_irq_o
) == 0
434 # read wb XIRR (8-bit)
435 data
= yield from wb_read(dut
.bus
, XIRR
, False)
436 print ("xirr", hex(data
), bin(data
))
437 assert (yield dut
.core_irq_o
) == 0
441 # read wb XIRR (32-bit)
442 data
= yield from wb_read(dut
.bus
, XIRR
)
443 print ("xirr", hex(data
), bin(data
))
444 assert (yield dut
.core_irq_o
) == 0
449 data
= yield from wb_read(dut
.bus
, XIRR_POLL
)
450 print ("xirr poll", hex(data
), bin(data
))
451 assert (yield dut
.core_irq_o
) == 0
453 ######################
456 yield from wb_write(dut
.bus
, XIRR
, data
)
457 print ("xirr written", hex(data
), bin(data
))
459 assert (yield dut
.core_irq_o
) == 1 # ok *now* it should be set
462 data
= yield from wb_read(dut
.bus
, XIRR_POLL
, False)
463 print ("xirr poll", hex(data
), bin(data
))
464 assert (yield dut
.core_irq_o
) == 1 # should not clear
466 # read wb XIRR (8-bit)
467 data
= yield from wb_read(dut
.bus
, XIRR
, False)
468 print ("xirr", hex(data
), bin(data
))
469 assert (yield dut
.core_irq_o
) == 1 # should not clear
471 # read wb XIRR (32-bit)
472 data
= yield from wb_read(dut
.bus
, XIRR
)
473 print ("xirr", hex(data
), bin(data
))
475 assert (yield dut
.core_irq_o
) == 0
481 return int.from_bytes(x
.to_bytes(4, byteorder
='little'),
482 byteorder
='big', signed
=False)
484 def get_field(x
, wid
, shift
):
486 return x
& ((1<<wid
)-1)
489 def sim_xics(icp
, ics
):
492 data
= yield from wb_read(ics
.bus
, 0)
493 print ("config", hex(data
), bin(data
))
495 base
= get_field(data
, 24, 0)
496 pri
= get_field(data
, 8, 24)
497 print (" base", hex(base
))
498 print (" pri", hex(pri
))
506 data
= yield from wb_read(ics
.bus
, 0x800//4)
507 print ("xive0", hex(data
), bin(data
))
509 irq
= get_field(data
, 1, 31)
510 rsvd
= get_field(data
, 1, 30)
511 p
= get_field(data
, 1, 29)
512 q
= get_field(data
, 1, 28)
513 rsvd2
= get_field(data
, 8, 20)
514 target
= get_field(data
, 12, 8)
515 prio
= get_field(data
, 8, 0)
516 print(" irq", hex(irq
))
517 print(" rsvd", hex(rsvd
))
520 print(" rsvd2", hex(rsvd2
))
521 print(" target", hex(target
))
522 print(" prio", hex(prio
))
523 assert irq
== 0 # not active
526 assert target
== 0 # not implemented
532 # raise XIVE 1 (just for fun)
533 yield ics
.int_level_i
.eq(1<<1)
535 yield # wait for interrupt to propagate through from ics to icp...
538 data
= yield from wb_read(ics
.bus
, 0x804//4)
539 print ("xive1", hex(data
), bin(data
))
541 irq
= get_field(data
, 1, 31)
542 rsvd
= get_field(data
, 1, 30)
543 p
= get_field(data
, 1, 29)
544 q
= get_field(data
, 1, 28)
545 rsvd2
= get_field(data
, 8, 20)
546 target
= get_field(data
, 12, 8)
547 prio
= get_field(data
, 8, 0)
548 print(" irq", hex(irq
))
549 print(" rsvd", hex(rsvd
))
552 print(" rsvd2", hex(rsvd2
))
553 print(" target", hex(target
))
554 print(" prio", hex(prio
))
555 assert irq
== 1 # active!
558 assert target
== 0 # not implemented
564 # check that after setting IRQ 2 core is still 0 because priority is 0xff
565 assert (yield icp
.core_irq_o
) == 0
568 # set XIVE1 priority to 0xf0
570 yield from wb_write(ics
.bus
, 0x804//4, data
)
571 print ("XIVE1 priority written", hex(data
), bin(data
))
573 ######################
576 yield from wb_write(icp
.bus
, XIRR
, data
)
577 print ("xirr written", hex(data
), bin(data
))
579 assert (yield icp
.core_irq_o
) == 1 # ok *now* it should be set
581 # read wb XIRR (32-bit)
582 data
= yield from wb_read(icp
.bus
, XIRR
)
583 print ("xirr", hex(data
), bin(data
))
585 cppr
= get_field(data
, 8, 24)
586 xisr
= get_field(data
, 24, 0)
587 print(" cppr", hex(cppr
))
588 print(" xisr", hex(xisr
))
590 assert (yield icp
.core_irq_o
) == 0
599 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
600 with
open("test_xics_icp.il", "w") as f
:
604 m
.submodules
.xics_icp
= dut
609 sim
.add_sync_process(wrap(sim_xics_icp(dut
)))
610 sim_writer
= sim
.write_vcd('test_xics_icp.vcd')
617 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
618 with
open("test_xics_ics.il", "w") as f
:
621 #run_simulation(dut, ldst_sim(dut), vcd_name='test_ldst_regspec.vcd')
626 m
.submodules
.icp
= icp
= XICS_ICP()
627 m
.submodules
.ics
= ics
= XICS_ICS()
628 m
.d
.comb
+= icp
.ics_i
.eq(ics
.icp_o
)
630 vl
= rtlil
.convert(m
, ports
=icp
.ports()+ics
.ports())
631 with
open("test_xics.il", "w") as f
:
637 sim
.add_sync_process(wrap(sim_xics(icp
, ics
)))
638 sim_writer
= sim
.write_vcd('test_xics.vcd')
643 if __name__
== '__main__':