diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-11-07 04:31:31 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-11-07 04:31:31 +0800 |
| commit | a7d5b2468553c6d3570ca2aa45c27cb93e26cb88 (patch) | |
| tree | 7031b1fac52e4f858c4e92041485135242768f7b /rv64cpu/rtl/core/privileged/pmp_checker.sv | |
| parent | 4ca723bc9dc1b9db1bd03e3b3db0c58079c3f3c4 (diff) | |
| parent | 3b0b67af63af86c5ec9b2a63defa06b70923b2e3 (diff) | |
| download | rcc-project-a7d5b2468553c6d3570ca2aa45c27cb93e26cb88.tar.gz rcc-project-a7d5b2468553c6d3570ca2aa45c27cb93e26cb88.tar.xz | |
Merge remote-tracking branch 'rv64cpu-import/master'
Diffstat (limited to 'rv64cpu/rtl/core/privileged/pmp_checker.sv')
| -rw-r--r-- | rv64cpu/rtl/core/privileged/pmp_checker.sv | 181 |
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 |
