split into separate module instead of array
[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 nmigen.hdl.ast import ArrayProxy
23 from math import log
24
25 DCACHE_SET_ASSOC = 8
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
29
30 class DCacheReqI:
31 def __init__(self):
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()
41
42
43 class DCacheReqO:
44 def __init__(self):
45 data_gnt = Signal()
46 data_rvalid = Signal()
47 data_rdata = Signal(64)
48
49 ASID_WIDTH = 8
50
51 class PTE: #(RecordObject):
52 def __init__(self):
53 self.reserved = Signal(10)
54 self.ppn = Signal(44)
55 self.rsw = Signal(2)
56 self.d = Signal()
57 self.a = Signal()
58 self.g = Signal()
59 self.u = Signal()
60 self.x = Signal()
61 self.w = Signal()
62 self.r = Signal()
63 self.v = Signal()
64
65 def flatten(self):
66 return Cat(*self.ports())
67
68 def eq(self, x):
69 if isinstance(x, ArrayProxy):
70 res = []
71 for o in self.ports():
72 i = getattr(x, o.name)
73 res.append(i)
74 x = Cat(*res)
75 else:
76 x = x.flatten()
77 return self.flatten().eq(x)
78
79 def ports(self):
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]
82
83
84 class TLBUpdate:
85 def __init__(self):
86 self.valid = Signal() # valid flag
87 self.is_2M = Signal()
88 self.is_1G = Signal()
89 self.vpn = Signal(27)
90 self.asid = Signal(ASID_WIDTH)
91 self.content = PTE()
92
93 def flatten(self):
94 return Cat(*self.ports())
95
96 def eq(self, x):
97 return self.flatten().eq(x.flatten())
98
99 def ports(self):
100 return [self.valid, self.is_2M, self.is_1G, self.vpn, self.asid] + \
101 self.content.ports()
102
103 # SV39 defines three levels of page tables
104 LVL1 = Const(0, 2)
105 LVL2 = Const(1, 2)
106 LVL3 = Const(2, 2)
107
108
109 class PTW:
110 def __init__(self):
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
119
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()
124
125 # to TLBs, update logic
126 itlb_update_o = TLBUpdate()
127 dtlb_update_o = TLBUpdate()
128
129 update_vaddr_o = Signal(39)
130
131 asid_i = Signal(ASID_WIDTH)
132 # from TLBs
133 # did we miss?
134 itlb_access_i = Signal()
135 itlb_hit_i = Signal()
136 itlb_vaddr_i = Signal(64)
137
138 dtlb_access_i = Signal()
139 dtlb_hit_i = Signal()
140 dtlb_vaddr_i = Signal(64)
141 # from CSR file
142 satp_ppn_i = Signal(44) # ppn from satp
143 mxr_i = Signal()
144 # Performance counters
145 itlb_miss_o = Signal()
146 dtlb_miss_o = Signal()
147
148
149 # input registers
150 data_rvalid = Signal()
151 data_rdata = Signal(64)
152
153 pte = PTE()
154 m.d.comb += pte.eq(data_rdata)
155
156 ptw_lvl = Signal(2, reset=LVL1)
157
158 # is this an instruction page table walk?
159 is_instr_ptw = Signal()
160 global_mapping = Signal()
161 # latched tag signal
162 tag_valid = Signal()
163 # register the ASID
164 tlb_update_asid = Signal(ASID_WIDTH)
165 # register VPN we need to walk, SV39 defines a 39 bit virtual addr
166 vaddr = Signal(64)
167 # 4 byte aligned physical pointer
168 ptw_pptr = Signal(56)
169
170 end = DCACHE_INDEX_WIDTH + DCACHE_TAG_WIDTH
171 m.d.sync += [
172 # Assignments
173 update_vaddr_o.eq(vaddr),
174
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)),
184 # -----------
185 # TLB Update
186 # -----------
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)),
200
201 ]
202 m.d.comb += [
203 req_port_o.tag_valid.eq(tag_valid),
204 ]
205 #-------------------
206 # Page table walker
207 #-------------------
208 # A virtual address va is translated into a physical address pa as
209 # follows:
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
215 # access exception.
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
218 # the page table.
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.
233
234 m.d.sync += tag_valid.eq(0)
235
236 # default assignments
237 m.d.comb += [
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),
243 ptw_error_o.eq(0),
244 itlb_update_o.valid.eq(0),
245 dtlb_update_o.valid.eq(0),
246
247 itlb_miss_o.eq(0),
248 dtlb_miss_o.eq(0),
249 ]
250
251 with m.FSM() as fsm:
252
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),
256 ptw_lvl.eq(LVL1),
257 global_mapping.eq(0),
258 ]
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),
264 is_instr_ptw.eq(1),
265 vaddr.eq(itlb_vaddr_i),
266 tlb_update_asid.eq(asid_i),
267 ]
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 & \
272 ~dtlb_hit_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),
277 ]
278 m.d.comb += [ dtlb_miss_o.eq(1)]
279 m.next = "WAIT_GRANT"
280
281 with m.State("WAIT_GRANT"):
282 # send a request out
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"
289
290 with m.State("PTE_LOOKUP"):
291 # we wait for the valid signal
292 with m.If(data_rvalid):
293
294 # check if the global mapping bit is set
295 with m.If (pte.g):
296 m.d.sync += global_mapping.eq(1)
297
298 # -------------
299 # Invalid PTE
300 # -------------
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"
305 # -----------
306 # Valid PTE
307 # -----------
308 with m.Else():
309 m.next = "IDLE"
310 # it is a valid PTE
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):
315 # ------------
316 # Update ITLB
317 # ------------
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):
325 m.next = "IDLE"
326 with m.Else():
327 m.d.comb += itlb_update_o.valid.eq(1)
328
329 with m.Else():
330 # ------------
331 # Update DTLB
332 # ------------
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)
343 with m.Else():
344 m.next = "PROPAGATE_ERROR"
345 # Request is a store: perform some
346 # additional checks
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"
353
354 # check if the ppn is correctly aligned: Case (6)
355 l1err = Signal()
356 l2err = Signal()
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))
361 ]
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)]
366
367 # this is a pointer to the next TLB level
368 with m.Else():
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],
373 pte.ppn)
374 m.d.sync += [ptw_pptr.eq(pptr),
375 ptw_lvl.eq(LVL2)]
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],
379 pte.ppn)
380 m.d.sync += [ptw_pptr.eq(pptr),
381 ptw_lvl.eq(LVL3)
382 ]
383 m.next = "WAIT_GRANT"
384
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
392
393 # Propagate error to MMU/LSU
394 with m.State("PROPAGATE_ERROR"):
395 m.next = "IDLE"
396 m.d.comb += ptw_error_o.eq(1)
397
398 # wait for the rvalid before going back to IDLE
399 with m.State("WAIT_RVALID"):
400 with m.If(data_rvalid):
401 m.next = "IDLE"
402
403 # -------
404 # Flush
405 # -------
406 # should we have flushed before we got an rvalid,
407 # wait for it until going back to IDLE
408 with m.If(flush_i):
409 # on a flush check whether we are
410 # 1. in the PTE Lookup check whether we still need to wait
411 # for an rvalid
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"
417 with m.Else():
418 m.next = "IDLE"
419
420 m.d.sync += [data_rdata.eq(req_port_i.data_rdata),
421 data_rvalid.eq(req_port_i.data_rvalid)
422 ]
423
424 """
425 if __name__ == '__main__':
426 dut = PTE()
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:
432 f.write(vl)
433 """