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
):
78 def __init__(self
, spec
=None):
85 self
.bus
= Record(make_wb_layout(spec
, cti
=False), name
="icp_wb")
86 self
.ics_i
= ICS2ICP("ics_i")
87 self
.core_irq_o
= Signal()
89 def elaborate(self
, platform
):
91 comb
, sync
= m
.d
.comb
, m
.d
.sync
94 r_next
= RegInternal()
97 # We delay core_irq_out by a cycle to help with timing
98 sync
+= self
.core_irq_o
.eq(r
.irq
)
100 comb
+= self
.bus
.ack
.eq(r
.wb_ack
& self
.bus
.cyc
)
101 with m
.If(self
.bus
.ack
):
102 comb
+= self
.bus
.dat_r
.eq(r
.wb_rd_data
)
105 xirr_accept_rd
= Signal()
110 pending_priority
= Signal(8)
113 comb
+= v
.eq(r
) # start from the register (r)
114 comb
+= v
.wb_ack
.eq(0)
116 comb
+= xirr_accept_rd
.eq(0)
118 comb
+= be_in
.eq(bswap(self
.bus
.dat_w
))
121 with m
.If(self
.bus
.cyc
& self
.bus
.stb
):
122 comb
+= v
.wb_ack
.eq(1) # always ack
123 with m
.If(self
.bus
.we
): # write
124 # writes to both XIRR are the same
125 with m
.Switch(self
.bus
.adr
[:6]):
126 with m
.Case(XIRR_POLL
):
127 # report "ICP XIRR_POLL write";
128 comb
+= v
.cppr
.eq(be_in
[24:32])
130 comb
+= v
.cppr
.eq(be_in
[24:32])
131 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
132 #report "ICP XIRR write word (EOI) :" & \
135 with m
.Elif(self
.bus
.sel
== 0x1): # 1 byte
136 #report "ICP XIRR write byte (CPPR):" & \
137 #to_hstring(be_in(31 downto 24));
140 #report "ICP XIRR UNSUPPORTED write ! sel=" & \
141 # to_hstring(self.bus.sel);
144 comb
+= v
.mfrr
.eq(be_in
[24:32])
145 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
146 # report "ICP MFRR write word:" & to_hstring(be_in);
148 with m
.Elif(self
.bus
.sel
== 0x1): # 1 byte
149 # report "ICP MFRR write byte:" & \
150 # to_hstring(be_in(31 downto 24));
153 # report "ICP MFRR UNSUPPORTED write ! sel=" & \
154 # to_hstring(self.bus.sel);
157 with m
.Else(): # read
159 with m
.Switch(self
.bus
.adr
[:6]):
160 with m
.Case(XIRR_POLL
):
161 # report "ICP XIRR_POLL read";
162 comb
+= be_out
.eq(Cat(r
.xisr
, r
.cppr
))
164 # report "ICP XIRR read";
165 comb
+= be_out
.eq(Cat(r
.xisr
, r
.cppr
))
166 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
167 comb
+= xirr_accept_rd
.eq(1)
169 # report "ICP MFRR read";
170 comb
+= be_out
[24:32].eq(r
.mfrr
)
172 comb
+= pending_priority
.eq(0xff)
173 comb
+= v
.xisr
.eq(0x0)
174 comb
+= v
.irq
.eq(0x0)
177 with m
.If(self
.ics_i
.pri
!= 0xff):
178 comb
+= v
.xisr
.eq(Cat(self
.ics_i
.src
, Const(0x00001, 20)))
179 comb
+= pending_priority
.eq(self
.ics_i
.pri
)
182 with m
.If(r
.mfrr
< pending_priority
):
183 comb
+= v
.xisr
.eq(Const(0x2, 24)) # special XICS MFRR IRQ src num
184 comb
+= min_pri
.eq(r
.mfrr
)
186 comb
+= min_pri
.eq(pending_priority
)
188 # Accept the interrupt
189 with m
.If(xirr_accept_rd
):
190 #report "XICS: ICP ACCEPT" &
191 # " cppr:" & to_hstring(r.cppr) &
192 # " xisr:" & to_hstring(r.xisr) &
193 # " mfrr:" & to_hstring(r.mfrr);
194 comb
+= v
.cppr
.eq(min_pri
)
196 comb
+= v
.wb_rd_data
.eq(bswap(be_out
))
198 # check if the core needs an interrupt notification (or clearing)
199 with m
.If(min_pri
< v
.cppr
):
213 for field
in self
.bus
.fields
.values():
215 yield from self
.ics_i
216 yield self
.core_irq_o
222 class Xive(RecordObject
):
223 def __init__(self
, name
, wid
, rst
):
224 super().__init
__(name
=name
)
225 self
.pri
= Signal(wid
, reset
=rst
)
229 class XICS_ICS(Elaboratable
):
230 def __init__(self
, spec
=None, SRC_NUM
=16, PRIO_BITS
=8):
231 self
.SRC_NUM
= SRC_NUM
232 self
.PRIO_BITS
= PRIO_BITS
233 self
.pri_masked
= (1<<self
.PRIO_BITS
)-1
240 self
.bus
= Record(make_wb_layout(spec
, cti
=False), name
="ics_wb")
242 self
.int_level_i
= Signal(SRC_NUM
)
243 self
.icp_o
= ICS2ICP("icp_o")
245 def prio_pack(self
, pri8
):
246 return pri8
[:self
.PRIO_BITS
]
248 def prio_unpack(self
, pri
):
249 return Mux(pri
== self
.pri_masked
, Const(0xff, 8), pri
[:self
.PRIO_BITS
])
251 # A more favored than b ?
252 def a_mf_b(self
, a
, b
):
253 #report "a_mf_b a=" & to_hstring(a) &
254 # " b=" & to_hstring(b) &
255 # " r=" & boolean'image(a < b);
258 def elaborate(self
, platform
):
260 comb
, sync
= m
.d
.comb
, m
.d
.sync
262 xives
= Array([Xive("xive%d" % i
, self
.PRIO_BITS
, self
.pri_masked
)
263 for i
in range(self
.SRC_NUM
)])
266 reg_idx
= Signal(log2_int(self
.SRC_NUM
))
267 icp_o_next
= ICS2ICP("icp_r")
268 int_level_l
= Signal(self
.SRC_NUM
)
272 # 4 : Debug/diagnostics
276 # Config register format:
278 # 23.. 0 : Interrupt base (hard wired to 16)
279 # 27.. 24 : #prio bits (1..8)
281 # XIVE register format:
283 # 31 : input bit (reflects interrupt input)
285 # 29 : P (mirrors input for now)
286 # 28 : Q (not implemented in this version)
288 # 19 .. 8 : target (not implemented in this version)
291 reg_is_xive
= Signal()
292 reg_is_config
= Signal()
293 reg_is_debug
= Signal()
295 assert self
.SRC_NUM
== 16, "Fixup address decode with log2"
297 comb
+= reg_is_xive
.eq(self
.bus
.adr
[9])
298 comb
+= reg_is_config
.eq(self
.bus
.adr
[0:10] == 0x0)
299 comb
+= reg_is_debug
.eq(self
.bus
.adr
[0:10] == 0x4)
301 # Register index XX FIXME: figure out bits from SRC_NUM
302 comb
+= reg_idx
.eq(self
.bus
.adr
[:4])
304 # Latch interrupt inputs for timing
305 sync
+= int_level_l
.eq(self
.int_level_i
)
307 # We don't stall. Acks are sent by the read machine one cycle
308 # after a request, but we can handle one access per cycle.
309 comb
+= wb_valid
.eq(self
.bus
.cyc
& self
.bus
.stb
)
311 # Big read mux. This could be replaced by a slower state
312 # machine iterating registers instead if timing gets tight.
317 with m
.If(reg_is_xive
):
318 pri_i
= self
.prio_unpack(xives
[reg_idx
].pri
)
320 comb
+= ibit
.eq(int_level_l
.bit_select(reg_idx
, 1))
321 comb
+= be_out
.eq(Cat(pri_i
, # bits 0..7
328 with m
.Elif(reg_is_config
):
329 comb
+= be_out
.eq(Cat(Const(self
.SRC_NUM
, 24), # 0-23
330 Const(self
.PRIO_BITS
, 4), # 24-27
331 Const(0, 4))) # 28-31
333 with m
.Elif(reg_is_debug
):
334 comb
+= be_out
.eq(Cat(icp_o_next
.pri
, # 0-7
336 icp_o_next
.src
)) # 28-31
338 sync
+= self
.bus
.dat_r
.eq(bswap(be_out
))
339 sync
+= self
.bus
.ack
.eq(wb_valid
)
341 # Register write machine
344 comb
+= be_in
.eq(bswap(self
.bus
.dat_w
))
346 with m
.If(wb_valid
& self
.bus
.we
):
347 with m
.If(reg_is_xive
):
348 # TODO: When adding support for other bits, make sure to
349 # properly implement self.bus.sel to allow partial writes.
350 sync
+= xives
[reg_idx
].pri
.eq(self
.prio_pack(be_in
[:8]))
351 #report "ICS irq " & integer'image(reg_idx) &
352 # " set to:" & to_hstring(be_in(7 downto 0));
355 # generate interrupt. This is a simple combinational process,
356 # potentially wasteful in HW for large number of interrupts.
358 # could be replaced with iterative state machines and a message
359 # system between ICSs' (plural) and ICP incl. reject etc...
361 sync
+= self
.icp_o
.eq(icp_o_next
)
363 max_idx
= Signal(log2_int(self
.SRC_NUM
))
364 max_pri
= Signal(self
.PRIO_BITS
)
366 # XXX FIXME: Use a tree (or examine each bit in turn)
367 comb
+= max_pri
.eq(self
.pri_masked
)
368 comb
+= max_idx
.eq(0)
369 for i
in range(self
.SRC_NUM
):
370 cur_idx
= Signal(log2_int(self
.SRC_NUM
), name
="cur_idx%d" % i
)
371 cur_pri
= Signal(self
.PRIO_BITS
, name
="cur_pri%d" % i
)
372 comb
+= cur_pri
.eq(max_pri
)
373 comb
+= cur_idx
.eq(max_idx
)
374 with m
.If(int_level_l
[i
] & self
.a_mf_b(xives
[i
].pri
, max_pri
)):
375 comb
+= cur_pri
.eq(xives
[i
].pri
)
376 comb
+= cur_idx
.eq(i
)
379 with m
.If(max_pri
!= self
.pri_masked
):
380 #report "MFI: " & integer'image(max_idx) &
381 #" pri=" & to_hstring(prio_unpack(max_pri));
383 comb
+= icp_o_next
.src
.eq(max_idx
)
384 comb
+= icp_o_next
.pri
.eq(self
.prio_unpack(max_pri
))
389 for field
in self
.bus
.fields
.values():
391 yield self
.int_level_i
392 yield from self
.icp_o
.ports()
399 def sim_xics_icp(dut
):
402 data
= yield from wb_read(dut
.bus
, MFRR
)
403 print ("mfrr", hex(data
), bin(data
))
404 assert (yield dut
.core_irq_o
) == 0
408 # read wb XIRR (8-bit)
409 data
= yield from wb_read(dut
.bus
, XIRR
, False)
410 print ("xirr", hex(data
), bin(data
))
411 assert (yield dut
.core_irq_o
) == 0
415 # read wb XIRR (32-bit)
416 data
= yield from wb_read(dut
.bus
, XIRR
)
417 print ("xirr", hex(data
), bin(data
))
418 assert (yield dut
.core_irq_o
) == 0
423 data
= yield from wb_read(dut
.bus
, XIRR_POLL
)
424 print ("xirr poll", hex(data
), bin(data
))
425 assert (yield dut
.core_irq_o
) == 0
428 # set dut src/pri to something, anything
430 yield dut
.ics_i
.src
.eq(9)
431 yield dut
.ics_i
.pri
.eq(0x1e)
434 data
= yield from wb_read(dut
.bus
, MFRR
)
435 print ("mfrr", hex(data
), bin(data
))
436 assert (yield dut
.core_irq_o
) == 0
440 # read wb XIRR (8-bit)
441 data
= yield from wb_read(dut
.bus
, XIRR
, False)
442 print ("xirr", hex(data
), bin(data
))
443 assert (yield dut
.core_irq_o
) == 0
447 # read wb XIRR (32-bit)
448 data
= yield from wb_read(dut
.bus
, XIRR
)
449 print ("xirr", hex(data
), bin(data
))
450 assert (yield dut
.core_irq_o
) == 0
455 data
= yield from wb_read(dut
.bus
, XIRR_POLL
)
456 print ("xirr poll", hex(data
), bin(data
))
457 assert (yield dut
.core_irq_o
) == 0
459 ######################
462 yield from wb_write(dut
.bus
, XIRR
, data
)
463 print ("xirr written", hex(data
), bin(data
))
465 assert (yield dut
.core_irq_o
) == 1 # ok *now* it should be set
468 data
= yield from wb_read(dut
.bus
, XIRR_POLL
, False)
469 print ("xirr poll", hex(data
), bin(data
))
470 assert (yield dut
.core_irq_o
) == 1 # should not clear
472 # read wb XIRR (8-bit)
473 data
= yield from wb_read(dut
.bus
, XIRR
, False)
474 print ("xirr", hex(data
), bin(data
))
475 assert (yield dut
.core_irq_o
) == 1 # should not clear
477 # read wb XIRR (32-bit)
478 data
= yield from wb_read(dut
.bus
, XIRR
)
479 print ("xirr", hex(data
), bin(data
))
481 assert (yield dut
.core_irq_o
) == 0
487 return int.from_bytes(x
.to_bytes(4, byteorder
='little'),
488 byteorder
='big', signed
=False)
490 def get_field(x
, wid
, shift
):
492 return x
& ((1<<wid
)-1)
495 def sim_xics(icp
, ics
):
498 data
= yield from wb_read(ics
.bus
, 0)
499 print ("config", hex(data
), bin(data
))
501 base
= get_field(data
, 24, 0)
502 pri
= get_field(data
, 8, 24)
503 print (" base", hex(base
))
504 print (" pri", hex(pri
))
512 data
= yield from wb_read(ics
.bus
, 0x800//4)
513 print ("xive0", hex(data
), bin(data
))
515 irq
= get_field(data
, 1, 31)
516 rsvd
= get_field(data
, 1, 30)
517 p
= get_field(data
, 1, 29)
518 q
= get_field(data
, 1, 28)
519 rsvd2
= get_field(data
, 8, 20)
520 target
= get_field(data
, 12, 8)
521 prio
= get_field(data
, 8, 0)
522 print(" irq", hex(irq
))
523 print(" rsvd", hex(rsvd
))
526 print(" rsvd2", hex(rsvd2
))
527 print(" target", hex(target
))
528 print(" prio", hex(prio
))
529 assert irq
== 0 # not active
532 assert target
== 0 # not implemented
538 # raise XIVE 1 (just for fun)
539 yield ics
.int_level_i
.eq(1<<1)
541 yield # wait for interrupt to propagate through from ics to icp...
544 data
= yield from wb_read(ics
.bus
, 0x804//4)
545 print ("xive1", hex(data
), bin(data
))
547 irq
= get_field(data
, 1, 31)
548 rsvd
= get_field(data
, 1, 30)
549 p
= get_field(data
, 1, 29)
550 q
= get_field(data
, 1, 28)
551 rsvd2
= get_field(data
, 8, 20)
552 target
= get_field(data
, 12, 8)
553 prio
= get_field(data
, 8, 0)
554 print(" irq", hex(irq
))
555 print(" rsvd", hex(rsvd
))
558 print(" rsvd2", hex(rsvd2
))
559 print(" target", hex(target
))
560 print(" prio", hex(prio
))
561 assert irq
== 1 # active!
564 assert target
== 0 # not implemented
570 # check that after setting IRQ 2 core is still 0 because priority is 0xff
571 assert (yield icp
.core_irq_o
) == 0
574 # set XIVE1 priority to 0xf0
576 yield from wb_write(ics
.bus
, 0x804//4, data
)
577 print ("XIVE1 priority written", hex(data
), bin(data
))
579 ######################
582 yield from wb_write(icp
.bus
, XIRR
, data
)
583 print ("xirr written", hex(data
), bin(data
))
585 assert (yield icp
.core_irq_o
) == 1 # ok *now* it should be set
587 # read wb XIRR (32-bit)
588 data
= yield from wb_read(icp
.bus
, XIRR
)
589 print ("xirr", hex(data
), bin(data
))
591 cppr
= get_field(data
, 8, 24)
592 xisr
= get_field(data
, 24, 0)
593 print(" cppr", hex(cppr
))
594 print(" xisr", hex(xisr
))
596 assert (yield icp
.core_irq_o
) == 0
605 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
606 with
open("test_xics_icp.il", "w") as f
:
610 m
.submodules
.xics_icp
= dut
615 sim
.add_sync_process(wrap(sim_xics_icp(dut
)))
616 sim_writer
= sim
.write_vcd('test_xics_icp.vcd')
623 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
624 with
open("test_xics_ics.il", "w") as f
:
627 #run_simulation(dut, ldst_sim(dut), vcd_name='test_ldst_regspec.vcd')
632 m
.submodules
.icp
= icp
= XICS_ICP()
633 m
.submodules
.ics
= ics
= XICS_ICS()
634 m
.d
.comb
+= icp
.ics_i
.eq(ics
.icp_o
)
636 vl
= rtlil
.convert(m
, ports
=icp
.ports()+ics
.ports())
637 with
open("test_xics.il", "w") as f
:
643 sim
.add_sync_process(wrap(sim_xics(icp
, ics
)))
644 sim_writer
= sim
.write_vcd('test_xics.vcd')
649 if __name__
== '__main__':