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.
12 # Author: David Schaffenrath, TU Graz
13 # Author: Florian Zaruba, ETH Zurich
15 # Description: Hardware-PTW
17 /* verilator lint_off WIDTH */
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()
37 data_rvalid
= Signal()
38 data_rdata
= Signal(64)
42 class PTE(RecordObject
):
44 self
.reserved
= Signal(10)
59 valid
= Signal() # valid flag
63 asid
= Signal(ASID_WIDTH
)
72 # SV39 defines three levels of page tables
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
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()
93 # to TLBs, update logic
94 itlb_update_o
= TLBUpdate()
95 dtlb_update_o
= TLBUpdate()
97 update_vaddr_o
= Signal(39)
99 asid_i
= Signal(ASID_WIDTH
)
102 itlb_access_i
= Signal()
103 itlb_hit_i
= Signal()
104 itlb_vaddr_i
= Signal(64)
106 dtlb_access_i
= Signal()
107 dtlb_hit_i
= Signal()
108 dtlb_vaddr_i
= Signal(64)
110 satp_ppn_i
= Signal(44) # ppn from satp
112 # Performance counters
113 itlb_miss_o
= Signal()
114 dtlb_miss_o
= Signal()
119 data_rvalid_q
= Signal()
120 data_rdata_q
= Signal(64)
123 assign pte
= riscv
::pte_t(data_rdata_q
);
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()
131 tag_valid_n
= Signal()
132 tag_valid_q
= Signal()
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
139 # 4 byte aligned physical pointer
140 ptw_pptr_q
= Signal(56)
141 ptw_pptr_n
= Signal(56)
143 end
= DCACHE_INDEX_WIDTH
+ DCACHE_TAG_WIDTH
146 update_vaddr_o
.eq(vaddr_q
),
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)),
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)),
174 req_port_o
.tag_valid
.eq(tag_valid_q
),
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
184 # 3. If pte.v = 0, or if pte.r = 0 and pte.w = 1, stop and raise an access
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
202 # PTW memory interface
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),
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
),
215 global_mapping_n
.eq(global_mapping_q
),
217 tlb_update_asid_n
.eq(tlb_update_asid_q
),
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
),
240 m
.next
= "WAIT_GRANT"
241 # we got an DTLB miss
242 with m
.Elif(en_ld_st_translation_i
& dtlb_access_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
),
249 m
.next
= "WAIT_GRANT"
251 with m
.State("WAIT_GRANT"):
253 m
.d
.comb
+= req_port_o
.data_req
.eq(1)
254 # wait for the WAIT_GRANT
255 with m
.If(req_port_i
.data_gnt
):
256 # send the tag valid signal one cycle later
257 m
.d
.comb
+= tag_valid_n
.eq(1)
258 m
.next
= "PTE_LOOKUP"
260 with m
.State("PTE_LOOKUP"):
261 # we wait for the valid signal
262 with m
.If(data_rvalid_q
):
264 # check if the global mapping bit is set
266 m
.d
.comb
+= global_mapping_n
.eq(1)
271 # If pte.v = 0, or if pte.r = 0 and pte.w = 1,
272 # stop and raise a page-fault exception.
273 with m
.If (~pte
.v |
(~pte
.r
& pte
.w
))
274 m
.next
= "PROPAGATE_ERROR"
281 # if pte.r = 1 or pte.x = 1 it is a valid PTE
282 with m
.If (pte
.r | pte
.x
):
283 # Valid translation found (either 1G, 2M or 4K entry)
284 with m
.If(is_instr_ptw_q
):
288 # If page is not executable, we can directly raise an error. This
289 # doesn't put a useless entry into the TLB. The same idea applies
290 # to the access flag since we let the access flag be managed by SW.
291 with m
.If (~pte
.x | ~pte
.a
):
294 m
.d
.comb
+= itlb_update_o
.valid
.eq(1)
300 # Check if the access flag has been set, otherwise throw a page-fault
301 # and let the software handle those bits.
302 # If page is not readable (there are no write-only pages)
303 # we can directly raise an error. This doesn't put a useless
304 # entry into the TLB.
305 with m
.If(pte
.a
& (pte
.r |
(pte
.x
& mxr_i
))):
306 m
.d
.comb
+= dtlb_update_o
.valid
.eq(1)
308 m
.next
= "PROPAGATE_ERROR"
309 # Request is a store: perform some additional checks
310 # If the request was a store and the page is not write-able, raise an error
311 # the same applies if the dirty flag is not set
312 with m
.If (lsu_is_store_i
& (~pte
.w | ~pte
.d
)):
313 m
.d
.comb
+= dtlb_update_o
.valid
.eq(0)
314 m
.next
= "PROPAGATE_ERROR"
316 # check if the ppn is correctly aligned:
317 # 6. If i > 0 and pa.ppn[i − 1 : 0] != 0, this is a misaligned superpage; stop and raise a page-fault
319 if (ptw_lvl_q
== LVL1
&& pte
.ppn
[17:0] != '0) begin
320 state_d = PROPAGATE_ERROR;
321 dtlb_update_o.valid = 1'b0
;
322 itlb_update_o
.valid
= 1'b0;
323 end else if (ptw_lvl_q == LVL2 && pte.ppn[8:0] != '0) begin
324 state_d
= PROPAGATE_ERROR
;
325 dtlb_update_o
.valid
= 1'b0;
326 itlb_update_o.valid = 1'b0
;
328 # this is a pointer to the next TLB level
330 # pointer to next level of page table
331 if (ptw_lvl_q
== LVL1
) begin
332 # we are in the second level now
334 ptw_pptr_n
= {pte
.ppn
, vaddr_q
[29:21], 3'b0};
337 if (ptw_lvl_q == LVL2) begin
338 # here we received a pointer to the third level
340 ptw_pptr_n = {pte.ppn, vaddr_q[20:12], 3'b0
};
343 state_d
= WAIT_GRANT
;
345 if (ptw_lvl_q
== LVL3
) begin
346 # Should already be the last level page table => Error
348 state_d
= PROPAGATE_ERROR
;
353 # we've got a data WAIT_GRANT so tell the cache that the tag is valid
355 # Propagate error to MMU/LSU
356 PROPAGATE_ERROR
: begin
360 # wait for the rvalid before going back to IDLE
373 # should we have flushed before we got an rvalid, wait for it until going back to IDLE
375 # on a flush check whether we are
376 # 1. in the PTE Lookup check whether we still need to wait for an rvalid
377 # 2. waiting for a grant, if so: wait for it
378 # if not, go back to idle
379 if ((state_q == PTE_LOOKUP && !data_rvalid_q) || ((state_q == WAIT_GRANT) && req_port_i.data_gnt))
380 state_d = WAIT_RVALID;
387 always_ff @(posedge clk_i or negedge rst_ni) begin
390 is_instr_ptw_q <= 1'b0
;
393 tlb_update_asid_q <= '0;
396 global_mapping_q
<= 1'b0;
398 data_rvalid_q
<= 1'b0;
401 ptw_pptr_q <= ptw_pptr_n;
402 is_instr_ptw_q <= is_instr_ptw_n;
403 ptw_lvl_q <= ptw_lvl_n;
404 tag_valid_q <= tag_valid_n;
405 tlb_update_asid_q <= tlb_update_asid_n;
407 global_mapping_q <= global_mapping_n;
408 data_rdata_q <= req_port_i.data_rdata;
409 data_rvalid_q <= req_port_i.data_rvalid;
414 /* verilator lint_on WIDTH */