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.
12 # Author: Florian Zaruba, ETH Zurich
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
21 from nmigen
import Const
, Signal
, Cat
, Module
22 from ptw
import DCacheReqI
, DCacheReqO
, TLBUpdate
, PTE
, PTW
26 PRIV_LVL_M
= Const(0b11, 2)
27 PRIV_LVL_S
= Const(0b01, 2)
28 PRIV_LVL_U
= Const(0b00, 2)
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
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
55 yield self
.fetch_valid
56 yield self
.fetch_paddr
57 yield from self
.fetch_exception
65 self
.fetch_req
= Signal() # address translation request
66 self
.fetch_vaddr
= Signal(64) # virtual address out
70 yield self
.fetch_vaddr
77 def __init__(self
, INSTR_TLB_ENTRIES
= 4,
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
84 self
.icache_areq_i
= ICacheReqO()
85 self
.icache_areq_o
= ICacheReqI()
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
97 self
.lsu_dtlb_hit_o
= Signal() # sent in the same cycle as the request
98 # if translation hits in the DTLB
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
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()
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
127 update_vaddr
= Signal(39)
128 uaddr64
= Cat(update_vaddr
, Const(0, 25)) # extend to 64bit with zeros
129 update_ptw_itlb
= TLBUpdate()
130 update_ptw_dtlb
= TLBUpdate()
132 itlb_lu_access
= Signal()
134 itlb_is_2M
= Signal()
135 itlb_is_1G
= Signal()
136 itlb_lu_hit
= Signal()
138 dtlb_lu_access
= Signal()
140 dtlb_is_2M
= Signal()
141 dtlb_is_1G
= Signal()
142 dtlb_lu_hit
= Signal()
145 m
.d
.comb
+= [itlb_lu_access
.eq(icache_areq_i
.fetch_req
),
146 dtlb_lu_access
.eq(lsu_req_i
)
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
),
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
),
176 m
.submodules
.ptw
= ptw
= PTW(ASID_WIDTH
)
177 m
.d
.comb
+= [ptw_active
.eq(ptw
.ptw_active_o
),
178 walking_instr
.eq(ptw
.walking_instr_o
),
179 ptw_error
.eq(ptw
.ptw_error_o
),
180 ptw
.enable_translation_i
.eq(enable_translation_i
),
182 update_vaddr
.eq(ptw
.update_vaddr_o
),
183 update_ptw_itlb
.eq(ptw
.itlb_update_o
),
184 update_ptw_dtlb
.eq(ptw
.dtlb_update_o
),
186 ptw
.itlb_access_i
.eq(itlb_lu_access
),
187 ptw
.itlb_hit_i
.eq(itlb_lu_hit
),
188 ptw
.itlb_vaddr_i
.eq(icache_areq_i
.fetch_vaddr
),
190 ptw
.dtlb_access_i
.eq(dtlb_lu_access
),
191 ptw
.dtlb_hit_i
.eq(dtlb_lu_hit
),
192 ptw
.dtlb_vaddr_i
.eq(lsu_vaddr_i
),
194 ptw
.req_port_i
.eq(req_port_i
),
195 req_port_o
.eq(ptw
.req_port_o
),
199 # .clk(clk_i), # input wire clk
200 # .probe0({req_port_o.address_tag, req_port_o.address_index}),
201 # .probe1(req_port_o.data_req), # input wire [63:0] probe1
202 # .probe2(req_port_i.data_gnt), # input wire [0:0] probe2
203 # .probe3(req_port_i.data_rdata), # input wire [0:0] probe3
204 # .probe4(req_port_i.data_rvalid), # input wire [0:0] probe4
205 # .probe5(ptw_error), # input wire [1:0] probe5
206 # .probe6(update_vaddr), # input wire [0:0] probe6
207 # .probe7(update_ptw_itlb.valid), # input wire [0:0] probe7
208 # .probe8(update_ptw_dtlb.valid), # input wire [0:0] probe8
209 # .probe9(dtlb_lu_access), # input wire [0:0] probe9
210 # .probe10(lsu_vaddr_i), # input wire [0:0] probe10
211 # .probe11(dtlb_lu_hit), # input wire [0:0] probe11
212 # .probe12(itlb_lu_access), # input wire [0:0] probe12
213 # .probe13(icache_areq_i.fetch_vaddr), # input wire [0:0] probe13
214 # .probe14(itlb_lu_hit) # input wire [0:0] probe13
217 #-----------------------
218 # Instruction Interface
219 #-----------------------
220 # The instruction interface is a simple request response interface
222 # MMU disabled: just pass through
223 m
.d
.comb
+= [icache_areq_o
.fetch_valid
.eq(icache_areq_i
.fetch_req
),
224 # play through in case we disabled address translation
225 icache_areq_o
.fetch_paddr
.eq(icache_areq_i
.fetch_vaddr
)
227 # two potential exception sources:
228 # 1. HPTW threw an exception -> signal with a page fault exception
229 # 2. We got an access error because of insufficient permissions ->
230 # throw an access exception
231 m
.d
.comb
+= icache_areq_o
.fetch_exception
.eq(0)
232 # Check whether we are allowed to access this memory region
233 # from a fetch perspective
235 # XXX TODO: use PermissionValidator instead [we like modules]
236 m
.d
.comb
+= iaccess_err
.eq(icache_areq_i
.fetch_req
& \
237 (((priv_lvl_i
== PRIV_LVL_U
) & \
239 ((priv_lvl_i
== PRIV_LVL_S
) & \
242 # MMU enabled: address from TLB, request delayed until hit.
243 # Error when TLB hit and no access right or TLB hit and
244 # translated address not valid (e.g. AXI decode error),
245 # or when PTW performs walk due to ITLB miss and raises
247 with m
.If (self
.enable_translation_i
):
248 # we work with SV39, so if VM is enabled, check that
249 # all bits [63:38] are equal
250 with m
.If (icache_areq_i
.fetch_req
& \
251 ~
(((~icache_areq_i
.fetch_vaddr
[38:64]) == 0) | \
252 (icache_areq_i
.fetch_vaddr
[38:64]) == 0)):
253 fe
= icache_areq_o
.fetch_exception
254 m
.d
.comb
+= [fe
.cause
.eq(INSTR_ACCESS_FAULT
),
255 fe
.tval
.eq(icache_areq_i
.fetch_vaddr
),
259 m
.d
.comb
+= icache_areq_o
.fetch_valid
.eq(0)
262 paddr
= Signal
.like(icache_areq_o
.fetch_paddr
)
263 paddr4k
= Cat(icache_areq_i
.fetch_vaddr
[0:12], itlb_content
.ppn
)
264 m
.d
.comb
+= paddr
.eq(paddr4k
)
266 with m
.If(itlb_is_2M
):
267 m
.d
.comb
+= paddr
[12:21].eq(icache_areq_i
.fetch_vaddr
[12:21])
270 with m
.If(itlb_is_1G
):
271 m
.d
.comb
+= paddr
[12:30].eq(icache_areq_i
.fetch_vaddr
[12:30])
272 m
.d
.comb
+= icache_areq_o
.fetch_paddr
.eq(paddr
)
277 # if we hit the ITLB output the request signal immediately
278 with m
.If(itlb_lu_hit
):
279 m
.d
.comb
+= icache_areq_o
.fetch_valid
.eq(
280 icache_areq_i
.fetch_req
)
281 # we got an access error
282 with m
.If (iaccess_err
):
284 fe
= icache_areq_o
.fetch_exception
285 m
.d
.comb
+= [fe
.cause
.eq(INSTR_ACCESS_FAULT
),
286 fe
.tval
.eq(icache_areq_i
.fetch_vaddr
),
292 # watch out for exceptions happening during walking the page table
293 with m
.Elif(ptw_active
& walking_instr
):
294 m
.d
.comb
+= icache_areq_o
.fetch_valid
.eq(ptw_error
)
295 fe
= icache_areq_o
.fetch_exception
296 m
.d
.comb
+= [fe
.cause
.eq(INSTR_PAGE_FAULT
),
301 #-----------------------
303 #-----------------------
305 lsu_vaddr
= Signal(64)
307 misaligned_ex
= RVException()
309 lsu_is_store
= Signal()
311 dtlb_is_2M
= Signal()
312 dtlb_is_1G
= Signal()
314 # check if we need to do translation or if we are always
315 # ready (e.g.: we are not translating anything)
316 m
.d
.comb
+= lsu_dtlb_hit_o
.eq(Mux(en_ld_st_translation_i
),
319 # The data interface is simpler and only consists of a
320 # request/response interface
322 # save request and DTLB response
323 lsu_vaddr
.eq(lsu_vaddr_i
),
324 lsu_req
.eq(lsu_req_i
),
325 misaligned_ex
.eq(misaligned_ex_i
),
326 dtlb_pte
.eq(dtlb_content
),
327 dtlb_hit
.eq(dtlb_lu_hit
),
328 lsu_is_store
.eq(lsu_is_store_i
),
329 dtlb_is_2M
.eq(dtlb_is_2M
),
330 dtlb_is_1G
.eq(dtlb_is_1G
),
333 lsu_paddr_o
.eq(lsu_vaddr
),
334 lsu_valid_o
.eq(lsu_req
),
335 lsu_exception_o
.eq(misaligned_ex
),
339 # mute misaligned exceptions if there is no request
340 # otherwise they will throw accidental exceptions
341 misaligned_ex_n
.valid
.eq(misaligned_ex_i
.valid
& lsu_req_i
),
343 # Check if the User flag is set, then we may only
344 # access it in supervisor mode if SUM is enabled
347 # SUM is not set and we are trying to access a user
348 # page in supervisor mode
349 ld_st_priv_lvl_i
== PRIV_LVL_S
& ~sum_i
& \
351 # this is not a user page but we are in user mode and
352 # trying to access it
353 (ld_st_priv_lvl_i
== PRIV_LVL_U
& ~dtlb_pte_q
.u
))
355 # translation is enabled and no misaligned exception occurred
356 with m
.If(en_ld_st_translation_i
& ~misaligned_ex_q
.valid
):
357 m
.d
.comb
+= lsu_valid_o
.eq(0)
359 paddr
= Signal
.like(lsu_vaddr_q
)
360 paddr4k
= Cat(lsu_vaddr_q
[0:12], itlb_content
.ppn
)
361 m
.d
.comb
+= paddr
.eq(paddr4k
)
363 with m
.If(dtlb_is_2M
):
364 m
.d
.comb
+= paddr
[12:21].eq(lsu_vaddr_q
[12:21])
367 with m
.If(dtlb_is_1G
):
368 m
.d
.comb
+= paddr
[12:30].eq(lsu_vaddr_q
[12:30])
369 m
.d
.comb
+= lsu_paddr_o
.eq(paddr
)
374 with m
.If(dtlb_hit_q
& lsu_req_q
):
375 m
.d
.comb
+= lsu_valid_o
.eq(1)
377 with m
.If (lsu_is_store_q
):
378 # check if the page is write-able and
379 # we are not violating privileges
380 # also check if the dirty flag is set
381 with m
.If(~dtlb_pte_q
.w | daccess_err | ~dtlb_pte_q
.d
):
383 m
.d
.comb
+= [le
.cause
.eq(STORE_PAGE_FAULT
),
384 le
.tval
.eq(lsu_vaddr_q
),
388 # this is a load, check for sufficient access
389 # privileges - throw a page fault if necessary
390 with m
.Elif(daccess_err
):
392 m
.d
.comb
+= [le
.cause
.eq(LOAD_PAGE_FAULT
),
393 le
.tval
.eq(lsu_vaddr_q
),
399 # watch out for exceptions
400 with m
.Elif (ptw_active
& ~walking_instr
):
401 # page table walker threw an exception
402 with m
.If (ptw_error
):
403 # an error makes the translation valid
404 m
.d
.comb
+= lsu_valid_o
.eq(1)
405 # the page table walker can only throw page faults
406 with m
.If (lsu_is_store_q
):
408 m
.d
.comb
+= [le
.cause
.eq(STORE_PAGE_FAULT
),
413 m
.d
.comb
+= [le
.cause
.eq(LOAD_PAGE_FAULT
),