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 // when compressed instructions are supported, there can't be misaligned instructions
localparam MEDELEG_MASK = P.ZCA_SUPPORTED ? 16'hB3FE : 16'hB3FF; localparam MEDELEG_MASK = P.ZCA_SUPPORTED ? 16'hB3FE : 16'hB3FF;
localparam MIDELEG_MASK = 12'h222; // we choose to not make machine interrupts delegable 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 // There are PMP_ENTRIES = 0, 16, or 64 PMPADDR registers, each of which has its own flop
genvar i; 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] 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 [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
// 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
@ -133,7 +135,8 @@ module csrm import cvw::*; #(parameter cvw_t P) (
end end
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 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]); flopenr #(8) PMPCFGreg(clk, reset, WritePMPCFGM[i], CSRPMPLegalizedWriteValM[i], PMPCFG_ARRAY_REGW[i]);
end end
end end
@ -217,13 +220,23 @@ module csrm import cvw::*; #(parameter cvw_t P) (
// 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) // reading a PMP entry if ($unsigned(CSRAdrM) >= PMPADDR0 & $unsigned(CSRAdrM) < PMPADDR0 + P.PMP_ENTRIES) begin // reading a PMP entry
CSRMReadValM = {{(P.XLEN-(P.PA_BITS-2)){1'b0}}, PMPADDR_ARRAY_REGW[CSRAdrM - PMPADDR0]}; pmpaddr = {{(P.XLEN-(P.PA_BITS-2)){1'b0}}, PMPADDR_ARRAY_REGW[CSRAdrM - PMPADDR0]}; // raw value in PMP registers
else if ($unsigned(CSRAdrM) >= PMPCFG0 & $unsigned(CSRAdrM) < PMPCFG0 + P.PMP_ENTRIES/4 & (P.XLEN==32 | CSRAdrM[0] == 0)) begin 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 // 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

View file

@ -21,10 +21,12 @@
module riscvassertions import cvw::*; #(parameter cvw_t P); module riscvassertions import cvw::*; #(parameter cvw_t P);
initial begin 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_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 > 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)-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.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)-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 >= $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.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.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)"); 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 20040000
2004003f 20040030
20040080
20040080 20040080
20040084
200400c0 200400c0
2004013f 20040130
2fffffff 2ffffff0
0009001f 0009001f
0018900c 0018980c
1f000000 1f000000
0018900c 0018980c
200400c0 200400c7
00000005 00000005
00000bad 00000bad
00600dbb 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. # 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 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 # pmpcfg2 is zeroed out, so it doesn't need a write
.4byte 0x3, 0x1F000000, write_pmpcfg_3 # write pmpcfg3, output 0x1F000000 .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 00000000
20040000 # writeback of value written to PMPADDR1 20040000 # writeback of value written to PMPADDR1, less trailing 4 bits for G = 4
00000000 00000000
2004003f # writeback of value written to PMPADDR2 20040030 # writeback of value written to PMPADDR2, less trailing 4 bits for G = 4
00000000 00000000
20040080 # writeback of value written to PMPADDR3 20040080 # writeback of value written to PMPADDR3, less trailing 4 bits for G = 4
00000000 00000000
20040084 # writeback of value written to PMPADDR4 20040080 # writeback of value written to PMPADDR4, less trailing 4 bits for G = 4
00000000 00000000
200400c0 # writeback of value written to PMPADDR5 200400c0 # writeback of value written to PMPADDR5, less trailing 4 bits for G = 4
00000000 00000000
2004013f # writeback of value written to PMPADDR6 20040130 # writeback of value written to PMPADDR6, less trailing 4 bits for G = 4
00000000 00000000
2fffffff # writeback of value written to PMPADDR15 2ffffff0 # writeback of value written to PMPADDR15, less trailing 4 bits for G = 4
00000000 00000000
0009001f # writeback of value written to PMPCFG0 0009001f # writeback of value written to PMPCFG0
0018900c 0018980c
00000000 # writeback of value written to PMPCFG2 00000000 # writeback of value written to PMPCFG2
1f000000 1f000000
0009001f # old value of PMPCFG0 after failed write to locked out region 0009001f # old value of PMPCFG0 after failed write to locked out region
0018900c 0018980c
200400c0 # old value of PMPADDR5 after failed write to locked out region 200400c7 # old value of PMPADDR5 after failed write to locked out region
00000000 00000000
00000005 # Test 12.3.2.2.2: read test with access fault to region with L=1, R=0 00000005 # Test 12.3.2.2.2: read test with access fault to region with L=1, R=0
00000000 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| .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. # 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 .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. # write known values to memory where W=0. This should be possible since we're in machine mode.