begin experimental ariane mmu.sv conversion
[soc.git] / TLB / src / ariane / mmu.py
1 """
2 # Copyright 2018 ETH Zurich and University of Bologna.
3 # Copyright and related rights are licensed under the Solderpad Hardware
4 # License, Version 0.51 (the "License"); you may not use this file except in
5 # compliance with the License. You may obtain a copy of the License at
6 # http:#solderpad.org/licenses/SHL-0.51. Unless required by applicable law
7 # or agreed to in writing, software, hardware and materials distributed under
8 # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
9 # CONDITIONS OF ANY KIND, either express or implied. See the License for the
10 # specific language governing permissions and limitations under the License.
11 #
12 # Author: Florian Zaruba, ETH Zurich
13 # Date: 19/04/2017
14 # Description: Memory Management Unit for Ariane, contains TLB and
15 # address translation unit. SV39 as defined in RISC-V
16 # privilege specification 1.11-WIP
17
18 import ariane_pkg::*;
19 """
20
21 from nmigen import Const, Signal, Cat, Module
22 from ptw import DCacheReqI, DCacheReqO
23
24 PRIV_LVL_M = Const(0b11, 2)
25 PRIV_LVL_S = Const(0b01, 2)
26 PRIV_LVL_U = Const(0b00, 2)
27
28
29 class RVException:
30 def __init__(self):
31 self.cause = Signal(64) # cause of exception
32 self.tval = Signal(64) # more info of causing exception
33 # (e.g.: instruction causing it),
34 # address of LD/ST fault
35 self.valid = Signal()
36
37 def __iter__(self):
38 yield self.cause
39 yield self.tval
40 yield self.valid
41
42 def ports(self):
43 return list(self)
44
45
46 class ICacheReqI:
47 def __init__(self):
48 self.fetch_valid = Signal() # address translation valid
49 self.fetch_paddr = Signal(64) # physical address in
50 self.fetch_exception = RVException() // exception occurred during fetch
51
52 def __iter__(self):
53 yield self.fetch_valid
54 yield self.fetch_paddr
55 yield from self.fetch_exception
56
57 def ports(self):
58 return list(self)
59
60
61 class ICacheReqO:
62 def __init__(self):
63 self.fetch_req = Signal() # address translation request
64 self.fetch_vaddr = Signal(64) # virtual address out
65
66 def __iter__(self):
67 yield self.fetch_req
68 yield self.fetch_vaddr
69
70 def ports(self):
71 return list(self)
72
73
74 class MMU:
75 def __init__(self, INSTR_TLB_ENTRIES = 4,
76 DATA_TLB_ENTRIES = 4,
77 ASID_WIDTH = 1):
78 self.flush_i = Signal()
79 self.enable_translation_i = Signal()
80 self.en_ld_st_translation_i = Signal() # enable VM translation for LD/ST
81 # IF interface
82 self.icache_areq_i = ICacheReqO()
83 self.icache_areq_o = ICacheReqI()
84 # LSU interface
85 # this is a more minimalistic interface because the actual addressing
86 # logic is handled in the LSU as we distinguish load and stores,
87 # what we do here is simple address translation
88 self.misaligned_ex_i = RVException()
89 self.lsu_req_i = Signal() # request address translation
90 self.lsu_vaddr_i = Signal(64) # virtual address in
91 self.lsu_is_store_i = Signal() # the translation is requested by a store
92 # if we need to walk the page table we can't grant in the same cycle
93
94 # Cycle 0
95 self.lsu_dtlb_hit_o = Signal() # sent in the same cycle as the request
96 # if translation hits in the DTLB
97 # Cycle 1
98 self.lsu_valid_o = Signal() # translation is valid
99 self.lsu_paddr_o = Signal(64) # translated address
100 self.lsu_exception_o = RVException() # addr translate threw exception
101
102 # General control signals
103 self.priv_lvl_i = Signal(2)
104 self.ld_st_priv_lvl_i = Signal(2)
105 self.sum_i = Signal()
106 self.mxr_i = Signal()
107 # input logic flag_mprv_i,
108 self.satp_ppn_i = Signal(44)
109 self.asid_i = Signal(ASID_WIDTH)
110 self.flush_tlb_i = Signal()
111 # Performance counters
112 self.itlb_miss_o = Signal()
113 self.dtlb_miss_o = Signal()
114 # PTW memory interface
115 self.req_port_i = DCacheReqO()
116 self.req_port_o = DCacheReqI()
117 );
118
119 logic iaccess_err; # insufficient priv to access instr page
120 logic daccess_err; # insufficient priv to access data page
121 logic ptw_active; # PTW is currently walking a page table
122 logic walking_instr; # PTW is walking because of an ITLB miss
123 logic ptw_error; # PTW threw an exception
124
125 logic [38:0] update_vaddr;
126 tlb_update_t update_ptw_itlb, update_ptw_dtlb;
127
128 logic itlb_lu_access;
129 riscv::pte_t itlb_content;
130 logic itlb_is_2M;
131 logic itlb_is_1G;
132 logic itlb_lu_hit;
133
134 logic dtlb_lu_access;
135 riscv::pte_t dtlb_content;
136 logic dtlb_is_2M;
137 logic dtlb_is_1G;
138 logic dtlb_lu_hit;
139
140
141 # Assignments
142 assign itlb_lu_access = icache_areq_i.fetch_req;
143 assign dtlb_lu_access = lsu_req_i;
144
145
146 tlb #(
147 .TLB_ENTRIES ( INSTR_TLB_ENTRIES ),
148 .ASID_WIDTH ( ASID_WIDTH )
149 ) i_itlb (
150 .clk_i ( clk_i ),
151 .rst_ni ( rst_ni ),
152 .flush_i ( flush_tlb_i ),
153
154 .update_i ( update_ptw_itlb ),
155
156 .lu_access_i ( itlb_lu_access ),
157 .lu_asid_i ( asid_i ),
158 .lu_vaddr_i ( icache_areq_i.fetch_vaddr ),
159 .lu_content_o ( itlb_content ),
160
161 .lu_is_2M_o ( itlb_is_2M ),
162 .lu_is_1G_o ( itlb_is_1G ),
163 .lu_hit_o ( itlb_lu_hit )
164 );
165
166 tlb #(
167 .TLB_ENTRIES ( DATA_TLB_ENTRIES ),
168 .ASID_WIDTH ( ASID_WIDTH )
169 ) i_dtlb (
170 .clk_i ( clk_i ),
171 .rst_ni ( rst_ni ),
172 .flush_i ( flush_tlb_i ),
173
174 .update_i ( update_ptw_dtlb ),
175
176 .lu_access_i ( dtlb_lu_access ),
177 .lu_asid_i ( asid_i ),
178 .lu_vaddr_i ( lsu_vaddr_i ),
179 .lu_content_o ( dtlb_content ),
180
181 .lu_is_2M_o ( dtlb_is_2M ),
182 .lu_is_1G_o ( dtlb_is_1G ),
183 .lu_hit_o ( dtlb_lu_hit )
184 );
185
186
187 ptw #(
188 .ASID_WIDTH ( ASID_WIDTH )
189 ) i_ptw (
190 .clk_i ( clk_i ),
191 .rst_ni ( rst_ni ),
192 .ptw_active_o ( ptw_active ),
193 .walking_instr_o ( walking_instr ),
194 .ptw_error_o ( ptw_error ),
195 .enable_translation_i ( enable_translation_i ),
196
197 .update_vaddr_o ( update_vaddr ),
198 .itlb_update_o ( update_ptw_itlb ),
199 .dtlb_update_o ( update_ptw_dtlb ),
200
201 .itlb_access_i ( itlb_lu_access ),
202 .itlb_hit_i ( itlb_lu_hit ),
203 .itlb_vaddr_i ( icache_areq_i.fetch_vaddr ),
204
205 .dtlb_access_i ( dtlb_lu_access ),
206 .dtlb_hit_i ( dtlb_lu_hit ),
207 .dtlb_vaddr_i ( lsu_vaddr_i ),
208
209 .req_port_i ( req_port_i ),
210 .req_port_o ( req_port_o ),
211
212 .*
213 );
214
215 # ila_1 i_ila_1 (
216 # .clk(clk_i), # input wire clk
217 # .probe0({req_port_o.address_tag, req_port_o.address_index}),
218 # .probe1(req_port_o.data_req), # input wire [63:0] probe1
219 # .probe2(req_port_i.data_gnt), # input wire [0:0] probe2
220 # .probe3(req_port_i.data_rdata), # input wire [0:0] probe3
221 # .probe4(req_port_i.data_rvalid), # input wire [0:0] probe4
222 # .probe5(ptw_error), # input wire [1:0] probe5
223 # .probe6(update_vaddr), # input wire [0:0] probe6
224 # .probe7(update_ptw_itlb.valid), # input wire [0:0] probe7
225 # .probe8(update_ptw_dtlb.valid), # input wire [0:0] probe8
226 # .probe9(dtlb_lu_access), # input wire [0:0] probe9
227 # .probe10(lsu_vaddr_i), # input wire [0:0] probe10
228 # .probe11(dtlb_lu_hit), # input wire [0:0] probe11
229 # .probe12(itlb_lu_access), # input wire [0:0] probe12
230 # .probe13(icache_areq_i.fetch_vaddr), # input wire [0:0] probe13
231 # .probe14(itlb_lu_hit) # input wire [0:0] probe13
232 # );
233
234 #-----------------------
235 # Instruction Interface
236 #-----------------------
237 # The instruction interface is a simple request response interface
238 always_comb begin : instr_interface
239 # MMU disabled: just pass through
240 icache_areq_o.fetch_valid = icache_areq_i.fetch_req;
241 icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr; # play through in case we disabled address translation
242 # two potential exception sources:
243 # 1. HPTW threw an exception -> signal with a page fault exception
244 # 2. We got an access error because of insufficient permissions -> throw an access exception
245 icache_areq_o.fetch_exception = '0;
246 # Check whether we are allowed to access this memory region from a fetch perspective
247 iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u)
248 || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u));
249
250 # MMU enabled: address from TLB, request delayed until hit. Error when TLB
251 # hit and no access right or TLB hit and translated address not valid (e.g.
252 # AXI decode error), or when PTW performs walk due to ITLB miss and raises
253 # an error.
254 if (enable_translation_i) begin
255 # we work with SV39, so if VM is enabled, check that all bits [63:38] are equal
256 if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[63:38]) == 1'b1 || (|icache_areq_i.fetch_vaddr[63:38]) == 1'b0)) begin
257 icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, icache_areq_i.fetch_vaddr, 1'b1};
258 end
259
260 icache_areq_o.fetch_valid = 1'b0;
261
262 # 4K page
263 icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]};
264 # Mega page
265 if (itlb_is_2M) begin
266 icache_areq_o.fetch_paddr[20:12] = icache_areq_i.fetch_vaddr[20:12];
267 end
268 # Giga page
269 if (itlb_is_1G) begin
270 icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12];
271 end
272
273 # ---------
274 # ITLB Hit
275 # --------
276 # if we hit the ITLB output the request signal immediately
277 if (itlb_lu_hit) begin
278 icache_areq_o.fetch_valid = icache_areq_i.fetch_req;
279 # we got an access error
280 if (iaccess_err) begin
281 # throw a page fault
282 icache_areq_o.fetch_exception = {riscv::INSTR_PAGE_FAULT, icache_areq_i.fetch_vaddr, 1'b1};
283 end
284 end else
285 # ---------
286 # ITLB Miss
287 # ---------
288 # watch out for exceptions happening during walking the page table
289 if (ptw_active && walking_instr) begin
290 icache_areq_o.fetch_valid = ptw_error;
291 icache_areq_o.fetch_exception = {riscv::INSTR_PAGE_FAULT, {25'b0, update_vaddr}, 1'b1};
292 end
293 end
294 end
295
296 #-----------------------
297 # Data Interface
298 #-----------------------
299 logic [63:0] lsu_vaddr_n, lsu_vaddr_q;
300 riscv::pte_t dtlb_pte_n, dtlb_pte_q;
301 exception_t misaligned_ex_n, misaligned_ex_q;
302 logic lsu_req_n, lsu_req_q;
303 logic lsu_is_store_n, lsu_is_store_q;
304 logic dtlb_hit_n, dtlb_hit_q;
305 logic dtlb_is_2M_n, dtlb_is_2M_q;
306 logic dtlb_is_1G_n, dtlb_is_1G_q;
307
308 # check if we need to do translation or if we are always ready (e.g.: we are not translating anything)
309 assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1;
310
311 # The data interface is simpler and only consists of a request/response interface
312 always_comb begin : data_interface
313 # save request and DTLB response
314 lsu_vaddr_n = lsu_vaddr_i;
315 lsu_req_n = lsu_req_i;
316 misaligned_ex_n = misaligned_ex_i;
317 dtlb_pte_n = dtlb_content;
318 dtlb_hit_n = dtlb_lu_hit;
319 lsu_is_store_n = lsu_is_store_i;
320 dtlb_is_2M_n = dtlb_is_2M;
321 dtlb_is_1G_n = dtlb_is_1G;
322
323 lsu_paddr_o = lsu_vaddr_q;
324 lsu_valid_o = lsu_req_q;
325 lsu_exception_o = misaligned_ex_q;
326 # mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions
327 misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i;
328
329 # Check if the User flag is set, then we may only access it in supervisor mode
330 # if SUM is enabled
331 daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || # SUM is not set and we are trying to access a user page in supervisor mode
332 (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); # this is not a user page but we are in user mode and trying to access it
333 # translation is enabled and no misaligned exception occurred
334 if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin
335 lsu_valid_o = 1'b0;
336 # 4K page
337 lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]};
338 # Mega page
339 if (dtlb_is_2M_q) begin
340 lsu_paddr_o[20:12] = lsu_vaddr_q[20:12];
341 end
342 # Giga page
343 if (dtlb_is_1G_q) begin
344 lsu_paddr_o[29:12] = lsu_vaddr_q[29:12];
345 end
346 # ---------
347 # DTLB Hit
348 # --------
349 if (dtlb_hit_q && lsu_req_q) begin
350 lsu_valid_o = 1'b1;
351 # this is a store
352 if (lsu_is_store_q) begin
353 # check if the page is write-able and we are not violating privileges
354 # also check if the dirty flag is set
355 if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin
356 lsu_exception_o = {riscv::STORE_PAGE_FAULT, lsu_vaddr_q, 1'b1};
357 end
358
359 # this is a load, check for sufficient access privileges - throw a page fault if necessary
360 end else if (daccess_err) begin
361 lsu_exception_o = {riscv::LOAD_PAGE_FAULT, lsu_vaddr_q, 1'b1};
362 end
363 end else
364
365 # ---------
366 # DTLB Miss
367 # ---------
368 # watch out for exceptions
369 if (ptw_active && !walking_instr) begin
370 # page table walker threw an exception
371 if (ptw_error) begin
372 # an error makes the translation valid
373 lsu_valid_o = 1'b1;
374 # the page table walker can only throw page faults
375 if (lsu_is_store_q) begin
376 lsu_exception_o = {riscv::STORE_PAGE_FAULT, {25'b0, update_vaddr}, 1'b1};
377 end else begin
378 lsu_exception_o = {riscv::LOAD_PAGE_FAULT, {25'b0, update_vaddr}, 1'b1};
379 end
380 end
381 end
382 end
383 end
384 # ----------
385 # Registers
386 # ----------
387 always_ff @(posedge clk_i or negedge rst_ni) begin
388 if (~rst_ni) begin
389 lsu_vaddr_q <= '0;
390 lsu_req_q <= '0;
391 misaligned_ex_q <= '0;
392 dtlb_pte_q <= '0;
393 dtlb_hit_q <= '0;
394 lsu_is_store_q <= '0;
395 dtlb_is_2M_q <= '0;
396 dtlb_is_1G_q <= '0;
397 end else begin
398 lsu_vaddr_q <= lsu_vaddr_n;
399 lsu_req_q <= lsu_req_n;
400 misaligned_ex_q <= misaligned_ex_n;
401 dtlb_pte_q <= dtlb_pte_n;
402 dtlb_hit_q <= dtlb_hit_n;
403 lsu_is_store_q <= lsu_is_store_n;
404 dtlb_is_2M_q <= dtlb_is_2M_n;
405 dtlb_is_1G_q <= dtlb_is_1G_n;
406 end
407 end
408 endmodule