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