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
29 from nmigen
.sim
.cxxsim
import Simulator
, Settle
31 from nmigen
.back
.pysim
import Simulator
, Settle
35 class ICS2ICP(RecordObject
):
37 # Level interrupts only, ICS just keeps prsenting the
38 # highest priority interrupt. Once handling edge, something
39 # smarter involving handshake & reject support will be needed
41 def __init__(self
, name
):
42 super().__init
__(name
=name
)
43 self
.src
= Signal(4, reset_less
=True)
44 self
.pri
= Signal(8, reset_less
=True)
46 # hardwire the hardware IRQ priority
47 HW_PRIORITY
= Const(0x80, 8)
49 # 8 bit offsets for each presentation
56 class RegInternal(RecordObject
):
57 def __init__(self
, name
=None):
58 super().__init
__(name
=name
)
59 self
.xisr
= Signal(24)
61 self
.mfrr
= Signal(8, reset
=0xff) # mask everything on reset
63 self
.wb_rd_data
= Signal(32)
64 self
.wb_ack
= Signal(1)
68 return Cat(v
[24:32], v
[16:24], v
[8:16], v
[0:8])
71 class XICS_ICP(Elaboratable
):
79 self
.bus
= Record(make_wb_layout(spec
), name
="icp_wb")
80 self
.ics_i
= ICS2ICP("ics_i")
81 self
.core_irq_o
= Signal()
83 def elaborate(self
, platform
):
85 comb
, sync
= m
.d
.comb
, m
.d
.sync
88 r_next
= RegInternal()
91 # We delay core_irq_out by a cycle to help with timing
92 sync
+= self
.core_irq_o
.eq(r
.irq
)
94 comb
+= self
.bus
.dat_r
.eq(r
.wb_rd_data
)
95 comb
+= self
.bus
.ack
.eq(r
.wb_ack
)
98 xirr_accept_rd
= Signal()
103 pending_priority
= Signal(8)
105 comb
+= v
.eq(r
) # start from the register (r)
106 comb
+= v
.wb_ack
.eq(0)
108 comb
+= xirr_accept_rd
.eq(0)
110 comb
+= be_in
.eq(bswap(self
.bus
.dat_w
))
113 with m
.If(self
.bus
.cyc
& self
.bus
.stb
):
114 comb
+= v
.wb_ack
.eq(1) # always ack
115 with m
.If(self
.bus
.we
): # write
116 # writes to both XIRR are the same
117 with m
.Switch( self
.bus
.adr
[:8]):
118 with m
.Case(XIRR_POLL
):
119 # report "ICP XIRR_POLL write";
120 comb
+= v
.cppr
.eq(be_in
[24:32])
122 comb
+= v
.cppr
.eq(be_in
[24:32])
123 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
124 #report "ICP XIRR write word (EOI) :" & \
127 with m
.Elif(self
.bus
.sel
== 0x1): # 1 byte
128 #report "ICP XIRR write byte (CPPR):" & \
129 #to_hstring(be_in(31 downto 24));
132 #report "ICP XIRR UNSUPPORTED write ! sel=" & \
133 # to_hstring(self.bus.sel);
136 comb
+= v
.mfrr
.eq(be_in
[24:32])
137 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
138 # report "ICP MFRR write word:" & to_hstring(be_in);
140 with m
.Elif(self
.bus
.sel
== 0x1): # 1 byte
141 # report "ICP MFRR write byte:" & \
142 # to_hstring(be_in(31 downto 24));
145 # report "ICP MFRR UNSUPPORTED write ! sel=" & \
146 # to_hstring(self.bus.sel);
149 with m
.Else(): # read
151 with m
.Switch(self
.bus
.adr
[:8]):
152 with m
.Case(XIRR_POLL
):
153 # report "ICP XIRR_POLL read";
154 comb
+= be_out
.eq(r
.xisr
& r
.cppr
)
156 # report "ICP XIRR read";
157 comb
+= be_out
.eq(Cat(r
.xisr
, r
.cppr
))
158 with m
.If(self
.bus
.sel
== 0xf): # # 4 byte
159 comb
+= xirr_accept_rd
.eq(1)
161 # report "ICP MFRR read";
162 comb
+= be_out
.eq(r
.mfrr
)
164 comb
+= pending_priority
.eq(0xff)
165 comb
+= v
.xisr
.eq(0x0)
166 comb
+= v
.irq
.eq(0x0)
169 with m
.If(self
.ics_i
.pri
!= 0xff):
170 comb
+= v
.xisr
.eq(Cat(self
.ics_i
.src
, Const(0x00001, 20)))
171 comb
+= pending_priority
.eq(self
.ics_i
.pri
)
174 with m
.If(r
.mfrr
< pending_priority
):
175 comb
+= v
.xisr
.eq(Const(0x2)) # special XICS MFRR IRQ source number
176 comb
+= pending_priority
.eq(r
.mfrr
)
178 # Accept the interrupt
179 with m
.If(xirr_accept_rd
):
180 #report "XICS: ICP ACCEPT" &
181 # " cppr:" & to_hstring(r.cppr) &
182 # " xisr:" & to_hstring(r.xisr) &
183 # " mfrr:" & to_hstring(r.mfrr);
184 comb
+= v
.cppr
.eq(pending_priority
)
186 comb
+= v
.wb_rd_data
.eq(bswap(be_out
))
188 # check if the core needs an interrupt notification (or clearing)
189 comb
+= v
.irq
.eq(pending_priority
< v
.cppr
)
203 for field
in self
.bus
.fields
.values():
205 yield from self
.ics_i
206 yield self
.core_irq_o
212 class Xive(RecordObject
):
213 def __init__(self
, name
, wid
, rst
):
214 super().__init
__(name
=name
)
215 self
.pri
= Signal(wid
, reset
=rst
)
219 class XICS_ICS(Elaboratable
):
220 def __init__(self
, SRC_NUM
=16, PRIO_BITS
=8):
221 self
.SRC_NUM
= SRC_NUM
222 self
.PRIO_BITS
= PRIO_BITS
223 self
.pri_masked
= (1<<self
.PRIO_BITS
)-1
229 self
.bus
= Record(make_wb_layout(spec
), name
="ics_wb")
231 self
.int_level_i
= Signal(SRC_NUM
)
232 self
.icp_o
= ICS2ICP("icp_o")
234 def prio_pack(self
, pri8
):
235 return pri8
[:self
.PRIO_BITS
]
237 def prio_unpack(self
, pri
):
238 return Mux(pri
== self
.pri_masked
, Const(0xff, 8), pri
[:self
.PRIO_BITS
])
240 # A more favored than b ?
241 def a_mf_b(self
, a
, b
):
242 #report "a_mf_b a=" & to_hstring(a) &
243 # " b=" & to_hstring(b) &
244 # " r=" & boolean'image(a < b);
247 def elaborate(self
, platform
):
249 comb
, sync
= m
.d
.comb
, m
.d
.sync
251 xives
= Array([Xive("xive%d" % i
, self
.PRIO_BITS
, self
.pri_masked
)
252 for i
in range(self
.SRC_NUM
)])
255 reg_idx
= Signal(log2_int(self
.SRC_NUM
))
256 icp_o_next
= ICS2ICP("icp_r")
257 int_level_l
= Signal(self
.SRC_NUM
)
261 # 4 : Debug/diagnostics
265 # Config register format:
267 # 23.. 0 : Interrupt base (hard wired to 16)
268 # 27.. 24 : #prio bits (1..8)
270 # XIVE register format:
272 # 31 : input bit (reflects interrupt input)
274 # 29 : P (mirrors input for now)
275 # 28 : Q (not implemented in this version)
277 # 19 .. 8 : target (not implemented in this version)
280 reg_is_xive
= Signal()
281 reg_is_config
= Signal()
282 reg_is_debug
= Signal()
284 assert self
.SRC_NUM
== 16, "Fixup address decode with log2"
286 comb
+= reg_is_xive
.eq(self
.bus
.adr
[11])
287 comb
+= reg_is_config
.eq(self
.bus
.adr
[0:12] == 0x0)
288 comb
+= reg_is_debug
.eq(self
.bus
.adr
[0:12] == 0x4)
290 # Register index XX FIXME: figure out bits from SRC_NUM
291 comb
+= reg_idx
.eq(self
.bus
.adr
[2:6])
293 # Latch interrupt inputs for timing
294 sync
+= int_level_l
.eq(self
.int_level_i
)
296 # We don't stall. Acks are sent by the read machine one cycle
297 # after a request, but we can handle one access per cycle.
298 comb
+= wb_valid
.eq(self
.bus
.cyc
& self
.bus
.stb
)
300 # Big read mux. This could be replaced by a slower state
301 # machine iterating registers instead if timing gets tight.
306 with m
.If(reg_is_xive
):
307 pri_i
= self
.prio_unpack(xives
[reg_idx
].pri
)
309 comb
+= ibit
.eq(int_level_l
.bit_select(reg_idx
, 1))
310 comb
+= be_out
.eq(Cat(pri_i
, # bits 0..7
317 with m
.Elif(reg_is_config
):
318 comb
+= be_out
.eq(Cat(Const(self
.SRC_NUM
, 24), # 0-23
319 Const(self
.PRIO_BITS
, 4), # 24-27
320 Const(0, 4))) # 28-31
322 with m
.Elif(reg_is_debug
):
323 comb
+= be_out
.eq(Cat(icp_o_next
.pri
, # 0-7
325 icp_o_next
.src
)) # 28-31
327 sync
+= self
.bus
.dat_r
.eq(bswap(be_out
))
328 sync
+= self
.bus
.ack
.eq(wb_valid
)
330 # Register write machine
333 comb
+= be_in
.eq(bswap(self
.bus
.dat_w
))
335 with m
.If(wb_valid
& self
.bus
.we
):
336 with m
.If(reg_is_xive
):
337 # TODO: When adding support for other bits, make sure to
338 # properly implement self.bus.sel to allow partial writes.
339 sync
+= xives
[reg_idx
].pri
.eq(self
.prio_pack(be_in
[:8]))
340 #report "ICS irq " & integer'image(reg_idx) &
341 # " set to:" & to_hstring(be_in(7 downto 0));
344 # generate interrupt. This is a simple combinational process,
345 # potentially wasteful in HW for large number of interrupts.
347 # could be replaced with iterative state machines and a message
348 # system between ICSs' (plural) and ICP incl. reject etc...
350 sync
+= self
.icp_o
.eq(icp_o_next
)
352 max_idx
= Signal(log2_int(self
.SRC_NUM
))
353 max_pri
= Signal(self
.PRIO_BITS
)
355 # XXX FIXME: Use a tree (or examine each bit in turn)
356 comb
+= max_pri
.eq(self
.pri_masked
)
357 comb
+= max_idx
.eq(0)
358 for i
in range(self
.SRC_NUM
):
359 cur_idx
= Signal(log2_int(self
.SRC_NUM
), name
="cur_idx%d" % i
)
360 cur_pri
= Signal(self
.PRIO_BITS
, name
="cur_pri%d" % i
)
361 comb
+= cur_pri
.eq(max_pri
)
362 comb
+= cur_idx
.eq(max_idx
)
363 with m
.If(int_level_l
[i
] & self
.a_mf_b(xives
[i
].pri
, max_pri
)):
364 comb
+= cur_pri
.eq(xives
[i
].pri
)
365 comb
+= cur_idx
.eq(i
)
368 with m
.If(max_pri
!= self
.pri_masked
):
369 #report "MFI: " & integer'image(max_idx) &
370 #" pri=" & to_hstring(prio_unpack(max_pri));
372 comb
+= icp_o_next
.src
.eq(max_idx
)
373 comb
+= icp_o_next
.pri
.eq(self
.prio_unpack(max_pri
))
378 for field
in self
.bus
.fields
.values():
380 yield self
.int_level_i
381 yield from self
.icp_o
.ports()
387 def wb_write(dut
, addr
, data
, sel
=True):
390 yield dut
.bus
.we
.eq(1)
391 yield dut
.bus
.cyc
.eq(1)
392 yield dut
.bus
.stb
.eq(1)
393 yield dut
.bus
.sel
.eq(0b1111 if sel
else 0b1) # 32-bit / 8-bit
394 yield dut
.bus
.adr
.eq(addr
)
395 yield dut
.bus
.dat_w
.eq(data
)
397 # wait for ack to go high
399 ack
= yield dut
.bus
.ack
403 yield # loop until ack
404 yield dut
.bus
.stb
.eq(0) # drop stb so only 1 thing into pipeline
406 # leave cyc/stb valid for 1 cycle while writing
409 # clear out before returning data
410 yield dut
.bus
.cyc
.eq(0)
411 yield dut
.bus
.stb
.eq(0)
412 yield dut
.bus
.we
.eq(0)
413 yield dut
.bus
.adr
.eq(0)
414 yield dut
.bus
.sel
.eq(0)
415 yield dut
.bus
.dat_w
.eq(0)
418 def wb_read(dut
, addr
, sel
=True):
421 yield dut
.bus
.cyc
.eq(1)
422 yield dut
.bus
.stb
.eq(1)
423 yield dut
.bus
.we
.eq(0)
424 yield dut
.bus
.sel
.eq(0b1111 if sel
else 0b1) # 32-bit / 8-bit
425 yield dut
.bus
.adr
.eq(addr
)
427 # wait for ack to go high
429 ack
= yield dut
.bus
.ack
433 yield # loop until ack
434 yield dut
.bus
.stb
.eq(0) # drop stb so only 1 thing into pipeline
436 # get data on same cycle that ack raises
437 data
= yield dut
.bus
.dat_r
439 # leave cyc/stb valid for 1 cycle while reading
442 # clear out before returning data
443 yield dut
.bus
.cyc
.eq(0)
444 yield dut
.bus
.stb
.eq(0)
445 yield dut
.bus
.we
.eq(0)
446 yield dut
.bus
.adr
.eq(0)
447 yield dut
.bus
.sel
.eq(0)
451 def sim_xics_icp(dut
):
454 data
= yield from wb_read(dut
, MFRR
)
455 print ("mfrr", hex(data
), bin(data
))
456 assert (yield dut
.core_irq_o
) == 0
460 # read wb XIRR (8-bit)
461 data
= yield from wb_read(dut
, XIRR
, False)
462 print ("xirr", hex(data
), bin(data
))
463 assert (yield dut
.core_irq_o
) == 0
467 # read wb XIRR (32-bit)
468 data
= yield from wb_read(dut
, XIRR
)
469 print ("xirr", hex(data
), bin(data
))
470 assert (yield dut
.core_irq_o
) == 0
475 data
= yield from wb_read(dut
, XIRR_POLL
)
476 print ("xirr poll", hex(data
), bin(data
))
477 assert (yield dut
.core_irq_o
) == 0
480 # set dut src/pri to something, anything
482 yield dut
.ics_i
.src
.eq(9)
483 yield dut
.ics_i
.pri
.eq(0x1e)
486 data
= yield from wb_read(dut
, MFRR
)
487 print ("mfrr", hex(data
), bin(data
))
488 assert (yield dut
.core_irq_o
) == 0
492 # read wb XIRR (8-bit)
493 data
= yield from wb_read(dut
, XIRR
, False)
494 print ("xirr", hex(data
), bin(data
))
495 assert (yield dut
.core_irq_o
) == 0
499 # read wb XIRR (32-bit)
500 data
= yield from wb_read(dut
, XIRR
)
501 print ("xirr", hex(data
), bin(data
))
502 assert (yield dut
.core_irq_o
) == 0
507 data
= yield from wb_read(dut
, XIRR_POLL
)
508 print ("xirr poll", hex(data
), bin(data
))
509 assert (yield dut
.core_irq_o
) == 0
511 ######################
514 yield from wb_write(dut
, XIRR
, data
)
515 print ("xirr written", hex(data
), bin(data
))
517 assert (yield dut
.core_irq_o
) == 1 # ok *now* it should be set
520 data
= yield from wb_read(dut
, XIRR_POLL
, False)
521 print ("xirr poll", hex(data
), bin(data
))
522 assert (yield dut
.core_irq_o
) == 1 # should not clear
524 # read wb XIRR (8-bit)
525 data
= yield from wb_read(dut
, XIRR
, False)
526 print ("xirr", hex(data
), bin(data
))
527 assert (yield dut
.core_irq_o
) == 1 # should not clear
529 # read wb XIRR (32-bit)
530 data
= yield from wb_read(dut
, XIRR
)
531 print ("xirr", hex(data
), bin(data
))
533 assert (yield dut
.core_irq_o
) == 0
539 return int.from_bytes(x
.to_bytes(4, byteorder
='little'),
540 byteorder
='big', signed
=False)
542 def get_field(x
, wid
, shift
):
544 return x
& ((1<<wid
)-1)
547 def sim_xics(icp
, ics
):
550 data
= yield from wb_read(ics
, 0)
551 print ("config", hex(data
), bin(data
))
553 base
= get_field(data
, 24, 0)
554 pri
= get_field(data
, 8, 24)
555 print (" base", hex(base
))
556 print (" pri", hex(pri
))
564 data
= yield from wb_read(ics
, 0x800)
565 print ("xive0", hex(data
), bin(data
))
567 irq
= get_field(data
, 1, 31)
568 rsvd
= get_field(data
, 1, 30)
569 p
= get_field(data
, 1, 29)
570 q
= get_field(data
, 1, 28)
571 rsvd2
= get_field(data
, 8, 20)
572 target
= get_field(data
, 12, 8)
573 prio
= get_field(data
, 8, 0)
574 print(" irq", hex(irq
))
575 print(" rsvd", hex(rsvd
))
578 print(" rsvd2", hex(rsvd2
))
579 print(" target", hex(target
))
580 print(" prio", hex(prio
))
581 assert irq
== 0 # not active
584 assert target
== 0 # not implemented
590 # raise XIVE 1 (just for fun)
591 yield ics
.int_level_i
.eq(1<<1)
593 yield # wait for interrupt to propagate through from ics to icp...
596 data
= yield from wb_read(ics
, 0x804)
597 print ("xive1", hex(data
), bin(data
))
599 irq
= get_field(data
, 1, 31)
600 rsvd
= get_field(data
, 1, 30)
601 p
= get_field(data
, 1, 29)
602 q
= get_field(data
, 1, 28)
603 rsvd2
= get_field(data
, 8, 20)
604 target
= get_field(data
, 12, 8)
605 prio
= get_field(data
, 8, 0)
606 print(" irq", hex(irq
))
607 print(" rsvd", hex(rsvd
))
610 print(" rsvd2", hex(rsvd2
))
611 print(" target", hex(target
))
612 print(" prio", hex(prio
))
613 assert irq
== 1 # active!
616 assert target
== 0 # not implemented
622 # check that after setting IRQ 2 core is still 0 because priority is 0xff
623 assert (yield icp
.core_irq_o
) == 0
626 # set XIVE1 priority to 0xf0
628 yield from wb_write(ics
, 0x804, data
)
629 print ("XIVE1 priority written", hex(data
), bin(data
))
631 ######################
634 yield from wb_write(icp
, XIRR
, data
)
635 print ("xirr written", hex(data
), bin(data
))
637 assert (yield icp
.core_irq_o
) == 1 # ok *now* it should be set
639 # read wb XIRR (32-bit)
640 data
= yield from wb_read(icp
, XIRR
)
641 print ("xirr", hex(data
), bin(data
))
643 cppr
= get_field(data
, 8, 24)
644 xisr
= get_field(data
, 24, 0)
645 print(" cppr", hex(cppr
))
646 print(" xisr", hex(xisr
))
648 assert (yield icp
.core_irq_o
) == 0
657 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
658 with
open("test_xics_icp.il", "w") as f
:
662 m
.submodules
.xics_icp
= dut
667 sim
.add_sync_process(wrap(sim_xics_icp(dut
)))
668 sim_writer
= sim
.write_vcd('test_xics_icp.vcd')
675 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
676 with
open("test_xics_ics.il", "w") as f
:
679 #run_simulation(dut, ldst_sim(dut), vcd_name='test_ldst_regspec.vcd')
684 m
.submodules
.icp
= icp
= XICS_ICP()
685 m
.submodules
.ics
= ics
= XICS_ICS()
686 m
.d
.comb
+= icp
.ics_i
.eq(ics
.icp_o
)
688 vl
= rtlil
.convert(m
, ports
=icp
.ports()+ics
.ports())
689 with
open("test_xics.il", "w") as f
:
695 sim
.add_sync_process(wrap(sim_xics(icp
, ics
)))
696 sim_writer
= sim
.write_vcd('test_xics.vcd')
701 if __name__
== '__main__':