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
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)
107 comb
+= v
.eq(r
) # start from the register (r)
108 comb
+= v
.wb_ack
.eq(0)
110 comb
+= xirr_accept_rd
.eq(0)
112 comb
+= be_in
.eq(bswap(self
.bus
.dat_w
))
115 with m
.If(self
.bus
.cyc
& self
.bus
.stb
):
116 comb
+= v
.wb_ack
.eq(1) # always ack
117 with m
.If(self
.bus
.we
): # write
118 # writes to both XIRR are the same
119 with m
.Switch( self
.bus
.adr
[:8]):
120 with m
.Case(XIRR_POLL
):
121 # report "ICP XIRR_POLL write";
122 comb
+= v
.cppr
.eq(be_in
[24:32])
124 comb
+= v
.cppr
.eq(be_in
[24:32])
125 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
126 #report "ICP XIRR write word (EOI) :" & \
129 with m
.Elif(self
.bus
.sel
== 0x1): # 1 byte
130 #report "ICP XIRR write byte (CPPR):" & \
131 #to_hstring(be_in(31 downto 24));
134 #report "ICP XIRR UNSUPPORTED write ! sel=" & \
135 # to_hstring(self.bus.sel);
138 comb
+= v
.mfrr
.eq(be_in
[24:32])
139 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
140 # report "ICP MFRR write word:" & to_hstring(be_in);
142 with m
.Elif(self
.bus
.sel
== 0x1): # 1 byte
143 # report "ICP MFRR write byte:" & \
144 # to_hstring(be_in(31 downto 24));
147 # report "ICP MFRR UNSUPPORTED write ! sel=" & \
148 # to_hstring(self.bus.sel);
151 with m
.Else(): # read
153 with m
.Switch(self
.bus
.adr
[:8]):
154 with m
.Case(XIRR_POLL
):
155 # report "ICP XIRR_POLL read";
156 comb
+= be_out
.eq(r
.xisr
& r
.cppr
)
158 # report "ICP XIRR read";
159 comb
+= be_out
.eq(Cat(r
.xisr
, r
.cppr
))
160 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
161 comb
+= xirr_accept_rd
.eq(1)
163 # report "ICP MFRR read";
164 comb
+= be_out
.eq(r
.mfrr
)
166 comb
+= pending_priority
.eq(0xff)
167 comb
+= v
.xisr
.eq(0x0)
168 comb
+= v
.irq
.eq(0x0)
171 with m
.If(self
.ics_i
.pri
!= 0xff):
172 comb
+= v
.xisr
.eq(Cat(self
.ics_i
.src
, Const(0x00001, 20)))
173 comb
+= pending_priority
.eq(self
.ics_i
.pri
)
176 with m
.If(r
.mfrr
< pending_priority
):
177 comb
+= v
.xisr
.eq(Const(0x2)) # special XICS MFRR IRQ source number
178 comb
+= pending_priority
.eq(r
.mfrr
)
180 # Accept the interrupt
181 with m
.If(xirr_accept_rd
):
182 #report "XICS: ICP ACCEPT" &
183 # " cppr:" & to_hstring(r.cppr) &
184 # " xisr:" & to_hstring(r.xisr) &
185 # " mfrr:" & to_hstring(r.mfrr);
186 comb
+= v
.cppr
.eq(pending_priority
)
188 comb
+= v
.wb_rd_data
.eq(bswap(be_out
))
190 # check if the core needs an interrupt notification (or clearing)
191 comb
+= v
.irq
.eq(pending_priority
< v
.cppr
)
205 for field
in self
.bus
.fields
.values():
207 yield from self
.ics_i
208 yield self
.core_irq_o
214 class Xive(RecordObject
):
215 def __init__(self
, name
, wid
, rst
):
216 super().__init
__(name
=name
)
217 self
.pri
= Signal(wid
, reset
=rst
)
221 class XICS_ICS(Elaboratable
):
222 def __init__(self
, SRC_NUM
=16, PRIO_BITS
=8):
223 self
.SRC_NUM
= SRC_NUM
224 self
.PRIO_BITS
= PRIO_BITS
225 self
.pri_masked
= (1<<self
.PRIO_BITS
)-1
231 self
.bus
= Record(make_wb_layout(spec
), name
="ics_wb")
233 self
.int_level_i
= Signal(SRC_NUM
)
234 self
.icp_o
= ICS2ICP("icp_o")
236 def prio_pack(self
, pri8
):
237 return pri8
[:self
.PRIO_BITS
]
239 def prio_unpack(self
, pri
):
240 return Mux(pri
== self
.pri_masked
, Const(0xff, 8), pri
[:self
.PRIO_BITS
])
242 # A more favored than b ?
243 def a_mf_b(self
, a
, b
):
244 #report "a_mf_b a=" & to_hstring(a) &
245 # " b=" & to_hstring(b) &
246 # " r=" & boolean'image(a < b);
249 def elaborate(self
, platform
):
251 comb
, sync
= m
.d
.comb
, m
.d
.sync
253 xives
= Array([Xive("xive%d" % i
, self
.PRIO_BITS
, self
.pri_masked
)
254 for i
in range(self
.SRC_NUM
)])
257 reg_idx
= Signal(log2_int(self
.SRC_NUM
))
258 icp_o_next
= ICS2ICP("icp_r")
259 int_level_l
= Signal(self
.SRC_NUM
)
263 # 4 : Debug/diagnostics
267 # Config register format:
269 # 23.. 0 : Interrupt base (hard wired to 16)
270 # 27.. 24 : #prio bits (1..8)
272 # XIVE register format:
274 # 31 : input bit (reflects interrupt input)
276 # 29 : P (mirrors input for now)
277 # 28 : Q (not implemented in this version)
279 # 19 .. 8 : target (not implemented in this version)
282 reg_is_xive
= Signal()
283 reg_is_config
= Signal()
284 reg_is_debug
= Signal()
286 assert self
.SRC_NUM
== 16, "Fixup address decode with log2"
288 comb
+= reg_is_xive
.eq(self
.bus
.adr
[11])
289 comb
+= reg_is_config
.eq(self
.bus
.adr
[0:12] == 0x0)
290 comb
+= reg_is_debug
.eq(self
.bus
.adr
[0:12] == 0x4)
292 # Register index XX FIXME: figure out bits from SRC_NUM
293 comb
+= reg_idx
.eq(self
.bus
.adr
[2:6])
295 # Latch interrupt inputs for timing
296 sync
+= int_level_l
.eq(self
.int_level_i
)
298 # We don't stall. Acks are sent by the read machine one cycle
299 # after a request, but we can handle one access per cycle.
300 comb
+= wb_valid
.eq(self
.bus
.cyc
& self
.bus
.stb
)
302 # Big read mux. This could be replaced by a slower state
303 # machine iterating registers instead if timing gets tight.
308 with m
.If(reg_is_xive
):
309 pri_i
= self
.prio_unpack(xives
[reg_idx
].pri
)
311 comb
+= ibit
.eq(int_level_l
.bit_select(reg_idx
, 1))
312 comb
+= be_out
.eq(Cat(pri_i
, # bits 0..7
319 with m
.Elif(reg_is_config
):
320 comb
+= be_out
.eq(Cat(Const(self
.SRC_NUM
, 24), # 0-23
321 Const(self
.PRIO_BITS
, 4), # 24-27
322 Const(0, 4))) # 28-31
324 with m
.Elif(reg_is_debug
):
325 comb
+= be_out
.eq(Cat(icp_o_next
.pri
, # 0-7
327 icp_o_next
.src
)) # 28-31
329 sync
+= self
.bus
.dat_r
.eq(bswap(be_out
))
330 sync
+= self
.bus
.ack
.eq(wb_valid
)
332 # Register write machine
335 comb
+= be_in
.eq(bswap(self
.bus
.dat_w
))
337 with m
.If(wb_valid
& self
.bus
.we
):
338 with m
.If(reg_is_xive
):
339 # TODO: When adding support for other bits, make sure to
340 # properly implement self.bus.sel to allow partial writes.
341 sync
+= xives
[reg_idx
].pri
.eq(self
.prio_pack(be_in
[:8]))
342 #report "ICS irq " & integer'image(reg_idx) &
343 # " set to:" & to_hstring(be_in(7 downto 0));
346 # generate interrupt. This is a simple combinational process,
347 # potentially wasteful in HW for large number of interrupts.
349 # could be replaced with iterative state machines and a message
350 # system between ICSs' (plural) and ICP incl. reject etc...
352 sync
+= self
.icp_o
.eq(icp_o_next
)
354 max_idx
= Signal(log2_int(self
.SRC_NUM
))
355 max_pri
= Signal(self
.PRIO_BITS
)
357 # XXX FIXME: Use a tree (or examine each bit in turn)
358 comb
+= max_pri
.eq(self
.pri_masked
)
359 comb
+= max_idx
.eq(0)
360 for i
in range(self
.SRC_NUM
):
361 cur_idx
= Signal(log2_int(self
.SRC_NUM
), name
="cur_idx%d" % i
)
362 cur_pri
= Signal(self
.PRIO_BITS
, name
="cur_pri%d" % i
)
363 comb
+= cur_pri
.eq(max_pri
)
364 comb
+= cur_idx
.eq(max_idx
)
365 with m
.If(int_level_l
[i
] & self
.a_mf_b(xives
[i
].pri
, max_pri
)):
366 comb
+= cur_pri
.eq(xives
[i
].pri
)
367 comb
+= cur_idx
.eq(i
)
370 with m
.If(max_pri
!= self
.pri_masked
):
371 #report "MFI: " & integer'image(max_idx) &
372 #" pri=" & to_hstring(prio_unpack(max_pri));
374 comb
+= icp_o_next
.src
.eq(max_idx
)
375 comb
+= icp_o_next
.pri
.eq(self
.prio_unpack(max_pri
))
380 for field
in self
.bus
.fields
.values():
382 yield self
.int_level_i
383 yield from self
.icp_o
.ports()
390 def sim_xics_icp(dut
):
393 data
= yield from wb_read(dut
.bus
, MFRR
)
394 print ("mfrr", hex(data
), bin(data
))
395 assert (yield dut
.core_irq_o
) == 0
399 # read wb XIRR (8-bit)
400 data
= yield from wb_read(dut
.bus
, XIRR
, False)
401 print ("xirr", hex(data
), bin(data
))
402 assert (yield dut
.core_irq_o
) == 0
406 # read wb XIRR (32-bit)
407 data
= yield from wb_read(dut
.bus
, XIRR
)
408 print ("xirr", hex(data
), bin(data
))
409 assert (yield dut
.core_irq_o
) == 0
414 data
= yield from wb_read(dut
.bus
, XIRR_POLL
)
415 print ("xirr poll", hex(data
), bin(data
))
416 assert (yield dut
.core_irq_o
) == 0
419 # set dut src/pri to something, anything
421 yield dut
.ics_i
.src
.eq(9)
422 yield dut
.ics_i
.pri
.eq(0x1e)
425 data
= yield from wb_read(dut
.bus
, MFRR
)
426 print ("mfrr", hex(data
), bin(data
))
427 assert (yield dut
.core_irq_o
) == 0
431 # read wb XIRR (8-bit)
432 data
= yield from wb_read(dut
.bus
, XIRR
, False)
433 print ("xirr", hex(data
), bin(data
))
434 assert (yield dut
.core_irq_o
) == 0
438 # read wb XIRR (32-bit)
439 data
= yield from wb_read(dut
.bus
, XIRR
)
440 print ("xirr", hex(data
), bin(data
))
441 assert (yield dut
.core_irq_o
) == 0
446 data
= yield from wb_read(dut
.bus
, XIRR_POLL
)
447 print ("xirr poll", hex(data
), bin(data
))
448 assert (yield dut
.core_irq_o
) == 0
450 ######################
453 yield from wb_write(dut
.bus
, XIRR
, data
)
454 print ("xirr written", hex(data
), bin(data
))
456 assert (yield dut
.core_irq_o
) == 1 # ok *now* it should be set
459 data
= yield from wb_read(dut
.bus
, XIRR_POLL
, False)
460 print ("xirr poll", hex(data
), bin(data
))
461 assert (yield dut
.core_irq_o
) == 1 # should not clear
463 # read wb XIRR (8-bit)
464 data
= yield from wb_read(dut
.bus
, XIRR
, False)
465 print ("xirr", hex(data
), bin(data
))
466 assert (yield dut
.core_irq_o
) == 1 # should not clear
468 # read wb XIRR (32-bit)
469 data
= yield from wb_read(dut
.bus
, XIRR
)
470 print ("xirr", hex(data
), bin(data
))
472 assert (yield dut
.core_irq_o
) == 0
478 return int.from_bytes(x
.to_bytes(4, byteorder
='little'),
479 byteorder
='big', signed
=False)
481 def get_field(x
, wid
, shift
):
483 return x
& ((1<<wid
)-1)
486 def sim_xics(icp
, ics
):
489 data
= yield from wb_read(ics
.bus
, 0)
490 print ("config", hex(data
), bin(data
))
492 base
= get_field(data
, 24, 0)
493 pri
= get_field(data
, 8, 24)
494 print (" base", hex(base
))
495 print (" pri", hex(pri
))
503 data
= yield from wb_read(ics
.bus
, 0x800)
504 print ("xive0", hex(data
), bin(data
))
506 irq
= get_field(data
, 1, 31)
507 rsvd
= get_field(data
, 1, 30)
508 p
= get_field(data
, 1, 29)
509 q
= get_field(data
, 1, 28)
510 rsvd2
= get_field(data
, 8, 20)
511 target
= get_field(data
, 12, 8)
512 prio
= get_field(data
, 8, 0)
513 print(" irq", hex(irq
))
514 print(" rsvd", hex(rsvd
))
517 print(" rsvd2", hex(rsvd2
))
518 print(" target", hex(target
))
519 print(" prio", hex(prio
))
520 assert irq
== 0 # not active
523 assert target
== 0 # not implemented
529 # raise XIVE 1 (just for fun)
530 yield ics
.int_level_i
.eq(1<<1)
532 yield # wait for interrupt to propagate through from ics to icp...
535 data
= yield from wb_read(ics
.bus
, 0x804)
536 print ("xive1", hex(data
), bin(data
))
538 irq
= get_field(data
, 1, 31)
539 rsvd
= get_field(data
, 1, 30)
540 p
= get_field(data
, 1, 29)
541 q
= get_field(data
, 1, 28)
542 rsvd2
= get_field(data
, 8, 20)
543 target
= get_field(data
, 12, 8)
544 prio
= get_field(data
, 8, 0)
545 print(" irq", hex(irq
))
546 print(" rsvd", hex(rsvd
))
549 print(" rsvd2", hex(rsvd2
))
550 print(" target", hex(target
))
551 print(" prio", hex(prio
))
552 assert irq
== 1 # active!
555 assert target
== 0 # not implemented
561 # check that after setting IRQ 2 core is still 0 because priority is 0xff
562 assert (yield icp
.core_irq_o
) == 0
565 # set XIVE1 priority to 0xf0
567 yield from wb_write(ics
.bus
, 0x804, data
)
568 print ("XIVE1 priority written", hex(data
), bin(data
))
570 ######################
573 yield from wb_write(icp
.bus
, XIRR
, data
)
574 print ("xirr written", hex(data
), bin(data
))
576 assert (yield icp
.core_irq_o
) == 1 # ok *now* it should be set
578 # read wb XIRR (32-bit)
579 data
= yield from wb_read(icp
.bus
, XIRR
)
580 print ("xirr", hex(data
), bin(data
))
582 cppr
= get_field(data
, 8, 24)
583 xisr
= get_field(data
, 24, 0)
584 print(" cppr", hex(cppr
))
585 print(" xisr", hex(xisr
))
587 assert (yield icp
.core_irq_o
) == 0
596 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
597 with
open("test_xics_icp.il", "w") as f
:
601 m
.submodules
.xics_icp
= dut
606 sim
.add_sync_process(wrap(sim_xics_icp(dut
)))
607 sim_writer
= sim
.write_vcd('test_xics_icp.vcd')
614 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
615 with
open("test_xics_ics.il", "w") as f
:
618 #run_simulation(dut, ldst_sim(dut), vcd_name='test_ldst_regspec.vcd')
623 m
.submodules
.icp
= icp
= XICS_ICP()
624 m
.submodules
.ics
= ics
= XICS_ICS()
625 m
.d
.comb
+= icp
.ics_i
.eq(ics
.icp_o
)
627 vl
= rtlil
.convert(m
, ports
=icp
.ports()+ics
.ports())
628 with
open("test_xics.il", "w") as f
:
634 sim
.add_sync_process(wrap(sim_xics(icp
, ics
)))
635 sim_writer
= sim
.write_vcd('test_xics.vcd')
640 if __name__
== '__main__':