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
, Cat
22 from nmigen
.hdl
.ast
import ArrayProxy
26 CONFIG_L1D_SIZE
= 32*1024
27 DCACHE_INDEX_WIDTH
= int(log(CONFIG_L1D_SIZE
/ DCACHE_SET_ASSOC
))
28 DCACHE_TAG_WIDTH
= 56 - DCACHE_INDEX_WIDTH
32 self
.address_index
= Signal(DCACHE_INDEX_WIDTH
)
33 self
.address_tag
= Signal(DCACHE_TAG_WIDTH
)
34 self
.data_wdata
= Signal(64)
35 self
.data_req
= Signal()
36 self
.data_we
= Signal()
37 self
.data_be
= Signal(8)
38 self
.data_size
= Signal(2)
39 self
.kill_req
= Signal()
40 self
.tag_valid
= Signal()
46 data_rvalid
= Signal()
47 data_rdata
= Signal(64)
51 class PTE
: #(RecordObject):
53 self
.reserved
= Signal(10)
66 return Cat(*self
.ports())
69 if isinstance(x
, ArrayProxy
):
71 for o
in self
.ports():
72 i
= getattr(x
, o
.name
)
77 return self
.flatten().eq(x
)
80 return [self
.reserved
, self
.ppn
, self
.rsw
, self
.d
, self
.a
, self
.g
,
81 self
.u
, self
.x
, self
.w
, self
.r
, self
.v
]
86 self
.valid
= Signal() # valid flag
90 self
.asid
= Signal(ASID_WIDTH
)
94 return Cat(*self
.ports())
97 return self
.flatten().eq(x
.flatten())
100 return [self
.valid
, self
.is_2M
, self
.is_1G
, self
.vpn
, self
.asid
] + \
103 # SV39 defines three levels of page tables
111 flush_i
= Signal() # flush everything, we need to do this because
112 # actually everything we do is speculative at this stage
113 # e.g.: there could be a CSR instruction that changes everything
114 ptw_active_o
= Signal()
115 walking_instr_o
= Signal() # set when walking for TLB
116 ptw_error_o
= Signal() # set when an error occurred
117 enable_translation_i
= Signal() # CSRs indicate to enable SV39
118 en_ld_st_translation_i
= Signal() # enable VM translation for ld/st
120 lsu_is_store_i
= Signal() , # this translation triggered by store
121 # PTW memory interface
122 req_port_i
= DCacheReqO()
123 req_port_o
= DCacheReqI()
125 # to TLBs, update logic
126 itlb_update_o
= TLBUpdate()
127 dtlb_update_o
= TLBUpdate()
129 update_vaddr_o
= Signal(39)
131 asid_i
= Signal(ASID_WIDTH
)
134 itlb_access_i
= Signal()
135 itlb_hit_i
= Signal()
136 itlb_vaddr_i
= Signal(64)
138 dtlb_access_i
= Signal()
139 dtlb_hit_i
= Signal()
140 dtlb_vaddr_i
= Signal(64)
142 satp_ppn_i
= Signal(44) # ppn from satp
144 # Performance counters
145 itlb_miss_o
= Signal()
146 dtlb_miss_o
= Signal()
150 data_rvalid
= Signal()
151 data_rdata
= Signal(64)
154 m
.d
.comb
+= pte
.eq(data_rdata
)
156 ptw_lvl
= Signal(2, reset
=LVL1
)
158 # is this an instruction page table walk?
159 is_instr_ptw
= Signal()
160 global_mapping
= Signal()
164 tlb_update_asid
= Signal(ASID_WIDTH
)
165 # register VPN we need to walk, SV39 defines a 39 bit virtual addr
167 # 4 byte aligned physical pointer
168 ptw_pptr
= Signal(56)
170 end
= DCACHE_INDEX_WIDTH
+ DCACHE_TAG_WIDTH
173 update_vaddr_o
.eq(vaddr
),
175 ptw_active_o
.eq(state
!= IDLE
),
176 walking_instr_o
.eq(is_instr_ptw
),
177 # directly output the correct physical address
178 req_port_o
.address_index
.eq(ptw_pptr
[0:DCACHE_INDEX_WIDTH
]),
179 req_port_o
.address_tag
.eq(ptw_pptr
[DCACHE_INDEX_WIDTH
:end
]),
180 # we are never going to kill this request
181 req_port_o
.kill_req
.eq(0),
182 # we are never going to write with the HPTW
183 req_port_o
.data_wdata
.eq(Const(0, 64)),
187 itlb_update_o
.vpn
.eq(vaddr
[12:39]),
188 dtlb_update_o
.vpn
.eq(vaddr
[12:39]),
189 # update the correct page table level
190 itlb_update_o
.is_2M
.eq(ptw_lvl
== LVL2
),
191 itlb_update_o
.is_1G
.eq(ptw_lvl
== LVL1
),
192 dtlb_update_o
.is_2M
.eq(ptw_lvl
== LVL2
),
193 dtlb_update_o
.is_1G
.eq(ptw_lvl
== LVL1
),
194 # output the correct ASID
195 itlb_update_o
.asid
.eq(tlb_update_asid
),
196 dtlb_update_o
.asid
.eq(tlb_update_asid
),
197 # set the global mapping bit
198 itlb_update_o
.content
.eq(pte |
(global_mapping
<< 5)),
199 dtlb_update_o
.content
.eq(pte |
(global_mapping
<< 5)),
203 req_port_o
.tag_valid
.eq(tag_valid
),
208 # A virtual address va is translated into a physical address pa as
210 # 1. Let a be sptbr.ppn × PAGESIZE, and let i = LEVELS-1. (For Sv39,
211 # PAGESIZE=2^12 and LEVELS=3.)
212 # 2. Let pte be the value of the PTE at address a+va.vpn[i]×PTESIZE.
213 # (For Sv32, PTESIZE=4.)
214 # 3. If pte.v = 0, or if pte.r = 0 and pte.w = 1, stop and raise an
216 # 4. Otherwise, the PTE is valid. If pte.r = 1 or pte.x = 1, go to
217 # step 5. Otherwise, this PTE is a pointer to the next level of
219 # Let i=i-1. If i < 0, stop and raise an access exception.
220 # Otherwise, let a = pte.ppn × PAGESIZE and go to step 2.
221 # 5. A leaf PTE has been found. Determine if the requested memory
222 # access is allowed by the pte.r, pte.w, and pte.x bits. If not,
223 # stop and raise an access exception. Otherwise, the translation is
224 # successful. Set pte.a to 1, and, if the memory access is a
225 # store, set pte.d to 1.
226 # The translated physical address is given as follows:
227 # - pa.pgoff = va.pgoff.
228 # - If i > 0, then this is a superpage translation and
229 # pa.ppn[i-1:0] = va.vpn[i-1:0].
230 # - pa.ppn[LEVELS-1:i] = pte.ppn[LEVELS-1:i].
231 # 6. If i > 0 and pa.ppn[i − 1 : 0] != 0, this is a misaligned
232 # superpage stop and raise a page-fault exception.
234 m
.d
.sync
+= tag_valid
.eq(0)
236 # default assignments
238 # PTW memory interface
239 req_port_o
.data_req
.eq(0),
240 req_port_o
.data_be
.eq(Const(0xFF, 8)),
241 req_port_o
.data_size
.eq(Const(0b11, 2)),
242 req_port_o
.data_we
.eq(0),
244 itlb_update_o
.valid
.eq(0),
245 dtlb_update_o
.valid
.eq(0),
253 with m
.State("IDLE"):
254 # by default we start with the top-most page table
255 m
.d
.sync
+= [is_instr_ptw
.eq(0),
257 global_mapping
.eq(0),
259 # we got an ITLB miss?
260 with m
.If(enable_translation_i
& itlb_access_i
& \
261 ~itlb_hit_i
& ~dtlb_access_i
):
262 pptr
= Cat(Const(0, 3), itlb_vaddr_i
[30:39], satp_ppn_i
)
263 m
.d
.sync
+= [ptw_pptr
.eq(pptr
),
265 vaddr
.eq(itlb_vaddr_i
),
266 tlb_update_asid
.eq(asid_i
),
268 m
.d
.comb
+= [itlb_miss_o
.eq(1)]
269 m
.next
= "WAIT_GRANT"
270 # we got a DTLB miss?
271 with m
.Elif(en_ld_st_translation_i
& dtlb_access_i
& \
273 pptr
= Cat(Const(0, 3), dtlb_vaddr_i
[30:39], satp_ppn_i
)
274 m
.d
.sync
+= [ptw_pptr
.eq(pptr
),
275 vaddr
.eq(dtlb_vaddr_i
),
276 tlb_update_asid
.eq(asid_i
),
278 m
.d
.comb
+= [ dtlb_miss_o
.eq(1)]
279 m
.next
= "WAIT_GRANT"
281 with m
.State("WAIT_GRANT"):
283 m
.d
.comb
+= req_port_o
.data_req
.eq(1)
284 # wait for the WAIT_GRANT
285 with m
.If(req_port_i
.data_gnt
):
286 # send the tag valid signal one cycle later
287 m
.d
.sync
+= tag_valid
.eq(1)
288 m
.next
= "PTE_LOOKUP"
290 with m
.State("PTE_LOOKUP"):
291 # we wait for the valid signal
292 with m
.If(data_rvalid
):
294 # check if the global mapping bit is set
296 m
.d
.sync
+= global_mapping
.eq(1)
301 # If pte.v = 0, or if pte.r = 0 and pte.w = 1,
302 # stop and raise a page-fault exception.
303 with m
.If (~pte
.v |
(~pte
.r
& pte
.w
)):
304 m
.next
= "PROPAGATE_ERROR"
311 # if pte.r = 1 or pte.x = 1 it is a valid PTE
312 with m
.If (pte
.r | pte
.x
):
313 # Valid translation found (either 1G, 2M or 4K entry)
314 with m
.If(is_instr_ptw
):
318 # If page is not executable, we can
319 # directly raise an error. This
320 # doesn't put a useless entry into
321 # the TLB. The same idea applies
322 # to the access flag since we let
323 # the access flag be managed by SW.
324 with m
.If (~pte
.x | ~pte
.a
):
327 m
.d
.comb
+= itlb_update_o
.valid
.eq(1)
333 # Check if the access flag has been set,
334 # otherwise throw a page-fault
335 # and let the software handle those bits.
336 # If page is not readable (there are
337 # no write-only pages)
338 # we can directly raise an error. This
339 # doesn't put a useless
340 # entry into the TLB.
341 with m
.If(pte
.a
& (pte
.r |
(pte
.x
& mxr_i
))):
342 m
.d
.comb
+= dtlb_update_o
.valid
.eq(1)
344 m
.next
= "PROPAGATE_ERROR"
345 # Request is a store: perform some
347 # If the request was a store and the
348 # page is not write-able, raise an error
349 # the same applies if the dirty flag is not set
350 with m
.If (lsu_is_store_i
& (~pte
.w | ~pte
.d
)):
351 m
.d
.comb
+= dtlb_update_o
.valid
.eq(0)
352 m
.next
= "PROPAGATE_ERROR"
354 # check if the ppn is correctly aligned: Case (6)
357 m
.d
.comb
+= [l2err
.eq((ptw_lvl
== LVL2
) & \
358 pte
.ppn
[0:9] != Const(0, 9)),
359 l1err
.eq((ptw_lvl
== LVL1
) & \
360 pte
.ppn
[0:18] != Const(0, 18))
362 with m
.If(l1err | l2err
):
363 m
.next
= "PROPAGATE_ERROR"
364 m
.d
.comb
+= [dtlb_update_o
.valid
.eq(0),
365 itlb_update_o
.valid
.eq(0)]
367 # this is a pointer to the next TLB level
369 # pointer to next level of page table
370 with m
.If (ptw_lvl
== LVL1
):
371 # we are in the second level now
372 pptr
= Cat(Const(0, 3), dtlb_vaddr_i
[21:30],
374 m
.d
.sync
+= [ptw_pptr
.eq(pptr
),
376 with m
.If(ptw_lvl
== LVL2
):
377 # here we received a pointer to the third level
378 pptr
= Cat(Const(0, 3), dtlb_vaddr_i
[12:21],
380 m
.d
.sync
+= [ptw_pptr
.eq(pptr
),
383 m
.next
= "WAIT_GRANT"
385 with m
.If (ptw_lvl
== LVL3
):
386 # Should already be the last level
387 # page table => Error
388 m
.d
.sync
+= ptw_lvl
.eq(LVL3
)
389 m
.next
= "PROPAGATE_ERROR"
390 # we've got a data WAIT_GRANT so tell the
391 # cache that the tag is valid
393 # Propagate error to MMU/LSU
394 with m
.State("PROPAGATE_ERROR"):
396 m
.d
.comb
+= ptw_error_o
.eq(1)
398 # wait for the rvalid before going back to IDLE
399 with m
.State("WAIT_RVALID"):
400 with m
.If(data_rvalid
):
406 # should we have flushed before we got an rvalid,
407 # wait for it until going back to IDLE
409 # on a flush check whether we are
410 # 1. in the PTE Lookup check whether we still need to wait
412 # 2. waiting for a grant, if so: wait for it
413 # if not, go back to idle
414 with m
.If (((state
== PTE_LOOKUP
) & ~data_rvalid
) | \
415 ((state
== WAIT_GRANT
) & req_port_i
.data_gnt
)):
416 m
.next
= "WAIT_RVALID"
420 m
.d
.sync
+= [data_rdata
.eq(req_port_i
.data_rdata
),
421 data_rvalid
.eq(req_port_i
.data_rvalid
)
425 if __name__ == '__main__':
427 ports = [dut.p.i_valid, dut.n.i_ready,
428 dut.n.o_valid, dut.p.o_ready] + \
429 [dut.p.i_data] + [dut.n.o_data]
430 vl = rtlil.convert(dut, ports=ports)
431 with open("test_bufunbuf999.il", "w") as f: