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