Skip to content

Commit 7785754

Browse files
authored
Add test for pmpaddr[G-1] edge cases (#609)
This adds a test for pmpaddr[G-1] edge cases. This bit is unusual because it has read legalisation that depends on another CSR. If the pmpcfg mode is OFF/TOR then it reads as 0. Otherwise it reads as its underlying value. Irrespective of pmpcfg it is always writable so you can always write it even when it reads as zero. Another edge case is when CSRS/C are used to modify a *different* bit and the underlying value of pmpaddr[G-1] is 1 but it reads as 0. The spec says "All CSR instructions atomically read-modify-write a single CSR" so even if these instructions don't directly modify pmpaddr[G-1] they do read it and then write it back, which will change its underlying value since it reads as 0.
1 parent 162d3e7 commit 7785754

File tree

4 files changed

+164
-0
lines changed

4 files changed

+164
-0
lines changed

isa/rv32mi/Makefrag

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ rv32mi_sc_tests = \
1818
sw-misaligned \
1919
zicntr \
2020
instret_overflow \
21+
pmpaddr \
2122

2223
rv32mi_p_tests = $(addprefix rv32mi-p-, $(rv32mi_sc_tests))

isa/rv32mi/pmpaddr.S

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# See LICENSE for license details.
2+
3+
#include "riscv_test.h"
4+
#undef RVTEST_RV64M
5+
#define RVTEST_RV64M RVTEST_RV32M
6+
#define __MACHINE_MODE
7+
8+
#include "../rv64mi/pmpaddr.S"

isa/rv64mi/Makefrag

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ rv64mi_sc_tests = \
1919
sd-misaligned \
2020
zicntr \
2121
instret_overflow \
22+
pmpaddr \
2223

2324
rv64mi_p_tests = $(addprefix rv64mi-p-, $(rv64mi_sc_tests))

isa/rv64mi/pmpaddr.S

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# See LICENSE for license details.
2+
3+
#*****************************************************************************
4+
# pmpaddr.S
5+
#-----------------------------------------------------------------------------
6+
#
7+
# Test edge cases around the pmpaddr[G-1] bit which sometimes reads as zero
8+
# but is always writable and retains its state. Also test CSRC and CSRS
9+
# modifications to other bits result in a correct read-modify-write.
10+
#
11+
# This test auto-detects G but assumes PMP is available. It supports a
12+
# maximum G of XLEN-1. There's no minimum but if G is 0 then the G-1 bit
13+
# does not exist and this test trivially passes.
14+
15+
#include "riscv_test.h"
16+
#include "test_macros.h"
17+
18+
RVTEST_RV64M
19+
RVTEST_CODE_BEGIN
20+
21+
li TESTNUM, 1
22+
23+
# Software may determine the PMP granularity by writing zero to pmpcfg0,
24+
# then writing all ones to pmpaddr0, then reading back pmpaddr0.
25+
# If G is the index of the least-significant bit set, the PMP granularity
26+
# is 2^(G+2) bytes.
27+
csrw pmpcfg0, zero
28+
li t0, -1
29+
csrw pmpaddr0, t0
30+
csrr t0, pmpaddr0
31+
32+
# Isolate the least significant bit.
33+
34+
neg t1, t0
35+
and a7, t0, t1
36+
37+
# a7 now contains only the lowest 1 that was set in pmpaddr0.
38+
39+
# If a7 is 0 then G is >=XLEN which this test does not support.
40+
beqz a7, fail
41+
# Shift so the G-1 bit is set.
42+
srl a7, a7, 1
43+
# If no bits are set now then G is 0, which trivially passes.
44+
beqz a7, pass
45+
46+
#define PMPADDR_Gm1_MASK a7
47+
#define PMPCFG_A_MASK (0x3 << 3)
48+
# Ok now we can begin the main test!
49+
50+
# Set pmpaddr0[G-1] to `value` (1 or 0).
51+
.macro set_pmpaddr_bit value
52+
.if \value
53+
csrs pmpaddr0, PMPADDR_Gm1_MASK
54+
.else
55+
csrc pmpaddr0, PMPADDR_Gm1_MASK
56+
.endif
57+
.endm
58+
59+
# Switch pmpcfg0 to OFF mode so pmpaddr0[G-1] reads as 0.
60+
.macro set_mode_off
61+
csrc pmpcfg0, PMPCFG_A_MASK
62+
.endm
63+
64+
# Switch pmpcfg0 to NAPOT mode so pmpaddr0[G-1] reads normally.
65+
.macro set_mode_napot
66+
csrs pmpcfg0, PMPCFG_A_MASK
67+
.endm
68+
69+
# Check that pmpaddr9[G] is set or unset depending on expected_value.
70+
.macro check_pmpaddr_bit expected_value
71+
# Note when gas 2.43 is common we can use \+ instead of \@ which
72+
# gives more sensible numbers. \@ still works but it gives 4, 6,
73+
# 8, 10, 15... instead of 0, 1, 2, 3.
74+
li TESTNUM, (2 + \@)
75+
csrr t6, pmpaddr0
76+
and t6, t6, PMPADDR_Gm1_MASK
77+
.if \expected_value
78+
beqz t6, fail
79+
.else
80+
bnez t6, fail
81+
.endif
82+
.endm
83+
84+
.macro check_pmpaddr_bit_clear
85+
csrr t6, pmpaddr0
86+
and t6, t6, PMPADDR_Gm1_MASK
87+
bnez t6, fail
88+
.endm
89+
90+
# Initialise pmpaddr and pmpcfg.
91+
92+
# M bit is writable in NAPOT mode.
93+
set_mode_napot
94+
# Clear it, it should read 0.
95+
set_pmpaddr_bit 0
96+
check_pmpaddr_bit 0
97+
# Set it, it shouldn't read 0.
98+
set_pmpaddr_bit 1
99+
check_pmpaddr_bit 1
100+
# M bit is writable but reads as 0 in OFF mode.
101+
set_mode_off
102+
# Should read as 0.
103+
check_pmpaddr_bit 0
104+
# Switch back to NAPOT. The 1 should be readable again.
105+
set_mode_napot
106+
check_pmpaddr_bit 1
107+
108+
# Test writing the bit while it is read-as-zero.
109+
set_pmpaddr_bit 0
110+
set_mode_off
111+
set_pmpaddr_bit 1
112+
set_mode_napot
113+
check_pmpaddr_bit 1
114+
115+
# Test modifying a *different* bit while its underlying
116+
# value is 1 but it reads as 0. Since csrs and csrc are
117+
# read-modify-write they reads-as value will be written
118+
# to the underlying value.
119+
set_mode_off
120+
# A csrs or csrc from the zero register does not have
121+
# any side effects.
122+
csrc pmpaddr0, zero
123+
csrs pmpaddr0, zero
124+
set_mode_napot
125+
check_pmpaddr_bit 1
126+
127+
set_mode_off
128+
# Set other bits. This should result in M being cleared
129+
# since it currently reads as 0.
130+
not t0, PMPADDR_Gm1_MASK
131+
csrs pmpaddr0, t0
132+
set_mode_napot
133+
check_pmpaddr_bit 0
134+
135+
j pass
136+
137+
TEST_PASSFAIL
138+
139+
.align 2
140+
.global mtvec_handler
141+
mtvec_handler:
142+
# We aren't expecting any exceptions unless PMP is not supported
143+
# in which case this test is also not supported. There's no
144+
# way to probe for PMP support so we can't just pass in this case.
145+
j fail
146+
147+
RVTEST_CODE_END
148+
149+
.data
150+
RVTEST_DATA_BEGIN
151+
152+
TEST_DATA
153+
154+
RVTEST_DATA_END

0 commit comments

Comments
 (0)