Implement slbia as a dTLB/iTLB flush
[microwatt.git] / mmu.vhdl
1 library ieee;
2 use ieee.std_logic_1164.all;
3 use ieee.numeric_std.all;
4
5 library work;
6 use work.common.all;
7
8 -- Radix MMU
9 -- Supports 4-level trees as in arch 3.0B, but not the two-step translation for
10 -- guests under a hypervisor (i.e. there is no gRA -> hRA translation).
11
12 entity mmu is
13 port (
14 clk : in std_ulogic;
15 rst : in std_ulogic;
16
17 l_in : in Loadstore1ToMmuType;
18 l_out : out MmuToLoadstore1Type;
19
20 d_out : out MmuToDcacheType;
21 d_in : in DcacheToMmuType;
22
23 i_out : out MmuToIcacheType
24 );
25 end mmu;
26
27 architecture behave of mmu is
28
29 type state_t is (IDLE,
30 TLB_WAIT,
31 SEGMENT_CHECK,
32 RADIX_LOOKUP,
33 RADIX_READ_WAIT,
34 RADIX_LOAD_TLB,
35 RADIX_ERROR
36 );
37
38 type reg_stage_t is record
39 -- latched request from loadstore1
40 valid : std_ulogic;
41 iside : std_ulogic;
42 store : std_ulogic;
43 priv : std_ulogic;
44 addr : std_ulogic_vector(63 downto 0);
45 -- internal state
46 state : state_t;
47 pgtbl0 : std_ulogic_vector(63 downto 0);
48 shift : unsigned(5 downto 0);
49 mask_size : unsigned(4 downto 0);
50 pgbase : std_ulogic_vector(55 downto 0);
51 pde : std_ulogic_vector(63 downto 0);
52 invalid : std_ulogic;
53 badtree : std_ulogic;
54 segerror : std_ulogic;
55 perm_err : std_ulogic;
56 rc_error : std_ulogic;
57 end record;
58
59 signal r, rin : reg_stage_t;
60
61 signal addrsh : std_ulogic_vector(15 downto 0);
62 signal mask : std_ulogic_vector(15 downto 0);
63 signal finalmask : std_ulogic_vector(43 downto 0);
64
65 begin
66 -- Multiplex internal SPR values back to loadstore1, selected
67 -- by l_in.sprn. Easy when there's only one...
68 l_out.sprval <= r.pgtbl0;
69
70 mmu_0: process(clk)
71 begin
72 if rising_edge(clk) then
73 if rst = '1' then
74 r.state <= IDLE;
75 r.valid <= '0';
76 r.pgtbl0 <= (others => '0');
77 else
78 if rin.valid = '1' then
79 report "MMU got tlb miss for " & to_hstring(rin.addr);
80 end if;
81 if l_out.done = '1' then
82 report "MMU completing op with invalid=" & std_ulogic'image(l_out.invalid) &
83 " badtree=" & std_ulogic'image(l_out.badtree);
84 end if;
85 if rin.state = RADIX_LOOKUP then
86 report "radix lookup shift=" & integer'image(to_integer(rin.shift)) &
87 " msize=" & integer'image(to_integer(rin.mask_size));
88 end if;
89 if r.state = RADIX_LOOKUP then
90 report "send load addr=" & to_hstring(d_out.addr) &
91 " addrsh=" & to_hstring(addrsh) & " mask=" & to_hstring(mask);
92 end if;
93 r <= rin;
94 end if;
95 end if;
96 end process;
97
98 -- Shift address bits 61--12 right by 0--47 bits and
99 -- supply the least significant 16 bits of the result.
100 addrshifter: process(all)
101 variable sh1 : std_ulogic_vector(30 downto 0);
102 variable sh2 : std_ulogic_vector(18 downto 0);
103 variable result : std_ulogic_vector(15 downto 0);
104 begin
105 case r.shift(5 downto 4) is
106 when "00" =>
107 sh1 := r.addr(42 downto 12);
108 when "01" =>
109 sh1 := r.addr(58 downto 28);
110 when others =>
111 sh1 := "0000000000000" & r.addr(61 downto 44);
112 end case;
113 case r.shift(3 downto 2) is
114 when "00" =>
115 sh2 := sh1(18 downto 0);
116 when "01" =>
117 sh2 := sh1(22 downto 4);
118 when "10" =>
119 sh2 := sh1(26 downto 8);
120 when others =>
121 sh2 := sh1(30 downto 12);
122 end case;
123 case r.shift(1 downto 0) is
124 when "00" =>
125 result := sh2(15 downto 0);
126 when "01" =>
127 result := sh2(16 downto 1);
128 when "10" =>
129 result := sh2(17 downto 2);
130 when others =>
131 result := sh2(18 downto 3);
132 end case;
133 addrsh <= result;
134 end process;
135
136 -- generate mask for extracting address fields for PTE address generation
137 addrmaskgen: process(all)
138 variable m : std_ulogic_vector(15 downto 0);
139 begin
140 -- mask_count has to be >= 5
141 m := x"001f";
142 for i in 5 to 15 loop
143 if i < to_integer(r.mask_size) then
144 m(i) := '1';
145 end if;
146 end loop;
147 mask <= m;
148 end process;
149
150 -- generate mask for extracting address bits to go in TLB entry
151 -- in order to support pages > 4kB
152 finalmaskgen: process(all)
153 variable m : std_ulogic_vector(43 downto 0);
154 begin
155 m := (others => '0');
156 for i in 0 to 43 loop
157 if i < to_integer(r.shift) then
158 m(i) := '1';
159 end if;
160 end loop;
161 finalmask <= m;
162 end process;
163
164 mmu_1: process(all)
165 variable v : reg_stage_t;
166 variable dcreq : std_ulogic;
167 variable done : std_ulogic;
168 variable tlb_load : std_ulogic;
169 variable itlb_load : std_ulogic;
170 variable tlbie_req : std_ulogic;
171 variable inval_all : std_ulogic;
172 variable rts : unsigned(5 downto 0);
173 variable mbits : unsigned(5 downto 0);
174 variable pgtable_addr : std_ulogic_vector(63 downto 0);
175 variable pte : std_ulogic_vector(63 downto 0);
176 variable tlb_data : std_ulogic_vector(63 downto 0);
177 variable nonzero : std_ulogic;
178 variable perm_ok : std_ulogic;
179 variable rc_ok : std_ulogic;
180 variable addr : std_ulogic_vector(63 downto 0);
181 variable data : std_ulogic_vector(63 downto 0);
182 begin
183 v := r;
184 v.valid := '0';
185 dcreq := '0';
186 done := '0';
187 v.invalid := '0';
188 v.badtree := '0';
189 v.segerror := '0';
190 v.perm_err := '0';
191 v.rc_error := '0';
192 tlb_load := '0';
193 itlb_load := '0';
194 tlbie_req := '0';
195 inval_all := '0';
196
197 -- Radix tree data structures in memory are big-endian,
198 -- so we need to byte-swap them
199 for i in 0 to 7 loop
200 data(i * 8 + 7 downto i * 8) := d_in.data((7 - i) * 8 + 7 downto (7 - i) * 8);
201 end loop;
202
203 case r.state is
204 when IDLE =>
205 -- rts == radix tree size, # address bits being translated
206 rts := unsigned('0' & r.pgtbl0(62 downto 61) & r.pgtbl0(7 downto 5));
207 -- mbits == # address bits to index top level of tree
208 mbits := unsigned('0' & r.pgtbl0(4 downto 0));
209 -- set v.shift to rts so that we can use finalmask for the segment check
210 v.shift := rts;
211 v.mask_size := mbits(4 downto 0);
212 v.pgbase := r.pgtbl0(55 downto 8) & x"00";
213
214 if l_in.valid = '1' then
215 v.addr := l_in.addr;
216 v.iside := l_in.iside;
217 v.store := not (l_in.load or l_in.iside);
218 v.priv := l_in.priv;
219 if l_in.tlbie = '1' then
220 dcreq := '1';
221 tlbie_req := '1';
222 -- Invalidate all iTLB/dTLB entries for tlbie with
223 -- RB[IS] != 0 or RB[AP] != 0, or for slbia
224 inval_all := l_in.slbia or l_in.addr(11) or l_in.addr(10) or
225 l_in.addr(7) or l_in.addr(6) or l_in.addr(5);
226 v.state := TLB_WAIT;
227 else
228 v.valid := '1';
229 -- Use RPDS = 0 to disable radix tree walks
230 if mbits = 0 then
231 v.state := RADIX_ERROR;
232 v.invalid := '1';
233 else
234 v.state := SEGMENT_CHECK;
235 end if;
236 end if;
237 end if;
238 if l_in.mtspr = '1' then
239 v.pgtbl0 := l_in.rs;
240 end if;
241
242 when TLB_WAIT =>
243 if d_in.done = '1' then
244 done := '1';
245 v.state := IDLE;
246 end if;
247
248 when SEGMENT_CHECK =>
249 mbits := '0' & r.mask_size;
250 v.shift := r.shift + (31 - 12) - mbits;
251 nonzero := or(r.addr(61 downto 31) and not finalmask(30 downto 0));
252 if r.addr(63) /= r.addr(62) or nonzero = '1' then
253 v.state := RADIX_ERROR;
254 v.segerror := '1';
255 elsif mbits < 5 or mbits > 16 or mbits > (r.shift + (31 - 12)) then
256 v.state := RADIX_ERROR;
257 v.badtree := '1';
258 else
259 v.state := RADIX_LOOKUP;
260 end if;
261
262 when RADIX_LOOKUP =>
263 dcreq := '1';
264 v.state := RADIX_READ_WAIT;
265
266 when RADIX_READ_WAIT =>
267 if d_in.done = '1' then
268 if d_in.err = '0' then
269 v.pde := data;
270 -- test valid bit
271 if data(63) = '1' then
272 -- test leaf bit
273 if data(62) = '1' then
274 -- check permissions and RC bits
275 perm_ok := '0';
276 if r.priv = '1' or data(3) = '0' then
277 if r.iside = '0' then
278 perm_ok := data(1) or (data(2) and not r.store);
279 else
280 -- no IAMR, so no KUEP support for now
281 -- deny execute permission if cache inhibited
282 perm_ok := data(0) and not data(5);
283 end if;
284 end if;
285 rc_ok := data(8) and (data(7) or not r.store);
286 if perm_ok = '1' and rc_ok = '1' then
287 v.state := RADIX_LOAD_TLB;
288 else
289 v.state := RADIX_ERROR;
290 v.perm_err := not perm_ok;
291 -- permission error takes precedence over RC error
292 v.rc_error := perm_ok;
293 end if;
294 else
295 mbits := unsigned('0' & data(4 downto 0));
296 if mbits < 5 or mbits > 16 or mbits > r.shift then
297 v.state := RADIX_ERROR;
298 v.badtree := '1';
299 else
300 v.shift := v.shift - mbits;
301 v.mask_size := mbits(4 downto 0);
302 v.pgbase := data(55 downto 8) & x"00";
303 v.state := RADIX_LOOKUP;
304 end if;
305 end if;
306 else
307 -- non-present PTE, generate a DSI
308 v.state := RADIX_ERROR;
309 v.invalid := '1';
310 end if;
311 else
312 v.state := RADIX_ERROR;
313 v.badtree := '1';
314 end if;
315 end if;
316
317 when RADIX_LOAD_TLB =>
318 tlb_load := '1';
319 if r.iside = '0' then
320 dcreq := '1';
321 v.state := TLB_WAIT;
322 else
323 itlb_load := '1';
324 done := '1';
325 v.state := IDLE;
326 end if;
327
328 when RADIX_ERROR =>
329 done := '1';
330 v.state := IDLE;
331
332 end case;
333
334 pgtable_addr := x"00" & r.pgbase(55 downto 19) &
335 ((r.pgbase(18 downto 3) and not mask) or (addrsh and mask)) &
336 "000";
337 pte := x"00" &
338 ((r.pde(55 downto 12) and not finalmask) or (r.addr(55 downto 12) and finalmask))
339 & r.pde(11 downto 0);
340
341 -- update registers
342 rin <= v;
343
344 -- drive outputs
345 if tlbie_req = '1' then
346 addr := l_in.addr;
347 tlb_data := l_in.rs;
348 elsif tlb_load = '1' then
349 addr := r.addr(63 downto 12) & x"000";
350 tlb_data := pte;
351 else
352 addr := pgtable_addr;
353 tlb_data := (others => '0');
354 end if;
355
356 l_out.done <= done;
357 l_out.invalid <= r.invalid;
358 l_out.badtree <= r.badtree;
359 l_out.segerr <= r.segerror;
360 l_out.perm_error <= r.perm_err;
361 l_out.rc_error <= r.rc_error;
362
363 d_out.valid <= dcreq;
364 d_out.tlbie <= tlbie_req;
365 d_out.doall <= inval_all;
366 d_out.tlbld <= tlb_load;
367 d_out.addr <= addr;
368 d_out.pte <= tlb_data;
369
370 i_out.tlbld <= itlb_load;
371 i_out.tlbie <= tlbie_req;
372 i_out.doall <= inval_all;
373 i_out.addr <= addr;
374 i_out.pte <= tlb_data;
375
376 end process;
377 end;