start on conversion of xics.vhdl to nmigen
[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
21 from nmutil.iocontrol import RecordObject
22 from nmigen.cli import rtlil
23 from soc.minerva.wishbone import make_wb_layout
24
25
26 class ICS2ICP(RecordObject):
27 """
28 # Level interrupts only, ICS just keeps prsenting the
29 # highest priority interrupt. Once handling edge, something
30 # smarter involving handshake & reject support will be needed
31 """
32 def __init__(self, name):
33 super().__init__(name=name)
34 self.src = Signal(4, reset_less=True)
35 self.pri = Signal(8, reset_less=True)
36
37 # hardwire the hardware IRQ priority
38 HW_PRIORITY = Const(0x80, 8)
39
40 # 8 bit offsets for each presentation
41 XIRR_POLL = 0x00
42 XIRR = 0x04
43 RESV0 = 0x08
44 MFRR = 0x0c
45
46
47 class RegInternal(RecordObject):
48 def __init__(self, name=None):
49 super().__init__(name=name)
50 self.xisr = Signal(24)
51 self.cppr = Signal(8)
52 self.mfrr = Signal(8, reset=0xff) # mask everything on reset
53 self.irq = Signal(1)
54 self.wb_rd_data = Signal(32)
55 self.wb_ack = Signal(1)
56
57
58 def bswap(v):
59 return Cat(v[24:32], v[16:24], v[8:16], v[0:8])
60
61
62 class XICS_ICP(Elaboratable):
63
64 def __init__(self):
65 class Spec: pass
66 spec = Spec()
67 spec.addr_wid = 30
68 spec.mask_wid = 4
69 spec.reg_wid = 32
70 self.bus = Record(make_wb_layout(spec))
71 self.ics_i = ICS2ICP("ics_i")
72 self.core_irq_o = Signal()
73
74 def elaborate(self, platform):
75 m = Module()
76 comb, sync = m.d.comb, m.d.sync
77
78 r = RegInternal()
79 r_next = RegInternal()
80
81 sync += r.eq(r_next)
82 # We delay core_irq_out by a cycle to help with timing
83 sync += self.core_irq_o.eq(r.irq)
84
85 comb += self.bus.dat_w.eq(r.wb_rd_data)
86 comb += self.bus.ack.eq(r.wb_ack)
87
88 v = RegInternal()
89 xirr_accept_rd = Signal()
90
91 be_in = Signal(32)
92 be_out = Signal(32)
93
94 pending_priority = Signal(8)
95
96 comb += v.eq(r)
97 comb += v.wb_ack.eq(0)
98
99 comb += xirr_accept_rd.eq(0)
100
101 comb += be_in.eq(bswap(self.bus.dat_r))
102 comb += be_out.eq(0)
103
104 with m.If(self.bus.cyc & self.bus.stb):
105 comb += v.wb_ack.eq(1) # always ack
106 with m.If(self.bus.we): # write
107 # writes to both XIRR are the same
108 with m.Switch( self.bus.adr[:8]):
109 with m.Case(XIRR_POLL):
110 # report "ICP XIRR_POLL write";
111 comb += v.cppr.eq(be_in[24:32])
112 with m.Case(XIRR):
113 comb += v.cppr.eq(be_in[24:32])
114 with m.If(self.bus.sel == 0xf): # # 4 byte
115 #report "ICP XIRR write word (EOI) :" & \
116 # to_hstring(be_in);
117 pass
118 with m.Elif(self.bus.sel == 0x1): # 1 byte
119 #report "ICP XIRR write byte (CPPR):" & \
120 #to_hstring(be_in(31 downto 24));
121 pass
122 with m.Else():
123 #report "ICP XIRR UNSUPPORTED write ! sel=" & \
124 # to_hstring(wb_in.sel);
125 pass
126 with m.Case(MFRR ):
127 comb += v.mfrr.eq(be_in[24:32])
128 with m.If(self.bus.sel == 0xf): # # 4 byte
129 # report "ICP MFRR write word:" & to_hstring(be_in);
130 pass
131 with m.Elif(self.bus.sel == 0x1): # 1 byte
132 # report "ICP MFRR write byte:" & \
133 # to_hstring(be_in(31 downto 24));
134 pass
135 with m.Else():
136 # report "ICP MFRR UNSUPPORTED write ! sel=" & \
137 # to_hstring(wb_in.sel);
138 pass
139
140 with m.Else(): # read
141
142 with m.Switch( self.bus.adr[:8]):
143 with m.Case(XIRR_POLL):
144 # report "ICP XIRR_POLL read";
145 comb += be_out.eq(r.xisr & r.cppr )
146 with m.Case(XIRR):
147 # report "ICP XIRR read";
148 comb += be_out.eq(Cat(r.xisr, r.cppr))
149 with m.If(self.bus.sel == 0xf): # # 4 byte
150 comb += xirr_accept_rd.eq(1)
151 with m.Case(MFRR):
152 # report "ICP MFRR read";
153 comb += be_out.eq(r.mfrr)
154
155 comb += pending_priority.eq(0xff)
156 comb += v.xisr.eq(0x0)
157 comb += v.irq.eq(0x0)
158
159 with m.If(self.ics_i.pri != 0xff):
160 comb += v.xisr.eq(Cat(self.ics_i.src, Const(0x00001)))
161 comb += pending_priority.eq(self.ics_i.pri)
162
163 # Check MFRR
164 with m.If(r.mfrr < pending_priority):
165 # special XICS MFRR IRQ source number
166 comb += v.xisr.eq(Const(0x000002))
167 comb += pending_priority.eq(r.mfrr)
168
169 # Accept the interrupt
170 with m.If(xirr_accept_rd):
171 #report "XICS: ICP ACCEPT" &
172 # " cppr:" & to_hstring(r.cppr) &
173 # " xisr:" & to_hstring(r.xisr) &
174 # " mfrr:" & to_hstring(r.mfrr);
175 comb += v.cppr.eq(pending_priority)
176
177 comb += v.wb_rd_data.eq(bswap(be_out))
178
179 with m.If(pending_priority < v.cppr):
180 with m.If(~r.irq):
181 #report "IRQ set";
182 pass
183 comb += v.irq.eq(1)
184 with m.Elif(r.irq):
185 #report "IRQ clr";
186 pass
187
188 comb += r_next.eq(v)
189
190 return m
191
192 def __iter__(self):
193 for field in self.bus.fields.values():
194 yield field
195 yield from self.ics_i
196 yield self.core_irq_o
197
198 def ports(self):
199 return list(self)
200
201
202 """
203 end architecture behaviour;
204
205 library ieee;
206 use ieee.std_logic_1164.all;
207 use ieee.numeric_std.all;
208
209 library work;
210 use work.common.all;
211 use work.wishbone_types.all;
212
213 entity xics_ics is
214 generic (
215 SRC_NUM : integer range 1 to 256 := 16;
216 PRIO_BITS : integer range 1 to 8 := 8
217 );
218 port (
219 clk : in std_logic;
220 rst : in std_logic;
221
222 wb_in : in wb_io_master_out;
223 wb_out : out wb_io_slave_out;
224
225 int_level_in : in std_ulogic_vector(SRC_NUM - 1 downto 0);
226 icp_out : out ics_to_icp_t
227 );
228 end xics_ics;
229
230 architecture rtl of xics_ics is
231
232 subtype pri_t is std_ulogic_vector(PRIO_BITS-1 downto 0);
233 type xive_t is record
234 pri : pri_t;
235 end record;
236 constant pri_masked : pri_t := (others => '1');
237
238 type xive_array_t is array(0 to SRC_NUM-1) of xive_t;
239 signal xives : xive_array_t;
240
241 signal wb_valid : std_ulogic;
242 signal reg_idx : integer range 0 to SRC_NUM - 1;
243 signal icp_out_next : ics_to_icp_t;
244 signal int_level_l : std_ulogic_vector(SRC_NUM - 1 downto 0);
245
246 function bswap(v : in std_ulogic_vector(31 downto 0)) return std_ulogic_vector is
247 variable r : std_ulogic_vector(31 downto 0);
248 begin
249 r( 7 downto 0) := v(31 downto 24);
250 r(15 downto 8) := v(23 downto 16);
251 r(23 downto 16) := v(15 downto 8);
252 r(31 downto 24) := v( 7 downto 0);
253 return r;
254 end function;
255
256 function get_config return std_ulogic_vector is
257 variable r: std_ulogic_vector(31 downto 0);
258 begin
259 r := (others => '0');
260 r(23 downto 0) := std_ulogic_vector(to_unsigned(SRC_NUM, 24));
261 r(27 downto 24) := std_ulogic_vector(to_unsigned(PRIO_BITS, 4));
262 return r;
263 end function;
264
265 function prio_pack(pri8: std_ulogic_vector(7 downto 0)) return pri_t is
266 begin
267 return pri8(PRIO_BITS-1 downto 0);
268 end function;
269
270 function prio_unpack(pri: pri_t) return std_ulogic_vector is
271 variable r : std_ulogic_vector(7 downto 0);
272 begin
273 if pri = pri_masked then
274 r := x"ff";
275 else
276 r := (others => '0');
277 r(PRIO_BITS-1 downto 0) := pri;
278 end if;
279 return r;
280 end function;
281
282
283 # Register map
284 # 0 : Config
285 # 4 : Debug/diagnostics
286 # 800 : XIVE0
287 # 804 : XIVE1 ...
288 --
289 # Config register format:
290 --
291 # 23.. 0 : Interrupt base (hard wired to 16)
292 # 27.. 24 : #prio bits (1..8)
293 --
294 # XIVE register format:
295 --
296 # 31 : input bit (reflects interrupt input)
297 # 30 : reserved
298 # 29 : P (mirrors input for now)
299 # 28 : Q (not implemented in this version)
300 # 30 .. : reserved
301 # 19 .. 8 : target (not implemented in this version)
302 # 7 .. 0 : prio/mask
303
304 signal reg_is_xive : std_ulogic;
305 signal reg_is_config : std_ulogic;
306 signal reg_is_debug : std_ulogic;
307
308 begin
309
310 assert SRC_NUM = 16 report "Fixup address decode with log2";
311
312 reg_is_xive <= wb_in.adr(11);
313 reg_is_config <= '1' when wb_in.adr(11 downto 0) = x"000" else '0';
314 reg_is_debug <= '1' when wb_in.adr(11 downto 0) = x"004" else '0';
315
316 # Register index XX FIXME: figure out bits from SRC_NUM
317 reg_idx <= to_integer(unsigned(wb_in.adr(5 downto 2)));
318
319 # Latch interrupt inputs for timing
320 int_latch: process(clk)
321 begin
322 if rising_edge(clk) then
323 int_level_l <= int_level_in;
324 end if;
325 end process;
326
327 # We don't stall. Acks are sent by the read machine one cycle
328 # after a request, but we can handle one access per cycle.
329 wb_out.stall <= '0';
330 wb_valid <= wb_in.cyc and wb_in.stb;
331
332 # Big read mux. This could be replaced by a slower state
333 # machine iterating registers instead if timing gets tight.
334 reg_read: process(clk)
335 variable be_out : std_ulogic_vector(31 downto 0);
336 begin
337 if rising_edge(clk) then
338 be_out := (others => '0');
339
340 if reg_is_xive = '1' then
341 be_out := int_level_l(reg_idx) &
342 '0' &
343 int_level_l(reg_idx) &
344 '0' &
345 x"00000" &
346 prio_unpack(xives(reg_idx).pri);
347 elsif reg_is_config = '1' then
348 be_out := get_config;
349 elsif reg_is_debug = '1' then
350 be_out := x"00000" & icp_out_next.src & icp_out_next.pri;
351 end if;
352 wb_out.dat <= bswap(be_out);
353 wb_out.ack <= wb_valid;
354 end if;
355 end process;
356
357 # Register write machine
358 reg_write: process(clk)
359 variable be_in : std_ulogic_vector(31 downto 0);
360 begin
361 # Byteswapped input
362 be_in := bswap(wb_in.dat);
363
364 if rising_edge(clk) then
365 if rst = '1' then
366 for i in 0 to SRC_NUM - 1 loop
367 xives(i) <= (pri => pri_masked);
368 end loop;
369 elsif wb_valid = '1' and wb_in.we = '1' then
370 if reg_is_xive then
371 # TODO: When adding support for other bits, make sure to
372 # properly implement wb_in.sel to allow partial writes.
373 xives(reg_idx).pri <= prio_pack(be_in(7 downto 0));
374 report "ICS irq " & integer'image(reg_idx) &
375 " set to:" & to_hstring(be_in(7 downto 0));
376 end if;
377 end if;
378 end if;
379 end process;
380
381 # generate interrupt. This is a simple combinational process,
382 # potentially wasteful in HW for large number of interrupts.
383 --
384 # could be replaced with iterative state machines and a message
385 # system between ICSs' (plural) and ICP incl. reject etc...
386 --
387 irq_gen_sync: process(clk)
388 begin
389 if rising_edge(clk) then
390 icp_out <= icp_out_next;
391 end if;
392 end process;
393
394 irq_gen: process(all)
395 variable max_idx : integer range 0 to SRC_NUM-1;
396 variable max_pri : pri_t;
397
398 # A more favored than b ?
399 function a_mf_b(a: pri_t; b: pri_t) return boolean is
400 variable a_i : unsigned(PRIO_BITS-1 downto 0);
401 variable b_i : unsigned(PRIO_BITS-1 downto 0);
402 begin
403 a_i := unsigned(a);
404 b_i := unsigned(b);
405 report "a_mf_b a=" & to_hstring(a) &
406 " b=" & to_hstring(b) &
407 " r=" & boolean'image(a < b);
408 return a_i < b_i;
409 end function;
410 begin
411 # XXX FIXME: Use a tree
412 max_pri := pri_masked;
413 max_idx := 0;
414 for i in 0 to SRC_NUM - 1 loop
415 if int_level_l(i) = '1' and a_mf_b(xives(i).pri, max_pri) then
416 max_pri := xives(i).pri;
417 max_idx := i;
418 end if;
419 end loop;
420 if max_pri /= pri_masked then
421 report "MFI: " & integer'image(max_idx) & " pri=" & to_hstring(prio_unpack(max_pri));
422 end if;
423 icp_out_next.src <= std_ulogic_vector(to_unsigned(max_idx, 4));
424 icp_out_next.pri <= prio_unpack(max_pri);
425 end process;
426
427 end architecture rtl;
428 """
429
430 def test_xics_icp():
431
432 dut = XICS_ICP()
433 vl = rtlil.convert(dut, ports=dut.ports())
434 with open("test_xics_icp.il", "w") as f:
435 f.write(vl)
436
437 #run_simulation(dut, ldst_sim(dut), vcd_name='test_ldst_regspec.vcd')
438
439
440 if __name__ == '__main__':
441 test_xics_icp()
442