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)
65 return [self
.reserved
.eq(x
.reserved
),
66 self
.ppn
.eq(x
.ppn
), self
.rsw
.eq(x
.rsw
),
67 self
.d
.eq(x
.d
), self
.a
.eq(x
.a
), self
.g
.eq(x
.g
),
68 self
.u
.eq(x
.u
), self
.x
.eq(x
.x
), self
.w
.eq(x
.w
),
69 self
.r
.eq(x
.r
), self
.v
.eq(x
.v
)]
72 return [self
.reserved
, self
.ppn
, self
.rsw
, self
.d
, self
.a
, self
.g
,
73 self
.u
, self
.x
, self
.w
, self
.r
, self
.v
]
78 self
.valid
= Signal() # valid flag
82 self
.asid
= Signal(ASID_WIDTH
)
86 return [self
.valid
, self
.is_2M
, self
.is_1G
, self
.vpn
, self
.asid
,
89 # SV39 defines three levels of page tables
97 flush_i
= Signal() # flush everything, we need to do this because
98 # actually everything we do is speculative at this stage
99 # e.g.: there could be a CSR instruction that changes everything
100 ptw_active_o
= Signal()
101 walking_instr_o
= Signal() # set when walking for TLB
102 ptw_error_o
= Signal() # set when an error occurred
103 enable_translation_i
= Signal() # CSRs indicate to enable SV39
104 en_ld_st_translation_i
= Signal() # enable VM translation for ld/st
106 lsu_is_store_i
= Signal() , # this translation triggered by store
107 # PTW memory interface
108 req_port_i
= DCacheReqO()
109 req_port_o
= DCacheReqI()
111 # to TLBs, update logic
112 itlb_update_o
= TLBUpdate()
113 dtlb_update_o
= TLBUpdate()
115 update_vaddr_o
= Signal(39)
117 asid_i
= Signal(ASID_WIDTH
)
120 itlb_access_i
= Signal()
121 itlb_hit_i
= Signal()
122 itlb_vaddr_i
= Signal(64)
124 dtlb_access_i
= Signal()
125 dtlb_hit_i
= Signal()
126 dtlb_vaddr_i
= Signal(64)
128 satp_ppn_i
= Signal(44) # ppn from satp
130 # Performance counters
131 itlb_miss_o
= Signal()
132 dtlb_miss_o
= Signal()
136 data_rvalid
= Signal()
137 data_rdata
= Signal(64)
140 m
.d
.comb
+= pte
.eq(data_rdata
)
142 ptw_lvl
= Signal(2, reset
=LVL1
)
144 # is this an instruction page table walk?
145 is_instr_ptw
= Signal()
146 global_mapping
= Signal()
150 tlb_update_asid
= Signal(ASID_WIDTH
)
151 # register the VPN we need to walk, SV39 defines a 39 bit virtual address
153 # 4 byte aligned physical pointer
154 ptw_pptr
= Signal(56)
156 end
= DCACHE_INDEX_WIDTH
+ DCACHE_TAG_WIDTH
159 update_vaddr_o
.eq(vaddr
),
161 ptw_active_o
.eq(state
!= IDLE
),
162 walking_instr_o
.eq(is_instr_ptw
),
163 # directly output the correct physical address
164 req_port_o
.address_index
.eq(ptw_pptr
[0:DCACHE_INDEX_WIDTH
]),
165 req_port_o
.address_tag
.eq(ptw_pptr
[DCACHE_INDEX_WIDTH
:end
]),
166 # we are never going to kill this request
167 req_port_o
.kill_req
.eq(0),
168 # we are never going to write with the HPTW
169 req_port_o
.data_wdata
.eq(Const(0, 64)),
173 itlb_update_o
.vpn
.eq(vaddr
[12:39]),
174 dtlb_update_o
.vpn
.eq(vaddr
[12:39]),
175 # update the correct page table level
176 itlb_update_o
.is_2M
.eq(ptw_lvl
== LVL2
),
177 itlb_update_o
.is_1G
.eq(ptw_lvl
== LVL1
),
178 dtlb_update_o
.is_2M
.eq(ptw_lvl
== LVL2
),
179 dtlb_update_o
.is_1G
.eq(ptw_lvl
== LVL1
),
180 # output the correct ASID
181 itlb_update_o
.asid
.eq(tlb_update_asid
),
182 dtlb_update_o
.asid
.eq(tlb_update_asid
),
183 # set the global mapping bit
184 itlb_update_o
.content
.eq(pte |
(global_mapping
<< 5)),
185 dtlb_update_o
.content
.eq(pte |
(global_mapping
<< 5)),
189 req_port_o
.tag_valid
.eq(tag_valid
),
194 # A virtual address va is translated into a physical address pa as follows:
195 # 1. Let a be sptbr.ppn × PAGESIZE, and let i = LEVELS-1. (For Sv39,
196 # PAGESIZE=2^12 and LEVELS=3.)
197 # 2. Let pte be the value of the PTE at address a+va.vpn[i]×PTESIZE. (For
199 # 3. If pte.v = 0, or if pte.r = 0 and pte.w = 1, stop and raise an access
201 # 4. Otherwise, the PTE is valid. If pte.r = 1 or pte.x = 1, go to step 5.
202 # Otherwise, this PTE is a pointer to the next level of the page table.
203 # Let i=i-1. If i < 0, stop and raise an access exception. Otherwise, let
204 # a = pte.ppn × PAGESIZE and go to step 2.
205 # 5. A leaf PTE has been found. Determine if the requested memory access
206 # is allowed by the pte.r, pte.w, and pte.x bits. If not, stop and
207 # raise an access exception. Otherwise, the translation is successful.
208 # Set pte.a to 1, and, if the memory access is a store, set pte.d to 1.
209 # The translated physical address is given as follows:
210 # - pa.pgoff = va.pgoff.
211 # - If i > 0, then this is a superpage translation and
212 # pa.ppn[i-1:0] = va.vpn[i-1:0].
213 # - pa.ppn[LEVELS-1:i] = pte.ppn[LEVELS-1:i].
214 # 6. If i > 0 and pa.ppn[i − 1 : 0] != 0, this is a misaligned superpage;
215 # stop and raise a page-fault exception.
217 m
.d
.sync
+= tag_valid
.eq(0)
219 # default assignments
221 # PTW memory interface
222 req_port_o
.data_req
.eq(0),
223 req_port_o
.data_be
.eq(Const(0xFF, 8)),
224 req_port_o
.data_size
.eq(Const(0b11, 2)),
225 req_port_o
.data_we
.eq(0),
227 itlb_update_o
.valid
.eq(0),
228 dtlb_update_o
.valid
.eq(0),
236 with m
.State("IDLE"):
237 # by default we start with the top-most page table
238 m
.d
.sync
+= [is_instr_ptw
.eq(0),
240 global_mapping
.eq(0),
242 # we got an ITLB miss?
243 with m
.If(enable_translation_i
& itlb_access_i
& \
244 ~itlb_hit_i
& ~dtlb_access_i
):
245 pptr
= Cat(Const(0, 3), itlb_vaddr_i
[30:39], satp_ppn_i
)
246 m
.d
.sync
+= [ptw_pptr
.eq(pptr
),
248 vaddr
.eq(itlb_vaddr_i
),
249 tlb_update_asid
.eq(asid_i
),
251 m
.d
.comb
+= [itlb_miss_o
.eq(1)]
252 m
.next
= "WAIT_GRANT"
253 # we got a DTLB miss?
254 with m
.Elif(en_ld_st_translation_i
& dtlb_access_i
& \
256 pptr
= Cat(Const(0, 3), dtlb_vaddr_i
[30:39], satp_ppn_i
)
257 m
.d
.sync
+= [ptw_pptr
.eq(pptr
),
258 vaddr
.eq(dtlb_vaddr_i
),
259 tlb_update_asid
.eq(asid_i
),
261 m
.d
.comb
+= [ dtlb_miss_o
.eq(1)]
262 m
.next
= "WAIT_GRANT"
264 with m
.State("WAIT_GRANT"):
266 m
.d
.comb
+= req_port_o
.data_req
.eq(1)
267 # wait for the WAIT_GRANT
268 with m
.If(req_port_i
.data_gnt
):
269 # send the tag valid signal one cycle later
270 m
.d
.sync
+= tag_valid
.eq(1)
271 m
.next
= "PTE_LOOKUP"
273 with m
.State("PTE_LOOKUP"):
274 # we wait for the valid signal
275 with m
.If(data_rvalid
):
277 # check if the global mapping bit is set
279 m
.d
.sync
+= global_mapping
.eq(1)
284 # If pte.v = 0, or if pte.r = 0 and pte.w = 1,
285 # stop and raise a page-fault exception.
286 with m
.If (~pte
.v |
(~pte
.r
& pte
.w
)):
287 m
.next
= "PROPAGATE_ERROR"
294 # if pte.r = 1 or pte.x = 1 it is a valid PTE
295 with m
.If (pte
.r | pte
.x
):
296 # Valid translation found (either 1G, 2M or 4K entry)
297 with m
.If(is_instr_ptw
):
301 # If page is not executable, we can
302 # directly raise an error. This
303 # doesn't put a useless entry into
304 # the TLB. The same idea applies
305 # to the access flag since we let
306 # the access flag be managed by SW.
307 with m
.If (~pte
.x | ~pte
.a
):
310 m
.d
.comb
+= itlb_update_o
.valid
.eq(1)
316 # Check if the access flag has been set,
317 # otherwise throw a page-fault
318 # and let the software handle those bits.
319 # If page is not readable (there are
320 # no write-only pages)
321 # we can directly raise an error. This
322 # doesn't put a useless
323 # entry into the TLB.
324 with m
.If(pte
.a
& (pte
.r |
(pte
.x
& mxr_i
))):
325 m
.d
.comb
+= dtlb_update_o
.valid
.eq(1)
327 m
.next
= "PROPAGATE_ERROR"
328 # Request is a store: perform some
330 # If the request was a store and the
331 # page is not write-able, raise an error
332 # the same applies if the dirty flag is not set
333 with m
.If (lsu_is_store_i
& (~pte
.w | ~pte
.d
)):
334 m
.d
.comb
+= dtlb_update_o
.valid
.eq(0)
335 m
.next
= "PROPAGATE_ERROR"
337 # check if the ppn is correctly aligned: Case (6)
340 m
.d
.comb
+= [l2err
.eq((ptw_lvl
== LVL2
) & \
341 pte
.ppn
[0:9] != Const(0, 9)),
342 l1err
.eq((ptw_lvl
== LVL1
) & \
343 pte
.ppn
[0:18] != Const(0, 18))
345 with m
.If(l1err | l2err
):
346 m
.next
= "PROPAGATE_ERROR"
347 m
.d
.comb
+= [dtlb_update_o
.valid
.eq(0),
348 itlb_update_o
.valid
.eq(0)]
350 # this is a pointer to the next TLB level
352 # pointer to next level of page table
353 with m
.If (ptw_lvl
== LVL1
):
354 # we are in the second level now
355 pptr
= Cat(Const(0, 3), dtlb_vaddr_i
[21:30],
357 m
.d
.sync
+= [ptw_pptr
.eq(pptr
),
359 with m
.If(ptw_lvl
== LVL2
):
360 # here we received a pointer to the third level
361 pptr
= Cat(Const(0, 3), dtlb_vaddr_i
[12:21],
363 m
.d
.sync
+= [ptw_pptr
.eq(pptr
),
366 m
.next
= "WAIT_GRANT"
368 with m
.If (ptw_lvl
== LVL3
):
369 # Should already be the last level
370 # page table => Error
371 m
.d
.sync
+= ptw_lvl
.eq(LVL3
)
372 m
.next
= "PROPAGATE_ERROR"
373 # we've got a data WAIT_GRANT so tell the
374 # cache that the tag is valid
376 # Propagate error to MMU/LSU
377 with m
.State("PROPAGATE_ERROR"):
379 m
.d
.comb
+= ptw_error_o
.eq(1)
381 # wait for the rvalid before going back to IDLE
382 with m
.State("WAIT_RVALID"):
383 with m
.If(data_rvalid
):
389 # should we have flushed before we got an rvalid,
390 # wait for it until going back to IDLE
392 # on a flush check whether we are
393 # 1. in the PTE Lookup check whether we still need to wait
395 # 2. waiting for a grant, if so: wait for it
396 # if not, go back to idle
397 with m
.If (((state
== PTE_LOOKUP
) & ~data_rvalid
) | \
398 ((state
== WAIT_GRANT
) & req_port_i
.data_gnt
)):
399 m
.next
= "WAIT_RVALID"
403 m
.d
.sync
+= [data_rdata
.eq(req_port_i
.data_rdata
),
404 data_rvalid
.eq(req_port_i
.data_rvalid
)
408 if __name__ == '__main__':
410 ports = [dut.p.i_valid, dut.n.i_ready,
411 dut.n.o_valid, dut.p.o_ready] + \
412 [dut.p.i_data] + [dut.n.o_data]
413 vl = rtlil.convert(dut, ports=ports)
414 with open("test_bufunbuf999.il", "w") as f: