I found an issue that OpenC910 can fail to deliver the expected PMP access-fault exception for a PMP-denied load. I provide a minimal testcase that triggers this by programming a locked no-access NAPOT PMP entry for a target page, printing BEFORE_DENIED_ACCESS immediately before the denied load, and then observing that the simulation runs until cutoff without reaching either the trap handler or the post-load success path.
Expected behavior
For a load denied by a matching PMP entry, the hart should raise a load access fault:
The testcase installs a trap handler that prints EXPECTED_ACCESS_FAULT when mcause == 5, so the expected output is:
BEFORE_DENIED_ACCESS
EXPECTED_ACCESS_FAULT
Here BEFORE_DENIED_ACCESS means the testcase reached the instruction immediately before the PMP-denied load, and EXPECTED_ACCESS_FAULT means the expected trap handler ran.
Actual behavior
With the failing layout, OpenC910 prints only:
BEFORE_DENIED_ACCESS
[sim_cutoff] time=50001
The testcase does not print EXPECTED_ACCESS_FAULT, so the expected load access-fault handler was not reached. It also does not print UNEXPECTED_ACCESS_RETURNED, so the denied load did not visibly return through the success path.
With the same PMP setup and target address, adding one nop before the denied load changes the result to:
BEFORE_DENIED_ACCESS
EXPECTED_ACCESS_FAULT
[sim_cutoff] time=50001
This one-nop control suggests a timing or pipeline handoff problem in delivering the PMP fault, rather than a bad PMP configuration in the testcase.
Minimal testcase
The testcase sets mtvec, configures PMP0 as a locked no-access NAPOT entry, prints BEFORE_DENIED_ACCESS, and then executes one denied lw.
.include "openc910_min_common.inc"
#ifndef DELAY_NOPS
#define DELAY_NOPS 0
#endif
.equ TARGET_ADDR, 0x60080
.equ TARGET_PAGE, 0x60000
.equ PMP_CFG, 0x98 # L=1, A=NAPOT, R/W/X=0
.text
.align 6
.global main
main:
la t0, trap_handler
csrw mtvec, t0
li t0, TARGET_PAGE
srli t0, t0, 2
li t1, 0x1ff
or t0, t0, t1
csrw pmpaddr0, t0
li t0, PMP_CFG
csrw pmpcfg0, t0
fence
.rept DELAY_NOPS
nop
.endr
la a0, msg_before_denied_access
jal ra, oracle_puts
li t0, TARGET_ADDR
lw t1, 0(t0)
la a0, msg_unexpected_return
jal ra, oracle_puts
1:
j 1b
trap_handler:
csrr t0, mcause
li t1, 5
beq t0, t1, expected_trap
la a0, msg_unexpected_trap
jal ra, oracle_puts
2:
j 2b
expected_trap:
la a0, msg_expected_fault
jal ra, oracle_puts
3:
j 3b
.section .rodata
msg_before_denied_access:
.asciz "BEFORE_DENIED_ACCESS\n"
msg_expected_fault:
.asciz "EXPECTED_ACCESS_FAULT\n"
msg_unexpected_return:
.asciz "UNEXPECTED_ACCESS_RETURNED\n"
msg_unexpected_trap:
.asciz "UNEXPECTED_TRAP\n"
Output meanings:
BEFORE_DENIED_ACCESS: reached the instruction immediately before the PMP-denied load.
EXPECTED_ACCESS_FAULT: reached the expected load access-fault handler.
UNEXPECTED_ACCESS_RETURNED: the denied load returned successfully.
UNEXPECTED_TRAP: reached a trap, but not the expected load access fault.
Source Code and dependencies:
appendix.zip
I found an issue that OpenC910 can fail to deliver the expected PMP access-fault exception for a PMP-denied load. I provide a minimal testcase that triggers this by programming a locked no-access NAPOT PMP entry for a target page, printing
BEFORE_DENIED_ACCESSimmediately before the denied load, and then observing that the simulation runs until cutoff without reaching either the trap handler or the post-load success path.Expected behavior
For a load denied by a matching PMP entry, the hart should raise a load access fault:
The testcase installs a trap handler that prints
EXPECTED_ACCESS_FAULTwhenmcause == 5, so the expected output is:Here
BEFORE_DENIED_ACCESSmeans the testcase reached the instruction immediately before the PMP-denied load, andEXPECTED_ACCESS_FAULTmeans the expected trap handler ran.Actual behavior
With the failing layout, OpenC910 prints only:
The testcase does not print
EXPECTED_ACCESS_FAULT, so the expected load access-fault handler was not reached. It also does not printUNEXPECTED_ACCESS_RETURNED, so the denied load did not visibly return through the success path.With the same PMP setup and target address, adding one
nopbefore the denied load changes the result to:This one-
nopcontrol suggests a timing or pipeline handoff problem in delivering the PMP fault, rather than a bad PMP configuration in the testcase.Minimal testcase
The testcase sets
mtvec, configuresPMP0as a locked no-access NAPOT entry, printsBEFORE_DENIED_ACCESS, and then executes one deniedlw.Output meanings:
BEFORE_DENIED_ACCESS: reached the instruction immediately before the PMP-denied load.EXPECTED_ACCESS_FAULT: reached the expected load access-fault handler.UNEXPECTED_ACCESS_RETURNED: the denied load returned successfully.UNEXPECTED_TRAP: reached a trap, but not the expected load access fault.Source Code and dependencies:
appendix.zip