From 6132b930345cb20dbace782acb1d624fd077c70e Mon Sep 17 00:00:00 2001 From: ybc-alkaid Date: Fri, 23 Jan 2026 17:22:52 +0800 Subject: [PATCH 1/4] add tests for the SPMP extension --- benchmarks/Makefile | 1 + isa/rv32si/Makefrag | 2 + isa/rv32si/spmpaddr.S | 7 ++ isa/rv32si/spmpcfg.S | 7 ++ isa/rv64si/Makefrag | 2 + isa/rv64si/spmpaddr.S | 208 ++++++++++++++++++++++++++++++++++ isa/rv64si/spmpcfg.S | 253 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 480 insertions(+) create mode 100644 isa/rv32si/spmpaddr.S create mode 100644 isa/rv32si/spmpcfg.S create mode 100644 isa/rv64si/spmpaddr.S create mode 100644 isa/rv64si/spmpcfg.S diff --git a/benchmarks/Makefile b/benchmarks/Makefile index 8c65579d1..1c14821c1 100644 --- a/benchmarks/Makefile +++ b/benchmarks/Makefile @@ -39,6 +39,7 @@ base_bmarks = \ mt-matmul \ mt-memcpy \ pmp \ + spmp \ vec_bmarks = \ vec-memcpy \ diff --git a/isa/rv32si/Makefrag b/isa/rv32si/Makefrag index 1392c24fc..7a4868701 100644 --- a/isa/rv32si/Makefrag +++ b/isa/rv32si/Makefrag @@ -9,5 +9,7 @@ rv32si_sc_tests = \ scall \ sbreak \ wfi \ + spmpaddr \ + spmpcfg \ rv32si_p_tests = $(addprefix rv32si-p-, $(rv32si_sc_tests)) diff --git a/isa/rv32si/spmpaddr.S b/isa/rv32si/spmpaddr.S new file mode 100644 index 000000000..0af9edc24 --- /dev/null +++ b/isa/rv32si/spmpaddr.S @@ -0,0 +1,7 @@ +# See LICENSE for license details. + +#***************************************************************************** +# spmpaddr.S +#----------------------------------------------------------------------------- + +#include "../rv64si/spmpaddr.S" diff --git a/isa/rv32si/spmpcfg.S b/isa/rv32si/spmpcfg.S new file mode 100644 index 000000000..086317881 --- /dev/null +++ b/isa/rv32si/spmpcfg.S @@ -0,0 +1,7 @@ +# See LICENSE for license details. + +#***************************************************************************** +# spmpcfg.S +#----------------------------------------------------------------------------- + +#include "../rv64si/spmpcfg.S" diff --git a/isa/rv64si/Makefrag b/isa/rv64si/Makefrag index 604005ca7..269ed7f18 100644 --- a/isa/rv64si/Makefrag +++ b/isa/rv64si/Makefrag @@ -10,5 +10,7 @@ rv64si_sc_tests = \ scall \ wfi \ sbreak \ + spmpaddr \ + spmpcfg \ rv64si_p_tests = $(addprefix rv64si-p-, $(rv64si_sc_tests)) diff --git a/isa/rv64si/spmpaddr.S b/isa/rv64si/spmpaddr.S new file mode 100644 index 000000000..6a60b5aff --- /dev/null +++ b/isa/rv64si/spmpaddr.S @@ -0,0 +1,208 @@ +# See LICENSE for license details. + +#***************************************************************************** +# spmpaddr.S +#----------------------------------------------------------------------------- +# +# Test SPMP address register (spmpaddr*) functionality via indirect access. +# +# According to SPMP specification: +# - SPMP registers are accessed ONLY via indirect access mechanism +# - siselect = 0x100 + i selects SPMP entry i (i = 0..63) +# - sireg accesses spmpaddr[i] +# - sireg2 accesses spmpcfg[i] +# +# This test verifies: +# - Indirect access to spmpaddr registers works correctly +# - Address granularity detection +# - Read/write behavior of spmpaddr via sireg + +#include "riscv_test.h" +#include "test_macros.h" + +# SPMP indirect access CSR numbers (from Sscsrind extension) +#define CSR_SISELECT 0x150 +#define CSR_SIREG 0x151 +#define CSR_SIREG2 0x152 + +# SPMP siselect base value for SPMP entries (0x100..0x13F for entries 0..63) +#define SPMP_SELECT_BASE 0x100 + +# SPMP configuration bits layout (SXLEN-bit register per entry): +# Bits [1:0]: R, W +# Bit 2: X +# Bits [4:3]: A (address matching mode) +# Bits [6:5]: Reserved (WPRI) +# Bit 7: L (lock) +# Bit 8: U (user mode) +# Bit 9: SHARED +# Bits [SXLEN-1:10]: Reserved (WPRI) +#define SPMP_R 0x01 +#define SPMP_W 0x02 +#define SPMP_X 0x04 +#define SPMP_A_OFF 0x00 +#define SPMP_A_TOR 0x08 +#define SPMP_A_NA4 0x10 +#define SPMP_A_NAPOT 0x18 +#define SPMP_A_MASK 0x18 +#define SPMP_L 0x80 +#define SPMP_U 0x100 +#define SPMP_SHARED 0x200 + +RVTEST_RV64S +RVTEST_CODE_BEGIN + + li TESTNUM, 1 + + # Select SPMP entry 0 (siselect = 0x100) + li t0, SPMP_SELECT_BASE + csrw CSR_SISELECT, t0 + + # Verify siselect was written correctly + csrr t1, CSR_SISELECT + bne t0, t1, fail + + li TESTNUM, 2 + + # Software may determine the SPMP granularity by: + # 1. Writing zero to spmpcfg[i] (via sireg2) + # 2. Writing all ones to spmpaddr[i] (via sireg) + # 3. Reading back spmpaddr[i] + # If G is the index of the least-significant bit set, the granularity is 2^(G+2) bytes. + + # Clear spmpcfg[0] via sireg2 + csrw CSR_SIREG2, zero + + # Write all ones to spmpaddr[0] via sireg + li t0, -1 + csrw CSR_SIREG, t0 + + # Read back spmpaddr[0] via sireg + csrr t0, CSR_SIREG + + # Isolate the least significant bit set + neg t1, t0 + and a7, t0, t1 + + # a7 now contains only the lowest 1 that was set in spmpaddr[0]. + + li TESTNUM, 3 + + # If a7 is 0 then G is >=XLEN which this test does not support. + beqz a7, fail + + # Shift so the G-1 bit is set. + srl a7, a7, 1 + + # If no bits are set now then G is 0, which trivially passes. + beqz a7, pass + +#define SPMPADDR_Gm1_MASK a7 + + # Ok now we can begin the main test! + +# Set spmpaddr[0][G-1] to `value` (1 or 0) via indirect access. +.macro set_spmpaddr_bit value + li t5, SPMP_SELECT_BASE + csrw CSR_SISELECT, t5 +.if \value + csrs CSR_SIREG, SPMPADDR_Gm1_MASK +.else + csrc CSR_SIREG, SPMPADDR_Gm1_MASK +.endif +.endm + +# Switch spmpcfg[0] to OFF mode (A=00) so spmpaddr[0][G-1] reads as 0. +.macro set_mode_off + li t5, SPMP_SELECT_BASE + csrw CSR_SISELECT, t5 + csrw CSR_SIREG2, zero +.endm + +# Switch spmpcfg[0] to NAPOT mode (A=11) so spmpaddr[0][G-1] reads normally. +.macro set_mode_napot + li t5, SPMP_SELECT_BASE + csrw CSR_SISELECT, t5 + li t5, SPMP_A_NAPOT + csrw CSR_SIREG2, t5 +.endm + +# Check that spmpaddr[0][G-1] is set or unset depending on expected_value. +.macro check_spmpaddr_bit expected_value + li TESTNUM, (4 + \@) + li t5, SPMP_SELECT_BASE + csrw CSR_SISELECT, t5 + csrr t6, CSR_SIREG + and t6, t6, SPMPADDR_Gm1_MASK +.if \expected_value + beqz t6, fail +.else + bnez t6, fail +.endif +.endm + + # Test: Bit is writable in NAPOT mode + set_mode_napot + # Clear it, it should read 0 + set_spmpaddr_bit 0 + check_spmpaddr_bit 0 + # Set it, it should read 1 + set_spmpaddr_bit 1 + check_spmpaddr_bit 1 + + # Test: Bit is writable but reads as 0 in OFF mode + set_mode_off + # Should read as 0 in OFF mode + check_spmpaddr_bit 0 + # Switch back to NAPOT. The 1 should be readable again + set_mode_napot + check_spmpaddr_bit 1 + + # Test: Writing the bit while in read-as-zero mode + set_spmpaddr_bit 0 + set_mode_off + set_spmpaddr_bit 1 + set_mode_napot + check_spmpaddr_bit 1 + + # Test: Modifying a *different* bit while its underlying + # value is 1 but it reads as 0 + set_mode_off + # A csrs or csrc from the zero register does not have any side effects + li t5, SPMP_SELECT_BASE + csrw CSR_SISELECT, t5 + csrc CSR_SIREG, zero + csrs CSR_SIREG, zero + set_mode_napot + check_spmpaddr_bit 1 + + # Test: Setting other bits clears the read-as-zero bit + set_mode_off + not t0, SPMPADDR_Gm1_MASK + li t5, SPMP_SELECT_BASE + csrw CSR_SISELECT, t5 + csrs CSR_SIREG, t0 + set_mode_napot + check_spmpaddr_bit 0 + + j pass + + TEST_PASSFAIL + + .align 2 + .global stvec_handler +stvec_handler: + # Check for illegal instruction (SPMP/Sscsrind not supported) + csrr t0, scause + li t1, CAUSE_ILLEGAL_INSTRUCTION + beq t0, t1, pass + j fail + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/isa/rv64si/spmpcfg.S b/isa/rv64si/spmpcfg.S new file mode 100644 index 000000000..ebb94b81c --- /dev/null +++ b/isa/rv64si/spmpcfg.S @@ -0,0 +1,253 @@ +# See LICENSE for license details. + +#***************************************************************************** +# spmpcfg.S +#----------------------------------------------------------------------------- +# +# Test SPMP configuration register (spmpcfg*) functionality via indirect access. +# +# According to SPMP specification: +# - Each SPMP entry has an SXLEN-bit configuration register (NOT packed like PMP!) +# - spmpcfg[i] layout (SXLEN bits): +# Bit 0: R (read) +# Bit 1: W (write) +# Bit 2: X (execute) +# Bits [4:3]: A (address matching: 00=OFF, 01=TOR, 10=NA4, 11=NAPOT) +# Bits [6:5]: Reserved (WPRI) +# Bit 7: L (lock) +# Bit 8: U (user mode) +# Bit 9: SHARED +# Bits [SXLEN-1:10]: Reserved (WPRI) +# +# Access via indirect mechanism: +# - siselect = 0x100 + i selects SPMP entry i +# - sireg2 accesses spmpcfg[i] + +#include "riscv_test.h" +#include "test_macros.h" + +# SPMP indirect access CSR numbers +#define CSR_SISELECT 0x150 +#define CSR_SIREG 0x151 +#define CSR_SIREG2 0x152 + +# SPMP siselect base value +#define SPMP_SELECT_BASE 0x100 + +# SPMP configuration bits (each spmpcfg is SXLEN bits, one per entry) +#define SPMP_R 0x01 +#define SPMP_W 0x02 +#define SPMP_X 0x04 +#define SPMP_A_OFF 0x00 +#define SPMP_A_TOR 0x08 +#define SPMP_A_NA4 0x10 +#define SPMP_A_NAPOT 0x18 +#define SPMP_A_MASK 0x18 +#define SPMP_L 0x80 +#define SPMP_U 0x100 +#define SPMP_SHARED 0x200 + +RVTEST_RV64S +RVTEST_CODE_BEGIN + + # Test 1: Basic read/write of spmpcfg[0] via indirect access + li TESTNUM, 1 + + # Select SPMP entry 0 + li t0, SPMP_SELECT_BASE + csrw CSR_SISELECT, t0 + + # Clear spmpcfg[0] + csrw CSR_SIREG2, zero + csrr t0, CSR_SIREG2 + # Should read 0 after writing 0 + bnez t0, fail + + # Test 2: Write TOR mode with R permission to spmpcfg[0] + li TESTNUM, 2 + + li t0, SPMP_SELECT_BASE + csrw CSR_SISELECT, t0 + + # TOR mode (A=01) + R permission + li t0, (SPMP_A_TOR | SPMP_R) # 0x09 + csrw CSR_SIREG2, t0 + csrr t1, CSR_SIREG2 + andi t1, t1, 0x1F # Mask to check R, W, X, A fields + li t2, 0x09 + bne t1, t2, fail + + # Test 3: Write NAPOT mode with R+W permissions + li TESTNUM, 3 + + li t0, SPMP_SELECT_BASE + csrw CSR_SISELECT, t0 + + # NAPOT mode (A=11) + R + W + li t0, (SPMP_A_NAPOT | SPMP_R | SPMP_W) # 0x1B + csrw CSR_SIREG2, t0 + csrr t1, CSR_SIREG2 + andi t1, t1, 0x1F + li t2, 0x1B + bne t1, t2, fail + + # Test 4: Write NA4 mode with R+W+X permissions + li TESTNUM, 4 + + li t0, SPMP_SELECT_BASE + csrw CSR_SISELECT, t0 + + # NA4 mode (A=10) + R + W + X + li t0, (SPMP_A_NA4 | SPMP_R | SPMP_W | SPMP_X) # 0x17 + csrw CSR_SIREG2, t0 + csrr t1, CSR_SIREG2 + andi t1, t1, 0x1F + li t2, 0x17 + bne t1, t2, fail + + # Test 5: Test U (user mode) bit + li TESTNUM, 5 + + li t0, SPMP_SELECT_BASE + csrw CSR_SISELECT, t0 + + # NAPOT + R + U + li t0, (SPMP_A_NAPOT | SPMP_R | SPMP_U) # 0x119 + csrw CSR_SIREG2, t0 + csrr t1, CSR_SIREG2 + li t2, (SPMP_A_NAPOT | SPMP_R | SPMP_U) + # Check key bits + andi t3, t1, 0x1FF + andi t4, t2, 0x1FF + bne t3, t4, fail + + # Test 6: Test SHARED bit + li TESTNUM, 6 + + li t0, SPMP_SELECT_BASE + csrw CSR_SISELECT, t0 + + # NAPOT + R + SHARED + U (shared region) + li t0, (SPMP_A_NAPOT | SPMP_R | SPMP_SHARED | SPMP_U) # 0x319 + csrw CSR_SIREG2, t0 + csrr t1, CSR_SIREG2 + li t2, (SPMP_A_NAPOT | SPMP_R | SPMP_SHARED | SPMP_U) + andi t3, t1, 0x3FF + andi t4, t2, 0x3FF + bne t3, t4, fail + + # Test 7: Test multiple SPMP entries (entry 0 and entry 1) + li TESTNUM, 7 + + # Configure entry 0 + li t0, SPMP_SELECT_BASE + csrw CSR_SISELECT, t0 + li t0, (SPMP_A_TOR | SPMP_R) + csrw CSR_SIREG2, t0 + + # Configure entry 1 + li t0, (SPMP_SELECT_BASE + 1) + csrw CSR_SISELECT, t0 + li t0, (SPMP_A_NAPOT | SPMP_R | SPMP_W | SPMP_X) + csrw CSR_SIREG2, t0 + + # Verify entry 0 + li t0, SPMP_SELECT_BASE + csrw CSR_SISELECT, t0 + csrr t1, CSR_SIREG2 + andi t1, t1, 0x1F + li t2, (SPMP_A_TOR | SPMP_R) + bne t1, t2, fail + + # Verify entry 1 + li t0, (SPMP_SELECT_BASE + 1) + csrw CSR_SISELECT, t0 + csrr t1, CSR_SIREG2 + andi t1, t1, 0x1F + li t2, (SPMP_A_NAPOT | SPMP_R | SPMP_W | SPMP_X) + bne t1, t2, fail + + # Test 8: Clear configuration + li TESTNUM, 8 + + li t0, SPMP_SELECT_BASE + csrw CSR_SISELECT, t0 + csrw CSR_SIREG2, zero + csrr t1, CSR_SIREG2 + bnez t1, fail + + # Test 9: Test CSR set bits on spmpcfg[0] + li TESTNUM, 9 + + li t0, SPMP_SELECT_BASE + csrw CSR_SISELECT, t0 + csrw CSR_SIREG2, zero + + # Set R bit using csrs + li t0, SPMP_R + csrs CSR_SIREG2, t0 + csrr t1, CSR_SIREG2 + andi t1, t1, SPMP_R + beqz t1, fail + + # Test 10: Test CSR clear bits on spmpcfg[0] + li TESTNUM, 10 + + li t0, SPMP_SELECT_BASE + csrw CSR_SISELECT, t0 + + # Write all permission bits + li t0, (SPMP_A_NAPOT | SPMP_R | SPMP_W | SPMP_X) + csrw CSR_SIREG2, t0 + + # Clear R bit using csrc + li t1, SPMP_R + csrc CSR_SIREG2, t1 + csrr t2, CSR_SIREG2 + andi t2, t2, SPMP_R + bnez t2, fail + + # Test 11: Verify OFF mode (A=00) disables entry + li TESTNUM, 11 + + li t0, SPMP_SELECT_BASE + csrw CSR_SISELECT, t0 + + # Set OFF mode with some permissions (should be null region) + li t0, (SPMP_A_OFF | SPMP_R | SPMP_W) # 0x03 + csrw CSR_SIREG2, t0 + csrr t1, CSR_SIREG2 + # A field should be 0 (OFF) + andi t2, t1, SPMP_A_MASK + bnez t2, fail + + # Test 12: Out-of-bounds siselect should return zero on read + li TESTNUM, 12 + + # Try to access entry 64 (out of bounds if only 64 entries) + li t0, (SPMP_SELECT_BASE + 64) + csrw CSR_SISELECT, t0 + csrr t1, CSR_SIREG2 + # Should return 0 for out-of-bounds + + j pass + + TEST_PASSFAIL + + .align 2 + .global stvec_handler +stvec_handler: + # Check for illegal instruction (SPMP/Sscsrind not supported) + csrr t0, scause + li t1, CAUSE_ILLEGAL_INSTRUCTION + beq t0, t1, pass + j fail + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END From 5c61ea164fb3b2c33446a4b358efec0dd61e043a Mon Sep 17 00:00:00 2001 From: Bicheng Yang Date: Sat, 24 Jan 2026 15:41:33 +0800 Subject: [PATCH 2/4] the SPMP unit tests are not included in the benchmarks Signed-off-by: Bicheng Yang --- benchmarks/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/benchmarks/Makefile b/benchmarks/Makefile index 1c14821c1..8c65579d1 100644 --- a/benchmarks/Makefile +++ b/benchmarks/Makefile @@ -39,7 +39,6 @@ base_bmarks = \ mt-matmul \ mt-memcpy \ pmp \ - spmp \ vec_bmarks = \ vec-memcpy \ From 179b65e468accb578705cb8cb3232c2af742b412 Mon Sep 17 00:00:00 2001 From: ybc-alkaid Date: Thu, 29 Jan 2026 16:15:32 +0800 Subject: [PATCH 3/4] update Makefile and spmpcfg test --- isa/Makefile | 7 +++++++ isa/rv64si/spmpcfg.S | 16 ++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/isa/Makefile b/isa/Makefile index 9eef64dd0..3f30a8866 100644 --- a/isa/Makefile +++ b/isa/Makefile @@ -75,6 +75,13 @@ vpath %.S $(src_dir) %.out32: % $(RISCV_SIM) --isa=rv32gc_ziccid_zfh_zicboz_svnapot_zicntr_zba_zbb_zbc_zbs_zicclsm $< 2> $@ +# SPMP tests need spmp extension +rv64si-p-spmpaddr.out rv64si-p-spmpcfg.out: %.out: % + $(RISCV_SIM) --isa=rv64gch_ziccid_zfh_zicboz_svnapot_zicntr_zba_zbb_zbc_zbs_zicclsm_sspmp $< 2> $@ + +rv32si-p-spmpaddr.out32 rv32si-p-spmpcfg.out32: %.out32: % + $(RISCV_SIM) --isa=rv32gc_ziccid_zfh_zicboz_svnapot_zicntr_zba_zbb_zbc_zbs_zicclsm_sspmp $< 2> $@ + define compile_template $$($(1)_p_tests): $(1)-p-%: $(1)/%.S diff --git a/isa/rv64si/spmpcfg.S b/isa/rv64si/spmpcfg.S index ebb94b81c..4b5c828d1 100644 --- a/isa/rv64si/spmpcfg.S +++ b/isa/rv64si/spmpcfg.S @@ -221,14 +221,14 @@ RVTEST_CODE_BEGIN andi t2, t1, SPMP_A_MASK bnez t2, fail - # Test 12: Out-of-bounds siselect should return zero on read - li TESTNUM, 12 - - # Try to access entry 64 (out of bounds if only 64 entries) - li t0, (SPMP_SELECT_BASE + 64) - csrw CSR_SISELECT, t0 - csrr t1, CSR_SIREG2 - # Should return 0 for out-of-bounds + # Test 12: Skipped - Out-of-bounds siselect causes illegal instruction exception + # which is handled in M-mode trap_vector, not S-mode stvec_handler. + # This behavior is correct per Sscsrind spec, but requires M-mode exception + # delegation to test properly. + # li TESTNUM, 12 + # li t0, (SPMP_SELECT_BASE + 64) + # csrw CSR_SISELECT, t0 + # csrr t1, CSR_SIREG2 j pass From e72221adcd773fcf7d4dd822e0f66ac27321ae3d Mon Sep 17 00:00:00 2001 From: ybc-alkaid Date: Fri, 30 Jan 2026 10:52:31 +0800 Subject: [PATCH 4/4] enable spmp extension by default --- isa/Makefile | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/isa/Makefile b/isa/Makefile index 3f30a8866..b4e220e2a 100644 --- a/isa/Makefile +++ b/isa/Makefile @@ -70,16 +70,9 @@ vpath %.S $(src_dir) $(RISCV_OBJDUMP) $< > $@ %.out: % - $(RISCV_SIM) --isa=rv64gch_ziccid_zfh_zicboz_svnapot_zicntr_zba_zbb_zbc_zbs_zicclsm $< 2> $@ - -%.out32: % - $(RISCV_SIM) --isa=rv32gc_ziccid_zfh_zicboz_svnapot_zicntr_zba_zbb_zbc_zbs_zicclsm $< 2> $@ - -# SPMP tests need spmp extension -rv64si-p-spmpaddr.out rv64si-p-spmpcfg.out: %.out: % $(RISCV_SIM) --isa=rv64gch_ziccid_zfh_zicboz_svnapot_zicntr_zba_zbb_zbc_zbs_zicclsm_sspmp $< 2> $@ -rv32si-p-spmpaddr.out32 rv32si-p-spmpcfg.out32: %.out32: % +%.out32: % $(RISCV_SIM) --isa=rv32gc_ziccid_zfh_zicboz_svnapot_zicntr_zba_zbb_zbc_zbs_zicclsm_sspmp $< 2> $@ define compile_template