Add Tercel PHY reset synchronization
[microwatt.git] / xics.vhdl
1 --
2 -- This is a simple XICS compliant interrupt controller. This is a
3 -- Presenter (ICP) and Source (ICS) in two small units directly
4 -- connected to each other with no routing layer.
5 --
6 -- The sources have a configurable IRQ priority set a set of ICS
7 -- registers in the source units.
8 --
9 -- The source ids start at 16 for int_level_in(0) and go up from
10 -- there (ie int_level_in(1) is source id 17). XXX Make a generic
11 --
12 -- The presentation layer will pick an interupt that is more
13 -- favourable than the current CPPR and present it via the XISR and
14 -- send an interrpt to the processor (via e_out). This may not be the
15 -- highest priority interrupt currently presented (which is allowed
16 -- via XICS)
17 --
18
19 library ieee;
20 use ieee.std_logic_1164.all;
21 use ieee.numeric_std.all;
22
23 library work;
24 use work.common.all;
25 use work.wishbone_types.all;
26
27 entity xics_icp is
28 port (
29 clk : in std_logic;
30 rst : in std_logic;
31
32 wb_in : in wb_io_master_out;
33 wb_out : out wb_io_slave_out;
34
35 ics_in : in ics_to_icp_t;
36 core_irq_out : out std_ulogic
37 );
38 end xics_icp;
39
40 architecture behaviour of xics_icp is
41 type reg_internal_t is record
42 xisr : std_ulogic_vector(23 downto 0);
43 cppr : std_ulogic_vector(7 downto 0);
44 mfrr : std_ulogic_vector(7 downto 0);
45 irq : std_ulogic;
46 wb_rd_data : std_ulogic_vector(31 downto 0);
47 wb_ack : std_ulogic;
48 end record;
49 constant reg_internal_init : reg_internal_t :=
50 (wb_ack => '0',
51 mfrr => x"ff", -- mask everything on reset
52 irq => '0',
53 others => (others => '0'));
54
55 signal r, r_next : reg_internal_t;
56
57 -- hardwire the hardware IRQ priority
58 constant HW_PRIORITY : std_ulogic_vector(7 downto 0) := x"80";
59
60 -- 8 bit offsets for each presentation
61 constant XIRR_POLL : std_ulogic_vector(7 downto 0) := x"00";
62 constant XIRR : std_ulogic_vector(7 downto 0) := x"04";
63 constant RESV0 : std_ulogic_vector(7 downto 0) := x"08";
64 constant MFRR : std_ulogic_vector(7 downto 0) := x"0c";
65
66 begin
67
68 regs : process(clk)
69 begin
70 if rising_edge(clk) then
71 r <= r_next;
72
73 -- We delay core_irq_out by a cycle to help with timing
74 core_irq_out <= r.irq;
75 end if;
76 end process;
77
78 wb_out.dat <= r.wb_rd_data;
79 wb_out.ack <= r.wb_ack;
80 wb_out.stall <= '0'; -- never stall wishbone
81
82 comb : process(all)
83 variable v : reg_internal_t;
84 variable xirr_accept_rd : std_ulogic;
85
86 function bswap(v : in std_ulogic_vector(31 downto 0)) return std_ulogic_vector is
87 variable r : std_ulogic_vector(31 downto 0);
88 begin
89 r( 7 downto 0) := v(31 downto 24);
90 r(15 downto 8) := v(23 downto 16);
91 r(23 downto 16) := v(15 downto 8);
92 r(31 downto 24) := v( 7 downto 0);
93 return r;
94 end function;
95
96 variable be_in : std_ulogic_vector(31 downto 0);
97 variable be_out : std_ulogic_vector(31 downto 0);
98
99 variable pending_priority : std_ulogic_vector(7 downto 0);
100 begin
101 v := r;
102
103 v.wb_ack := '0';
104
105 xirr_accept_rd := '0';
106
107 be_in := bswap(wb_in.dat);
108 be_out := (others => '0');
109
110 if wb_in.cyc = '1' and wb_in.stb = '1' then
111 v.wb_ack := '1'; -- always ack
112 if wb_in.we = '1' then -- write
113 -- writes to both XIRR are the same
114 case wb_in.adr(7 downto 0) is
115 when XIRR_POLL =>
116 report "ICP XIRR_POLL write";
117 v.cppr := be_in(31 downto 24);
118 when XIRR =>
119 v.cppr := be_in(31 downto 24);
120 if wb_in.sel = x"f" then -- 4 byte
121 report "ICP XIRR write word (EOI) :" & to_hstring(be_in);
122 elsif wb_in.sel = x"1" then -- 1 byte
123 report "ICP XIRR write byte (CPPR):" & to_hstring(be_in(31 downto 24));
124 else
125 report "ICP XIRR UNSUPPORTED write ! sel=" & to_hstring(wb_in.sel);
126 end if;
127 when MFRR =>
128 v.mfrr := be_in(31 downto 24);
129 if wb_in.sel = x"f" then -- 4 bytes
130 report "ICP MFRR write word:" & to_hstring(be_in);
131 elsif wb_in.sel = x"1" then -- 1 byte
132 report "ICP MFRR write byte:" & to_hstring(be_in(31 downto 24));
133 else
134 report "ICP MFRR UNSUPPORTED write ! sel=" & to_hstring(wb_in.sel);
135 end if;
136 when others =>
137 end case;
138
139 else -- read
140
141 case wb_in.adr(7 downto 0) is
142 when XIRR_POLL =>
143 report "ICP XIRR_POLL read";
144 be_out := r.cppr & r.xisr;
145 when XIRR =>
146 report "ICP XIRR read";
147 be_out := r.cppr & r.xisr;
148 if wb_in.sel = x"f" then
149 xirr_accept_rd := '1';
150 end if;
151 when MFRR =>
152 report "ICP MFRR read";
153 be_out(31 downto 24) := r.mfrr;
154 when others =>
155 end case;
156 end if;
157 end if;
158
159 pending_priority := x"ff";
160 v.xisr := x"000000";
161 v.irq := '0';
162
163 if ics_in.pri /= x"ff" then
164 v.xisr := x"00001" & ics_in.src;
165 pending_priority := ics_in.pri;
166 end if;
167
168 -- Check MFRR
169 if unsigned(r.mfrr) < unsigned(pending_priority) then --
170 v.xisr := x"000002"; -- special XICS MFRR IRQ source number
171 pending_priority := r.mfrr;
172 end if;
173
174 -- Accept the interrupt
175 if xirr_accept_rd = '1' then
176 report "XICS: ICP ACCEPT" &
177 " cppr:" & to_hstring(r.cppr) &
178 " xisr:" & to_hstring(r.xisr) &
179 " mfrr:" & to_hstring(r.mfrr);
180 v.cppr := pending_priority;
181 end if;
182
183 v.wb_rd_data := bswap(be_out);
184
185 if unsigned(pending_priority) < unsigned(v.cppr) then
186 if r.irq = '0' then
187 report "IRQ set";
188 end if;
189 v.irq := '1';
190 elsif r.irq = '1' then
191 report "IRQ clr";
192 end if;
193
194 if rst = '1' then
195 v := reg_internal_init;
196 end if;
197
198 r_next <= v;
199
200 end process;
201
202 end architecture behaviour;
203
204 library ieee;
205 use ieee.std_logic_1164.all;
206 use ieee.numeric_std.all;
207
208 library work;
209 use work.common.all;
210 use work.wishbone_types.all;
211
212 entity xics_ics is
213 generic (
214 SRC_NUM : integer range 1 to 256 := 16;
215 PRIO_BITS : integer range 1 to 8 := 8
216 );
217 port (
218 clk : in std_logic;
219 rst : in std_logic;
220
221 wb_in : in wb_io_master_out;
222 wb_out : out wb_io_slave_out;
223
224 int_level_in : in std_ulogic_vector(SRC_NUM - 1 downto 0);
225 icp_out : out ics_to_icp_t
226 );
227 end xics_ics;
228
229 architecture rtl of xics_ics is
230
231 subtype pri_t is std_ulogic_vector(PRIO_BITS-1 downto 0);
232 type xive_t is record
233 pri : pri_t;
234 end record;
235 constant pri_masked : pri_t := (others => '1');
236
237 type xive_array_t is array(0 to SRC_NUM-1) of xive_t;
238 signal xives : xive_array_t;
239
240 signal wb_valid : std_ulogic;
241 signal reg_idx : integer range 0 to SRC_NUM - 1;
242 signal icp_out_next : ics_to_icp_t;
243 signal int_level_l : std_ulogic_vector(SRC_NUM - 1 downto 0);
244
245 function bswap(v : in std_ulogic_vector(31 downto 0)) return std_ulogic_vector is
246 variable r : std_ulogic_vector(31 downto 0);
247 begin
248 r( 7 downto 0) := v(31 downto 24);
249 r(15 downto 8) := v(23 downto 16);
250 r(23 downto 16) := v(15 downto 8);
251 r(31 downto 24) := v( 7 downto 0);
252 return r;
253 end function;
254
255 function get_config return std_ulogic_vector is
256 variable r: std_ulogic_vector(31 downto 0);
257 begin
258 r := (others => '0');
259 r(23 downto 0) := std_ulogic_vector(to_unsigned(SRC_NUM, 24));
260 r(27 downto 24) := std_ulogic_vector(to_unsigned(PRIO_BITS, 4));
261 return r;
262 end function;
263
264 function prio_pack(pri8: std_ulogic_vector(7 downto 0)) return pri_t is
265 begin
266 return pri8(PRIO_BITS-1 downto 0);
267 end function;
268
269 function prio_unpack(pri: pri_t) return std_ulogic_vector is
270 variable r : std_ulogic_vector(7 downto 0);
271 begin
272 if pri = pri_masked then
273 r := x"ff";
274 else
275 r := (others => '0');
276 r(PRIO_BITS-1 downto 0) := pri;
277 end if;
278 return r;
279 end function;
280
281
282 -- Register map
283 -- 0 : Config
284 -- 4 : Debug/diagnostics
285 -- 800 : XIVE0
286 -- 804 : XIVE1 ...
287 --
288 -- Config register format:
289 --
290 -- 23.. 0 : Interrupt base (hard wired to 16)
291 -- 27.. 24 : #prio bits (1..8)
292 --
293 -- XIVE register format:
294 --
295 -- 31 : input bit (reflects interrupt input)
296 -- 30 : reserved
297 -- 29 : P (mirrors input for now)
298 -- 28 : Q (not implemented in this version)
299 -- 30 .. : reserved
300 -- 19 .. 8 : target (not implemented in this version)
301 -- 7 .. 0 : prio/mask
302
303 signal reg_is_xive : std_ulogic;
304 signal reg_is_config : std_ulogic;
305 signal reg_is_debug : std_ulogic;
306
307 begin
308
309 assert SRC_NUM = 16 report "Fixup address decode with log2";
310
311 reg_is_xive <= wb_in.adr(11);
312 reg_is_config <= '1' when wb_in.adr(11 downto 0) = x"000" else '0';
313 reg_is_debug <= '1' when wb_in.adr(11 downto 0) = x"004" else '0';
314
315 -- Register index XX FIXME: figure out bits from SRC_NUM
316 reg_idx <= to_integer(unsigned(wb_in.adr(5 downto 2)));
317
318 -- Latch interrupt inputs for timing
319 int_latch: process(clk)
320 begin
321 if rising_edge(clk) then
322 int_level_l <= int_level_in;
323 end if;
324 end process;
325
326 -- We don't stall. Acks are sent by the read machine one cycle
327 -- after a request, but we can handle one access per cycle.
328 wb_out.stall <= '0';
329 wb_valid <= wb_in.cyc and wb_in.stb;
330
331 -- Big read mux. This could be replaced by a slower state
332 -- machine iterating registers instead if timing gets tight.
333 reg_read: process(clk)
334 variable be_out : std_ulogic_vector(31 downto 0);
335 begin
336 if rising_edge(clk) then
337 be_out := (others => '0');
338
339 if reg_is_xive = '1' then
340 be_out := int_level_l(reg_idx) &
341 '0' &
342 int_level_l(reg_idx) &
343 '0' &
344 x"00000" &
345 prio_unpack(xives(reg_idx).pri);
346 elsif reg_is_config = '1' then
347 be_out := get_config;
348 elsif reg_is_debug = '1' then
349 be_out := x"00000" & icp_out_next.src & icp_out_next.pri;
350 end if;
351 wb_out.dat <= bswap(be_out);
352 wb_out.ack <= wb_valid;
353 end if;
354 end process;
355
356 -- Register write machine
357 reg_write: process(clk)
358 variable be_in : std_ulogic_vector(31 downto 0);
359 begin
360 -- Byteswapped input
361 be_in := bswap(wb_in.dat);
362
363 if rising_edge(clk) then
364 if rst = '1' then
365 for i in 0 to SRC_NUM - 1 loop
366 xives(i) <= (pri => pri_masked);
367 end loop;
368 elsif wb_valid = '1' and wb_in.we = '1' then
369 if reg_is_xive then
370 -- TODO: When adding support for other bits, make sure to
371 -- properly implement wb_in.sel to allow partial writes.
372 xives(reg_idx).pri <= prio_pack(be_in(7 downto 0));
373 report "ICS irq " & integer'image(reg_idx) &
374 " set to:" & to_hstring(be_in(7 downto 0));
375 end if;
376 end if;
377 end if;
378 end process;
379
380 -- generate interrupt. This is a simple combinational process,
381 -- potentially wasteful in HW for large number of interrupts.
382 --
383 -- could be replaced with iterative state machines and a message
384 -- system between ICSs' (plural) and ICP incl. reject etc...
385 --
386 irq_gen_sync: process(clk)
387 begin
388 if rising_edge(clk) then
389 icp_out <= icp_out_next;
390 end if;
391 end process;
392
393 irq_gen: process(all)
394 variable max_idx : integer range 0 to SRC_NUM-1;
395 variable max_pri : pri_t;
396
397 -- A more favored than b ?
398 function a_mf_b(a: pri_t; b: pri_t) return boolean is
399 variable a_i : unsigned(PRIO_BITS-1 downto 0);
400 variable b_i : unsigned(PRIO_BITS-1 downto 0);
401 begin
402 a_i := unsigned(a);
403 b_i := unsigned(b);
404 report "a_mf_b a=" & to_hstring(a) &
405 " b=" & to_hstring(b) &
406 " r=" & boolean'image(a < b);
407 return a_i < b_i;
408 end function;
409 begin
410 -- XXX FIXME: Use a tree
411 max_pri := pri_masked;
412 max_idx := 0;
413 for i in 0 to SRC_NUM - 1 loop
414 if int_level_l(i) = '1' and a_mf_b(xives(i).pri, max_pri) then
415 max_pri := xives(i).pri;
416 max_idx := i;
417 end if;
418 end loop;
419 if max_pri /= pri_masked then
420 report "MFI: " & integer'image(max_idx) & " pri=" & to_hstring(prio_unpack(max_pri));
421 end if;
422 icp_out_next.src <= std_ulogic_vector(to_unsigned(max_idx, 4));
423 icp_out_next.pri <= prio_unpack(max_pri);
424 end process;
425
426 end architecture rtl;