tidyup
[soc.git] / TLB / src / ariane / ptw.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: David Schaffenrath, TU Graz
13 # Author: Florian Zaruba, ETH Zurich
14 # Date: 24.4.2017
15 # Description: Hardware-PTW
16
17 /* verilator lint_off WIDTH */
18 import ariane_pkg::*;
19 """
20
21 class DCacheReqI:
22 def __init__(self):
23 self.address_index = Signal(DCACHE_INDEX_WIDTH)
24 self.address_tag = Signal(DCACHE_TAG_WIDTH)
25 self.data_wdata = Signal(64)
26 self.data_req = Signal()
27 self.data_we = Signal()
28 self.data_be = Signal(8)
29 self.data_size = Signal(2)
30 self.kill_req = Signal()
31 self.tag_valid = Signal()
32
33
34 class DCacheReqO:
35 def __init__(self):
36 data_gnt = Signal()
37 data_rvalid = Signal()
38 data_rdata = Signal(64)
39
40 ASID_WIDTH = 1
41
42 class PTE(RecordObject):
43 def __init__(self):
44 self.reserved = Signal(10)
45 self.ppn = Signal(44)
46 self.rsw = Signal(2)
47 self.d = Signal()
48 self.a = Signal()
49 self.g = Signal()
50 self.u = Signal()
51 self.x = Signal()
52 self.w = Signal()
53 self.r = Signal()
54 self.v = Signal()
55
56
57 class TLBUpdate:
58 def __init__(self):
59 valid = Signal() # valid flag
60 is_2M = Signal()
61 is_1G = Signal()
62 vpn = Signal(27)
63 asid = Signal(ASID_WIDTH)
64 content = PTE()
65
66 # SV39 defines three levels of page tables
67 LVL1 = Const(0, 2)
68 LVL2 = Const(1, 2)
69 LVL3 = Const(2, 2)
70
71
72 class PTW:
73 flush_i = Signal() # flush everything, we need to do this because
74 # actually everything we do is speculative at this stage
75 # e.g.: there could be a CSR instruction that changes everything
76 ptw_active_o = Signal()
77 walking_instr_o = Signal() # set when walking for TLB
78 ptw_error_o = Signal() # set when an error occurred
79 enable_translation_i = Signal() # CSRs indicate to enable SV39
80 en_ld_st_translation_i = Signal() # enable VM translation for load/stores
81
82 lsu_is_store_i = Signal() , # this translation triggered by a store
83 # PTW memory interface
84 req_port_i = DCacheReqO()
85 req_port_o = DCacheReqI()
86
87 # to TLBs, update logic
88 itlb_update_o = TLBUpdate()
89 dtlb_update_o = TLBUpdate()
90
91 update_vaddr_o = Signal(39)
92
93 asid_i = Signal(ASID_WIDTH)
94 # from TLBs
95 # did we miss?
96 itlb_access_i = Signal()
97 itlb_hit_i = Signal()
98 itlb_vaddr_i = Signal(64)
99
100 dtlb_access_i = Signal()
101 dtlb_hit_i = Signal()
102 dtlb_vaddr_i = Signal(64)
103 # from CSR file
104 satp_ppn_i = Signal(44) # ppn from satp
105 mxr_i = Signal()
106 # Performance counters
107 itlb_miss_o = Signal()
108 dtlb_miss_o = Signal()
109
110 );
111
112 # input registers
113 data_rvalid = Signal()
114 data_rdata = Signal(64)
115
116 pte = PTE()
117 assign pte = riscv::pte_t(data_rdata);
118
119 ptw_lvl = Signal(2, reset=LVL1)
120
121 # is this an instruction page table walk?
122 is_instr_ptw = Signal()
123 global_mapping = Signal()
124 # latched tag signal
125 tag_valid = Signal()
126 # register the ASID
127 tlb_update_asid = Signal(ASID_WIDTH)
128 # register the VPN we need to walk, SV39 defines a 39 bit virtual address
129 vaddr = Signal(64)
130 # 4 byte aligned physical pointer
131 ptw_pptr = Signal(56)
132
133 end = DCACHE_INDEX_WIDTH + DCACHE_TAG_WIDTH
134 m.d.sync += [
135 # Assignments
136 update_vaddr_o.eq(vaddr),
137
138 ptw_active_o.eq(state != IDLE),
139 walking_instr_o.eq(is_instr_ptw),
140 # directly output the correct physical address
141 req_port_o.address_index.eq(ptw_pptr[0:DCACHE_INDEX_WIDTH]),
142 req_port_o.address_tag.eq(ptw_pptr[DCACHE_INDEX_WIDTH:end]),
143 # we are never going to kill this request
144 req_port_o.kill_req.eq(0),
145 # we are never going to write with the HPTW
146 req_port_o.data_wdata.eq(Const(0, 64)),
147 # -----------
148 # TLB Update
149 # -----------
150 itlb_update_o.vpn.eq(vaddr[12:39]),
151 dtlb_update_o.vpn.eq(vaddr[12:39]),
152 # update the correct page table level
153 itlb_update_o.is_2M.eq(ptw_lvl == LVL2),
154 itlb_update_o.is_1G.eq(ptw_lvl == LVL1),
155 dtlb_update_o.is_2M.eq(ptw_lvl == LVL2),
156 dtlb_update_o.is_1G.eq(ptw_lvl == LVL1),
157 # output the correct ASID
158 itlb_update_o.asid.eq(tlb_update_asid),
159 dtlb_update_o.asid.eq(tlb_update_asid),
160 # set the global mapping bit
161 itlb_update_o.content.eq(pte | (global_mapping << 5)),
162 dtlb_update_o.content.eq(pte | (global_mapping << 5)),
163
164 ]
165 m.d.comb += [
166 req_port_o.tag_valid.eq(tag_valid),
167 ]
168 #-------------------
169 # Page table walker
170 #-------------------
171 # A virtual address va is translated into a physical address pa as follows:
172 # 1. Let a be sptbr.ppn × PAGESIZE, and let i = LEVELS-1. (For Sv39,
173 # PAGESIZE=2^12 and LEVELS=3.)
174 # 2. Let pte be the value of the PTE at address a+va.vpn[i]×PTESIZE. (For
175 # Sv32, PTESIZE=4.)
176 # 3. If pte.v = 0, or if pte.r = 0 and pte.w = 1, stop and raise an access
177 # exception.
178 # 4. Otherwise, the PTE is valid. If pte.r = 1 or pte.x = 1, go to step 5.
179 # Otherwise, this PTE is a pointer to the next level of the page table.
180 # Let i=i-1. If i < 0, stop and raise an access exception. Otherwise, let
181 # a = pte.ppn × PAGESIZE and go to step 2.
182 # 5. A leaf PTE has been found. Determine if the requested memory access
183 # is allowed by the pte.r, pte.w, and pte.x bits. If not, stop and
184 # raise an access exception. Otherwise, the translation is successful.
185 # Set pte.a to 1, and, if the memory access is a store, set pte.d to 1.
186 # The translated physical address is given as follows:
187 # - pa.pgoff = va.pgoff.
188 # - If i > 0, then this is a superpage translation and
189 # pa.ppn[i-1:0] = va.vpn[i-1:0].
190 # - pa.ppn[LEVELS-1:i] = pte.ppn[LEVELS-1:i].
191 # 6. If i > 0 and pa.ppn[i − 1 : 0] != 0, this is a misaligned superpage;
192 # stop and raise a page-fault exception.
193
194 m.d.sync += tag_valid.eq(0)
195
196 # default assignments
197 m.d.comb += [
198 # PTW memory interface
199 req_port_o.data_req.eq(0),
200 req_port_o.data_be.eq(Const(0xFF, 8))
201 req_port_o.data_size.eq(Const(0bb11, 2))
202 req_port_o.data_we.eq(0),
203 ptw_error_o.eq(0),
204 itlb_update_o.valid.eq(0)
205 dtlb_update_o.valid.eq(0),
206
207 itlb_miss_o.eq(0),
208 dtlb_miss_o.eq(0),
209 ]
210
211 with m.FSM() as fsm:
212
213 with m.State("IDLE"):
214 # by default we start with the top-most page table
215 m.d.sync += [is_instr_ptw.eq(0),
216 ptw_lvl.eq(LVL1),
217 global_mapping.eq(0),
218 ]
219 # we got an ITLB miss?
220 with m.If(enable_translation_i & itlb_access_i & \
221 ~itlb_hit_i & ~dtlb_access_i):
222 pptr = Cat(Const(0, 3), itlb_vaddr_i[30:39], satp_ppn_i)
223 m.d.sync += [ptw_pptr.eq(pptr)
224 is_instr_ptw.eq(1),
225 vaddr.eq(itlb_vaddr_i),
226 tlb_update_asid.eq(asid_i).
227 ]
228 m.d.comb += [itlb_miss_o.eq(1)]
229 m.next = "WAIT_GRANT"
230 # we got a DTLB miss?
231 with m.Elif(en_ld_st_translation_i & dtlb_access_i & \
232 ~dtlb_hit_i):
233 pptr = Cat(Const(0, 3), dtlb_vaddr_i[30:39], satp_ppn_i)
234 m.d.sync += [ptw_pptr.eq(pptr)
235 vaddr.eq(dtlb_vaddr_i),
236 tlb_update_asid.eq(asid_i).
237 ]
238 m.d.comb += [ dtlb_miss_o.eq(1)]
239 m.next = "WAIT_GRANT"
240
241 with m.State("WAIT_GRANT"):
242 # send a request out
243 m.d.comb += req_port_o.data_req.eq(1)
244 # wait for the WAIT_GRANT
245 with m.If(req_port_i.data_gnt):
246 # send the tag valid signal one cycle later
247 m.d.sync += tag_valid.eq(1)
248 m.next = "PTE_LOOKUP"
249
250 with m.State("PTE_LOOKUP"):
251 # we wait for the valid signal
252 with m.If(data_rvalid):
253
254 # check if the global mapping bit is set
255 with m.If (pte.g):
256 m.d.sync += global_mapping.eq(1)
257
258 # -------------
259 # Invalid PTE
260 # -------------
261 # If pte.v = 0, or if pte.r = 0 and pte.w = 1,
262 # stop and raise a page-fault exception.
263 with m.If (~pte.v | (~pte.r & pte.w))
264 m.next = "PROPAGATE_ERROR"
265 # -----------
266 # Valid PTE
267 # -----------
268 with m.Else():
269 m.next = "IDLE"
270 # it is a valid PTE
271 # if pte.r = 1 or pte.x = 1 it is a valid PTE
272 with m.If (pte.r | pte.x):
273 # Valid translation found (either 1G, 2M or 4K entry)
274 with m.If(is_instr_ptw):
275 # ------------
276 # Update ITLB
277 # ------------
278 # If page is not executable, we can
279 # directly raise an error. This
280 # doesn't put a useless entry into
281 # the TLB. The same idea applies
282 # to the access flag since we let
283 # the access flag be managed by SW.
284 with m.If (~pte.x | ~pte.a):
285 m.next = "IDLE"
286 with m.Else():
287 m.d.comb += itlb_update_o.valid.eq(1)
288
289 with m.Else():
290 # ------------
291 # Update DTLB
292 # ------------
293 # Check if the access flag has been set,
294 # otherwise throw a page-fault
295 # and let the software handle those bits.
296 # If page is not readable (there are
297 # no write-only pages)
298 # we can directly raise an error. This
299 # doesn't put a useless
300 # entry into the TLB.
301 with m.If(pte.a & (pte.r | (pte.x & mxr_i))):
302 m.d.comb += dtlb_update_o.valid.eq(1)
303 with m.Else():
304 m.next = "PROPAGATE_ERROR"
305 # Request is a store: perform some
306 # additional checks
307 # If the request was a store and the
308 # page is not write-able, raise an error
309 # the same applies if the dirty flag is not set
310 with m.If (lsu_is_store_i & (~pte.w | ~pte.d)):
311 m.d.comb += dtlb_update_o.valid.eq(0)
312 m.next = "PROPAGATE_ERROR"
313
314 # check if the ppn is correctly aligned: Case (6)
315 l1err = Signal()
316 l2err = Signal()
317 m.d.comb += [l2err.eq((ptw_lvl == LVL2) & \
318 pte.ppn[0:9] != Const(0, 9)),
319 l1err.eq((ptw_lvl == LVL1) & \
320 pte.ppn[0:18] != Const(0, 18))
321 ]
322 with m.If(l1err | l2err):
323 m.next = "PROPAGATE_ERROR"
324 m.d.comb += [dtlb_update_o.valid.eq(0),
325 itlb_update_o.valid.eq(0)]
326
327 # this is a pointer to the next TLB level
328 with m.Else():
329 # pointer to next level of page table
330 with m.If (ptw_lvl == LVL1):
331 # we are in the second level now
332 pptr = Cat(Const(0, 3), dtlb_vaddr_i[21:30],
333 pte.ppn)
334 m.d.sync += [ptw_pptr.eq(pptr),
335 ptw_lvl.eq(LVL2)]
336 with m.If(ptw_lvl == LVL2):
337 # here we received a pointer to the third level
338 pptr = Cat(Const(0, 3), dtlb_vaddr_i[12:21],
339 pte.ppn)
340 m.d.sync += [ptw_pptr.eq(pptr),
341 ptw_lvl.eq(LVL3)
342 ]
343 m.next = "WAIT_GRANT"
344
345 with m.If (ptw_lvl == LVL3):
346 # Should already be the last level
347 # page table => Error
348 m.d.sync += ptw_lvl.eq(LVL3)
349 m.next = "PROPAGATE_ERROR"
350 # we've got a data WAIT_GRANT so tell the
351 # cache that the tag is valid
352
353 # Propagate error to MMU/LSU
354 with m.State("PROPAGATE_ERROR"):
355 m.next = "IDLE"
356 m.d.comb += ptw_error_o.eq(1)
357
358 # wait for the rvalid before going back to IDLE
359 with m.State("WAIT_RVALID"):
360 m.If (data_rvalid):
361 m.next = "IDLE"
362
363 # -------
364 # Flush
365 # -------
366 # should we have flushed before we got an rvalid,
367 # wait for it until going back to IDLE
368 with m.If(flush_i):
369 # on a flush check whether we are
370 # 1. in the PTE Lookup check whether we still need to wait
371 # for an rvalid
372 # 2. waiting for a grant, if so: wait for it
373 # if not, go back to idle
374 with m.If (((state == PTE_LOOKUP) & ~data_rvalid) | \
375 ((state == WAIT_GRANT) & req_port_i.data_gnt)):
376 m.next = "WAIT_RVALID"
377 with m.Else():
378 m.next = "IDLE"
379
380 m.d.sync += [data_rdata.eq(req_port_i.data_rdata),
381 data_rvalid.eq(req_port_i.data_rvalid)
382 ]
383