start on test joining XICS ICS to ICP
[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))
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)))
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))
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, 0xff, 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
343 # generate interrupt. This is a simple combinational process,
344 # potentially wasteful in HW for large number of interrupts.
345 #
346 # could be replaced with iterative state machines and a message
347 # system between ICSs' (plural) and ICP incl. reject etc...
348 #
349 sync += self.icp_o.eq(icp_o_next)
350
351 max_idx = Signal(log2_int(self.SRC_NUM))
352 max_pri = Signal(self.PRIO_BITS)
353
354 # XXX FIXME: Use a tree
355 comb += max_pri.eq(self.pri_masked)
356 comb += max_idx.eq(0)
357 for i in range(self.SRC_NUM):
358 with m.If(int_level_l[i] & self.a_mf_b(xives[i].pri, max_pri)):
359 comb += max_pri.eq(xives[i].pri)
360 comb += max_idx.eq(i)
361 with m.If(max_pri != self.pri_masked):
362 #report "MFI: " & integer'image(max_idx) &
363 #" pri=" & to_hstring(prio_unpack(max_pri));
364 pass
365 comb += icp_o_next.src.eq(max_idx)
366 comb += icp_o_next.pri.eq(self.prio_unpack(max_pri))
367
368 return m
369
370 def __iter__(self):
371 for field in self.bus.fields.values():
372 yield field
373 yield self.int_level_i
374 yield from self.icp_o.ports()
375
376 def ports(self):
377 return list(self)
378
379
380 def wb_write(dut, addr, data, sel=True):
381
382 # read wb
383 yield dut.bus.we.eq(1)
384 yield dut.bus.cyc.eq(1)
385 yield dut.bus.stb.eq(1)
386 yield dut.bus.sel.eq(0b1111 if sel else 0b1) # 32-bit / 8-bit
387 yield dut.bus.adr.eq(addr)
388 yield dut.bus.dat_w.eq(data)
389
390 # wait for ack to go high
391 while True:
392 ack = yield dut.bus.ack
393 print ("ack", ack)
394 if ack:
395 break
396 yield # loop until ack
397
398 # leave cyc/stb valid for 1 cycle while writing
399 yield
400
401 # clear out before returning data
402 yield dut.bus.cyc.eq(0)
403 yield dut.bus.stb.eq(0)
404 yield dut.bus.we.eq(0)
405 yield dut.bus.adr.eq(0)
406 yield dut.bus.sel.eq(0)
407 yield dut.bus.dat_w.eq(0)
408
409
410 def wb_read(dut, addr, sel=True):
411
412 # read wb
413 yield dut.bus.cyc.eq(1)
414 yield dut.bus.stb.eq(1)
415 yield dut.bus.we.eq(0)
416 yield dut.bus.sel.eq(0b1111 if sel else 0b1) # 32-bit / 8-bit
417 yield dut.bus.adr.eq(addr)
418
419 # wait for ack to go high
420 while True:
421 ack = yield dut.bus.ack
422 print ("ack", ack)
423 if ack:
424 break
425 yield # loop until ack
426
427 # get data on same cycle that ack raises
428 data = yield dut.bus.dat_r
429
430 # leave cyc/stb valid for 1 cycle while reading
431 yield
432
433 # clear out before returning data
434 yield dut.bus.cyc.eq(0)
435 yield dut.bus.stb.eq(0)
436 yield dut.bus.we.eq(0)
437 yield dut.bus.adr.eq(0)
438 yield dut.bus.sel.eq(0)
439 return data
440
441
442 def sim_xics_icp(dut):
443
444 # read wb XIRR_MFRR
445 data = yield from wb_read(dut, MFRR)
446 print ("mfrr", hex(data), bin(data))
447 assert (yield dut.core_irq_o) == 0
448
449 yield
450
451 # read wb XIRR (8-bit)
452 data = yield from wb_read(dut, XIRR, False)
453 print ("xirr", hex(data), bin(data))
454 assert (yield dut.core_irq_o) == 0
455
456 yield
457
458 # read wb XIRR (32-bit)
459 data = yield from wb_read(dut, XIRR)
460 print ("xirr", hex(data), bin(data))
461 assert (yield dut.core_irq_o) == 0
462
463 yield
464
465 # read wb XIRR_POLL
466 data = yield from wb_read(dut, XIRR_POLL)
467 print ("xirr poll", hex(data), bin(data))
468 assert (yield dut.core_irq_o) == 0
469
470 ##################
471 # set dut src/pri to something, anything
472
473 yield dut.ics_i.src.eq(9)
474 yield dut.ics_i.pri.eq(0x1e)
475
476 # read wb XIRR_MFRR
477 data = yield from wb_read(dut, MFRR)
478 print ("mfrr", hex(data), bin(data))
479 assert (yield dut.core_irq_o) == 0
480
481 yield
482
483 # read wb XIRR (8-bit)
484 data = yield from wb_read(dut, XIRR, False)
485 print ("xirr", hex(data), bin(data))
486 assert (yield dut.core_irq_o) == 0
487
488 yield
489
490 # read wb XIRR (32-bit)
491 data = yield from wb_read(dut, XIRR)
492 print ("xirr", hex(data), bin(data))
493 assert (yield dut.core_irq_o) == 0
494
495 yield
496
497 # read wb XIRR_POLL
498 data = yield from wb_read(dut, XIRR_POLL)
499 print ("xirr poll", hex(data), bin(data))
500 assert (yield dut.core_irq_o) == 0
501
502 ######################
503 # write XIRR
504 data = 0xfe
505 yield from wb_write(dut, XIRR, data)
506 print ("xirr written", hex(data), bin(data))
507
508 assert (yield dut.core_irq_o) == 0 # write takes 1 cycle to propagate
509 yield # wait for it...
510 assert (yield dut.core_irq_o) == 1 # ok *now* it should be set
511
512 # read wb XIRR_POLL
513 data = yield from wb_read(dut, XIRR_POLL, False)
514 print ("xirr poll", hex(data), bin(data))
515 assert (yield dut.core_irq_o) == 1 # should not clear
516
517 yield # XXX only works if there is a 2-clock delay between POLL and XIRR
518 yield
519
520 # read wb XIRR (8-bit)
521 data = yield from wb_read(dut, XIRR, False)
522 print ("xirr", hex(data), bin(data))
523 yield
524 assert (yield dut.core_irq_o) == 1 # should not clear
525
526 yield
527
528 # read wb XIRR (32-bit)
529 data = yield from wb_read(dut, XIRR)
530 print ("xirr", hex(data), bin(data))
531 yield
532 assert (yield dut.core_irq_o) == 0
533
534 yield
535
536
537 def test_xics_icp():
538
539 dut = XICS_ICP()
540 vl = rtlil.convert(dut, ports=dut.ports())
541 with open("test_xics_icp.il", "w") as f:
542 f.write(vl)
543
544 m = Module()
545 m.submodules.xics_icp = dut
546
547 sim = Simulator(m)
548 sim.add_clock(1e-6)
549
550 sim.add_sync_process(wrap(sim_xics_icp(dut)))
551 sim_writer = sim.write_vcd('test_xics_icp.vcd')
552 with sim_writer:
553 sim.run()
554
555 def test_xics_ics():
556
557 dut = XICS_ICS()
558 vl = rtlil.convert(dut, ports=dut.ports())
559 with open("test_xics_ics.il", "w") as f:
560 f.write(vl)
561
562 #run_simulation(dut, ldst_sim(dut), vcd_name='test_ldst_regspec.vcd')
563
564 def test_xics():
565
566 m = Module()
567 m.submodules.icp = icp = XICS_ICP()
568 m.submodules.ics = ics = XICS_ICS()
569 m.d.comb += icp.ics_i.eq(ics.icp_o)
570
571 vl = rtlil.convert(m, ports=icp.ports()+ics.ports())
572 with open("test_xics.il", "w") as f:
573 f.write(vl)
574
575 sim = Simulator(m)
576 sim.add_clock(1e-6)
577
578 #sim.add_sync_process(wrap(sim_xics_icp(dut)))
579 sim_writer = sim.write_vcd('test_xics.vcd')
580 with sim_writer:
581 sim.run()
582
583
584 if __name__ == '__main__':
585 test_xics_icp()
586 test_xics_ics()
587 test_xics()
588