Merge pull request #178 from antonblanchard/intercon
[microwatt.git] / xics.vhdl
1 --
2 -- This is a simple XICS compliant interrupt controller. This is a
3 -- Presenter (ICP) and Source (ICS) in a single unit with no routing
4 -- layer.
5 --
6 -- The sources have a fixed IRQ priority set by HW_PRIORITY. The
7 -- source id starts at 16 for int_level_in(0) and go up from
8 -- there (ie int_level_in(1) is source id 17).
9 --
10 -- The presentation layer will pick an interupt that is more
11 -- favourable than the current CPPR and present it via the XISR and
12 -- send an interrpt to the processor (via e_out). This may not be the
13 -- highest priority interrupt currently presented (which is allowed
14 -- via XICS)
15 --
16
17 library ieee;
18 use ieee.std_logic_1164.all;
19 use ieee.numeric_std.all;
20
21 library work;
22 use work.common.all;
23 use work.wishbone_types.all;
24
25 entity xics is
26 generic (
27 LEVEL_NUM : positive := 16
28 );
29 port (
30 clk : in std_logic;
31 rst : in std_logic;
32
33 wb_in : in wb_io_master_out;
34 wb_out : out wb_io_slave_out;
35
36 int_level_in : in std_ulogic_vector(LEVEL_NUM - 1 downto 0);
37
38 core_irq_out : out std_ulogic
39 );
40 end xics;
41
42 architecture behaviour of xics is
43 type reg_internal_t is record
44 xisr : std_ulogic_vector(23 downto 0);
45 cppr : std_ulogic_vector(7 downto 0);
46 pending_priority : std_ulogic_vector(7 downto 0);
47 mfrr : std_ulogic_vector(7 downto 0);
48 mfrr_pending : std_ulogic;
49 irq : std_ulogic;
50 wb_rd_data : std_ulogic_vector(31 downto 0);
51 wb_ack : std_ulogic;
52 end record;
53 constant reg_internal_init : reg_internal_t :=
54 (wb_ack => '0',
55 mfrr_pending => '0',
56 mfrr => x"00", -- mask everything on reset
57 irq => '0',
58 others => (others => '0'));
59
60 signal r, r_next : reg_internal_t;
61
62 -- hardwire the hardware IRQ priority
63 constant HW_PRIORITY : std_ulogic_vector(7 downto 0) := x"80";
64
65 -- 8 bit offsets for each presentation
66 constant XIRR_POLL : std_ulogic_vector(7 downto 0) := x"00";
67 constant XIRR : std_ulogic_vector(7 downto 0) := x"04";
68 constant RESV0 : std_ulogic_vector(7 downto 0) := x"08";
69 constant MFRR : std_ulogic_vector(7 downto 0) := x"0c";
70
71 begin
72
73 regs : process(clk)
74 begin
75 if rising_edge(clk) then
76 r <= r_next;
77 end if;
78 end process;
79
80 wb_out.dat <= r.wb_rd_data;
81 wb_out.ack <= r.wb_ack;
82 wb_out.stall <= '0'; -- never stall wishbone
83 core_irq_out <= r.irq;
84
85 comb : process(all)
86 variable v : reg_internal_t;
87 variable xirr_accept_rd : std_ulogic;
88 variable irq_eoi : std_ulogic;
89 begin
90 v := r;
91
92 v.wb_ack := '0';
93
94 xirr_accept_rd := '0';
95 irq_eoi := '0';
96
97 if wb_in.cyc = '1' and wb_in.stb = '1' then
98 v.wb_ack := '1'; -- always ack
99 if wb_in.we = '1' then -- write
100 -- writes to both XIRR are the same
101 case wb_in.adr(7 downto 0) is
102 when XIRR_POLL =>
103 report "XICS XIRR_POLL write";
104 if wb_in.sel = x"f" then -- 4 bytes
105 v.cppr := wb_in.dat(31 downto 24);
106 elsif wb_in.sel = x"1" then -- 1 byte
107 v.cppr := wb_in.dat(7 downto 0);
108 end if;
109 when XIRR =>
110 if wb_in.sel = x"f" then -- 4 byte
111 report "XICS XIRR write word:" & to_hstring(wb_in.dat);
112 v.cppr := wb_in.dat(31 downto 24);
113 irq_eoi := '1';
114 elsif wb_in.sel = x"1" then -- 1 byte
115 report "XICS XIRR write byte:" & to_hstring(wb_in.dat(7 downto 0));
116 v.cppr := wb_in.dat(7 downto 0);
117 else
118 report "XICS XIRR UNSUPPORTED write ! sel=" & to_hstring(wb_in.sel);
119 end if;
120 when MFRR =>
121 if wb_in.sel = x"f" then -- 4 bytes
122 report "XICS MFRR write word:" & to_hstring(wb_in.dat);
123 v.mfrr_pending := '1';
124 v.mfrr := wb_in.dat(31 downto 24);
125 elsif wb_in.sel = x"1" then -- 1 byte
126 report "XICS MFRR write byte:" & to_hstring(wb_in.dat(7 downto 0));
127 v.mfrr_pending := '1';
128 v.mfrr := wb_in.dat(7 downto 0);
129 else
130 report "XICS MFRR UNSUPPORTED write ! sel=" & to_hstring(wb_in.sel);
131 end if;
132 when others =>
133 end case;
134
135 else -- read
136 v.wb_rd_data := (others => '0');
137
138 case wb_in.adr(7 downto 0) is
139 when XIRR_POLL =>
140 report "XICS XIRR_POLL read";
141 if wb_in.sel = x"f" then
142 v.wb_rd_data(23 downto 0) := r.xisr;
143 v.wb_rd_data(31 downto 24) := r.cppr;
144 elsif wb_in.sel = x"1" then
145 v.wb_rd_data(7 downto 0) := r.cppr;
146 end if;
147 when XIRR =>
148 report "XICS XIRR read";
149 if wb_in.sel = x"f" then
150 v.wb_rd_data(23 downto 0) := r.xisr;
151 v.wb_rd_data(31 downto 24) := r.cppr;
152 xirr_accept_rd := '1';
153 elsif wb_in.sel = x"1" then
154 v.wb_rd_data(7 downto 0) := r.cppr;
155 end if;
156 when MFRR =>
157 report "XICS MFRR read";
158 if wb_in.sel = x"f" then -- 4 bytes
159 v.wb_rd_data(31 downto 24) := r.mfrr;
160 elsif wb_in.sel = x"1" then -- 1 byte
161 v.wb_rd_data( 7 downto 0) := r.mfrr;
162 end if;
163 when others =>
164 end case;
165 end if;
166 end if;
167
168 -- generate interrupt
169 if r.irq = '0' then
170 -- Here we just present any interrupt that's valid and
171 -- below cppr. For ordering, we ignore hardware
172 -- priorities.
173 if unsigned(HW_PRIORITY) < unsigned(r.cppr) then --
174 -- lower HW sources are higher priority
175 for i in LEVEL_NUM - 1 downto 0 loop
176 if int_level_in(i) = '1' then
177 v.irq := '1';
178 v.xisr := std_ulogic_vector(to_unsigned(16 + i, 24));
179 v.pending_priority := HW_PRIORITY; -- hardware HW IRQs
180 end if;
181 end loop;
182 end if;
183
184 -- Do mfrr as a higher priority so mfrr_pending is cleared
185 if unsigned(r.mfrr) < unsigned(r.cppr) then --
186 report "XICS: MFRR INTERRUPT";
187 -- IPI
188 if r.mfrr_pending = '1' then
189 v.irq := '1';
190 v.xisr := x"000002"; -- special XICS MFRR IRQ source number
191 v.pending_priority := r.mfrr;
192 v.mfrr_pending := '0';
193 end if;
194 end if;
195 end if;
196
197 -- Accept the interrupt
198 if xirr_accept_rd = '1' then
199 report "XICS: ACCEPT" &
200 " cppr:" & to_hstring(r.cppr) &
201 " xisr:" & to_hstring(r.xisr) &
202 " mfrr:" & to_hstring(r.mfrr);
203 v.cppr := r.pending_priority;
204 end if;
205
206 if irq_eoi = '1' then
207 v.irq := '0';
208 end if;
209
210 if rst = '1' then
211 v := reg_internal_init;
212 end if;
213
214 r_next <= v;
215
216 end process;
217
218 end architecture behaviour;