/////////////////////////////////////////// // pmpchecker.sv // // Written: tfleming@hmc.edu & jtorrey@hmc.edu 28 April 2021 // Modified: // // Purpose: Examines all physical memory accesses and checks them against the // current values of the physical memory protection (PMP) registers. // Can raise an access fault on illegal reads, writes, and instruction // fetches. // // A component of the Wally configurable RISC-V project. // // Copyright (C) 2021 Harvey Mudd College & Oklahoma State University // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, // modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software // is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT // OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /////////////////////////////////////////// `include "wally-config.vh" module pmpchecker ( input logic [`PA_BITS-1:0] PhysicalAddress, input logic [1:0] PrivilegeModeW, // *** ModelSim has a switch -svinputport which controls whether input ports // are nets (wires) or vars by default. The default setting of this switch is // `relaxed`, which means that signals are nets if and only if they are // scalars or one-dimensional vectors. Since this is a two-dimensional vector, // this will be understood as a var. However, if we don't supply the `var` // keyword, the compiler warns us that it's interpreting the signal as a var, // which we might not intend. input var logic [63:0] PMPCFG_ARRAY_REGW[`PMP_ENTRIES/8-1:0], input var logic [`XLEN-1:0] PMPADDR_ARRAY_REGW [`PMP_ENTRIES-1:0], input logic ExecuteAccessF, WriteAccessM, ReadAccessM, output logic PMPSquashBusAccess, output logic PMPInstrAccessFaultF, output logic PMPLoadAccessFaultM, output logic PMPStoreAccessFaultM ); // verilator lint_off UNOPTFLAT // Bit i is high when the address falls in PMP region i logic EnforcePMP; logic [7:0] PMPCFG [`PMP_ENTRIES-1:0]; logic [`PMP_ENTRIES-1:0] Match; // PMP Entry matches logic [`PMP_ENTRIES-1:0] Active; // PMP register i is non-null logic [`PMP_ENTRIES-1:0] L, X, W, R; // PMP matches and has flag set logic [`PMP_ENTRIES:0] NoLowerMatch; // None of the lower PMP entries match logic [`PMP_ENTRIES:0] PAgePMPAdr; // for TOR PMP matching, PhysicalAddress > PMPAdr[i] genvar i,j; assign PAgePMPAdr[0] = 1'b1; assign NoLowerMatch[0] = 1'b1; generate // verilator lint_off WIDTH for (j=0; j<`PMP_ENTRIES; j = j+8) assign {PMPCFG[j+7], PMPCFG[j+6], PMPCFG[j+5], PMPCFG[j+4], PMPCFG[j+3], PMPCFG[j+2], PMPCFG[j+1], PMPCFG[j]} = PMPCFG_ARRAY_REGW[j/8]; // verilator lint_on WIDTH for (i=0; i<`PMP_ENTRIES; i++) // *** can this be done with array notation? pmpadrdec pmpadrdec(.PhysicalAddress, .PMPCfg(PMPCFG[i]), .PMPAdr(PMPADDR_ARRAY_REGW[i]), .PAgePMPAdrIn(PAgePMPAdr[i]), .PAgePMPAdrOut(PAgePMPAdr[i+1]), .NoLowerMatchIn(NoLowerMatch[i]), .NoLowerMatchOut(NoLowerMatch[i+1]), .Match(Match[i]), .Active(Active[i]), .L(L[i]), .X(X[i]), .W(W[i]), .R(R[i]) ); // verilator lint_on UNOPTFLAT endgenerate // Only enforce PMP checking for S and U modes when at least one PMP is active or in Machine mode when L bit is set in selected region assign EnforcePMP = (PrivilegeModeW == `M_MODE) ? |L : |Active; assign PMPInstrAccessFaultF = EnforcePMP && ExecuteAccessF && ~|X; assign PMPStoreAccessFaultM = EnforcePMP && WriteAccessM && ~|W; assign PMPLoadAccessFaultM = EnforcePMP && ReadAccessM && ~|R; assign PMPSquashBusAccess = PMPInstrAccessFaultF | PMPLoadAccessFaultM | PMPStoreAccessFaultM; endmodule