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
))
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)))
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
))
231 self
.int_level_i
= Signal(SRC_NUM
)
232 self
.icp_out
= 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
, 0xff, 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_out_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_out_next
.pri
, # 0-7
325 icp_out_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));
343 # generate interrupt. This is a simple combinational process,
344 # potentially wasteful in HW for large number of interrupts.
346 # could be replaced with iterative state machines and a message
347 # system between ICSs' (plural) and ICP incl. reject etc...
349 sync
+= self
.icp_out
.eq(icp_out_next
)
351 max_idx
= Signal(log2_int(self
.SRC_NUM
))
352 max_pri
= Signal(self
.PRIO_BITS
)
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));
365 comb
+= icp_out_next
.src
.eq(max_idx
)
366 comb
+= icp_out_next
.pri
.eq(self
.prio_unpack(max_pri
))
371 for field
in self
.bus
.fields
.values():
373 yield self
.int_level_i
374 yield from self
.icp_out
.ports()
380 def wb_write(dut
, addr
, data
, sel
=True):
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
)
390 # wait for ack to go high
392 ack
= yield dut
.bus
.ack
396 yield # loop until ack
398 # leave cyc/stb valid for 1 cycle while writing
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)
410 def wb_read(dut
, addr
, sel
=True):
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
)
419 # wait for ack to go high
421 ack
= yield dut
.bus
.ack
425 yield # loop until ack
427 # get data on same cycle that ack raises
428 data
= yield dut
.bus
.dat_r
430 # leave cyc/stb valid for 1 cycle while reading
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)
442 def sim_xics_icp(dut
):
445 data
= yield from wb_read(dut
, MFRR
)
446 print ("mfrr", hex(data
), bin(data
))
447 assert (yield dut
.core_irq_o
) == 0
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
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
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
471 # set dut src/pri to something, anything
473 yield dut
.ics_i
.src
.eq(9)
474 yield dut
.ics_i
.pri
.eq(0x1e)
477 data
= yield from wb_read(dut
, MFRR
)
478 print ("mfrr", hex(data
), bin(data
))
479 assert (yield dut
.core_irq_o
) == 0
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
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
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
502 ######################
505 yield from wb_write(dut
, XIRR
, data
)
506 print ("xirr written", hex(data
), bin(data
))
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
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
517 yield # XXX only works if there is a 2-clock delay between POLL and XIRR
520 # read wb XIRR (8-bit)
521 data
= yield from wb_read(dut
, XIRR
, False)
522 print ("xirr", hex(data
), bin(data
))
524 assert (yield dut
.core_irq_o
) == 1 # should not clear
528 # read wb XIRR (32-bit)
529 data
= yield from wb_read(dut
, XIRR
)
530 print ("xirr", hex(data
), bin(data
))
532 assert (yield dut
.core_irq_o
) == 0
540 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
541 with
open("test_xics_icp.il", "w") as f
:
545 m
.submodules
.xics_icp
= dut
550 sim
.add_sync_process(wrap(sim_xics_icp(dut
)))
551 sim_writer
= sim
.write_vcd('test_xics_icp.vcd')
558 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
559 with
open("test_xics_ics.il", "w") as f
:
562 #run_simulation(dut, ldst_sim(dut), vcd_name='test_ldst_regspec.vcd')
565 if __name__
== '__main__':