work towards getting PTW translation working
[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 from nmigen import Const, Signal
22 from math import log
23
24 DCACHE_SET_ASSOC = 8
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
28
29 class DCacheReqI:
30 def __init__(self):
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()
40
41
42 class DCacheReqO:
43 def __init__(self):
44 data_gnt = Signal()
45 data_rvalid = Signal()
46 data_rdata = Signal(64)
47
48 ASID_WIDTH = 1
49
50 class PTE: #(RecordObject):
51 def __init__(self):
52 self.reserved = Signal(10)
53 self.ppn = Signal(44)
54 self.rsw = Signal(2)
55 self.d = Signal()
56 self.a = Signal()
57 self.g = Signal()
58 self.u = Signal()
59 self.x = Signal()
60 self.w = Signal()
61 self.r = Signal()
62 self.v = Signal()
63
64 def eq(self, x):
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)]
70
71 def ports(self):
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]
74
75
76 class TLBUpdate:
77 def __init__(self):
78 self.valid = Signal() # valid flag
79 self.is_2M = Signal()
80 self.is_1G = Signal()
81 self.vpn = Signal(27)
82 self.asid = Signal(ASID_WIDTH)
83 self.content = PTE()
84
85 def ports(self):
86 return [self.valid, self.is_2M, self.is_1G, self.vpn, self.asid,
87 self.content.ports()]
88
89 # SV39 defines three levels of page tables
90 LVL1 = Const(0, 2)
91 LVL2 = Const(1, 2)
92 LVL3 = Const(2, 2)
93
94
95 class PTW:
96 def __init__(self):
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
105
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()
110
111 # to TLBs, update logic
112 itlb_update_o = TLBUpdate()
113 dtlb_update_o = TLBUpdate()
114
115 update_vaddr_o = Signal(39)
116
117 asid_i = Signal(ASID_WIDTH)
118 # from TLBs
119 # did we miss?
120 itlb_access_i = Signal()
121 itlb_hit_i = Signal()
122 itlb_vaddr_i = Signal(64)
123
124 dtlb_access_i = Signal()
125 dtlb_hit_i = Signal()
126 dtlb_vaddr_i = Signal(64)
127 # from CSR file
128 satp_ppn_i = Signal(44) # ppn from satp
129 mxr_i = Signal()
130 # Performance counters
131 itlb_miss_o = Signal()
132 dtlb_miss_o = Signal()
133
134
135 # input registers
136 data_rvalid = Signal()
137 data_rdata = Signal(64)
138
139 pte = PTE()
140 m.d.comb += pte.eq(data_rdata)
141
142 ptw_lvl = Signal(2, reset=LVL1)
143
144 # is this an instruction page table walk?
145 is_instr_ptw = Signal()
146 global_mapping = Signal()
147 # latched tag signal
148 tag_valid = Signal()
149 # register the ASID
150 tlb_update_asid = Signal(ASID_WIDTH)
151 # register the VPN we need to walk, SV39 defines a 39 bit virtual address
152 vaddr = Signal(64)
153 # 4 byte aligned physical pointer
154 ptw_pptr = Signal(56)
155
156 end = DCACHE_INDEX_WIDTH + DCACHE_TAG_WIDTH
157 m.d.sync += [
158 # Assignments
159 update_vaddr_o.eq(vaddr),
160
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)),
170 # -----------
171 # TLB Update
172 # -----------
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)),
186
187 ]
188 m.d.comb += [
189 req_port_o.tag_valid.eq(tag_valid),
190 ]
191 #-------------------
192 # Page table walker
193 #-------------------
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
198 # Sv32, PTESIZE=4.)
199 # 3. If pte.v = 0, or if pte.r = 0 and pte.w = 1, stop and raise an access
200 # exception.
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.
216
217 m.d.sync += tag_valid.eq(0)
218
219 # default assignments
220 m.d.comb += [
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),
226 ptw_error_o.eq(0),
227 itlb_update_o.valid.eq(0),
228 dtlb_update_o.valid.eq(0),
229
230 itlb_miss_o.eq(0),
231 dtlb_miss_o.eq(0),
232 ]
233
234 with m.FSM() as fsm:
235
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),
239 ptw_lvl.eq(LVL1),
240 global_mapping.eq(0),
241 ]
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),
247 is_instr_ptw.eq(1),
248 vaddr.eq(itlb_vaddr_i),
249 tlb_update_asid.eq(asid_i),
250 ]
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 & \
255 ~dtlb_hit_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),
260 ]
261 m.d.comb += [ dtlb_miss_o.eq(1)]
262 m.next = "WAIT_GRANT"
263
264 with m.State("WAIT_GRANT"):
265 # send a request out
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"
272
273 with m.State("PTE_LOOKUP"):
274 # we wait for the valid signal
275 with m.If(data_rvalid):
276
277 # check if the global mapping bit is set
278 with m.If (pte.g):
279 m.d.sync += global_mapping.eq(1)
280
281 # -------------
282 # Invalid PTE
283 # -------------
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"
288 # -----------
289 # Valid PTE
290 # -----------
291 with m.Else():
292 m.next = "IDLE"
293 # it is a valid PTE
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):
298 # ------------
299 # Update ITLB
300 # ------------
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):
308 m.next = "IDLE"
309 with m.Else():
310 m.d.comb += itlb_update_o.valid.eq(1)
311
312 with m.Else():
313 # ------------
314 # Update DTLB
315 # ------------
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)
326 with m.Else():
327 m.next = "PROPAGATE_ERROR"
328 # Request is a store: perform some
329 # additional checks
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"
336
337 # check if the ppn is correctly aligned: Case (6)
338 l1err = Signal()
339 l2err = Signal()
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))
344 ]
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)]
349
350 # this is a pointer to the next TLB level
351 with m.Else():
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],
356 pte.ppn)
357 m.d.sync += [ptw_pptr.eq(pptr),
358 ptw_lvl.eq(LVL2)]
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],
362 pte.ppn)
363 m.d.sync += [ptw_pptr.eq(pptr),
364 ptw_lvl.eq(LVL3)
365 ]
366 m.next = "WAIT_GRANT"
367
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
375
376 # Propagate error to MMU/LSU
377 with m.State("PROPAGATE_ERROR"):
378 m.next = "IDLE"
379 m.d.comb += ptw_error_o.eq(1)
380
381 # wait for the rvalid before going back to IDLE
382 with m.State("WAIT_RVALID"):
383 with m.If(data_rvalid):
384 m.next = "IDLE"
385
386 # -------
387 # Flush
388 # -------
389 # should we have flushed before we got an rvalid,
390 # wait for it until going back to IDLE
391 with m.If(flush_i):
392 # on a flush check whether we are
393 # 1. in the PTE Lookup check whether we still need to wait
394 # for an rvalid
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"
400 with m.Else():
401 m.next = "IDLE"
402
403 m.d.sync += [data_rdata.eq(req_port_i.data_rdata),
404 data_rvalid.eq(req_port_i.data_rvalid)
405 ]
406
407 """
408 if __name__ == '__main__':
409 dut = PTE()
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:
415 f.write(vl)
416 """