aboutsummaryrefslogtreecommitdiff
path: root/rv64cpu/rtl/core/privileged/pmp_checker.sv
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-11-07 03:10:08 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-11-07 03:10:08 +0800
commit1410d3309ae9557ab102942bbc4d8449f2206fe5 (patch)
tree9e8f45cf9bd7378c42d683afbc427afdb4678557 /rv64cpu/rtl/core/privileged/pmp_checker.sv
parentbc3ec25107314be3cd816c0b669d82338e892806 (diff)
downloadrcc-project-1410d3309ae9557ab102942bbc4d8449f2206fe5.tar.gz
rcc-project-1410d3309ae9557ab102942bbc4d8449f2206fe5.tar.xz
refactor: reorganize RTL directory structure
Diffstat (limited to 'rv64cpu/rtl/core/privileged/pmp_checker.sv')
-rw-r--r--rv64cpu/rtl/core/privileged/pmp_checker.sv181
1 files changed, 181 insertions, 0 deletions
diff --git a/rv64cpu/rtl/core/privileged/pmp_checker.sv b/rv64cpu/rtl/core/privileged/pmp_checker.sv
new file mode 100644
index 0000000..361e507
--- /dev/null
+++ b/rv64cpu/rtl/core/privileged/pmp_checker.sv
@@ -0,0 +1,181 @@
+// 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/pmp_checker.sv
+// Description: RISC-V Physical Memory Protection access checker.
+
+module pmp_checker
+ import rv64_pkg::*;
+ import csr_pkg::*;
+#(
+ parameter int PHYS_ADDR_WIDTH = 56
+)(
+ input logic req_valid,
+ input logic [XLEN-1:0] req_paddr,
+ input logic [3:0] req_size,
+ input logic req_is_fetch,
+ input logic req_is_store,
+ input logic [1:0] priv_mode,
+ input pmpcfg_entry_t pmpcfg_entries [PMP_ENTRY_COUNT],
+ input pmpaddr_entry_t pmpaddr_entries [PMP_ENTRY_COUNT],
+ output logic access_fault
+);
+
+ localparam logic [1:0] PRIV_MODE_M = 2'b11;
+
+ typedef struct packed {
+ logic valid;
+ logic [XLEN-1:0] lower_bound;
+ logic [XLEN-1:0] upper_bound;
+ } pmp_region_t;
+
+ function automatic logic [XLEN-1:0] pmpaddr_to_paddr(input pmpaddr_entry_t pmpaddr);
+ logic [XLEN-1:0] paddr;
+ paddr = pmpaddr << 2;
+ if (PHYS_ADDR_WIDTH < XLEN) begin
+ paddr[XLEN-1:PHYS_ADDR_WIDTH] = '0;
+ end
+ return paddr;
+ endfunction
+
+ function automatic int unsigned napot_trailing_ones(input pmpaddr_entry_t pmpaddr);
+ int unsigned ones;
+ ones = 0;
+ for (int bit_idx = 0; bit_idx < (PHYS_ADDR_WIDTH - 2); bit_idx++) begin
+ if (pmpaddr[bit_idx]) begin
+ ones++;
+ end else begin
+ break;
+ end
+ end
+ return ones;
+ endfunction
+
+ function automatic pmp_region_t pmp_region(input int unsigned entry_idx);
+ pmp_region_t region;
+ logic [1:0] address_matching;
+ int unsigned ones;
+ logic [XLEN-1:0] napot_mask;
+ logic [XLEN-1:0] napot_base;
+
+ region.valid = 1'b0;
+ region.lower_bound = '0;
+ region.upper_bound = '0;
+ address_matching = pmpcfg_entries[entry_idx][PMPCFG_A_HI:PMPCFG_A_LO];
+
+ unique case (address_matching)
+ PMP_MODE_OFF: begin
+ region.valid = 1'b0;
+ end
+ PMP_MODE_TOR: begin
+ region.valid = 1'b1;
+ region.lower_bound = (entry_idx == 0) ? '0 : pmpaddr_to_paddr(pmpaddr_entries[entry_idx - 1]);
+ region.upper_bound = pmpaddr_to_paddr(pmpaddr_entries[entry_idx]);
+ if (region.upper_bound <= region.lower_bound) begin
+ region.valid = 1'b0;
+ end
+ end
+ PMP_MODE_NA4: begin
+ region.valid = 1'b1;
+ region.lower_bound = pmpaddr_to_paddr(pmpaddr_entries[entry_idx]);
+ region.upper_bound = region.lower_bound + 64'd4;
+ end
+ PMP_MODE_NAPOT: begin
+ region.valid = 1'b1;
+ ones = napot_trailing_ones(pmpaddr_entries[entry_idx]);
+ napot_mask = (64'd1 << (ones + 1)) - 64'd1;
+ napot_base = (pmpaddr_entries[entry_idx] & ~napot_mask) << 2;
+ if (PHYS_ADDR_WIDTH < XLEN) begin
+ napot_base[XLEN-1:PHYS_ADDR_WIDTH] = '0;
+ end
+ region.lower_bound = napot_base;
+ region.upper_bound = napot_base + (64'd1 << (ones + 3));
+ end
+ default: begin
+ region.valid = 1'b0;
+ end
+ endcase
+
+ return region;
+ endfunction
+
+ function automatic logic ranges_overlap(
+ input logic [XLEN-1:0] access_lower,
+ input logic [XLEN-1:0] access_upper,
+ input logic [XLEN-1:0] region_lower,
+ input logic [XLEN-1:0] region_upper
+ );
+ return (access_lower < region_upper) && (access_upper > region_lower);
+ endfunction
+
+ function automatic logic range_contains(
+ input logic [XLEN-1:0] access_lower,
+ input logic [XLEN-1:0] access_upper,
+ input logic [XLEN-1:0] region_lower,
+ input logic [XLEN-1:0] region_upper
+ );
+ return (access_lower >= region_lower) && (access_upper <= region_upper);
+ endfunction
+
+ function automatic logic permissions_allow(
+ input pmpcfg_entry_t cfg,
+ input logic is_fetch,
+ input logic is_store
+ );
+ if (is_fetch) begin
+ return cfg[PMPCFG_X_BIT];
+ end
+ if (is_store) begin
+ return cfg[PMPCFG_W_BIT];
+ end
+ return cfg[PMPCFG_R_BIT];
+ endfunction
+
+ always_comb begin
+ logic [XLEN-1:0] access_lower;
+ logic [XLEN-1:0] access_upper;
+ logic [3:0] normalized_size;
+ logic matched;
+ pmp_region_t region;
+
+ normalized_size = (req_size == 4'd0) ? 4'd1 : req_size;
+ access_lower = req_paddr;
+ access_upper = req_paddr + {{(XLEN-4){1'b0}}, normalized_size};
+ matched = 1'b0;
+ region = '0;
+ access_fault = 1'b0;
+
+ if (req_valid) begin
+ for (int unsigned entry_idx = 0; entry_idx < PMP_ENTRY_COUNT; entry_idx++) begin
+ region = pmp_region(entry_idx);
+ if (!matched && region.valid &&
+ ranges_overlap(access_lower, access_upper, region.lower_bound, region.upper_bound)) begin
+ matched = 1'b1;
+ if (!range_contains(access_lower, access_upper, region.lower_bound, region.upper_bound)) begin
+ access_fault = 1'b1;
+ end else if ((priv_mode != PRIV_MODE_M) || pmpcfg_entries[entry_idx][PMPCFG_L_BIT]) begin
+ access_fault = !permissions_allow(pmpcfg_entries[entry_idx], req_is_fetch, req_is_store);
+ end
+ end
+ end
+
+ if (!matched && (priv_mode != PRIV_MODE_M)) begin
+ access_fault = 1'b1;
+ end
+ end
+ end
+
+endmodule