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: David Schaffenrath, TU Graz
13 # Author: Florian Zaruba, ETH Zurich
15 # Description: Hardware-PTW
17 /* verilator lint_off WIDTH */
21 from nmigen
import Const
, Signal
25 CONFIG_L1D_SIZE
= 32*1024
26 DCACHE_INDEX_WIDTH
= int(log(CONFIG_L1D_SIZE
/ DCACHE_SET_ASSOC
))
27 DCACHE_TAG_WIDTH
= 56 - DCACHE_INDEX_WIDTH
31 self
.address_index
= Signal(DCACHE_INDEX_WIDTH
)
32 self
.address_tag
= Signal(DCACHE_TAG_WIDTH
)
33 self
.data_wdata
= Signal(64)
34 self
.data_req
= Signal()
35 self
.data_we
= Signal()
36 self
.data_be
= Signal(8)
37 self
.data_size
= Signal(2)
38 self
.kill_req
= Signal()
39 self
.tag_valid
= Signal()
45 data_rvalid
= Signal()
46 data_rdata
= Signal(64)
50 class PTE
: #(RecordObject):
52 self
.reserved
= Signal(10)
67 valid
= Signal() # valid flag
71 asid
= Signal(ASID_WIDTH
)
74 # SV39 defines three levels of page tables
82 flush_i
= Signal() # flush everything, we need to do this because
83 # actually everything we do is speculative at this stage
84 # e.g.: there could be a CSR instruction that changes everything
85 ptw_active_o
= Signal()
86 walking_instr_o
= Signal() # set when walking for TLB
87 ptw_error_o
= Signal() # set when an error occurred
88 enable_translation_i
= Signal() # CSRs indicate to enable SV39
89 en_ld_st_translation_i
= Signal() # enable VM translation for ld/st
91 lsu_is_store_i
= Signal() , # this translation triggered by store
92 # PTW memory interface
93 req_port_i
= DCacheReqO()
94 req_port_o
= DCacheReqI()
96 # to TLBs, update logic
97 itlb_update_o
= TLBUpdate()
98 dtlb_update_o
= TLBUpdate()
100 update_vaddr_o
= Signal(39)
102 asid_i
= Signal(ASID_WIDTH
)
105 itlb_access_i
= Signal()
106 itlb_hit_i
= Signal()
107 itlb_vaddr_i
= Signal(64)
109 dtlb_access_i
= Signal()
110 dtlb_hit_i
= Signal()
111 dtlb_vaddr_i
= Signal(64)
113 satp_ppn_i
= Signal(44) # ppn from satp
115 # Performance counters
116 itlb_miss_o
= Signal()
117 dtlb_miss_o
= Signal()
121 data_rvalid
= Signal()
122 data_rdata
= Signal(64)
125 m
.d
.comb
+= pte
.eq(data_rdata
)
127 ptw_lvl
= Signal(2, reset
=LVL1
)
129 # is this an instruction page table walk?
130 is_instr_ptw
= Signal()
131 global_mapping
= Signal()
135 tlb_update_asid
= Signal(ASID_WIDTH
)
136 # register the VPN we need to walk, SV39 defines a 39 bit virtual address
138 # 4 byte aligned physical pointer
139 ptw_pptr
= Signal(56)
141 end
= DCACHE_INDEX_WIDTH
+ DCACHE_TAG_WIDTH
144 update_vaddr_o
.eq(vaddr
),
146 ptw_active_o
.eq(state
!= IDLE
),
147 walking_instr_o
.eq(is_instr_ptw
),
148 # directly output the correct physical address
149 req_port_o
.address_index
.eq(ptw_pptr
[0:DCACHE_INDEX_WIDTH
]),
150 req_port_o
.address_tag
.eq(ptw_pptr
[DCACHE_INDEX_WIDTH
:end
]),
151 # we are never going to kill this request
152 req_port_o
.kill_req
.eq(0),
153 # we are never going to write with the HPTW
154 req_port_o
.data_wdata
.eq(Const(0, 64)),
158 itlb_update_o
.vpn
.eq(vaddr
[12:39]),
159 dtlb_update_o
.vpn
.eq(vaddr
[12:39]),
160 # update the correct page table level
161 itlb_update_o
.is_2M
.eq(ptw_lvl
== LVL2
),
162 itlb_update_o
.is_1G
.eq(ptw_lvl
== LVL1
),
163 dtlb_update_o
.is_2M
.eq(ptw_lvl
== LVL2
),
164 dtlb_update_o
.is_1G
.eq(ptw_lvl
== LVL1
),
165 # output the correct ASID
166 itlb_update_o
.asid
.eq(tlb_update_asid
),
167 dtlb_update_o
.asid
.eq(tlb_update_asid
),
168 # set the global mapping bit
169 itlb_update_o
.content
.eq(pte |
(global_mapping
<< 5)),
170 dtlb_update_o
.content
.eq(pte |
(global_mapping
<< 5)),
174 req_port_o
.tag_valid
.eq(tag_valid
),
179 # A virtual address va is translated into a physical address pa as follows:
180 # 1. Let a be sptbr.ppn × PAGESIZE, and let i = LEVELS-1. (For Sv39,
181 # PAGESIZE=2^12 and LEVELS=3.)
182 # 2. Let pte be the value of the PTE at address a+va.vpn[i]×PTESIZE. (For
184 # 3. If pte.v = 0, or if pte.r = 0 and pte.w = 1, stop and raise an access
186 # 4. Otherwise, the PTE is valid. If pte.r = 1 or pte.x = 1, go to step 5.
187 # Otherwise, this PTE is a pointer to the next level of the page table.
188 # Let i=i-1. If i < 0, stop and raise an access exception. Otherwise, let
189 # a = pte.ppn × PAGESIZE and go to step 2.
190 # 5. A leaf PTE has been found. Determine if the requested memory access
191 # is allowed by the pte.r, pte.w, and pte.x bits. If not, stop and
192 # raise an access exception. Otherwise, the translation is successful.
193 # Set pte.a to 1, and, if the memory access is a store, set pte.d to 1.
194 # The translated physical address is given as follows:
195 # - pa.pgoff = va.pgoff.
196 # - If i > 0, then this is a superpage translation and
197 # pa.ppn[i-1:0] = va.vpn[i-1:0].
198 # - pa.ppn[LEVELS-1:i] = pte.ppn[LEVELS-1:i].
199 # 6. If i > 0 and pa.ppn[i − 1 : 0] != 0, this is a misaligned superpage;
200 # stop and raise a page-fault exception.
202 m
.d
.sync
+= tag_valid
.eq(0)
204 # default assignments
206 # PTW memory interface
207 req_port_o
.data_req
.eq(0),
208 req_port_o
.data_be
.eq(Const(0xFF, 8)),
209 req_port_o
.data_size
.eq(Const(0b11, 2)),
210 req_port_o
.data_we
.eq(0),
212 itlb_update_o
.valid
.eq(0),
213 dtlb_update_o
.valid
.eq(0),
221 with m
.State("IDLE"):
222 # by default we start with the top-most page table
223 m
.d
.sync
+= [is_instr_ptw
.eq(0),
225 global_mapping
.eq(0),
227 # we got an ITLB miss?
228 with m
.If(enable_translation_i
& itlb_access_i
& \
229 ~itlb_hit_i
& ~dtlb_access_i
):
230 pptr
= Cat(Const(0, 3), itlb_vaddr_i
[30:39], satp_ppn_i
)
231 m
.d
.sync
+= [ptw_pptr
.eq(pptr
),
233 vaddr
.eq(itlb_vaddr_i
),
234 tlb_update_asid
.eq(asid_i
),
236 m
.d
.comb
+= [itlb_miss_o
.eq(1)]
237 m
.next
= "WAIT_GRANT"
238 # we got a DTLB miss?
239 with m
.Elif(en_ld_st_translation_i
& dtlb_access_i
& \
241 pptr
= Cat(Const(0, 3), dtlb_vaddr_i
[30:39], satp_ppn_i
)
242 m
.d
.sync
+= [ptw_pptr
.eq(pptr
),
243 vaddr
.eq(dtlb_vaddr_i
),
244 tlb_update_asid
.eq(asid_i
),
246 m
.d
.comb
+= [ dtlb_miss_o
.eq(1)]
247 m
.next
= "WAIT_GRANT"
249 with m
.State("WAIT_GRANT"):
251 m
.d
.comb
+= req_port_o
.data_req
.eq(1)
252 # wait for the WAIT_GRANT
253 with m
.If(req_port_i
.data_gnt
):
254 # send the tag valid signal one cycle later
255 m
.d
.sync
+= tag_valid
.eq(1)
256 m
.next
= "PTE_LOOKUP"
258 with m
.State("PTE_LOOKUP"):
259 # we wait for the valid signal
260 with m
.If(data_rvalid
):
262 # check if the global mapping bit is set
264 m
.d
.sync
+= global_mapping
.eq(1)
269 # If pte.v = 0, or if pte.r = 0 and pte.w = 1,
270 # stop and raise a page-fault exception.
271 with m
.If (~pte
.v |
(~pte
.r
& pte
.w
)):
272 m
.next
= "PROPAGATE_ERROR"
279 # if pte.r = 1 or pte.x = 1 it is a valid PTE
280 with m
.If (pte
.r | pte
.x
):
281 # Valid translation found (either 1G, 2M or 4K entry)
282 with m
.If(is_instr_ptw
):
286 # If page is not executable, we can
287 # directly raise an error. This
288 # doesn't put a useless entry into
289 # the TLB. The same idea applies
290 # to the access flag since we let
291 # the access flag be managed by SW.
292 with m
.If (~pte
.x | ~pte
.a
):
295 m
.d
.comb
+= itlb_update_o
.valid
.eq(1)
301 # Check if the access flag has been set,
302 # otherwise throw a page-fault
303 # and let the software handle those bits.
304 # If page is not readable (there are
305 # no write-only pages)
306 # we can directly raise an error. This
307 # doesn't put a useless
308 # entry into the TLB.
309 with m
.If(pte
.a
& (pte
.r |
(pte
.x
& mxr_i
))):
310 m
.d
.comb
+= dtlb_update_o
.valid
.eq(1)
312 m
.next
= "PROPAGATE_ERROR"
313 # Request is a store: perform some
315 # If the request was a store and the
316 # page is not write-able, raise an error
317 # the same applies if the dirty flag is not set
318 with m
.If (lsu_is_store_i
& (~pte
.w | ~pte
.d
)):
319 m
.d
.comb
+= dtlb_update_o
.valid
.eq(0)
320 m
.next
= "PROPAGATE_ERROR"
322 # check if the ppn is correctly aligned: Case (6)
325 m
.d
.comb
+= [l2err
.eq((ptw_lvl
== LVL2
) & \
326 pte
.ppn
[0:9] != Const(0, 9)),
327 l1err
.eq((ptw_lvl
== LVL1
) & \
328 pte
.ppn
[0:18] != Const(0, 18))
330 with m
.If(l1err | l2err
):
331 m
.next
= "PROPAGATE_ERROR"
332 m
.d
.comb
+= [dtlb_update_o
.valid
.eq(0),
333 itlb_update_o
.valid
.eq(0)]
335 # this is a pointer to the next TLB level
337 # pointer to next level of page table
338 with m
.If (ptw_lvl
== LVL1
):
339 # we are in the second level now
340 pptr
= Cat(Const(0, 3), dtlb_vaddr_i
[21:30],
342 m
.d
.sync
+= [ptw_pptr
.eq(pptr
),
344 with m
.If(ptw_lvl
== LVL2
):
345 # here we received a pointer to the third level
346 pptr
= Cat(Const(0, 3), dtlb_vaddr_i
[12:21],
348 m
.d
.sync
+= [ptw_pptr
.eq(pptr
),
351 m
.next
= "WAIT_GRANT"
353 with m
.If (ptw_lvl
== LVL3
):
354 # Should already be the last level
355 # page table => Error
356 m
.d
.sync
+= ptw_lvl
.eq(LVL3
)
357 m
.next
= "PROPAGATE_ERROR"
358 # we've got a data WAIT_GRANT so tell the
359 # cache that the tag is valid
361 # Propagate error to MMU/LSU
362 with m
.State("PROPAGATE_ERROR"):
364 m
.d
.comb
+= ptw_error_o
.eq(1)
366 # wait for the rvalid before going back to IDLE
367 with m
.State("WAIT_RVALID"):
368 with m
.If(data_rvalid
):
374 # should we have flushed before we got an rvalid,
375 # wait for it until going back to IDLE
377 # on a flush check whether we are
378 # 1. in the PTE Lookup check whether we still need to wait
380 # 2. waiting for a grant, if so: wait for it
381 # if not, go back to idle
382 with m
.If (((state
== PTE_LOOKUP
) & ~data_rvalid
) | \
383 ((state
== WAIT_GRANT
) & req_port_i
.data_gnt
)):
384 m
.next
= "WAIT_RVALID"
388 m
.d
.sync
+= [data_rdata
.eq(req_port_i
.data_rdata
),
389 data_rvalid
.eq(req_port_i
.data_rvalid
)