Support PMP granularity G > 0 in PMP registers

This commit is contained in:
David Harris 2025-05-05 15:29:30 -07:00
parent 97f5ea0fd1
commit 90b4337a49
6 changed files with 43 additions and 28 deletions

View file

@ -103,6 +103,7 @@ module csrm import cvw::*; #(parameter cvw_t P) (
// when compressed instructions are supported, there can't be misaligned instructions
localparam MEDELEG_MASK = P.ZCA_SUPPORTED ? 16'hB3FE : 16'hB3FF;
localparam MIDELEG_MASK = 12'h222; // we choose to not make machine interrupts delegable
localparam Gm1 = P.PMP_G > 0 ? P.PMP_G - 1 : 0; // max(G-1, 0)
// There are PMP_ENTRIES = 0, 16, or 64 PMPADDR registers, each of which has its own flop
genvar i;
@ -112,6 +113,7 @@ module csrm import cvw::*; #(parameter cvw_t P) (
logic [7:0] CSRPMPWriteValM[P.PMP_ENTRIES-1:0];
logic [7:0] CSRPMPLegalizedWriteValM[P.PMP_ENTRIES-1:0];
logic [1:0] CSRPMPWRLegalizedWriteValM[P.PMP_ENTRIES-1:0];
logic CSRPMPA0LegalizedWriteValM[P.PMP_ENTRIES-1:0];
logic [P.PMP_ENTRIES-1:0] ADDRLocked, CFGLocked;
for(i=0; i<P.PMP_ENTRIES; i++) begin
// when the lock bit is set, don't allow writes to the PMPCFG or PMPADDR
@ -133,7 +135,8 @@ module csrm import cvw::*; #(parameter cvw_t P) (
end
assign CSRPMPWRLegalizedWriteValM[i] = {(CSRPMPWriteValM[i][1] & CSRPMPWriteValM[i][0]), CSRPMPWriteValM[i][0]}; // legalize WR fields (reserved 10 written as 00)
assign CSRPMPLegalizedWriteValM[i] = {CSRPMPWriteValM[i][7], 2'b00, CSRPMPWriteValM[i][4:2], CSRPMPWRLegalizedWriteValM[i]};
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, CSRPMPWriteValM[i][4], CSRPMPA0LegalizedWriteValM[i], CSRPMPWriteValM[i][2], CSRPMPWRLegalizedWriteValM[i]};
flopenr #(8) PMPCFGreg(clk, reset, WritePMPCFGM[i], CSRPMPLegalizedWriteValM[i], PMPCFG_ARRAY_REGW[i]);
end
end
@ -217,13 +220,23 @@ module csrm import cvw::*; #(parameter cvw_t P) (
// Read machine mode CSRs
// verilator lint_off WIDTH
logic [5:0] entry;
logic [P.XLEN-1:0] pmpaddr; // correct for grain size and PMP mode
logic napot;
always_comb begin
entry = '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
if ($unsigned(CSRAdrM) >= PMPADDR0 & $unsigned(CSRAdrM) < PMPADDR0 + P.PMP_ENTRIES) // reading a PMP entry
CSRMReadValM = {{(P.XLEN-(P.PA_BITS-2)){1'b0}}, PMPADDR_ARRAY_REGW[CSRAdrM - PMPADDR0]};
else if ($unsigned(CSRAdrM) >= PMPCFG0 & $unsigned(CSRAdrM) < PMPCFG0 + P.PMP_ENTRIES/4 & (P.XLEN==32 | CSRAdrM[0] == 0)) begin
if ($unsigned(CSRAdrM) >= PMPADDR0 & $unsigned(CSRAdrM) < PMPADDR0 + P.PMP_ENTRIES) begin // reading a PMP entry
pmpaddr = {{(P.XLEN-(P.PA_BITS-2)){1'b0}}, PMPADDR_ARRAY_REGW[CSRAdrM - PMPADDR0]}; // raw value in PMP registers
if (P.PMP_G > 0) begin // bottom bits read as 0/1 depending on mode
napot = PMPCFG_ARRAY_REGW[CSRAdrM - PMPADDR0][4]; // read from corresponding pmpcfg register, indicating NAPOT mode
if ((P.PMP_G > 1) & napot) pmpaddr = {pmpaddr[P.XLEN-1:Gm1], {Gm1 {1'b1}}}; // in NAPOT, bottom G-1 bits read as all 1s
else if (!napot) 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
if (P.XLEN==64) begin
entry = ({CSRAdrM[11:1], 1'b0} - PMPCFG0)*4; // disregard odd entries in RV64

View file

@ -21,10 +21,12 @@
module riscvassertions import cvw::*; #(parameter cvw_t P);
initial begin
//$display("PMPG = %d LLEN = %d !ZCA = %b PMPENTRIES = %d !ICS = %b", P.PMP_G, P.ICACHE_LINELENINBITS, !P.ZCA_SUPPORTED, P.PMP_ENTRIES, !P.ICACHE_SUPPORTED);
assert (P.PMP_ENTRIES == 0 | P.PMP_ENTRIES==16 | P.PMP_ENTRIES==64) else $fatal(1, "Illegal number of PMP entries: PMP_ENTRIES must be 0, 16, or 64");
assert (P.PMP_G > 0 | P.XLEN == 32) else $fatal(1, "RV64 requires PMP_G at least 1 to avoid checking for 8-byte accesses to 4-byte region");
assert (P.PMP_G >= $clog2(P.DCACHE_LINELENINBITS)-2 | !P.ZICCLSM_SUPPORTED | P.PMP_ENTRIES == 0) else $fatal(1, "Systems that support misaligned data with PMP must have grain size of at least one cache line so accesses that span grains will also cause spills");
assert (P.PMP_G >= $clog2(P.ICACHE_LINELENINBITS)-2 | !P.ZCA_SUPPORTED | P.PMP_ENTRIES == 0 | !P.ICACHE_SUPPORTED) else $fatal(1, "Systems that support compressed instructions with PMP must have grain size of at least one cache line so fetches that span grains will also cause spills");
assert (P.PMP_G > 0 | P.XLEN == 32 | P.PMP_ENTRIES == 0) else $fatal(1, "RV64 requires PMP_G at least 1 to avoid checking for 8-byte accesses to 4-byte region");
assert ((P.PMP_G >= $clog2(P.DCACHE_LINELENINBITS/8)-2) | !P.ZICCLSM_SUPPORTED | P.PMP_ENTRIES == 0) else $fatal(1, "Systems that support misaligned data with PMP must have grain size of at least one cache line so accesses that span grains will also cause spills");
assert ((P.PMP_G >= $clog2(P.ICACHE_LINELENINBITS/8)-2) | !P.ZCA_SUPPORTED | (P.PMP_ENTRIES == 0) | !P.ICACHE_SUPPORTED) else $fatal(1, "Systems that support compressed instructions with PMP must have grain size of at least one cache line so fetches that span grains will also cause spills");
assert (P.PMP_G < P.PA_BITS-2 | P.PMP_ENTRIES == 0) else $fatal(1, "PMP granularity must be less than the number of physical address bits");
assert (P.IDIV_BITSPERCYCLE == 1 | P.IDIV_BITSPERCYCLE==2 | P.IDIV_BITSPERCYCLE==4) else $fatal(1, "Illegal number of divider bits/cycle: IDIV_BITSPERCYCLE must be 1, 2, or 4");
assert (P.F_SUPPORTED | !P.D_SUPPORTED) else $fatal(1, "Can't support double fp (D) without supporting float (F)");
assert (P.D_SUPPORTED | !P.Q_SUPPORTED) else $fatal(1, "Can't support quad fp (Q) without supporting double (D)");

View file

@ -1,16 +1,16 @@
0fffffff
0ffffff0 # writeback of value written to PMPADDR0, less trailing 4 bits for G = 4
20040000
2004003f
20040030
20040080
20040080
20040084
200400c0
2004013f
2fffffff
20040130
2ffffff0
0009001f
0018900c
0018980c
1f000000
0018900c
200400c0
0018980c
200400c7
00000005
00000bad
00600dbb

View file

@ -73,7 +73,7 @@ test_cases:
# write pmpcfg regs with the information in the table above. this should also write the value of these registers to the output.
.4byte 0x0, 0x0009001F, write_pmpcfg_0 # write pmpcfg0, output 0x0009001F
.4byte 0x1, 0x0018900C, write_pmpcfg_1 # write pmpcfg1, output 0x0018900C
.4byte 0x1, 0x0018900C, write_pmpcfg_1 # write pmpcfg1, output 0x0018980C because NA4 reads as NAPOT with G > 0
# pmpcfg2 is zeroed out, so it doesn't need a write
.4byte 0x3, 0x1F000000, write_pmpcfg_3 # write pmpcfg3, output 0x1F000000

View file

@ -1,26 +1,26 @@
0fffffff # Test 12.3.2.2.1: writeback of value written to PMPADDR0
0ffffff0 # Test 12.3.2.2.1: writeback of value written to PMPADDR0, less trailing 4 bits for G = 4
00000000
20040000 # writeback of value written to PMPADDR1
20040000 # writeback of value written to PMPADDR1, less trailing 4 bits for G = 4
00000000
2004003f # writeback of value written to PMPADDR2
20040030 # writeback of value written to PMPADDR2, less trailing 4 bits for G = 4
00000000
20040080 # writeback of value written to PMPADDR3
20040080 # writeback of value written to PMPADDR3, less trailing 4 bits for G = 4
00000000
20040084 # writeback of value written to PMPADDR4
20040080 # writeback of value written to PMPADDR4, less trailing 4 bits for G = 4
00000000
200400c0 # writeback of value written to PMPADDR5
200400c0 # writeback of value written to PMPADDR5, less trailing 4 bits for G = 4
00000000
2004013f # writeback of value written to PMPADDR6
20040130 # writeback of value written to PMPADDR6, less trailing 4 bits for G = 4
00000000
2fffffff # writeback of value written to PMPADDR15
2ffffff0 # writeback of value written to PMPADDR15, less trailing 4 bits for G = 4
00000000
0009001f # writeback of value written to PMPCFG0
0018900c
0018980c
00000000 # writeback of value written to PMPCFG2
1f000000
0009001f # old value of PMPCFG0 after failed write to locked out region
0018900c
200400c0 # old value of PMPADDR5 after failed write to locked out region
0018980c
200400c7 # old value of PMPADDR5 after failed write to locked out region
00000000
00000005 # Test 12.3.2.2.2: read test with access fault to region with L=1, R=0
00000000

View file

@ -71,7 +71,7 @@ test_cases:
.8byte 0xF, 0x2FFFFFFF, write_pmpaddr_15 # | 15 | 0x2FFFFFFF | 1F | 0 | NAPOT | 1 | 1 | 1 | Main mem 80000000-FFFFFFFF RWX|
# write pmpcfg regs with the information in the table above. this should also write the value of these registers to the output.
.8byte 0x0, 0x0018900C0009001F, write_pmpcfg_0 # write pmpcfg0, output 0x0018900C0009001F
.8byte 0x0, 0x0018900C0009001F, write_pmpcfg_0 # write pmpcfg0, output 0x0018980C0009001F because NA4 writes as NAPOT
.8byte 0x2, 0x1F00000000000000, write_pmpcfg_2 # write pmpcfg2, output 0x1F00000000000000
# write known values to memory where W=0. This should be possible since we're in machine mode.