whoops, combinatorial loop on pending_priority
[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 from soc.bus.test.wb_rw import wb_read, wb_write
27
28
29 cxxsim = False
30 if cxxsim:
31 from nmigen.sim.cxxsim import Simulator, Settle
32 else:
33 from nmigen.back.pysim import Simulator, Settle
34
35
36
37 class ICS2ICP(RecordObject):
38 """
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
42 """
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)
47
48 # hardwire the hardware IRQ priority
49 HW_PRIORITY = Const(0x80, 8)
50
51 # 8 bit offsets for each presentation - all addresses are in "words"
52 XIRR_POLL = 0x00 # 0x000
53 XIRR = 0x01 # 0x004
54 RESV0 = 0x02 # 0x008
55 MFRR = 0x03 # 0x00c
56
57
58 class RegInternal(RecordObject):
59 def __init__(self, name=None):
60 super().__init__(name=name)
61 self.xisr = Signal(24)
62 self.cppr = Signal(8)
63 self.mfrr = Signal(8, reset=0xff) # mask everything on reset
64 self.irq = Signal(1)
65 self.wb_rd_data = Signal(32)
66 self.wb_ack = Signal(1)
67
68
69 def bswap(v):
70 return Cat(v[24:32], v[16:24], v[8:16], v[0:8])
71
72
73 class XICS_ICP(Elaboratable):
74
75 def __init__(self):
76 class Spec: pass
77 spec = Spec()
78 spec.addr_wid = 30
79 spec.mask_wid = 4
80 spec.reg_wid = 32
81 self.bus = Record(make_wb_layout(spec), name="icp_wb")
82 self.ics_i = ICS2ICP("ics_i")
83 self.core_irq_o = Signal()
84
85 def elaborate(self, platform):
86 m = Module()
87 comb, sync = m.d.comb, m.d.sync
88
89 r = RegInternal()
90 r_next = RegInternal()
91
92 sync += r.eq(r_next)
93 # We delay core_irq_out by a cycle to help with timing
94 sync += self.core_irq_o.eq(r.irq)
95
96 comb += self.bus.dat_r.eq(r.wb_rd_data)
97 comb += self.bus.ack.eq(r.wb_ack)
98
99 v = RegInternal()
100 xirr_accept_rd = Signal()
101
102 be_in = Signal(32)
103 be_out = Signal(32)
104
105 pending_priority = Signal(8)
106 min_pri = Signal(8)
107
108 comb += v.eq(r) # start from the register (r)
109 comb += v.wb_ack.eq(0)
110
111 comb += xirr_accept_rd.eq(0)
112
113 comb += be_in.eq(bswap(self.bus.dat_w))
114 comb += be_out.eq(0)
115
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])
124 with m.Case(XIRR):
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) :" & \
128 # to_hstring(be_in);
129 pass
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));
133 pass
134 with m.Else():
135 #report "ICP XIRR UNSUPPORTED write ! sel=" & \
136 # to_hstring(self.bus.sel);
137 pass
138 with m.Case(MFRR ):
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);
142 pass
143 with m.Elif(self.bus.sel == 0x1): # 1 byte
144 # report "ICP MFRR write byte:" & \
145 # to_hstring(be_in(31 downto 24));
146 pass
147 with m.Else():
148 # report "ICP MFRR UNSUPPORTED write ! sel=" & \
149 # to_hstring(self.bus.sel);
150 pass
151
152 with m.Else(): # read
153
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)
158 with m.Case(XIRR):
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)
163 with m.Case(MFRR):
164 # report "ICP MFRR read";
165 comb += be_out.eq(r.mfrr)
166
167 comb += pending_priority.eq(0xff)
168 comb += v.xisr.eq(0x0)
169 comb += v.irq.eq(0x0)
170
171 # set XISR
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)
175
176 # Check MFRR
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)
180 with m.Else():
181 comb += min_pri.eq(pending_priority)
182
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)
190
191 comb += v.wb_rd_data.eq(bswap(be_out))
192
193 # check if the core needs an interrupt notification (or clearing)
194 comb += v.irq.eq(min_pri < v.cppr)
195 with m.If(v.irq):
196 with m.If(~r.irq):
197 #report "IRQ set";
198 pass
199 with m.Elif(r.irq):
200 #report "IRQ clr";
201 pass
202
203 comb += r_next.eq(v)
204
205 return m
206
207 def __iter__(self):
208 for field in self.bus.fields.values():
209 yield field
210 yield from self.ics_i
211 yield self.core_irq_o
212
213 def ports(self):
214 return list(self)
215
216
217 class Xive(RecordObject):
218 def __init__(self, name, wid, rst):
219 super().__init__(name=name)
220 self.pri = Signal(wid, reset=rst)
221
222
223
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
229 class Spec: pass
230 spec = Spec()
231 spec.addr_wid = 30
232 spec.mask_wid = 4
233 spec.reg_wid = 32
234 self.bus = Record(make_wb_layout(spec), name="ics_wb")
235
236 self.int_level_i = Signal(SRC_NUM)
237 self.icp_o = ICS2ICP("icp_o")
238
239 def prio_pack(self, pri8):
240 return pri8[:self.PRIO_BITS]
241
242 def prio_unpack(self, pri):
243 return Mux(pri == self.pri_masked, Const(0xff, 8), pri[:self.PRIO_BITS])
244
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);
250 return a < b;
251
252 def elaborate(self, platform):
253 m = Module()
254 comb, sync = m.d.comb, m.d.sync
255
256 xives = Array([Xive("xive%d" % i, self.PRIO_BITS, self.pri_masked)
257 for i in range(self.SRC_NUM)])
258
259 wb_valid = Signal()
260 reg_idx = Signal(log2_int(self.SRC_NUM))
261 icp_o_next = ICS2ICP("icp_r")
262 int_level_l = Signal(self.SRC_NUM)
263
264 # Register map
265 # 0 : Config
266 # 4 : Debug/diagnostics
267 # 800 : XIVE0
268 # 804 : XIVE1 ...
269 #
270 # Config register format:
271 #
272 # 23.. 0 : Interrupt base (hard wired to 16)
273 # 27.. 24 : #prio bits (1..8)
274 #
275 # XIVE register format:
276 #
277 # 31 : input bit (reflects interrupt input)
278 # 30 : reserved
279 # 29 : P (mirrors input for now)
280 # 28 : Q (not implemented in this version)
281 # 30 .. : reserved
282 # 19 .. 8 : target (not implemented in this version)
283 # 7 .. 0 : prio/mask
284
285 reg_is_xive = Signal()
286 reg_is_config = Signal()
287 reg_is_debug = Signal()
288
289 assert self.SRC_NUM == 16, "Fixup address decode with log2"
290
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)
294
295 # Register index XX FIXME: figure out bits from SRC_NUM
296 comb += reg_idx.eq(self.bus.adr[:4])
297
298 # Latch interrupt inputs for timing
299 sync += int_level_l.eq(self.int_level_i)
300
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)
304
305 # Big read mux. This could be replaced by a slower state
306 # machine iterating registers instead if timing gets tight.
307 be_out = Signal(32)
308 comb += be_out.eq(0)
309
310 # XIVE reg
311 with m.If(reg_is_xive):
312 pri_i = self.prio_unpack(xives[reg_idx].pri)
313 ibit = Signal()
314 comb += ibit.eq(int_level_l.bit_select(reg_idx, 1))
315 comb += be_out.eq(Cat(pri_i, # bits 0..7
316 Const(0, 20), # 8-27
317 0, # 28
318 ibit, # 29
319 0, # 30
320 ibit)) # 31
321 # Config reg
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
326 # Debug reg
327 with m.Elif(reg_is_debug):
328 comb += be_out.eq(Cat(icp_o_next.pri, # 0-7
329 Const(0, 20), # 8-27
330 icp_o_next.src)) # 28-31
331
332 sync += self.bus.dat_r.eq(bswap(be_out))
333 sync += self.bus.ack.eq(wb_valid)
334
335 # Register write machine
336 be_in = Signal(32)
337 # Byteswapped input
338 comb += be_in.eq(bswap(self.bus.dat_w))
339
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));
347 pass
348
349 # generate interrupt. This is a simple combinational process,
350 # potentially wasteful in HW for large number of interrupts.
351 #
352 # could be replaced with iterative state machines and a message
353 # system between ICSs' (plural) and ICP incl. reject etc...
354 #
355 sync += self.icp_o.eq(icp_o_next)
356
357 max_idx = Signal(log2_int(self.SRC_NUM))
358 max_pri = Signal(self.PRIO_BITS)
359
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)
371 max_pri = cur_pri
372 max_idx = cur_idx
373 with m.If(max_pri != self.pri_masked):
374 #report "MFI: " & integer'image(max_idx) &
375 #" pri=" & to_hstring(prio_unpack(max_pri));
376 pass
377 comb += icp_o_next.src.eq(max_idx)
378 comb += icp_o_next.pri.eq(self.prio_unpack(max_pri))
379
380 return m
381
382 def __iter__(self):
383 for field in self.bus.fields.values():
384 yield field
385 yield self.int_level_i
386 yield from self.icp_o.ports()
387
388 def ports(self):
389 return list(self)
390
391
392
393 def sim_xics_icp(dut):
394
395 # read wb XIRR_MFRR
396 data = yield from wb_read(dut.bus, MFRR)
397 print ("mfrr", hex(data), bin(data))
398 assert (yield dut.core_irq_o) == 0
399
400 yield
401
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
406
407 yield
408
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
413
414 yield
415
416 # read wb XIRR_POLL
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
420
421 ##################
422 # set dut src/pri to something, anything
423
424 yield dut.ics_i.src.eq(9)
425 yield dut.ics_i.pri.eq(0x1e)
426
427 # read wb XIRR_MFRR
428 data = yield from wb_read(dut.bus, MFRR)
429 print ("mfrr", hex(data), bin(data))
430 assert (yield dut.core_irq_o) == 0
431
432 yield
433
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
438
439 yield
440
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
445
446 yield
447
448 # read wb XIRR_POLL
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
452
453 ######################
454 # write XIRR
455 data = 0xfe
456 yield from wb_write(dut.bus, XIRR, data)
457 print ("xirr written", hex(data), bin(data))
458
459 assert (yield dut.core_irq_o) == 1 # ok *now* it should be set
460
461 # read wb XIRR_POLL
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
465
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
470
471 # read wb XIRR (32-bit)
472 data = yield from wb_read(dut.bus, XIRR)
473 print ("xirr", hex(data), bin(data))
474 yield
475 assert (yield dut.core_irq_o) == 0
476
477 yield
478
479
480 def swap32(x):
481 return int.from_bytes(x.to_bytes(4, byteorder='little'),
482 byteorder='big', signed=False)
483
484 def get_field(x, wid, shift):
485 x = x >> shift
486 return x & ((1<<wid)-1)
487
488
489 def sim_xics(icp, ics):
490
491 # read config
492 data = yield from wb_read(ics.bus, 0)
493 print ("config", hex(data), bin(data))
494 data = swap32(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))
499 assert base == 16
500 assert pri == 8
501
502 yield
503 yield
504
505 # read XIVE0
506 data = yield from wb_read(ics.bus, 0x800//4)
507 print ("xive0", hex(data), bin(data))
508 data = swap32(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))
518 print(" p", hex(p))
519 print(" q", hex(q))
520 print(" rsvd2", hex(rsvd2))
521 print(" target", hex(target))
522 print(" prio", hex(prio))
523 assert irq == 0 # not active
524 assert rsvd == 0
525 assert rsvd2 == 0
526 assert target == 0 # not implemented
527 assert prio == 0xff
528
529 yield
530 yield
531
532 # raise XIVE 1 (just for fun)
533 yield ics.int_level_i.eq(1<<1)
534
535 yield # wait for interrupt to propagate through from ics to icp...
536
537 # read XIVE1
538 data = yield from wb_read(ics.bus, 0x804//4)
539 print ("xive1", hex(data), bin(data))
540 data = swap32(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))
550 print(" p", hex(p))
551 print(" q", hex(q))
552 print(" rsvd2", hex(rsvd2))
553 print(" target", hex(target))
554 print(" prio", hex(prio))
555 assert irq == 1 # active!
556 assert rsvd == 0
557 assert rsvd2 == 0
558 assert target == 0 # not implemented
559 assert prio == 0xff
560
561 yield
562 yield
563
564 # check that after setting IRQ 2 core is still 0 because priority is 0xff
565 assert (yield icp.core_irq_o) == 0
566 yield
567
568 # set XIVE1 priority to 0xf0
569 data = swap32(0xf0)
570 yield from wb_write(ics.bus, 0x804//4, data)
571 print ("XIVE1 priority written", hex(data), bin(data))
572
573 ######################
574 # write XIRR
575 data = 0xfe
576 yield from wb_write(icp.bus, XIRR, data)
577 print ("xirr written", hex(data), bin(data))
578
579 assert (yield icp.core_irq_o) == 1 # ok *now* it should be set
580
581 # read wb XIRR (32-bit)
582 data = yield from wb_read(icp.bus, XIRR)
583 print ("xirr", hex(data), bin(data))
584 data = swap32(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))
589 yield
590 assert (yield icp.core_irq_o) == 0
591
592 yield
593
594
595
596 def test_xics_icp():
597
598 dut = XICS_ICP()
599 vl = rtlil.convert(dut, ports=dut.ports())
600 with open("test_xics_icp.il", "w") as f:
601 f.write(vl)
602
603 m = Module()
604 m.submodules.xics_icp = dut
605
606 sim = Simulator(m)
607 sim.add_clock(1e-6)
608
609 sim.add_sync_process(wrap(sim_xics_icp(dut)))
610 sim_writer = sim.write_vcd('test_xics_icp.vcd')
611 with sim_writer:
612 sim.run()
613
614 def test_xics_ics():
615
616 dut = XICS_ICS()
617 vl = rtlil.convert(dut, ports=dut.ports())
618 with open("test_xics_ics.il", "w") as f:
619 f.write(vl)
620
621 #run_simulation(dut, ldst_sim(dut), vcd_name='test_ldst_regspec.vcd')
622
623 def test_xics():
624
625 m = Module()
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)
629
630 vl = rtlil.convert(m, ports=icp.ports()+ics.ports())
631 with open("test_xics.il", "w") as f:
632 f.write(vl)
633
634 sim = Simulator(m)
635 sim.add_clock(1e-6)
636
637 sim.add_sync_process(wrap(sim_xics(icp, ics)))
638 sim_writer = sim.write_vcd('test_xics.vcd')
639 with sim_writer:
640 sim.run()
641
642
643 if __name__ == '__main__':
644 test_xics_icp()
645 test_xics_ics()
646 test_xics()
647