split out front of div into separate stage, still too long combinatorial
[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 # Bugreports:
20 #
21 # * https://bugs.libre-soc.org/show_bug.cgi?id=407
22 """
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
30
31
32 cxxsim = False
33 if cxxsim:
34 from nmigen.sim.cxxsim import Simulator, Settle
35 else:
36 from nmigen.back.pysim import Simulator, Settle
37
38
39
40 class ICS2ICP(RecordObject):
41 """
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
45 """
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)
50
51 # hardwire the hardware IRQ priority
52 HW_PRIORITY = Const(0x80, 8)
53
54 # 8 bit offsets for each presentation - all addresses are in "words"
55 XIRR_POLL = 0x00 # 0x000
56 XIRR = 0x01 # 0x004
57 RESV0 = 0x02 # 0x008
58 MFRR = 0x03 # 0x00c
59
60
61 class RegInternal(RecordObject):
62 def __init__(self, name=None):
63 super().__init__(name=name)
64 self.xisr = Signal(24)
65 self.cppr = Signal(8)
66 self.mfrr = Signal(8, reset=0xff) # mask everything on reset
67 self.irq = Signal(1)
68 self.wb_rd_data = Signal(32)
69 self.wb_ack = Signal(1)
70
71
72 def bswap(v):
73 return Cat(v[24:32], v[16:24], v[8:16], v[0:8])
74
75
76 class XICS_ICP(Elaboratable):
77
78 def __init__(self, spec=None):
79 if spec is None:
80 class Spec: pass
81 spec = Spec()
82 spec.addr_wid = 30
83 spec.mask_wid = 4
84 spec.reg_wid = 32
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()
88
89 def elaborate(self, platform):
90 m = Module()
91 comb, sync = m.d.comb, m.d.sync
92
93 r = RegInternal()
94 r_next = RegInternal()
95
96 sync += r.eq(r_next)
97 # We delay core_irq_out by a cycle to help with timing
98 sync += self.core_irq_o.eq(r.irq)
99
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)
103
104 v = RegInternal()
105 xirr_accept_rd = Signal()
106
107 be_in = Signal(32)
108 be_out = Signal(32)
109
110 pending_priority = Signal(8)
111 min_pri = Signal(8)
112
113 comb += v.eq(r) # start from the register (r)
114 comb += v.wb_ack.eq(0)
115
116 comb += xirr_accept_rd.eq(0)
117
118 comb += be_in.eq(bswap(self.bus.dat_w))
119 comb += be_out.eq(0)
120
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])
129 with m.Case(XIRR):
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) :" & \
133 # to_hstring(be_in);
134 pass
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));
138 pass
139 with m.Else():
140 #report "ICP XIRR UNSUPPORTED write ! sel=" & \
141 # to_hstring(self.bus.sel);
142 pass
143 with m.Case(MFRR):
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);
147 pass
148 with m.Elif(self.bus.sel == 0x1): # 1 byte
149 # report "ICP MFRR write byte:" & \
150 # to_hstring(be_in(31 downto 24));
151 pass
152 with m.Else():
153 # report "ICP MFRR UNSUPPORTED write ! sel=" & \
154 # to_hstring(self.bus.sel);
155 pass
156
157 with m.Else(): # read
158
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))
163 with m.Case(XIRR):
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)
168 with m.Case(MFRR):
169 # report "ICP MFRR read";
170 comb += be_out[24:32].eq(r.mfrr)
171
172 comb += pending_priority.eq(0xff)
173 comb += v.xisr.eq(0x0)
174 comb += v.irq.eq(0x0)
175
176 # set XISR
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)
180
181 # Check MFRR
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)
185 with m.Else():
186 comb += min_pri.eq(pending_priority)
187
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)
195
196 comb += v.wb_rd_data.eq(bswap(be_out))
197
198 # check if the core needs an interrupt notification (or clearing)
199 with m.If(min_pri < v.cppr):
200 with m.If(~r.irq):
201 #report "IRQ set";
202 pass
203 comb += v.irq.eq(1)
204 with m.Elif(r.irq):
205 #report "IRQ clr";
206 pass
207
208 comb += r_next.eq(v)
209
210 return m
211
212 def __iter__(self):
213 for field in self.bus.fields.values():
214 yield field
215 yield from self.ics_i
216 yield self.core_irq_o
217
218 def ports(self):
219 return list(self)
220
221
222 class Xive(RecordObject):
223 def __init__(self, name, wid, rst):
224 super().__init__(name=name)
225 self.pri = Signal(wid, reset=rst)
226
227
228
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
234 if spec is None:
235 class Spec: pass
236 spec = Spec()
237 spec.addr_wid = 30
238 spec.mask_wid = 4
239 spec.reg_wid = 32
240 self.bus = Record(make_wb_layout(spec, cti=False), name="ics_wb")
241
242 self.int_level_i = Signal(SRC_NUM)
243 self.icp_o = ICS2ICP("icp_o")
244
245 def prio_pack(self, pri8):
246 return pri8[:self.PRIO_BITS]
247
248 def prio_unpack(self, pri):
249 return Mux(pri == self.pri_masked, Const(0xff, 8), pri[:self.PRIO_BITS])
250
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);
256 return a < b;
257
258 def elaborate(self, platform):
259 m = Module()
260 comb, sync = m.d.comb, m.d.sync
261
262 xives = Array([Xive("xive%d" % i, self.PRIO_BITS, self.pri_masked)
263 for i in range(self.SRC_NUM)])
264
265 wb_valid = Signal()
266 reg_idx = Signal(log2_int(self.SRC_NUM))
267 icp_o_next = ICS2ICP("icp_r")
268 int_level_l = Signal(self.SRC_NUM)
269
270 # Register map
271 # 0 : Config
272 # 4 : Debug/diagnostics
273 # 800 : XIVE0
274 # 804 : XIVE1 ...
275 #
276 # Config register format:
277 #
278 # 23.. 0 : Interrupt base (hard wired to 16)
279 # 27.. 24 : #prio bits (1..8)
280 #
281 # XIVE register format:
282 #
283 # 31 : input bit (reflects interrupt input)
284 # 30 : reserved
285 # 29 : P (mirrors input for now)
286 # 28 : Q (not implemented in this version)
287 # 30 .. : reserved
288 # 19 .. 8 : target (not implemented in this version)
289 # 7 .. 0 : prio/mask
290
291 reg_is_xive = Signal()
292 reg_is_config = Signal()
293 reg_is_debug = Signal()
294
295 assert self.SRC_NUM == 16, "Fixup address decode with log2"
296
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)
300
301 # Register index XX FIXME: figure out bits from SRC_NUM
302 comb += reg_idx.eq(self.bus.adr[:4])
303
304 # Latch interrupt inputs for timing
305 sync += int_level_l.eq(self.int_level_i)
306
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)
310
311 # Big read mux. This could be replaced by a slower state
312 # machine iterating registers instead if timing gets tight.
313 be_out = Signal(32)
314 comb += be_out.eq(0)
315
316 # XIVE reg
317 with m.If(reg_is_xive):
318 pri_i = self.prio_unpack(xives[reg_idx].pri)
319 ibit = Signal()
320 comb += ibit.eq(int_level_l.bit_select(reg_idx, 1))
321 comb += be_out.eq(Cat(pri_i, # bits 0..7
322 Const(0, 20), # 8-27
323 0, # 28
324 ibit, # 29
325 0, # 30
326 ibit)) # 31
327 # Config reg
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
332 # Debug reg
333 with m.Elif(reg_is_debug):
334 comb += be_out.eq(Cat(icp_o_next.pri, # 0-7
335 Const(0, 20), # 8-27
336 icp_o_next.src)) # 28-31
337
338 sync += self.bus.dat_r.eq(bswap(be_out))
339 sync += self.bus.ack.eq(wb_valid)
340
341 # Register write machine
342 be_in = Signal(32)
343 # Byteswapped input
344 comb += be_in.eq(bswap(self.bus.dat_w))
345
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));
353 pass
354
355 # generate interrupt. This is a simple combinational process,
356 # potentially wasteful in HW for large number of interrupts.
357 #
358 # could be replaced with iterative state machines and a message
359 # system between ICSs' (plural) and ICP incl. reject etc...
360 #
361 sync += self.icp_o.eq(icp_o_next)
362
363 max_idx = Signal(log2_int(self.SRC_NUM))
364 max_pri = Signal(self.PRIO_BITS)
365
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)
377 max_pri = cur_pri
378 max_idx = cur_idx
379 with m.If(max_pri != self.pri_masked):
380 #report "MFI: " & integer'image(max_idx) &
381 #" pri=" & to_hstring(prio_unpack(max_pri));
382 pass
383 comb += icp_o_next.src.eq(max_idx)
384 comb += icp_o_next.pri.eq(self.prio_unpack(max_pri))
385
386 return m
387
388 def __iter__(self):
389 for field in self.bus.fields.values():
390 yield field
391 yield self.int_level_i
392 yield from self.icp_o.ports()
393
394 def ports(self):
395 return list(self)
396
397
398
399 def sim_xics_icp(dut):
400
401 # read wb XIRR_MFRR
402 data = yield from wb_read(dut.bus, MFRR)
403 print ("mfrr", hex(data), bin(data))
404 assert (yield dut.core_irq_o) == 0
405
406 yield
407
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
412
413 yield
414
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
419
420 yield
421
422 # read wb XIRR_POLL
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
426
427 ##################
428 # set dut src/pri to something, anything
429
430 yield dut.ics_i.src.eq(9)
431 yield dut.ics_i.pri.eq(0x1e)
432
433 # read wb XIRR_MFRR
434 data = yield from wb_read(dut.bus, MFRR)
435 print ("mfrr", hex(data), bin(data))
436 assert (yield dut.core_irq_o) == 0
437
438 yield
439
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
444
445 yield
446
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
451
452 yield
453
454 # read wb XIRR_POLL
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
458
459 ######################
460 # write XIRR
461 data = 0xfe
462 yield from wb_write(dut.bus, XIRR, data)
463 print ("xirr written", hex(data), bin(data))
464
465 assert (yield dut.core_irq_o) == 1 # ok *now* it should be set
466
467 # read wb XIRR_POLL
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
471
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
476
477 # read wb XIRR (32-bit)
478 data = yield from wb_read(dut.bus, XIRR)
479 print ("xirr", hex(data), bin(data))
480 yield
481 assert (yield dut.core_irq_o) == 0
482
483 yield
484
485
486 def swap32(x):
487 return int.from_bytes(x.to_bytes(4, byteorder='little'),
488 byteorder='big', signed=False)
489
490 def get_field(x, wid, shift):
491 x = x >> shift
492 return x & ((1<<wid)-1)
493
494
495 def sim_xics(icp, ics):
496
497 # read config
498 data = yield from wb_read(ics.bus, 0)
499 print ("config", hex(data), bin(data))
500 data = swap32(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))
505 assert base == 16
506 assert pri == 8
507
508 yield
509 yield
510
511 # read XIVE0
512 data = yield from wb_read(ics.bus, 0x800//4)
513 print ("xive0", hex(data), bin(data))
514 data = swap32(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))
524 print(" p", hex(p))
525 print(" q", hex(q))
526 print(" rsvd2", hex(rsvd2))
527 print(" target", hex(target))
528 print(" prio", hex(prio))
529 assert irq == 0 # not active
530 assert rsvd == 0
531 assert rsvd2 == 0
532 assert target == 0 # not implemented
533 assert prio == 0xff
534
535 yield
536 yield
537
538 # raise XIVE 1 (just for fun)
539 yield ics.int_level_i.eq(1<<1)
540
541 yield # wait for interrupt to propagate through from ics to icp...
542
543 # read XIVE1
544 data = yield from wb_read(ics.bus, 0x804//4)
545 print ("xive1", hex(data), bin(data))
546 data = swap32(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))
556 print(" p", hex(p))
557 print(" q", hex(q))
558 print(" rsvd2", hex(rsvd2))
559 print(" target", hex(target))
560 print(" prio", hex(prio))
561 assert irq == 1 # active!
562 assert rsvd == 0
563 assert rsvd2 == 0
564 assert target == 0 # not implemented
565 assert prio == 0xff
566
567 yield
568 yield
569
570 # check that after setting IRQ 2 core is still 0 because priority is 0xff
571 assert (yield icp.core_irq_o) == 0
572 yield
573
574 # set XIVE1 priority to 0xf0
575 data = swap32(0xf0)
576 yield from wb_write(ics.bus, 0x804//4, data)
577 print ("XIVE1 priority written", hex(data), bin(data))
578
579 ######################
580 # write XIRR
581 data = 0xfe
582 yield from wb_write(icp.bus, XIRR, data)
583 print ("xirr written", hex(data), bin(data))
584
585 assert (yield icp.core_irq_o) == 1 # ok *now* it should be set
586
587 # read wb XIRR (32-bit)
588 data = yield from wb_read(icp.bus, XIRR)
589 print ("xirr", hex(data), bin(data))
590 data = swap32(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))
595 yield
596 assert (yield icp.core_irq_o) == 0
597
598 yield
599
600
601
602 def test_xics_icp():
603
604 dut = XICS_ICP()
605 vl = rtlil.convert(dut, ports=dut.ports())
606 with open("test_xics_icp.il", "w") as f:
607 f.write(vl)
608
609 m = Module()
610 m.submodules.xics_icp = dut
611
612 sim = Simulator(m)
613 sim.add_clock(1e-6)
614
615 sim.add_sync_process(wrap(sim_xics_icp(dut)))
616 sim_writer = sim.write_vcd('test_xics_icp.vcd')
617 with sim_writer:
618 sim.run()
619
620 def test_xics_ics():
621
622 dut = XICS_ICS()
623 vl = rtlil.convert(dut, ports=dut.ports())
624 with open("test_xics_ics.il", "w") as f:
625 f.write(vl)
626
627 #run_simulation(dut, ldst_sim(dut), vcd_name='test_ldst_regspec.vcd')
628
629 def test_xics():
630
631 m = Module()
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)
635
636 vl = rtlil.convert(m, ports=icp.ports()+ics.ports())
637 with open("test_xics.il", "w") as f:
638 f.write(vl)
639
640 sim = Simulator(m)
641 sim.add_clock(1e-6)
642
643 sim.add_sync_process(wrap(sim_xics(icp, ics)))
644 sim_writer = sim.write_vcd('test_xics.vcd')
645 with sim_writer:
646 sim.run()
647
648
649 if __name__ == '__main__':
650 test_xics_icp()
651 test_xics_ics()
652 test_xics()
653