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