diff --git a/test/sim/rvcpp/include/rv_csr.h b/test/sim/rvcpp/include/rv_csr.h
index c857d42..5e37820 100644
--- a/test/sim/rvcpp/include/rv_csr.h
+++ b/test/sim/rvcpp/include/rv_csr.h
@@ -127,5 +127,8 @@ public:
 		return mcause;
 	}
 
+	// Return region, or -1 for no match
+	int get_pmp_match(ux_t addr);
+
 	uint get_pmp_xwr(ux_t addr);
 };
diff --git a/test/sim/rvcpp/rv_core.cpp b/test/sim/rvcpp/rv_core.cpp
index 804abc0..bf89403 100644
--- a/test/sim/rvcpp/rv_core.cpp
+++ b/test/sim/rvcpp/rv_core.cpp
@@ -143,10 +143,15 @@ void RVCore::step(bool trace) {
 	uint funct3 = instr >> 12 & 0x7;
 	uint funct7 = instr >> 25 & 0x7f;
 
+	bool pmp_straddle = false;
+	if (fetch0 && (*fetch0 & 0x3) == 0x3) {
+		pmp_straddle = csr.get_pmp_match(pc) != csr.get_pmp_match(pc + 2);
+	}
+
 	std::optional<ux_t> irq_target_pc = csr.trap_check_enter_irq(pc);
 	if (irq_target_pc) {
 		// Replace current instruction with IRQ entry
-	} else if (!fetch0 || ((*fetch0 & 0x3) == 0x3 && !fetch1)) {
+	} else if (!fetch0 || ((*fetch0 & 0x3) == 0x3 && (!fetch1 || pmp_straddle))) {
 		exception_cause = XCAUSE_INSTR_FAULT;
 	} else if ((instr & 0x3) == 0x3) {
 		// 32-bit instruction
diff --git a/test/sim/rvcpp/rv_csr.cpp b/test/sim/rvcpp/rv_csr.cpp
index 926589b..5ad8d74 100644
--- a/test/sim/rvcpp/rv_csr.cpp
+++ b/test/sim/rvcpp/rv_csr.cpp
@@ -2,6 +2,7 @@
 #include "encoding/rv_csr.h"
 
 #include <cassert>
+#include <cstdio>
 
 // Inclusive msb:lsb style, like Verilog (and like the ISA manual)
 #define BITS_UPTO(msb) (~((-1u << (msb)) << 1))
@@ -251,27 +252,38 @@ ux_t RVCSR::trap_mret() {
 	return mepc;
 }
 
-uint RVCSR::get_pmp_xwr(ux_t addr) {
-	bool match = false;
-	uint matching_xwr = 0;
-	uint matching_l = 0;
-	bool trace_pmp = get_true_priv() == PRV_U && true;
+int RVCSR::get_pmp_match(ux_t addr) {
 	for (int i = 0; i < PMP_REGIONS; ++i) {
 		if (pmpcfg_a(i) == 0u) {
 			continue;
 		}
-		uint32_t mask = 0xffffffffu;
-		if (pmpcfg_a(i) == 2) {
-			mask = 0xfffffffeu << __builtin_ctz(~pmpaddr[i]);
+		ux_t mask = 0xffffffffu;
+		if (pmpcfg_a(i) == 3) {
+			if (pmpaddr[i] == 0xffffffffu) {
+				mask = 0u;
+			} else {
+				mask = 0xfffffffeu << __builtin_ctz(~pmpaddr[i]);
+			}
 		}
-		match = ((addr >> 2) & mask) == (pmpaddr[i] & mask);
+		bool match = ((addr >> 2) & mask) == (pmpaddr[i] & mask);
 		if (match) {
-			matching_xwr = pmpcfg_xwr(i);
-			matching_l = pmpcfg_l(i);
 			// Lowest-numbered match determines success/failure:
-			break;
+			return i;
 		}
 	}
+	return -1;
+}
+
+uint RVCSR::get_pmp_xwr(ux_t addr) {
+	int region = get_pmp_match(addr);
+	bool match = false;
+	uint matching_xwr = 0;
+	uint matching_l = 0;
+	if (region >= 0) {
+		match = true;
+		matching_xwr = pmpcfg_xwr(region);
+		matching_l = pmpcfg_l(region);
+	}
 	if (match) {
 		// TODO MPRV
 		if (get_true_priv() == PRV_M && !matching_l) {