PMP granularity support

This commit is contained in:
David Harris 2025-05-27 03:59:26 -07:00
parent ca96ed48ae
commit 11ff08d0ac
3 changed files with 32 additions and 40 deletions

View file

@ -52,52 +52,40 @@ module pmpadrdec import cvw::*; #(parameter cvw_t P) (
logic TORMatch, NAMatch; logic TORMatch, NAMatch;
logic PAltPMPAdr; logic PAltPMPAdr;
logic [P.PA_BITS-1:0] PMPAdrTORGrain, PMPAdrNAPOTGrain, CurrentAdrFull; logic [P.PA_BITS-1:0] PMPAdrFull;
logic [1:0] AdrMode; logic [1:0] AdrMode;
logic [P.PA_BITS-1:0] PMPTop1, PMPTopTOR, PMPTopNaturallyAligned; logic [P.PA_BITS-1:0] PMPTop1, PMPTopTOR, PMPTopNaturallyAligned;
assign AdrMode = PMPCfg[4:3]; assign AdrMode = PMPCfg[4:3];
// The two lsb of the physical address don't matter for this checking. // Bottom two bits of PMPAdr are 00
// The following code includes them, but hardwires the PMP checker lsbs to 00 assign PMPAdrFull = {PMPAdr, 2'b00};
// and masks them later. Logic synthesis should optimize away these bottom bits.
// Top-of-range (TOR) // Top-of-range (TOR)
// Append two implicit trailing 0's to PMPAdr value // Append two implicit trailing 0's to PMPAdr value
assign PMPAdrTORGrain = (P.PMP_G > 0) ? {PMPAdr[P.PA_BITS-3:P.PMP_G], {P.PMP_G{1'b0}}} : PMPAdr; // In TOR, bottom G bits read as 0 assign PAltPMPAdr = {1'b0, PhysicalAddress} < {1'b0, PMPAdrFull}; // unsigned comparison
assign CurrentAdrFull = {PMPAdrTORGrain, 2'b00};
assign PAltPMPAdr = {1'b0, PhysicalAddress} < {1'b0, CurrentAdrFull}; // unsigned comparison
assign PAgePMPAdrOut = ~PAltPMPAdr; assign PAgePMPAdrOut = ~PAltPMPAdr;
assign TORMatch = PAgePMPAdrIn & PAltPMPAdr; // exclusion-tag: PAgePMPAdrIn assign TORMatch = PAgePMPAdrIn & PAltPMPAdr; // exclusion-tag: PAgePMPAdrIn
// Naturally aligned regions // Naturally aligned regions
logic [P.PA_BITS-1:0] NAMask, NABase; logic [P.PA_BITS-1:0] NAMask, NABase;
assign PMPAdrNAPOTGrain = {PMPAdr[P.PA_BITS-3:Gm1], {Gm1{1'b1}}}; // in NAPOT, if G >= 2, bottom G-1 bits read as all 1s
assign NAMask[1:0] = {2'b11}; assign NAMask[1:0] = {2'b11};
assign NAMask[P.PA_BITS-1:2] = (PMPAdr + {{(P.PA_BITS-3){1'b0}}, (AdrMode == NAPOT)}) ^ PMPAdr;
// assign NAMask[P.PA_BITS-1:2] = (PMPAdr + {{(P.PA_BITS-3){1'b0}}, (AdrMode == NAPOT)}) ^ PMPAdr;
assign NAMask[P.PA_BITS-1:2] = (PMPAdrNAPOTGrain + {{(P.PA_BITS-3){1'b0}}, (AdrMode == NAPOT)}) ^ PMPAdrNAPOTGrain;
// form a mask where the bottom k bits are 1, corresponding to a size of 2^k bytes for this memory region. // form a mask where the bottom k bits are 1, corresponding to a size of 2^k bytes for this memory region.
// This assumes we're using at least an NA4 region, but works for any size NAPOT region. // This assumes we're using at least an NA4 region, but works for any size NAPOT region.
/* in progress, fix soon dh 5/8/25
assign NABase = {(PMPAdrNAPOTGrain & ~NAMask[P.PA_BITS-1:2]), 2'b00}; // base physical address of the pmp region
assign NAMatch = &((NABase ~^ PhysicalAddress) | NAMask); // check if upper bits of base address match, ignore lower bits correspoonding to inside the memory range
*/
assign NABase = {(PMPAdr & ~NAMask[P.PA_BITS-1:2]), 2'b00}; // base physical address of the pmp region assign NABase = {(PMPAdr & ~NAMask[P.PA_BITS-1:2]), 2'b00}; // base physical address of the pmp region
assign NAMatch = &((NABase ~^ PhysicalAddress) | NAMask); // check if upper bits of base address match, ignore lower bits corresponding to inside the memory range assign NAMatch = &((NABase ~^ PhysicalAddress) | NAMask); // check if upper bits of base address match, ignore lower bits corresponding to inside the memory range
// finally pick the appropriate match for the access type // finally pick the appropriate match for the access type
assign Match = (AdrMode == TOR) ? TORMatch : assign Match = (AdrMode == TOR) ? TORMatch :
(AdrMode == NA4 | AdrMode == NAPOT) ? NAMatch : (AdrMode == NA4 | AdrMode == NAPOT) ? NAMatch :
1'b0; 1'b0; // OFF never matches
// Report top of region for first matching region // Report top of region for first matching region
// PMP should match but fail if the size is too big (8-byte accesses spanning to TOR or NA4 region) // PMP should match but fail if the size is too big (8-byte accesses spanning to TOR or NA4 region)
assign PMPTopTOR = {PMPAdrTORGrain-1, 2'b11}; // TOR goes to (pmpaddr << 2) - 1 assign PMPTopTOR = {PMPAdr-1, 2'b11}; // TOR goes to (pmpaddr << 2) - 1
assign PMPTopNaturallyAligned = {PMPAdrNAPOTGrain, 2'b00} | NAMask; // top of the pmp region for NA4 and NAPOT. All 1s in the lower bits. Used to check the address doesn't pass the top assign PMPTopNaturallyAligned = PMPAdrFull | NAMask; // top of the pmp region for NA4 and NAPOT. All 1s in the lower bits. Used to check the address doesn't pass the top
assign PMPTop1 = (AdrMode == TOR) ? PMPTopTOR : PMPTopNaturallyAligned; assign PMPTop1 = (AdrMode == TOR) ? PMPTopTOR : PMPTopNaturallyAligned;
assign PMPTop = FirstMatch ? PMPTop1 : '0; // AND portion of distributed AND-OR mux (OR portion in pmpchhecker) assign PMPTop = FirstMatch ? PMPTop1 : '0; // AND portion of distributed AND-OR mux (OR portion in pmpchhecker)

View file

@ -96,6 +96,9 @@ module pmpchecker import cvw::*; #(parameter cvw_t P) (
endcase endcase
// Then find the top of the access and see if it is beyond the top of the region // Then find the top of the access and see if it is beyond the top of the region
assign PhysicalAddressTop = PhysicalAddress + {{P.PA_BITS-3{1'b0}}, SizeBytesMinus1}; // top of the access range assign PhysicalAddressTop = PhysicalAddress + {{P.PA_BITS-3{1'b0}}, SizeBytesMinus1}; // top of the access range
// DH 5/27/25 *** TooBig should never occur because granularity is a line size, and anything wrapping line should be decomposed into multiple accesses.
// Therefore, it should be possilbe to remove TooBig and all the logic that depends on it
// including PhysicalAddressTop, SizeBytesMinus1, and the pmpadrdecs PMPTop output, which is expensive
assign TooBig = PhysicalAddressTop > MatchingPMPTop; // check if the access goes beyond the top of the PMP region assign TooBig = PhysicalAddressTop > MatchingPMPTop; // check if the access goes beyond the top of the PMP region
// Only enforce PMP checking for effective S and U modes (accounting for mstatus.MPRV) or in Machine mode when L bit is set in selected region // Only enforce PMP checking for effective S and U modes (accounting for mstatus.MPRV) or in Machine mode when L bit is set in selected region

View file

@ -46,14 +46,15 @@ module csrm import cvw::*; #(parameter cvw_t P) (
output logic [15:0] MEDELEG_REGW, output logic [15:0] MEDELEG_REGW,
output logic [11:0] MIDELEG_REGW, output logic [11:0] MIDELEG_REGW,
/* verilator lint_off UNDRIVEN */ // PMP registers are only used when PMP_ENTRIES > 0 /* verilator lint_off UNDRIVEN */ // PMP registers are only used when PMP_ENTRIES > 0
output var logic [P.PA_BITS-3:0] PMPADDR_ARRAY_REGW[P.PMP_ENTRIES-1:0],
output var logic [7:0] PMPCFG_ARRAY_REGW[P.PMP_ENTRIES-1:0], output var logic [7:0] PMPCFG_ARRAY_REGW[P.PMP_ENTRIES-1:0],
output var logic [P.PA_BITS-3:0] PMPADDR_ARRAY_REGW [P.PMP_ENTRIES-1:0],
/* verilator lint_on UNDRIVEN */ /* verilator lint_on UNDRIVEN */
output logic WriteMSTATUSM, WriteMSTATUSHM, output logic WriteMSTATUSM, WriteMSTATUSHM,
output logic IllegalCSRMAccessM, IllegalCSRMWriteReadonlyM, output logic IllegalCSRMAccessM, IllegalCSRMWriteReadonlyM,
output logic [63:0] MENVCFG_REGW output logic [63:0] MENVCFG_REGW
); );
logic [P.PA_BITS-3:0] PMPADDR_ARRAY_PREGRAIN_REGW[P.PMP_ENTRIES-1:0];
logic [P.XLEN-1:0] MISA_REGW, MHARTID_REGW; logic [P.XLEN-1:0] MISA_REGW, MHARTID_REGW;
logic [P.XLEN-1:0] MSCRATCH_REGW, MTVAL_REGW, MCAUSE_REGW; logic [P.XLEN-1:0] MSCRATCH_REGW, MTVAL_REGW, MCAUSE_REGW;
logic [P.XLEN-1:0] MENVCFGH_REGW; logic [P.XLEN-1:0] MENVCFGH_REGW;
@ -113,9 +114,9 @@ module csrm import cvw::*; #(parameter cvw_t P) (
logic [7:0] CSRPMPWriteValM[P.PMP_ENTRIES-1:0]; logic [7:0] CSRPMPWriteValM[P.PMP_ENTRIES-1:0];
logic [7:0] CSRPMPLegalizedWriteValM[P.PMP_ENTRIES-1:0]; logic [7:0] CSRPMPLegalizedWriteValM[P.PMP_ENTRIES-1:0];
logic [1:0] CSRPMPWRLegalizedWriteValM[P.PMP_ENTRIES-1:0]; logic [1:0] CSRPMPWRLegalizedWriteValM[P.PMP_ENTRIES-1:0];
logic CSRPMPA0LegalizedWriteValM[P.PMP_ENTRIES-1:0]; logic [1:0] CSRPMPALegalizedWriteValM[P.PMP_ENTRIES-1:0];
logic [P.PMP_ENTRIES-1:0] ADDRLocked, CFGLocked; logic [P.PMP_ENTRIES-1:0] ADDRLocked, CFGLocked;
for(i=0; i<P.PMP_ENTRIES; i++) begin for(i=0; i<P.PMP_ENTRIES; i++) begin:pmp
// when the lock bit is set, don't allow writes to the PMPCFG or PMPADDR // when the lock bit is set, don't allow writes to the PMPCFG or PMPADDR
// also, when the lock bit of the next entry is set and the next entry is TOR, don't allow writes to this entry PMPADDR // also, when the lock bit of the next entry is set and the next entry is TOR, don't allow writes to this entry PMPADDR
assign CFGLocked[i] = PMPCFG_ARRAY_REGW[i][7]; assign CFGLocked[i] = PMPCFG_ARRAY_REGW[i][7];
@ -125,7 +126,8 @@ module csrm import cvw::*; #(parameter cvw_t P) (
assign ADDRLocked[i] = PMPCFG_ARRAY_REGW[i][7] | (PMPCFG_ARRAY_REGW[i+1][7] & PMPCFG_ARRAY_REGW[i+1][4:3] == 2'b01); assign ADDRLocked[i] = PMPCFG_ARRAY_REGW[i][7] | (PMPCFG_ARRAY_REGW[i+1][7] & PMPCFG_ARRAY_REGW[i+1][4:3] == 2'b01);
assign WritePMPADDRM[i] = (CSRMWriteM & (CSRAdrM == (PMPADDR0+i))) & ~ADDRLocked[i]; assign WritePMPADDRM[i] = (CSRMWriteM & (CSRAdrM == (PMPADDR0+i))) & ~ADDRLocked[i];
flopenr #(P.PA_BITS-2) PMPADDRreg(clk, reset, WritePMPADDRM[i], CSRWriteValM[P.PA_BITS-3:0], PMPADDR_ARRAY_REGW[i]); // PMPADDR_ARRAY_PREGRAIN_REGW flip-flops hold all the bits even though all but G-1 lsbs can be controlled by PMP mode and granularity
flopenr #(P.PA_BITS-2) PMPADDRreg(clk, reset, WritePMPADDRM[i], CSRWriteValM[P.PA_BITS-3:0], PMPADDR_ARRAY_PREGRAIN_REGW[i]);
if (P.XLEN==64) begin if (P.XLEN==64) begin
assign WritePMPCFGM[i] = (CSRMWriteM & (CSRAdrM == (PMPCFG0+2*(i/8)))) & ~CFGLocked[i]; assign WritePMPCFGM[i] = (CSRMWriteM & (CSRAdrM == (PMPCFG0+2*(i/8)))) & ~CFGLocked[i];
assign CSRPMPWriteValM[i] = CSRWriteValM[(i%8)*8+7:(i%8)*8]; assign CSRPMPWriteValM[i] = CSRWriteValM[(i%8)*8+7:(i%8)*8];
@ -134,9 +136,9 @@ module csrm import cvw::*; #(parameter cvw_t P) (
assign CSRPMPWriteValM[i] = CSRWriteValM[(i%4)*8+7:(i%4)*8]; assign CSRPMPWriteValM[i] = CSRWriteValM[(i%4)*8+7:(i%4)*8];
end end
assign CSRPMPALegalizedWriteValM[i] = ((P.PMP_G > 0) & (CSRPMPWriteValM[i][4:3] == 2'b10)) ? PMPCFG_ARRAY_REGW[i][4:3] : CSRPMPWriteValM[i][4:3]; // WARL A field keeps its old value when attempting to write unselectable NA4 mode
assign CSRPMPWRLegalizedWriteValM[i] = {(CSRPMPWriteValM[i][1] & CSRPMPWriteValM[i][0]), CSRPMPWriteValM[i][0]}; // legalize WR fields (reserved 10 written as 00) assign CSRPMPWRLegalizedWriteValM[i] = {(CSRPMPWriteValM[i][1] & CSRPMPWriteValM[i][0]), CSRPMPWriteValM[i][0]}; // legalize WR fields (reserved 10 written as 00)
assign CSRPMPA0LegalizedWriteValM[i] = (P.PMP_G > 0) & CSRPMPWriteValM[i][4] | CSRPMPWriteValM[i][3]; // if G > 0, when trying to write A = NA4 (10), actually write A = NAPOT (11) assign CSRPMPLegalizedWriteValM[i] = {CSRPMPWriteValM[i][7], 2'b00, CSRPMPALegalizedWriteValM[i], CSRPMPWriteValM[i][2], CSRPMPWRLegalizedWriteValM[i]};
assign CSRPMPLegalizedWriteValM[i] = {CSRPMPWriteValM[i][7], 2'b00, CSRPMPWriteValM[i][4], CSRPMPA0LegalizedWriteValM[i], CSRPMPWriteValM[i][2], CSRPMPWRLegalizedWriteValM[i]};
flopenr #(8) PMPCFGreg(clk, reset, WritePMPCFGM[i], CSRPMPLegalizedWriteValM[i], PMPCFG_ARRAY_REGW[i]); flopenr #(8) PMPCFGreg(clk, reset, WritePMPCFGM[i], CSRPMPLegalizedWriteValM[i], PMPCFG_ARRAY_REGW[i]);
end end
end end
@ -217,26 +219,25 @@ module csrm import cvw::*; #(parameter cvw_t P) (
assign MENVCFGH_REGW = '0; assign MENVCFGH_REGW = '0;
end end
// Grain alignment for PMPADDR read values.
for(i=0; i<P.PMP_ENTRIES; i++)
always_comb begin
logic [P.XLEN-1:0] pmpaddr;
pmpaddr = {{(P.XLEN-(P.PA_BITS-2)){1'b0}}, PMPADDR_ARRAY_PREGRAIN_REGW[i]}; // raw value in PMP registers
if (PMPCFG_ARRAY_REGW[i][4]) PMPADDR_ARRAY_REGW[i] = {pmpaddr[P.PA_BITS-3:Gm1], {Gm1 {1'b1}}}; // in NAPOT, bottom G-1 bits read as all 1s
else PMPADDR_ARRAY_REGW[i] = {pmpaddr[P.PA_BITS-3:P.PMP_G], {P.PMP_G{1'b0}}}; // in TOR/OFF, bottom G bits read as 0s
end
// Read machine mode CSRs // Read machine mode CSRs
// verilator lint_off WIDTH // verilator lint_off WIDTH
logic [5:0] entry; logic [5:0] entry;
logic [P.XLEN-1:0] pmpaddr; // correct for grain size and PMP mode
logic napot;
always_comb begin always_comb begin
entry = '0; entry = '0;
CSRMReadValM = '0; CSRMReadValM = '0;
pmpaddr = '0;
napot = 0;
IllegalCSRMAccessM = !(P.S_SUPPORTED) & (CSRAdrM == MEDELEG | CSRAdrM == MIDELEG); // trap on DELEG register access when no S or N-mode IllegalCSRMAccessM = !(P.S_SUPPORTED) & (CSRAdrM == MEDELEG | CSRAdrM == MIDELEG); // trap on DELEG register access when no S or N-mode
if ($unsigned(CSRAdrM) >= PMPADDR0 & $unsigned(CSRAdrM) < PMPADDR0 + P.PMP_ENTRIES) begin // reading a PMP entry if ($unsigned(CSRAdrM) >= PMPADDR0 & $unsigned(CSRAdrM) < PMPADDR0 + P.PMP_ENTRIES)
pmpaddr = {{(P.XLEN-(P.PA_BITS-2)){1'b0}}, PMPADDR_ARRAY_REGW[CSRAdrM - PMPADDR0]}; // raw value in PMP registers CSRMReadValM = {{(P.XLEN-(P.PA_BITS-2)){1'b0}}, PMPADDR_ARRAY_REGW[CSRAdrM - PMPADDR0]}; // read PMPADDR entry with lsbs aligned to grain based on NAPOT vs. TOR
if (P.PMP_G > 0) begin // bottom bits read as 0/1 depending on mode else if ($unsigned(CSRAdrM) >= PMPCFG0 & $unsigned(CSRAdrM) < PMPCFG0 + P.PMP_ENTRIES/4 & (P.XLEN==32 | CSRAdrM[0] == 0)) begin
napot = PMPCFG_ARRAY_REGW[CSRAdrM - PMPADDR0][4]; // read from corresponding pmpcfg register, indicating NAPOT mode
if (napot) pmpaddr = {pmpaddr[P.XLEN-1:Gm1], {Gm1 {1'b1}}}; // in NAPOT, bottom G-1 bits read as all 1s
else pmpaddr = {pmpaddr[P.XLEN-1:P.PMP_G], {P.PMP_G{1'b0}}}; // in TOR/OFF, bottom G bits read as 0s
end
CSRMReadValM = pmpaddr;
end else if ($unsigned(CSRAdrM) >= PMPCFG0 & $unsigned(CSRAdrM) < PMPCFG0 + P.PMP_ENTRIES/4 & (P.XLEN==32 | CSRAdrM[0] == 0)) begin
// only odd-numbered PMPCFG entries exist in RV64 // only odd-numbered PMPCFG entries exist in RV64
if (P.XLEN==64) begin if (P.XLEN==64) begin
entry = ({CSRAdrM[11:1], 1'b0} - PMPCFG0)*4; // disregard odd entries in RV64 entry = ({CSRAdrM[11:1], 1'b0} - PMPCFG0)*4; // disregard odd entries in RV64