adding option to include XICS external interrupts.
[soc.git] / src / soc / interrupts / xics.py
1 """Microwatt xics.vhdl converted to nmigen
2 #
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.
6 #
7 # The sources have a configurable IRQ priority set a set of ICS
8 # registers in the source units.
9 #
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
12 #
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
17 # via XICS)
18 #
19 """
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
27 cxxsim = False
28 if cxxsim:
29 from nmigen.sim.cxxsim import Simulator, Settle
30 else:
31 from nmigen.back.pysim import Simulator, Settle
32
33
34
35 class ICS2ICP(RecordObject):
36 """
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
40 """
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)
45
46 # hardwire the hardware IRQ priority
47 HW_PRIORITY = Const(0x80, 8)
48
49 # 8 bit offsets for each presentation
50 XIRR_POLL = 0x00
51 XIRR = 0x04
52 RESV0 = 0x08
53 MFRR = 0x0c
54
55
56 class RegInternal(RecordObject):
57 def __init__(self, name=None):
58 super().__init__(name=name)
59 self.xisr = Signal(24)
60 self.cppr = Signal(8)
61 self.mfrr = Signal(8, reset=0xff) # mask everything on reset
62 self.irq = Signal(1)
63 self.wb_rd_data = Signal(32)
64 self.wb_ack = Signal(1)
65
66
67 def bswap(v):
68 return Cat(v[24:32], v[16:24], v[8:16], v[0:8])
69
70
71 class XICS_ICP(Elaboratable):
72
73 def __init__(self):
74 class Spec: pass
75 spec = Spec()
76 spec.addr_wid = 30
77 spec.mask_wid = 4
78 spec.reg_wid = 32
79 self.bus = Record(make_wb_layout(spec), name="icp_wb")
80 self.ics_i = ICS2ICP("ics_i")
81 self.core_irq_o = Signal()
82
83 def elaborate(self, platform):
84 m = Module()
85 comb, sync = m.d.comb, m.d.sync
86
87 r = RegInternal()
88 r_next = RegInternal()
89
90 sync += r.eq(r_next)
91 # We delay core_irq_out by a cycle to help with timing
92 sync += self.core_irq_o.eq(r.irq)
93
94 comb += self.bus.dat_r.eq(r.wb_rd_data)
95 comb += self.bus.ack.eq(r.wb_ack)
96
97 v = RegInternal()
98 xirr_accept_rd = Signal()
99
100 be_in = Signal(32)
101 be_out = Signal(32)
102
103 pending_priority = Signal(8)
104
105 comb += v.eq(r) # start from the register (r)
106 comb += v.wb_ack.eq(0)
107
108 comb += xirr_accept_rd.eq(0)
109
110 comb += be_in.eq(bswap(self.bus.dat_w))
111 comb += be_out.eq(0)
112
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])
121 with m.Case(XIRR):
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) :" & \
125 # to_hstring(be_in);
126 pass
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));
130 pass
131 with m.Else():
132 #report "ICP XIRR UNSUPPORTED write ! sel=" & \
133 # to_hstring(self.bus.sel);
134 pass
135 with m.Case(MFRR ):
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);
139 pass
140 with m.Elif(self.bus.sel == 0x1): # 1 byte
141 # report "ICP MFRR write byte:" & \
142 # to_hstring(be_in(31 downto 24));
143 pass
144 with m.Else():
145 # report "ICP MFRR UNSUPPORTED write ! sel=" & \
146 # to_hstring(self.bus.sel);
147 pass
148
149 with m.Else(): # read
150
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)
155 with m.Case(XIRR):
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)
160 with m.Case(MFRR):
161 # report "ICP MFRR read";
162 comb += be_out.eq(r.mfrr)
163
164 comb += pending_priority.eq(0xff)
165 comb += v.xisr.eq(0x0)
166 comb += v.irq.eq(0x0)
167
168 # set XISR
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)
172
173 # Check MFRR
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)
177
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)
185
186 comb += v.wb_rd_data.eq(bswap(be_out))
187
188 # check if the core needs an interrupt notification (or clearing)
189 comb += v.irq.eq(pending_priority < v.cppr)
190 with m.If(v.irq):
191 with m.If(~r.irq):
192 #report "IRQ set";
193 pass
194 with m.Elif(r.irq):
195 #report "IRQ clr";
196 pass
197
198 comb += r_next.eq(v)
199
200 return m
201
202 def __iter__(self):
203 for field in self.bus.fields.values():
204 yield field
205 yield from self.ics_i
206 yield self.core_irq_o
207
208 def ports(self):
209 return list(self)
210
211
212 class Xive(RecordObject):
213 def __init__(self, name, wid, rst):
214 super().__init__(name=name)
215 self.pri = Signal(wid, reset=rst)
216
217
218
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
224 class Spec: pass
225 spec = Spec()
226 spec.addr_wid = 30
227 spec.mask_wid = 4
228 spec.reg_wid = 32
229 self.bus = Record(make_wb_layout(spec), name="ics_wb")
230
231 self.int_level_i = Signal(SRC_NUM)
232 self.icp_o = ICS2ICP("icp_o")
233
234 def prio_pack(self, pri8):
235 return pri8[:self.PRIO_BITS]
236
237 def prio_unpack(self, pri):
238 return Mux(pri == self.pri_masked, Const(0xff, 8), pri[:self.PRIO_BITS])
239
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);
245 return a < b;
246
247 def elaborate(self, platform):
248 m = Module()
249 comb, sync = m.d.comb, m.d.sync
250
251 xives = Array([Xive("xive%d" % i, self.PRIO_BITS, self.pri_masked)
252 for i in range(self.SRC_NUM)])
253
254 wb_valid = Signal()
255 reg_idx = Signal(log2_int(self.SRC_NUM))
256 icp_o_next = ICS2ICP("icp_r")
257 int_level_l = Signal(self.SRC_NUM)
258
259 # Register map
260 # 0 : Config
261 # 4 : Debug/diagnostics
262 # 800 : XIVE0
263 # 804 : XIVE1 ...
264 #
265 # Config register format:
266 #
267 # 23.. 0 : Interrupt base (hard wired to 16)
268 # 27.. 24 : #prio bits (1..8)
269 #
270 # XIVE register format:
271 #
272 # 31 : input bit (reflects interrupt input)
273 # 30 : reserved
274 # 29 : P (mirrors input for now)
275 # 28 : Q (not implemented in this version)
276 # 30 .. : reserved
277 # 19 .. 8 : target (not implemented in this version)
278 # 7 .. 0 : prio/mask
279
280 reg_is_xive = Signal()
281 reg_is_config = Signal()
282 reg_is_debug = Signal()
283
284 assert self.SRC_NUM == 16, "Fixup address decode with log2"
285
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)
289
290 # Register index XX FIXME: figure out bits from SRC_NUM
291 comb += reg_idx.eq(self.bus.adr[2:6])
292
293 # Latch interrupt inputs for timing
294 sync += int_level_l.eq(self.int_level_i)
295
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)
299
300 # Big read mux. This could be replaced by a slower state
301 # machine iterating registers instead if timing gets tight.
302 be_out = Signal(32)
303 comb += be_out.eq(0)
304
305 # XIVE reg
306 with m.If(reg_is_xive):
307 pri_i = self.prio_unpack(xives[reg_idx].pri)
308 ibit = Signal()
309 comb += ibit.eq(int_level_l.bit_select(reg_idx, 1))
310 comb += be_out.eq(Cat(pri_i, # bits 0..7
311 Const(0, 20), # 8-27
312 0, # 28
313 ibit, # 29
314 0, # 30
315 ibit)) # 31
316 # Config reg
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
321 # Debug reg
322 with m.Elif(reg_is_debug):
323 comb += be_out.eq(Cat(icp_o_next.pri, # 0-7
324 Const(0, 20), # 8-27
325 icp_o_next.src)) # 28-31
326
327 sync += self.bus.dat_r.eq(bswap(be_out))
328 sync += self.bus.ack.eq(wb_valid)
329
330 # Register write machine
331 be_in = Signal(32)
332 # Byteswapped input
333 comb += be_in.eq(bswap(self.bus.dat_w))
334
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));
342 pass
343
344 # generate interrupt. This is a simple combinational process,
345 # potentially wasteful in HW for large number of interrupts.
346 #
347 # could be replaced with iterative state machines and a message
348 # system between ICSs' (plural) and ICP incl. reject etc...
349 #
350 sync += self.icp_o.eq(icp_o_next)
351
352 max_idx = Signal(log2_int(self.SRC_NUM))
353 max_pri = Signal(self.PRIO_BITS)
354
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)
366 max_pri = cur_pri
367 max_idx = cur_idx
368 with m.If(max_pri != self.pri_masked):
369 #report "MFI: " & integer'image(max_idx) &
370 #" pri=" & to_hstring(prio_unpack(max_pri));
371 pass
372 comb += icp_o_next.src.eq(max_idx)
373 comb += icp_o_next.pri.eq(self.prio_unpack(max_pri))
374
375 return m
376
377 def __iter__(self):
378 for field in self.bus.fields.values():
379 yield field
380 yield self.int_level_i
381 yield from self.icp_o.ports()
382
383 def ports(self):
384 return list(self)
385
386
387 def wb_write(dut, addr, data, sel=True):
388
389 # read wb
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)
396
397 # wait for ack to go high
398 while True:
399 ack = yield dut.bus.ack
400 print ("ack", ack)
401 if ack:
402 break
403 yield # loop until ack
404 yield dut.bus.stb.eq(0) # drop stb so only 1 thing into pipeline
405
406 # leave cyc/stb valid for 1 cycle while writing
407 yield
408
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)
416
417
418 def wb_read(dut, addr, sel=True):
419
420 # read wb
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)
426
427 # wait for ack to go high
428 while True:
429 ack = yield dut.bus.ack
430 print ("ack", ack)
431 if ack:
432 break
433 yield # loop until ack
434 yield dut.bus.stb.eq(0) # drop stb so only 1 thing into pipeline
435
436 # get data on same cycle that ack raises
437 data = yield dut.bus.dat_r
438
439 # leave cyc/stb valid for 1 cycle while reading
440 yield
441
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)
448 return data
449
450
451 def sim_xics_icp(dut):
452
453 # read wb XIRR_MFRR
454 data = yield from wb_read(dut, MFRR)
455 print ("mfrr", hex(data), bin(data))
456 assert (yield dut.core_irq_o) == 0
457
458 yield
459
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
464
465 yield
466
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
471
472 yield
473
474 # read wb XIRR_POLL
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
478
479 ##################
480 # set dut src/pri to something, anything
481
482 yield dut.ics_i.src.eq(9)
483 yield dut.ics_i.pri.eq(0x1e)
484
485 # read wb XIRR_MFRR
486 data = yield from wb_read(dut, MFRR)
487 print ("mfrr", hex(data), bin(data))
488 assert (yield dut.core_irq_o) == 0
489
490 yield
491
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
496
497 yield
498
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
503
504 yield
505
506 # read wb XIRR_POLL
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
510
511 ######################
512 # write XIRR
513 data = 0xfe
514 yield from wb_write(dut, XIRR, data)
515 print ("xirr written", hex(data), bin(data))
516
517 assert (yield dut.core_irq_o) == 1 # ok *now* it should be set
518
519 # read wb XIRR_POLL
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
523
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
528
529 # read wb XIRR (32-bit)
530 data = yield from wb_read(dut, XIRR)
531 print ("xirr", hex(data), bin(data))
532 yield
533 assert (yield dut.core_irq_o) == 0
534
535 yield
536
537
538 def swap32(x):
539 return int.from_bytes(x.to_bytes(4, byteorder='little'),
540 byteorder='big', signed=False)
541
542 def get_field(x, wid, shift):
543 x = x >> shift
544 return x & ((1<<wid)-1)
545
546
547 def sim_xics(icp, ics):
548
549 # read config
550 data = yield from wb_read(ics, 0)
551 print ("config", hex(data), bin(data))
552 data = swap32(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))
557 assert base == 16
558 assert pri == 8
559
560 yield
561 yield
562
563 # read XIVE0
564 data = yield from wb_read(ics, 0x800)
565 print ("xive0", hex(data), bin(data))
566 data = swap32(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))
576 print(" p", hex(p))
577 print(" q", hex(q))
578 print(" rsvd2", hex(rsvd2))
579 print(" target", hex(target))
580 print(" prio", hex(prio))
581 assert irq == 0 # not active
582 assert rsvd == 0
583 assert rsvd2 == 0
584 assert target == 0 # not implemented
585 assert prio == 0xff
586
587 yield
588 yield
589
590 # raise XIVE 1 (just for fun)
591 yield ics.int_level_i.eq(1<<1)
592
593 yield # wait for interrupt to propagate through from ics to icp...
594
595 # read XIVE1
596 data = yield from wb_read(ics, 0x804)
597 print ("xive1", hex(data), bin(data))
598 data = swap32(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))
608 print(" p", hex(p))
609 print(" q", hex(q))
610 print(" rsvd2", hex(rsvd2))
611 print(" target", hex(target))
612 print(" prio", hex(prio))
613 assert irq == 1 # active!
614 assert rsvd == 0
615 assert rsvd2 == 0
616 assert target == 0 # not implemented
617 assert prio == 0xff
618
619 yield
620 yield
621
622 # check that after setting IRQ 2 core is still 0 because priority is 0xff
623 assert (yield icp.core_irq_o) == 0
624 yield
625
626 # set XIVE1 priority to 0xf0
627 data = swap32(0xf0)
628 yield from wb_write(ics, 0x804, data)
629 print ("XIVE1 priority written", hex(data), bin(data))
630
631 ######################
632 # write XIRR
633 data = 0xfe
634 yield from wb_write(icp, XIRR, data)
635 print ("xirr written", hex(data), bin(data))
636
637 assert (yield icp.core_irq_o) == 1 # ok *now* it should be set
638
639 # read wb XIRR (32-bit)
640 data = yield from wb_read(icp, XIRR)
641 print ("xirr", hex(data), bin(data))
642 data = swap32(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))
647 yield
648 assert (yield icp.core_irq_o) == 0
649
650 yield
651
652
653
654 def test_xics_icp():
655
656 dut = XICS_ICP()
657 vl = rtlil.convert(dut, ports=dut.ports())
658 with open("test_xics_icp.il", "w") as f:
659 f.write(vl)
660
661 m = Module()
662 m.submodules.xics_icp = dut
663
664 sim = Simulator(m)
665 sim.add_clock(1e-6)
666
667 sim.add_sync_process(wrap(sim_xics_icp(dut)))
668 sim_writer = sim.write_vcd('test_xics_icp.vcd')
669 with sim_writer:
670 sim.run()
671
672 def test_xics_ics():
673
674 dut = XICS_ICS()
675 vl = rtlil.convert(dut, ports=dut.ports())
676 with open("test_xics_ics.il", "w") as f:
677 f.write(vl)
678
679 #run_simulation(dut, ldst_sim(dut), vcd_name='test_ldst_regspec.vcd')
680
681 def test_xics():
682
683 m = Module()
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)
687
688 vl = rtlil.convert(m, ports=icp.ports()+ics.ports())
689 with open("test_xics.il", "w") as f:
690 f.write(vl)
691
692 sim = Simulator(m)
693 sim.add_clock(1e-6)
694
695 sim.add_sync_process(wrap(sim_xics(icp, ics)))
696 sim_writer = sim.write_vcd('test_xics.vcd')
697 with sim_writer:
698 sim.run()
699
700
701 if __name__ == '__main__':
702 test_xics_icp()
703 test_xics_ics()
704 test_xics()
705