aboutsummaryrefslogtreecommitdiff
path: root/rv64cpu/rtl/core/mmu/ptw.sv
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-11-07 04:31:31 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-11-07 04:31:31 +0800
commita7d5b2468553c6d3570ca2aa45c27cb93e26cb88 (patch)
tree7031b1fac52e4f858c4e92041485135242768f7b /rv64cpu/rtl/core/mmu/ptw.sv
parent4ca723bc9dc1b9db1bd03e3b3db0c58079c3f3c4 (diff)
parent3b0b67af63af86c5ec9b2a63defa06b70923b2e3 (diff)
downloadrcc-project-a7d5b2468553c6d3570ca2aa45c27cb93e26cb88.tar.gz
rcc-project-a7d5b2468553c6d3570ca2aa45c27cb93e26cb88.tar.xz
Merge remote-tracking branch 'rv64cpu-import/master'
Diffstat (limited to 'rv64cpu/rtl/core/mmu/ptw.sv')
-rw-r--r--rv64cpu/rtl/core/mmu/ptw.sv416
1 files changed, 416 insertions, 0 deletions
diff --git a/rv64cpu/rtl/core/mmu/ptw.sv b/rv64cpu/rtl/core/mmu/ptw.sv
new file mode 100644
index 0000000..6447b5a
--- /dev/null
+++ b/rv64cpu/rtl/core/mmu/ptw.sv
@@ -0,0 +1,416 @@
+// Richard CPU
+// Copyright (C) 2026 Richard Qin
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+// File path: rtl/core/mmu/ptw.sv
+// Description: Page-table walker for Sv39. Sv48/Sv57/Sv64 are explicit stubs.
+
+module ptw
+ import rv64_pkg::*;
+#(
+ parameter int ASID_WIDTH = 16
+)(
+ input logic clk,
+ input logic rst,
+
+ input logic req_valid,
+ output logic req_ready,
+ input logic [XLEN-1:0] req_vaddr,
+ input logic [ASID_WIDTH-1:0] req_asid,
+ input logic [3:0] req_mode,
+ input logic [1:0] req_priv,
+ input logic req_is_fetch,
+ input logic req_is_store,
+ input logic req_sum,
+ input logic req_mxr,
+ input logic [XLEN-1:0] satp_value,
+
+ output logic mem_req_valid,
+ input logic mem_req_ready,
+ output logic [XLEN-1:0] mem_req_addr,
+ input logic mem_resp_valid,
+ input logic [XLEN-1:0] mem_resp_data,
+
+ output logic resp_valid,
+ output logic resp_fault,
+ output logic [XLEN-1:0] resp_paddr,
+
+ output logic refill_valid,
+ output logic [44:0] refill_vpn,
+ output logic [43:0] refill_ppn,
+ output logic [2:0] refill_level,
+ output logic [3:0] refill_mode,
+ output logic [ASID_WIDTH-1:0] refill_asid,
+ output logic refill_g,
+ output logic refill_u,
+ output logic refill_r,
+ output logic refill_w,
+ output logic refill_x,
+ output logic refill_a,
+ output logic refill_d
+);
+
+ localparam logic [3:0] SATP_MODE_BARE = 4'd0;
+ localparam logic [3:0] SATP_MODE_SV39 = 4'd8;
+ localparam logic [3:0] SATP_MODE_SV48 = 4'd9;
+ localparam logic [3:0] SATP_MODE_SV57 = 4'd10;
+ localparam logic [3:0] SATP_MODE_SV64 = 4'd11; // Reserved/future stub.
+
+ typedef enum logic [2:0] {
+ PTW_IDLE,
+ PTW_ISSUE,
+ PTW_WAIT,
+ PTW_DONE
+ } ptw_state_t;
+
+ ptw_state_t state_q;
+
+ logic [XLEN-1:0] req_vaddr_q;
+ logic [ASID_WIDTH-1:0] req_asid_q;
+ logic [3:0] req_mode_q;
+ logic [1:0] req_priv_q;
+ logic req_is_fetch_q;
+ logic req_is_store_q;
+ logic req_sum_q;
+ logic req_mxr_q;
+
+ logic [43:0] table_ppn_q;
+ logic [2:0] level_q;
+
+ logic pte_v;
+ logic pte_r;
+ logic pte_w;
+ logic pte_x;
+ logic pte_u;
+ logic pte_g;
+ logic pte_a;
+ logic pte_d;
+ logic [43:0] pte_ppn;
+
+ logic leaf_pte;
+ logic bad_pte;
+ logic superpage_misaligned;
+
+ logic [44:0] req_vpn_q;
+ logic [XLEN-1:0] next_paddr;
+
+ logic resp_fault_q;
+ logic [XLEN-1:0] resp_paddr_q;
+ logic refill_valid_q;
+
+ logic [44:0] refill_vpn_q;
+ logic [43:0] refill_ppn_q;
+ logic [2:0] refill_level_q;
+ logic [3:0] refill_mode_q;
+ logic [ASID_WIDTH-1:0] refill_asid_q;
+ logic refill_g_q;
+ logic refill_u_q;
+ logic refill_r_q;
+ logic refill_w_q;
+ logic refill_x_q;
+ logic refill_a_q;
+ logic refill_d_q;
+
+ function automatic logic is_supported_mode(input logic [3:0] mode);
+ return (mode == SATP_MODE_SV39);
+ endfunction
+
+ function automatic int vpn_stride(input logic [3:0] mode);
+ case (mode)
+ SATP_MODE_SV39: vpn_stride = 9;
+ default: vpn_stride = 0;
+ endcase
+ endfunction
+
+ function automatic logic [44:0] vaddr_to_vpn(
+ input logic [XLEN-1:0] vaddr,
+ input logic [3:0] mode
+ );
+ logic [44:0] vpn;
+ begin
+ vpn = '0;
+ case (mode)
+ SATP_MODE_SV39: vpn[26:0] = vaddr[38:12];
+ default: vpn = '0;
+ endcase
+ return vpn;
+ end
+ endfunction
+
+ function automatic logic [9:0] vpn_index(
+ input logic [XLEN-1:0] vaddr,
+ input logic [3:0] mode,
+ input logic [2:0] level
+ );
+ logic [9:0] idx;
+ begin
+ idx = '0;
+ case (mode)
+ SATP_MODE_SV39: idx = {1'b0, vaddr[12 + level * 9 +: 9]};
+ default: idx = '0;
+ endcase
+ return idx;
+ end
+ endfunction
+
+ function automatic logic vaddr_is_canonical(
+ input logic [XLEN-1:0] vaddr,
+ input logic [3:0] mode
+ );
+ case (mode)
+ SATP_MODE_SV39: vaddr_is_canonical = (vaddr[63:39] == {25{vaddr[38]}});
+ default: vaddr_is_canonical = 1'b1;
+ endcase
+ endfunction
+
+ function automatic logic [5:0] page_shift(
+ input logic [3:0] mode,
+ input logic [2:0] level
+ );
+ int stride;
+ begin
+ stride = vpn_stride(mode);
+ return 6'(12 + level * stride);
+ end
+ endfunction
+
+ function automatic logic allow_access(
+ input logic is_fetch,
+ input logic is_store,
+ input logic [1:0] priv,
+ input logic sum,
+ input logic mxr,
+ input logic u,
+ input logic r,
+ input logic w,
+ input logic x,
+ input logic a,
+ input logic d
+ );
+ logic is_load;
+ logic readable;
+ logic priv_u;
+ logic priv_ok;
+ begin
+ is_load = ~is_fetch && ~is_store;
+ readable = r || (mxr && x);
+ priv_u = (priv == 2'b00);
+
+ if (u) begin
+ if (priv_u) begin
+ priv_ok = 1'b1;
+ end else begin
+ priv_ok = ~is_fetch && sum;
+ end
+ end else begin
+ priv_ok = ~priv_u;
+ end
+
+ return a && priv_ok && (
+ (is_fetch && x) ||
+ (is_store && w && d) ||
+ (is_load && readable)
+ );
+ end
+ endfunction
+
+ assign pte_v = mem_resp_data[0];
+ assign pte_r = mem_resp_data[1];
+ assign pte_w = mem_resp_data[2];
+ assign pte_x = mem_resp_data[3];
+ assign pte_u = mem_resp_data[4];
+ assign pte_g = mem_resp_data[5];
+ assign pte_a = mem_resp_data[6];
+ assign pte_d = mem_resp_data[7];
+ assign pte_ppn = mem_resp_data[53:10];
+
+ assign leaf_pte = pte_r || pte_x;
+ assign bad_pte = (!pte_v) || (pte_w && !pte_r);
+
+ always_comb begin
+ int stride;
+ int lower_bits;
+ logic [63:0] ppn_mask;
+ stride = vpn_stride(req_mode_q);
+ lower_bits = level_q * stride;
+ ppn_mask = '0;
+ superpage_misaligned = 1'b0;
+ if (leaf_pte && (lower_bits > 0)) begin
+ if (lower_bits >= 44) begin
+ ppn_mask = 64'h0000_0FFF_FFFF_FFFF;
+ end else begin
+ ppn_mask = (64'd1 << lower_bits) - 1;
+ end
+ superpage_misaligned = |({20'b0, pte_ppn} & ppn_mask);
+ end
+ end
+
+ always_comb begin
+ logic [5:0] shift;
+ logic [XLEN-1:0] low_mask;
+ int shift_index;
+ shift = page_shift(req_mode_q, level_q);
+ shift_index = int'(shift);
+ if (shift_index >= XLEN) begin
+ low_mask = {XLEN{1'b1}};
+ end else begin
+ low_mask = ({{(XLEN-1){1'b0}}, 1'b1} << shift_index) - 1;
+ end
+ next_paddr = (({{(XLEN-56){1'b0}}, pte_ppn, 12'b0}) & ~low_mask) | (req_vaddr_q & low_mask);
+ end
+
+ assign req_ready = (state_q == PTW_IDLE);
+ assign mem_req_valid = (state_q == PTW_ISSUE);
+ assign mem_req_addr = {{(XLEN-56){1'b0}}, table_ppn_q, 12'b0} +
+ ({{(XLEN-10){1'b0}}, vpn_index(req_vaddr_q, req_mode_q, level_q)} << 3);
+
+ assign resp_valid = (state_q == PTW_DONE);
+ assign resp_fault = resp_fault_q;
+ assign resp_paddr = resp_paddr_q;
+
+ assign refill_valid = refill_valid_q && (state_q == PTW_DONE);
+ assign refill_vpn = refill_vpn_q;
+ assign refill_ppn = refill_ppn_q;
+ assign refill_level = refill_level_q;
+ assign refill_mode = refill_mode_q;
+ assign refill_asid = refill_asid_q;
+ assign refill_g = refill_g_q;
+ assign refill_u = refill_u_q;
+ assign refill_r = refill_r_q;
+ assign refill_w = refill_w_q;
+ assign refill_x = refill_x_q;
+ assign refill_a = refill_a_q;
+ assign refill_d = refill_d_q;
+
+ always_ff @(posedge clk) begin
+ if (rst) begin
+ state_q <= PTW_IDLE;
+ req_vaddr_q <= '0;
+ req_asid_q <= '0;
+ req_mode_q <= SATP_MODE_BARE;
+ req_priv_q <= '0;
+ req_is_fetch_q <= 1'b0;
+ req_is_store_q <= 1'b0;
+ req_sum_q <= 1'b0;
+ req_mxr_q <= 1'b0;
+ table_ppn_q <= '0;
+ level_q <= '0;
+ req_vpn_q <= '0;
+ resp_fault_q <= 1'b0;
+ resp_paddr_q <= '0;
+ refill_valid_q <= 1'b0;
+ refill_vpn_q <= '0;
+ refill_ppn_q <= '0;
+ refill_level_q <= '0;
+ refill_mode_q <= SATP_MODE_BARE;
+ refill_asid_q <= '0;
+ refill_g_q <= 1'b0;
+ refill_u_q <= 1'b0;
+ refill_r_q <= 1'b0;
+ refill_w_q <= 1'b0;
+ refill_x_q <= 1'b0;
+ refill_a_q <= 1'b0;
+ refill_d_q <= 1'b0;
+ end else begin
+ case (state_q)
+ PTW_IDLE: begin
+ refill_valid_q <= 1'b0;
+ if (req_valid) begin
+ req_vaddr_q <= req_vaddr;
+ req_asid_q <= req_asid;
+ req_mode_q <= req_mode;
+ req_priv_q <= req_priv;
+ req_is_fetch_q <= req_is_fetch;
+ req_is_store_q <= req_is_store;
+ req_sum_q <= req_sum;
+ req_mxr_q <= req_mxr;
+ req_vpn_q <= vaddr_to_vpn(req_vaddr, req_mode);
+
+ if (!is_supported_mode(req_mode) || !vaddr_is_canonical(req_vaddr, req_mode)) begin
+ resp_fault_q <= 1'b1;
+ resp_paddr_q <= '0;
+ state_q <= PTW_DONE;
+ end else begin
+ table_ppn_q <= satp_value[43:0];
+ case (req_mode)
+ SATP_MODE_SV39: level_q <= 3'd2;
+ default: level_q <= '0;
+ endcase
+ state_q <= PTW_ISSUE;
+ end
+ end
+ end
+
+ PTW_ISSUE: begin
+ if (mem_req_ready) begin
+ state_q <= PTW_WAIT;
+ end
+ end
+
+ PTW_WAIT: begin
+ if (mem_resp_valid) begin
+ if (bad_pte) begin
+ resp_fault_q <= 1'b1;
+ resp_paddr_q <= '0;
+ refill_valid_q <= 1'b0;
+ state_q <= PTW_DONE;
+ end else if (leaf_pte) begin
+ if (superpage_misaligned ||
+ !allow_access(req_is_fetch_q, req_is_store_q, req_priv_q, req_sum_q, req_mxr_q,
+ pte_u, pte_r, pte_w, pte_x, pte_a, pte_d)) begin
+ resp_fault_q <= 1'b1;
+ resp_paddr_q <= '0;
+ refill_valid_q <= 1'b0;
+ end else begin
+ resp_fault_q <= 1'b0;
+ resp_paddr_q <= next_paddr;
+ refill_valid_q <= 1'b1;
+ refill_vpn_q <= req_vpn_q;
+ refill_ppn_q <= pte_ppn;
+ refill_level_q <= level_q;
+ refill_mode_q <= req_mode_q;
+ refill_asid_q <= req_asid_q;
+ refill_g_q <= pte_g;
+ refill_u_q <= pte_u;
+ refill_r_q <= pte_r;
+ refill_w_q <= pte_w;
+ refill_x_q <= pte_x;
+ refill_a_q <= pte_a;
+ refill_d_q <= pte_d;
+ end
+ state_q <= PTW_DONE;
+ end else if (level_q == 0) begin
+ resp_fault_q <= 1'b1;
+ resp_paddr_q <= '0;
+ refill_valid_q <= 1'b0;
+ state_q <= PTW_DONE;
+ end else begin
+ table_ppn_q <= pte_ppn;
+ level_q <= level_q - 1'b1;
+ state_q <= PTW_ISSUE;
+ end
+ end
+ end
+
+ PTW_DONE: begin
+ state_q <= PTW_IDLE;
+ end
+
+ default: state_q <= PTW_IDLE;
+ endcase
+ end
+ end
+
+endmodule