blob: 0fee9b5c1c5eeef44d62d67d5a562eb59353e3be [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
module ibex_pmp #(
// Granularity of NAPOT access,
// 0 = No restriction, 1 = 8 byte, 2 = 16 byte, 3 = 32 byte, etc.
parameter int unsigned PMPGranularity = 0,
// Number of access channels (e.g. i-side + d-side)
parameter int unsigned PMPNumChan = 2,
// Number of implemented regions
parameter int unsigned PMPNumRegions = 4
) (
// Clock and Reset
input logic clk_i,
input logic rst_ni,
// Interface to CSRs
input ibex_pkg::pmp_cfg_t csr_pmp_cfg_i [PMPNumRegions],
input logic [33:0] csr_pmp_addr_i [PMPNumRegions],
input ibex_pkg::priv_lvl_e priv_mode_i [PMPNumChan],
// Access checking channels
input logic [33:0] pmp_req_addr_i [PMPNumChan],
input ibex_pkg::pmp_req_e pmp_req_type_i [PMPNumChan],
output logic pmp_req_err_o [PMPNumChan]
);
import ibex_pkg::*;
// Access Checking Signals
logic [33:0] region_start_addr [PMPNumRegions];
logic [33:PMPGranularity+2] region_addr_mask [PMPNumRegions];
logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_match_high;
logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_match_low;
logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_match_both;
logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_perm_check;
logic [PMPNumChan-1:0][PMPNumRegions-1:0] machine_access_fault;
logic [PMPNumChan-1:0][PMPNumRegions-1:0] user_access_allowed;
logic [PMPNumChan-1:0] access_fault;
// ---------------
// Access checking
// ---------------
for (genvar r = 0; r < PMPNumRegions; r++) begin : g_addr_exp
// Start address for TOR matching
if (r == 0) begin : g_entry0
assign region_start_addr[r] = (csr_pmp_cfg_i[r].mode == PMP_MODE_TOR) ? 34'h000000000 :
csr_pmp_addr_i[r];
end else begin : g_oth
assign region_start_addr[r] = (csr_pmp_cfg_i[r].mode == PMP_MODE_TOR) ? csr_pmp_addr_i[r-1] :
csr_pmp_addr_i[r];
end
// Address mask for NA matching
for (genvar b = PMPGranularity+2; b < 34; b++) begin : g_bitmask
if (b == PMPGranularity+2) begin : g_bit0
// Always mask bit (G+2) for NAPOT
assign region_addr_mask[r][b] = (csr_pmp_cfg_i[r].mode != PMP_MODE_NAPOT);
end else begin : g_others
// We will mask this bit if it is within the programmed granule
// i.e. addr = yyyy 0111
// ^
// | This bit pos is the top of the mask, all lower bits set
// thus mask = 1111 0000
assign region_addr_mask[r][b] = (csr_pmp_cfg_i[r].mode != PMP_MODE_NAPOT) |
~&csr_pmp_addr_i[r][b-1:PMPGranularity+2];
end
end
end
for (genvar c = 0; c < PMPNumChan; c++) begin : g_access_check
for (genvar r = 0; r < PMPNumRegions; r++) begin : g_regions
// TOR Region high/low matching is reused for all match types
assign region_match_low[c][r] = (pmp_req_addr_i[c][33:PMPGranularity+2] >=
// Comparators are sized according to granularity
(region_start_addr[r][33:PMPGranularity+2] &
region_addr_mask[r]));
assign region_match_high[c][r] = (pmp_req_addr_i[c][33:PMPGranularity+2] <=
csr_pmp_addr_i[r][33:PMPGranularity+2]);
assign region_match_both[c][r] = region_match_low[c][r] & region_match_high[c][r] &
(csr_pmp_cfg_i[r].mode != PMP_MODE_OFF);
// Check specific required permissions
assign region_perm_check[c][r] =
((pmp_req_type_i[c] == PMP_ACC_EXEC) & csr_pmp_cfg_i[r].exec) |
((pmp_req_type_i[c] == PMP_ACC_WRITE) & csr_pmp_cfg_i[r].write) |
((pmp_req_type_i[c] == PMP_ACC_READ) & csr_pmp_cfg_i[r].read);
// In machine mode, any match to a locked region without sufficient permissions is a fault
assign machine_access_fault[c][r] = region_match_both[c][r] & csr_pmp_cfg_i[r].lock &
~region_perm_check[c][r];
// In any other mode, any access should fault unless is matches a region
assign user_access_allowed[c][r] = region_match_both[c][r] & region_perm_check[c][r];
end
assign access_fault[c] = (priv_mode_i[c] == PRIV_LVL_M) ? |machine_access_fault[c] :
~|user_access_allowed[c];
assign pmp_req_err_o[c] = access_fault[c];
end
endmodule