From 94b5633d6789d792a092b5f356aa7665fc04b229 Mon Sep 17 00:00:00 2001 From: David Harris Date: Sat, 3 Oct 2026 13:47:47 -0700 Subject: [PATCH 1/9] Cover the last rv64gc code coverage rows with three directed tests fetchperiph fetches from the CLINT, PLIC, PWM, SPI and GPIO, which match their decoders in the instruction PMA checker but take an instruction access fault. The UART row is excluded instead: it only accepts bytes, and the IFU always checks a word. icachebusbusy misses on the second half of a spilled instruction in the cycle after a CLINT load starts on the bus, so the I$ asks for the bus while the APB bridge holds HREADY low. cbofaulthit fills all four ways of a D$ set, then runs cbo.inval in supervisor mode on each line under a PMP region with no read or write permission, so the hit's valid-bit clear and the squash of the faulting access (SelfFaultM, part of LSUFlushW) coincide. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: David Harris --- sim/questa/coverage-exclusions-rv64gc.do | 6 ++- tests/coverage/cbofaulthit.S | 61 ++++++++++++++++++++++++ tests/coverage/fetchperiph.S | 39 +++++++++++++++ tests/coverage/icachebusbusy.S | 37 ++++++++++++++ 4 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 tests/coverage/cbofaulthit.S create mode 100644 tests/coverage/fetchperiph.S create mode 100644 tests/coverage/icachebusbusy.S diff --git a/sim/questa/coverage-exclusions-rv64gc.do b/sim/questa/coverage-exclusions-rv64gc.do index 9978765c24..6a0920197f 100644 --- a/sim/questa/coverage-exclusions-rv64gc.do +++ b/sim/questa/coverage-exclusions-rv64gc.do @@ -295,10 +295,12 @@ set line [GetLineNum ${SRC}/mmu/adrdec.sv "exclusion-tag: adrdecSel"] # The instruction side drives the MMU with ReadAccessM and WriteAccessM tied low and ExecuteAccessF tied high (see # the mmu instantiation in ifu.sv), so AccessRW is 0 and AccessRX/AccessRWXC are 1 there, and the fetch size is fixed. # Peripherals qualified by AccessRW never select: every row except AccessValid_0 needs AccessValid = 1. AccessValid_0 -# (row 5) is reachable by fetching from the peripheral's address range, so it is counted. -foreach dec {clintdec gpiodec uartdec plicdec spidec pwmdec} { +# (row 5) is reachable by fetching from the peripheral's address range (tests/coverage/fetchperiph.S), so it is +# counted, except for the UART: it only accepts byte accesses, so SizeValid is 0 for the word-sized fetch. +foreach dec {clintdec gpiodec plicdec spidec pwmdec} { coverage exclude -scope /core/ifu/immu/immu/pmachecker/adrdecs/$dec -fecexprrow $line 1-4 6-8 } +coverage exclude -scope /core/ifu/immu/immu/pmachecker/adrdecs/uartdec -fecexprrow $line 1-8 # DTIM and SDC are not supported and qualified by AccessRW: no row can be reached. IROM and external memory are not # supported: Supported_0 (row 3) is hit and every other row needs Supported = 1. coverage exclude -scope /core/ifu/immu/immu/pmachecker/adrdecs/dtimdec -fecexprrow $line 1-8 diff --git a/tests/coverage/cbofaulthit.S b/tests/coverage/cbofaulthit.S new file mode 100644 index 0000000000..c08906dacb --- /dev/null +++ b/tests/coverage/cbofaulthit.S @@ -0,0 +1,61 @@ +/////////////////////////////////////////// +// cbofaulthit.S +// +// Written: David_Harris@hmc.edu 3 October 2026 +// +// Purpose: Test coverage for a cbo.inval that hits in the data cache but takes an access fault. +// The hit asks the way to clear its valid bit, and in the same cycle the access's own +// fault squashes it (lsu.sv SelfFaultM, part of LSUFlushW), which must cancel the clear. +// Four lines that share a set are loaded first, so the faulting cbo.invals hit in each +// of the four ways. +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +/////////////////////////////////////////// + +#include "WALLY-init-lib.h" + +main: + li t0, 0xF0 # menvcfg.CBZE, CBCFE and CBIE, so supervisor mode may use cbo.inval + csrw menvcfg, t0 + + // Load four lines 4 KiB apart: they share a set, so they fill all four ways + la s0, lines + li s1, 4096 + mv t0, s0 + li t1, 4 +fill: + ld t2, 0(t0) + add t0, t0, s1 + addi t1, t1, -1 + bnez t1, fill + + // PMP region 0 denies all access to the four lines (16 KiB NAPOT); region 2 allows everything else + srli t0, s0, 2 + ori t0, t0, 0x7FF + csrw pmpaddr0, t0 + csrw pmpaddr1, zero + li t0, 0xFFFFFFFF + csrw pmpaddr2, t0 + li t0, 0x0F0018 # region 0 NAPOT no access, region 1 off, region 2 TOR RWX + csrw pmpcfg0, t0 + + li a0, 1 # supervisor mode, so the PMP applies + ecall + + // a cbo.inval needs read or write permission, so each takes a store/AMO access fault + mv t0, s0 + li t1, 4 +inval: + cbo.inval (t0) + add t0, t0, s1 + addi t1, t1, -1 + bnez t1, inval + + li a0, 3 # back to machine mode + ecall + j done + +.data +.align 14 # 16 KiB, for the NAPOT region +lines: + .fill 2048, 8, 0 diff --git a/tests/coverage/fetchperiph.S b/tests/coverage/fetchperiph.S new file mode 100644 index 0000000000..f535085e0b --- /dev/null +++ b/tests/coverage/fetchperiph.S @@ -0,0 +1,39 @@ +/////////////////////////////////////////// +// fetchperiph.S +// +// Written: David_Harris@hmc.edu 3 October 2026 +// +// Purpose: Test coverage for instruction fetches from the peripherals. The peripherals only +// accept reads and writes, so a fetch from one matches its address decoder in the +// instruction PMA checker but is not a valid access, and takes an instruction access +// fault. The UART is not fetched from: it only accepts byte accesses, and the IFU +// always checks a word. +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +/////////////////////////////////////////// + +#include "WALLY-init-lib.h" + +main: + la t0, fetchfault # the fault returns to ra, after the jump that faulted + csrw mtvec, t0 + + li t1, 0x02000000 # CLINT + jalr ra, t1 + li t1, 0x0C000000 # PLIC + jalr ra, t1 + li t1, 0x10020000 # PWM + jalr ra, t1 + li t1, 0x10040000 # SPI + jalr ra, t1 + li t1, 0x10060000 # GPIO + jalr ra, t1 + + la t0, trap_handler + csrw mtvec, t0 + j done + +.align 2 +fetchfault: + csrw mepc, ra + mret diff --git a/tests/coverage/icachebusbusy.S b/tests/coverage/icachebusbusy.S new file mode 100644 index 0000000000..4f244c11e7 --- /dev/null +++ b/tests/coverage/icachebusbusy.S @@ -0,0 +1,37 @@ +/////////////////////////////////////////// +// icachebusbusy.S +// +// Written: David_Harris@hmc.edu 3 October 2026 +// +// Purpose: Test coverage for an instruction cache miss that asks for the bus while the LSU's +// transfer still holds it. The IFU and LSU normally miss in the same cycle, when the +// pipeline advances, and the EBU arbitrates between them. A spill is the exception: +// the IFU fetches the second half of a spilled instruction a cycle after the first, +// even while the pipeline is stalled. Here the first half hits in the instruction +// cache in the cycle a load from the CLINT starts on the bus, and the second half +// misses in the next cycle, while the APB bridge holds HREADY low for the load. +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +/////////////////////////////////////////// + +#include "WALLY-init-lib.h" +.option norvc + +main: + li t0, 0x0200BFF8 # CLINT mtime + j linea + +.align 6 # start of an instruction cache line +linea: + .rept 12 + nop + .endr + .option rvc + c.nop # puts the lw at 0x32, so the spilled addi is at 0x3E + .option norvc + lw t1, 0(t0) # in the Memory stage when the addi is in Fetch + nop + nop + addi t2, t2, 1 # spills into the next line, which has not been fetched yet + + j done From 9333cb1afa7ad0a198b394ca340ef3b89ea1aea9 Mon Sep 17 00:00:00 2001 From: David Harris Date: Fri, 9 Oct 2026 16:36:56 -0700 Subject: [PATCH 2/9] fetchperiph, icachebusbusy, cbofaulthit: check results fetchperiph checks that each peripheral fetch takes an instruction access fault with mtval at that peripheral, five in all. icachebusbusy checks the spilled addi's result and that the CLINT load returned the running mtime. cbofaulthit stores a value into each of the four lines before the faulting cbo.invals, checks each fault's cause, mepc and mtval, and reads the four values back, which fails if the fault did not cancel the valid-bit clear. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: David Harris --- tests/coverage/cbofaulthit.S | 96 ++++++++++++++++++++++++++++++++-- tests/coverage/fetchperiph.S | 41 +++++++++++---- tests/coverage/icachebusbusy.S | 17 ++++++ 3 files changed, 140 insertions(+), 14 deletions(-) diff --git a/tests/coverage/cbofaulthit.S b/tests/coverage/cbofaulthit.S index c08906dacb..677d4d7a9a 100644 --- a/tests/coverage/cbofaulthit.S +++ b/tests/coverage/cbofaulthit.S @@ -7,24 +7,41 @@ // The hit asks the way to clear its valid bit, and in the same cycle the access's own // fault squashes it (lsu.sv SelfFaultM, part of LSUFlushW), which must cancel the clear. // Four lines that share a set are loaded first, so the faulting cbo.invals hit in each -// of the four ways. +// of the four ways. Each line is then stored to, so it holds data memory does not: +// reading it back after the faults shows the clear was cancelled. // // SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 /////////////////////////////////////////// #include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" + +// CHECKTRAP n, cause, epc, tval, index: trap n recorded by record_trap had mcause = cause, +// mepc = epc and mtval = tval (registers). Uses a1 = traps. +.macro CHECKTRAP n, cause, epc, tval, index + ld t2, 8+24*\n(a1) + SC_CHECK_EQ t2, \cause, \index + ld t2, 16+24*\n(a1) + SC_CHECK_EQ t2, \epc, \index+1 + ld t2, 24+24*\n(a1) + SC_CHECK_EQ t2, \tval, \index+2 +.endm main: + la t0, record_trap + csrw mtvec, t0 li t0, 0xF0 # menvcfg.CBZE, CBCFE and CBIE, so supervisor mode may use cbo.inval csrw menvcfg, t0 - // Load four lines 4 KiB apart: they share a set, so they fill all four ways + // Load four lines 4 KiB apart: they share a set, so they fill all four ways. Store each line's + // address into it, so the line is dirty and holds a value memory does not. la s0, lines li s1, 4096 mv t0, s0 li t1, 4 fill: ld t2, 0(t0) + sd t0, 0(t0) add t0, t0, s1 addi t1, t1, -1 bnez t1, fill @@ -51,11 +68,82 @@ inval: addi t1, t1, -1 bnez t1, inval - li a0, 3 # back to machine mode + li a0, 3 # back to machine mode, which the unlocked PMP regions do not restrict ecall + + // Traps: the ecall from machine mode, a store/AMO access fault on each cbo.inval with mtval = + // the line's address, and the ecall from supervisor mode + la a1, traps + ld t2, 0(a1) + li t3, 6 + SC_CHECK_EQ t2, t3, 1 + li a2, 11 + ld t2, 8(a1) + SC_CHECK_EQ t2, a2, 2 + li a2, 7 + la a3, inval + mv a4, s0 + CHECKTRAP 1, a2, a3, a4, 3 + add a4, a4, s1 + CHECKTRAP 2, a2, a3, a4, 6 + add a4, a4, s1 + CHECKTRAP 3, a2, a3, a4, 9 + add a4, a4, s1 + CHECKTRAP 4, a2, a3, a4, 12 + li a2, 9 + ld t2, 8+24*5(a1) + SC_CHECK_EQ t2, a2, 15 + + // The faults cancelled the valid-bit clears, so each line still holds its dirty data + mv a4, s0 + li a5, 4 +readback: + ld t2, 0(a4) + SC_CHECK_EQ_ADR t2, a4, a4, 16 + add a4, a4, s1 + addi a5, a5, -1 + bnez a5, readback + + SC_PASS j done +// Record mcause, mepc and mtval of each trap in traps, then continue in the library trap handler. +// The faulting cbo.invals run in supervisor mode, which cannot read these CSRs itself. +.align 4 +record_trap: + csrrw t0, mscratch, t0 # t0 = trap stack pointer, mscratch = saved t0 + sd t1, -16(t0) + sd t2, -24(t0) + sd t3, -32(t0) + la t1, traps + ld t2, 0(t1) # number of traps so far + addi t3, t2, 1 + sd t3, 0(t1) + li t3, 8 + bgeu t2, t3, 1f # the table holds the first 8 + slli t3, t2, 1 + add t3, t3, t2 + slli t3, t3, 3 + add t1, t1, t3 # t1 = traps + 24 * n + csrr t2, mcause + sd t2, 8(t1) + csrr t2, mepc + sd t2, 16(t1) + csrr t2, mtval + sd t2, 24(t1) +1: ld t3, -32(t0) + ld t2, -24(t0) + ld t1, -16(t0) + csrrw t0, mscratch, t0 # restore t0 and the trap stack pointer + j trap_handler + .data -.align 14 # 16 KiB, for the NAPOT region +// The trap record must not share the D$ set of the lines (address bits 11:6 = 0), or the +// handler would evict one of them before its cbo.inval +.align 12 +.skip 2048 // address bits 11:6 = 32 +traps: // number of traps, then mcause, mepc, mtval of each + .fill 1+3*8, 8, 0 +.align 14 // 16 KiB, for the NAPOT region lines: .fill 2048, 8, 0 diff --git a/tests/coverage/fetchperiph.S b/tests/coverage/fetchperiph.S index f535085e0b..4ac97d89cc 100644 --- a/tests/coverage/fetchperiph.S +++ b/tests/coverage/fetchperiph.S @@ -13,27 +13,48 @@ /////////////////////////////////////////// #include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" + +// FETCHFAULT adr, index: jump to adr, which must take an instruction access fault (mcause 1) with +// mtval = adr. fetchfault records mcause in s10 and mtval in s11, and counts the faults in s9. +.macro FETCHFAULT adr, index + li s10, 0 + li s11, 0 + li t1, \adr + jalr ra, t1 + li t2, 1 + SC_CHECK_EQ s10, t2, \index // mcause = instruction access fault + li t2, \adr + SC_CHECK_EQ s11, t2, \index+1 // mtval = the fetch address +.endm main: + li s9, 0 la t0, fetchfault # the fault returns to ra, after the jump that faulted csrw mtvec, t0 - li t1, 0x02000000 # CLINT - jalr ra, t1 - li t1, 0x0C000000 # PLIC - jalr ra, t1 - li t1, 0x10020000 # PWM - jalr ra, t1 - li t1, 0x10040000 # SPI - jalr ra, t1 - li t1, 0x10060000 # GPIO - jalr ra, t1 + FETCHFAULT 0x02000000, 1 # CLINT + FETCHFAULT 0x0C000000, 3 # PLIC + FETCHFAULT 0x10020000, 5 # PWM + FETCHFAULT 0x10040000, 7 # SPI + FETCHFAULT 0x10060000, 9 # GPIO la t0, trap_handler csrw mtvec, t0 + li t2, 5 + SC_CHECK_EQ s9, t2, 11 # exactly five instruction access faults + + SC_PASS j done +// Instruction access faults: record mcause and mtval, count, and return after the jump that +// faulted. Anything else goes to sc_trap_handler, which fails the test. .align 2 fetchfault: + csrr s10, mcause + addi s8, s10, -1 + bnez s8, sc_trap_handler + csrr s11, mtval + addi s9, s9, 1 csrw mepc, ra mret diff --git a/tests/coverage/icachebusbusy.S b/tests/coverage/icachebusbusy.S index 4f244c11e7..d411fdfa80 100644 --- a/tests/coverage/icachebusbusy.S +++ b/tests/coverage/icachebusbusy.S @@ -10,14 +10,17 @@ // even while the pipeline is stalled. Here the first half hits in the instruction // cache in the cycle a load from the CLINT starts on the bus, and the second half // misses in the next cycle, while the APB bridge holds HREADY low for the load. +// The results of the load and of the spilled instruction are checked afterwards. // // SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 /////////////////////////////////////////// #include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" .option norvc main: + li t2, 0x55 # the spilled addi adds 1 li t0, 0x0200BFF8 # CLINT mtime j linea @@ -34,4 +37,18 @@ linea: nop addi t2, t2, 1 # spills into the next line, which has not been fetched yet + // The checks follow the sequence, so they do not change its timing + li t3, 0x56 + SC_CHECK_EQ t2, t3, 1 # the spilled addi ran once + bnez t1, 1f # the load returned mtime, which has counted cycles since reset + SC_FAIL 2 +1: li t0, 0x0200BFF8 + ld t3, 0(t0) # mtime now, a few cycles after the load read it + sub t3, t3, t1 + bnez t3, 2f + SC_FAIL 3 +2: li t4, 1000 + bltu t3, t4, 3f + SC_FAIL 4 +3: SC_PASS j done From 644ebb4531933b5a29fe58ab2391044a2046c7da Mon Sep 17 00:00:00 2001 From: David Harris Date: Fri, 9 Oct 2026 16:37:11 -0700 Subject: [PATCH 3/9] Drop 18 coverage tests that add no coverage With the riscv-arch-test suites and the other coverage tests, rv64gc code coverage stays at 100% without btbthrash, dcache2, ebu, ebuArb, ebuIcacheDTLBMiss, gshare, hptwAccessFault, ifuCamlineWrite, lsu, pmpTOR7, pmpcfg1, pmpcfg2, pmppriority, tlbASID, tlbGLB, tlbGP, tlbMP and tlbTP. None checked its own results. The tests written against a specific bug stay. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: David Harris --- tests/coverage/btbthrash.S | 144 ---------- tests/coverage/dcache2.S | 62 ----- tests/coverage/ebu.S | 313 --------------------- tests/coverage/ebuArb.S | 186 ------------- tests/coverage/ebuIcacheDTLBMiss.S | 418 ----------------------------- tests/coverage/gshare.S | 277 ------------------- tests/coverage/hptwAccessFault.S | 168 ------------ tests/coverage/ifuCamlineWrite.S | 147 ---------- tests/coverage/lsu.S | 37 --- tests/coverage/pmpTOR7.S | 86 ------ tests/coverage/pmpcfg1.S | 48 ---- tests/coverage/pmpcfg2.S | 12 - tests/coverage/pmppriority.S | 158 ----------- tests/coverage/tlbASID.S | 131 --------- tests/coverage/tlbGLB.S | 181 ------------- tests/coverage/tlbGP.S | 154 ----------- tests/coverage/tlbMP.S | 199 -------------- tests/coverage/tlbTP.S | 145 ---------- 18 files changed, 2866 deletions(-) delete mode 100644 tests/coverage/btbthrash.S delete mode 100644 tests/coverage/dcache2.S delete mode 100644 tests/coverage/ebu.S delete mode 100644 tests/coverage/ebuArb.S delete mode 100644 tests/coverage/ebuIcacheDTLBMiss.S delete mode 100644 tests/coverage/gshare.S delete mode 100644 tests/coverage/hptwAccessFault.S delete mode 100644 tests/coverage/ifuCamlineWrite.S delete mode 100644 tests/coverage/lsu.S delete mode 100644 tests/coverage/pmpTOR7.S delete mode 100644 tests/coverage/pmpcfg1.S delete mode 100644 tests/coverage/pmpcfg2.S delete mode 100644 tests/coverage/pmppriority.S delete mode 100644 tests/coverage/tlbASID.S delete mode 100644 tests/coverage/tlbGLB.S delete mode 100644 tests/coverage/tlbGP.S delete mode 100644 tests/coverage/tlbMP.S delete mode 100644 tests/coverage/tlbTP.S diff --git a/tests/coverage/btbthrash.S b/tests/coverage/btbthrash.S deleted file mode 100644 index 19a1abe521..0000000000 --- a/tests/coverage/btbthrash.S +++ /dev/null @@ -1,144 +0,0 @@ -/////////////////////////////////////////// -// btbtrash.S -// -// Written: Rose Thompson rose@rosethompson.net 23 October 2024 -// -// Purpose: Test the branch target buffer alias with divide and cache pipeline stalls -// -// A component of the CORE-V-WALLY configurable RISC-V project. -// https://github.com/openhwfoundation/cvw -// -// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at -// -// https://solderpad.org/licenses/SHL-2.1/ -// -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions -// and limitations under the License. -//////////////////////////////////////////////////////////////////////////////////////////////// - -// load code to initialize stack, handle interrupts, terminate -#include "WALLY-init-lib.h" - -main: - - # Division test (having trouble with buildroot) - li x1, 1938759018 - li x2, 3745029 - li x3, 458 - li x4, 29587209347 - li x5, 28957 - li x6, 298 - li x7, 238562 - li x8, 198674 - li x9, 134 - li x10, 906732 - li x11, 29 - li x12, 50912 - li x13, 59 - li x14, 6902385 - li x15, 1923857 - li x16, 3985 - li x17, 3947 - li x18, 15984 - li x19, 5 - li x20, 9684658489 - li x21, 6548 - li x22, 3564 - li x23, 94 - li x24, 689464 - li x25, 42567 - li x26, 98453 - li x27, 648 - li x28, 984 - li x29, 6984 - li x30, 864 - - # x31 will be our loop counter - li x31, 4 - -.align 12 -jump1: - divuw x0, x1, x2 - j jump3 -jump4: - divuw x0, x5, x6 - j jump5 -jump6: - divuw x0, x10, x9 - j jump7 -jump8: - divuw x0, x14, x3 - j jump9 -jump10: - divuw x0, x18, x17 - j jump11 -jump12: - divuw x0, x21, x22 - j jump13 -jump14: - divuw x0, x24, x25 - j jump15 -jump16: - divuw x0, x29, x28 - j jump17 -jump18: - divuw x0, x1, x30 - j jump19 -jump20: - divuw x0, x3, x19 - j jump21 -jump22: - divuw x0, x12, x13 - j jump23 - -.align 12 # size of the 1024 btb apart -jump2: - j jump1 -jump3: - divuw x0, x4, x3 - j jump4 -jump5: - divuw x0, x7, x8 - j jump6 -jump7: - divuw x0, x12, x11 - j jump8 -jump9: - divuw x0, x15, x16 - j jump10 -jump11: - divuw x0, x20, x19 - j jump12 -jump13: - divuw x0, x24, x23 - j jump14 -jump15: - divuw x0, x26, x27 - j jump16 -jump17: - divuw x0, x29, x30 - j jump18 -jump19: - divuw x0, x2, x3 - j jump20 -jump21: - divuw x0, x4, x5 - j jump22 -jump23: - divuw x0, x20, x21 - #j jump22 - - fence.i - - addi x31, x31, -1 - bne x31, x0, jump1 -finished: - j done diff --git a/tests/coverage/dcache2.S b/tests/coverage/dcache2.S deleted file mode 100644 index f7c21188b6..0000000000 --- a/tests/coverage/dcache2.S +++ /dev/null @@ -1,62 +0,0 @@ -/////////////////////////////////////////// -// dcache2.S -// -// Written: avercruysse@hmc.edu 18 April 2023 -// -// Purpose: Test Coverage for D$ -// (for all 4 cache ways, trigger a FlushStage while SetDirtyWay=1) -// -// A component of the CORE-V-WALLY configurable RISC-V project. -// https://github.com/openhwfoundation/cvw -// -// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at -// -// https://solderpad.org/licenses/SHL-2.1/ -// -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions -// and limitations under the License. -//////////////////////////////////////////////////////////////////////////////////////////////// - -#include "WALLY-init-lib.h" -main: - // way 0 - li t0, 0x80100770 - sd zero, 0(t0) - sd zero, 1(t0) - lr.d t1, (t0) - addi t0, t0, 1 - sc.d t2, t1, (t0) - - // way 1 - li t0, 0x80101770 - sd zero, 0(t0) - sd zero, 1(t0) - lr.d t1, (t0) - addi t0, t0, 1 - sc.d t2, t1, (t0) - - // way 2 - li t0, 0x80102770 - sd zero, 0(t0) - sd zero, 1(t0) - lr.d t1, (t0) - addi t0, t0, 1 - sc.d t2, t1, (t0) - - // way 3 - li t0, 0x80103770 - sd zero, 0(t0) - sd zero, 1(t0) - lr.d t1, (t0) - addi t0, t0, 1 - sc.d t2, t1, (t0) - - j done diff --git a/tests/coverage/ebu.S b/tests/coverage/ebu.S deleted file mode 100644 index 90feb8b8e8..0000000000 --- a/tests/coverage/ebu.S +++ /dev/null @@ -1,313 +0,0 @@ -/////////////////////////////////////////// -// ebu.S -// -// Written: David_Harris@hmc.edu 21 January 2024 -// -// Purpose: Test coverage for EBU -// -// A component of the CORE-V-WALLY configurable RISC-V project. -// https://github.com/openhwfoundation/cvw -// -// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at -// -// https://solderpad.org/licenses/SHL-2.1/ -// -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions -// and limitations under the License. -//////////////////////////////////////////////////////////////////////////////////////////////// - -// load code to initialize stack, handle interrupts, terminate - -#include "WALLY-init-lib.h" - -# run-elf.bash find this in project description -main: - li t5, 0x1 - slli t5, t5, 62 - ori t5, t5, 0xF0 - csrs menvcfg, t5 # menvcfg.PBMTE = 1, CBZE, CBCFE, CBIE all 1 - - # Page table root address at 0x80010000; SV48 - li t5, 0x9000000000080010 - csrw satp, t5 - - # sfence.vma x0, x0 - - # switch to supervisor mode - li a0, 1 - ecall - - # - -# Tricky case to cover. I$ miss concurrent with DTLB miss. HPTW has to hit the first -# access in the cache and miss a later one. Trigger this by doing a load that touches -# a page not in the DTLB but where the top-level PTE is already there. Has to happen -# near the end of the 16-instruction I$ line. -# -# Condition Coverage for instance /core/ebu/ebu/ebufsmarb -- -# -# File ../src/ebu/ebufsmarb.sv -#----------------Focused Condition View------------------- -#Line 72 Item 1 ((HREADY & FinalBeatD) & (LSUReq ~& IFUReq)) -#Condition totals: 2 of 4 input terms covered = 50.00% -# -# Input Term Covered Reason for no coverage Hint -# ----------- -------- ----------------------- -------------- -# HREADY Y -# FinalBeatD Y -# LSUReq N '_1' not hit Hit '_1' -# IFUReq N No hits Hit '_0' and '_1' -# -# Rows: Hits FEC Target Non-masking condition(s) -# --------- --------- -------------------- ------------------------- -# Row 1: 2 HREADY_0 ((LSUReq ~& IFUReq) && FinalBeatD) -# Row 2: 14 HREADY_1 ((LSUReq ~& IFUReq) && FinalBeatD) -# Row 3: 1 FinalBeatD_0 ((LSUReq ~& IFUReq) && HREADY) -# Row 4: 14 FinalBeatD_1 ((LSUReq ~& IFUReq) && HREADY) -# Row 5: 14 LSUReq_0 ((HREADY & FinalBeatD) && IFUReq) -# Row 6: ***0*** LSUReq_1 ((HREADY & FinalBeatD) && IFUReq) -# Row 7: ***0*** IFUReq_0 ((HREADY & FinalBeatD) && LSUReq) -# Row 8: ***0*** IFUReq_1 ((HREADY & FinalBeatD) && LSUReq) - - - li a0, 0x80000000 - li a1, 0x80A00000 - j label1 - -.align 6 # start on multiple of 64 bytes / 16 instruction cache line -label1: - addi t2, t3, 0x100 # occupy part of cache line - sfence.vma # flush tlb - lw t0, 0x234(a0) # load to get an entry in the DTLB accessing top-level PTE - addi t2, t3, 0x103 # occupy part of cache line - addi t2, t3, 0x104 # occupy part of cache line - addi t2, t3, 0x105 # occupy part of cache line - addi t2, t3, 0x106 # occupy part of cache line - addi t2, t3, 0x107 # occupy part of cache line - addi t2, t3, 0x108 # occupy part of cache line - addi t2, t3, 0x109 # occupy part of cache line - addi t2, t3, 0x10A # occupy part of cache line - lw t0, 0x234(a1) # trigger DTLB miss - addi t2, t3, 0x10C # occupy part of cache line - addi t2, t3, 0x10D # occupy part of cache line - addi t2, t3, 0x10E # occupy part of cache line - addi t2, t3, 0x10F # occupy part of cache line - -# next multiple of 16 - addi t2, t3, 0x100 # occupy part of cache line - sfence.vma # flush tlb - lw t0, 0x334(a0) # load to get an entry in the DTLB accessing top-level PTE - addi t2, t3, 0x103 # occupy part of cache line - addi t2, t3, 0x104 # occupy part of cache line - addi t2, t3, 0x105 # occupy part of cache line - addi t2, t3, 0x106 # occupy part of cache line - addi t2, t3, 0x107 # occupy part of cache line - addi t2, t3, 0x108 # occupy part of cache line - addi t2, t3, 0x109 # occupy part of cache line - addi t2, t3, 0x10A # occupy part of cache line - addi t2, t3, 0x10B # occupy part of cache line - lw t0, 0x334(a1) # trigger DTLB miss - addi t2, t3, 0x10D # occupy part of cache line - addi t2, t3, 0x10E # occupy part of cache line - addi t2, t3, 0x10F # occupy part of cache line - -# next multiple of 16 - addi t2, t3, 0x100 # occupy part of cache line - sfence.vma # flush tlb - lw t0, 0x434(a0) # load to get an entry in the DTLB accessing top-level PTE - addi t2, t3, 0x103 # occupy part of cache line - addi t2, t3, 0x104 # occupy part of cache line - addi t2, t3, 0x105 # occupy part of cache line - addi t2, t3, 0x106 # occupy part of cache line - addi t2, t3, 0x107 # occupy part of cache line - addi t2, t3, 0x108 # occupy part of cache line - addi t2, t3, 0x109 # occupy part of cache line - addi t2, t3, 0x10A # occupy part of cache line - addi t2, t3, 0x10B # occupy part of cache line - addi t2, t3, 0x10C # occupy part of cache line - lw t0, 0x434(a1) # trigger DTLB miss - addi t2, t3, 0x10E # occupy part of cache line - addi t2, t3, 0x10F # occupy part of cache line - -# next multiple of 16 - addi t2, t3, 0x100 # occupy part of cache line - sfence.vma # flush tlb - lw t0, 0x534(a0) # load to get an entry in the DTLB accessing top-level PTE - addi t2, t3, 0x103 # occupy part of cache line - addi t2, t3, 0x104 # occupy part of cache line - addi t2, t3, 0x105 # occupy part of cache line - addi t2, t3, 0x106 # occupy part of cache line - addi t2, t3, 0x107 # occupy part of cache line - addi t2, t3, 0x108 # occupy part of cache line - addi t2, t3, 0x109 # occupy part of cache line - addi t2, t3, 0x10A # occupy part of cache line - addi t2, t3, 0x10B # occupy part of cache line - addi t2, t3, 0x10C # occupy part of cache line - addi t2, t3, 0x10D # occupy part of cache line - lw t0, 0x534(a1) # trigger DTLB miss - addi t2, t3, 0x10F # occupy part of cache line - - # wrap up - li a0, 3 # switch back to machine mode because code at 0x80000000 may not have clean page table entry - ecall - j done - - - -.data - -.align 16 -# root Page table situated at 0x80010000 -pagetable: - .8byte 0x20004401 # 0x00000000-0x80_00000000: PTE at 0x80011000 01 valid - .8byte 0x000000000000100F # misaligned terapage at 0x80_00000000 - -# next page table at 0x80011000 -.align 12 - .8byte 0x000000000000100F # misaligned gigapage at 0x00000000 - .8byte 0x0000000020005801 # PTE for pages at 0x40000000 - .8byte 0x0000000020004801 # gigapage at 0x80000000 pointing to 0x80120000 - - -# Next page table at 0x80012000 for gigapage at 0x80000000 -.align 12 - .8byte 0x0000000020004C01 # for VA starting at 80000000 (pointer to NAPOT 64 KiB pages) - .8byte 0x0000000020014C0F # for VA starting at 80200000 (misaligned megapage) - .8byte 0x0000000020005001 # for VA starting at 80400000 (bad PBMT pages) - .8byte 0x4000000020004C01 # for VA starting at 80600000 (bad entry: nonleaf PTE can't have PBMT != 0) - .8byte 0x0000000020005401 # for VA starting at 80800000 (testing rwx permissions with cbom/cboz) - .8byte 0x0000000020005801 # for VA starting at 80A00000 (pointer to NAPOT 64 KiB pages like at 80000000) - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - -# Leaf page table at 0x80013000 with NAPOT pages -.align 12 - #80000000 - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - - .8byte 0x800000002000E0CF - .8byte 0x800000002000E0CF - .8byte 0x800000002000E0CF - .8byte 0x800000002000E0CF - - .8byte 0x800000002000E0CF - .8byte 0x800000002000E0CF - -# Leaf page table at 0x80014000 with PBMT pages -.align 12 - #80400000 - .8byte 0x60000000200020CF # reserved entry - -# Leaf page table at 0x80015000 with various permissions for testing CBOM and CBOZ -.align 12 - #80800000 - .8byte 0x00000000200000CF # valid rwx for VA 80800000 - .8byte 0x00000000200000CB # valid r x for VA 80801000 - .8byte 0x00000000200000C3 # valid r for VA 80802000 - .8byte 0x00000000200000C9 # valid x for VA 80803000 - .8byte 0x00000000200000CD # valid wx for VA 80804000 (illegal combination, but used to test tlbcontrol) - .8byte 0x000000002000000F # valid rwx for VA 80805000 for covering ITLB translate and UpdateDA - .8byte 0x20000000200000CF # PBMT=1 for VA 80806000 for covering ITLB BadPBMT - -# Leaf page table at 0x80016000 with NAPOT pages -.align 12 - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF diff --git a/tests/coverage/ebuArb.S b/tests/coverage/ebuArb.S deleted file mode 100644 index 54eec98ed6..0000000000 --- a/tests/coverage/ebuArb.S +++ /dev/null @@ -1,186 +0,0 @@ -/////////////////////////////////////////// -// ebuArb.S -// -// Written: Rose Thompson rose@rosethompson.net -// -// Purpose: Force EBU arbitration. -// -// A component of the CORE-V-WALLY configurable RISC-V project. -// https://github.com/openhwfoundation/cvw -// -// Copyright (C) 2021-25 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at -// -// https://solderpad.org/licenses/SHL-2.1/ -// -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions -// and limitations under the License. -//////////////////////////////////////////////////////////////////////////////////////////////// - -// load code to initialize stack, handle interrupts, terminate - -#include "WALLY-init-lib.h" - - # cannot do this with a loop, must invalidate frequently to force as many possible lsu bus requests. -main: - la x31, data - addi x30, x31, 0x40 - addi x29, x31, 0x80 - addi x28, x31, 0xc0 - addi x27, x31, 0x100 - addi x26, x31, 0x140 - addi x25, x31, 0x180 - addi x24, x31, 0x1c0 - - ld x0, 0(x31) - cbo.inval (x31) - ld x1, 8(x31) - cbo.inval (x31) - ld x2, 0x10(x31) - cbo.inval (x31) - ld x3, 0x18(x31) - cbo.inval (x31) - ld x4, 0x20(x31) - cbo.inval (x31) - ld x5, 0x28(x31) - cbo.inval (x31) - ld x6, 0x30(x31) - cbo.inval (x31) - ld x7, 0x38(x31) - cbo.inval (x31) - ld x8, 0x40(x31) - cbo.inval (x30) - ld x9, 0x48(x31) - cbo.inval (x30) - ld x10, 0x50(x31) - cbo.inval (x30) - ld x11, 0x58(x31) - cbo.inval (x30) - ld x12, 0x60(x31) - cbo.inval (x30) - ld x13, 0x68(x31) - cbo.inval (x30) - ld x14, 0x70(x31) - cbo.inval (x30) - ld x15, 0x78(x31) - cbo.inval (x30) - ld x16, 0x80(x31) - cbo.inval (x29) - ld x17, 0x88(x31) - cbo.inval (x29) - ld x18, 0x90(x31) - cbo.inval (x29) - ld x19, 0x98(x31) - cbo.inval (x29) - ld x20, 0xa0(x31) - cbo.inval (x29) - ld x21, 0xa8(x31) - cbo.inval (x29) - ld x22, 0xb0(x31) - cbo.inval (x29) - ld x23, 0xb8(x31) - cbo.inval (x29) - ld x23, 0xc0(x31) - cbo.inval (x28) - ld x23, 0xc8(x31) - cbo.inval (x28) - ld x23, 0xd0(x31) - cbo.inval (x28) - ld x23, 0xd8(x31) - cbo.inval (x28) - ld x23, 0xe0(x31) - cbo.inval (x28) - ld x23, 0xe8(x31) - cbo.inval (x28) - ld x23, 0xf0(x31) - cbo.inval (x28) - ld x0, 0xf8(x31) - cbo.inval (x28) - ld x1, 0x100(x31) - cbo.inval (x27) - ld x1, 0x108(x31) - cbo.inval (x27) - ld x1, 0x110(x31) - cbo.inval (x27) - ld x1, 0x118(x31) - cbo.inval (x27) - ld x1, 0x120(x31) - cbo.inval (x27) - ld x1, 0x128(x31) - cbo.inval (x27) - ld x1, 0x130(x31) - cbo.inval (x27) - ld x1, 0x138(x31) - cbo.inval (x27) - ld x1, 0x140(x31) - cbo.inval (x26) - ld x1, 0x148(x31) - cbo.inval (x26) - ld x1, 0x150(x31) - cbo.inval (x26) - ld x1, 0x158(x31) - cbo.inval (x26) - ld x1, 0x160(x31) - cbo.inval (x26) - ld x1, 0x168(x31) - cbo.inval (x26) - ld x1, 0x170(x31) - cbo.inval (x26) - ld x1, 0x178(x31) - cbo.inval (x26) - ld x1, 0x180(x31) - cbo.inval (x25) - ld x1, 0x188(x31) - cbo.inval (x25) - ld x1, 0x190(x31) - cbo.inval (x25) - ld x1, 0x198(x31) - cbo.inval (x25) - ld x1, 0x1a0(x31) - cbo.inval (x25) - ld x1, 0x1a8(x31) - cbo.inval (x25) - ld x1, 0x1b0(x31) - cbo.inval (x25) - ld x1, 0x1b8(x31) - cbo.inval (x25) - ld x1, 0x1c0(x31) - cbo.inval (x24) - ld x1, 0x1c8(x31) - cbo.inval (x24) - ld x1, 0x1d0(x31) - cbo.inval (x24) - ld x1, 0x1d8(x31) - cbo.inval (x24) - ld x1, 0x1e0(x31) - cbo.inval (x24) - ld x1, 0x1e8(x31) - cbo.inval (x24) - ld x1, 0x1f0(x31) - cbo.inval (x24) - ld x1, 0x1f8(x31) - - - -finished: - j done - -.data - -.align 3 -data: - .8byte 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 - .8byte 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf - .8byte 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 - .8byte 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f - .8byte 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 - .8byte 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f - .8byte 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 - .8byte 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f diff --git a/tests/coverage/ebuIcacheDTLBMiss.S b/tests/coverage/ebuIcacheDTLBMiss.S deleted file mode 100644 index e03ffd54f4..0000000000 --- a/tests/coverage/ebuIcacheDTLBMiss.S +++ /dev/null @@ -1,418 +0,0 @@ -/////////////////////////////////////////// -// ebu.S -// -// Written: Rose Thompson rose@rosethompson.net 4/29/2025 -// -// Purpose: Test coverage for EBU -// -// A component of the CORE-V-WALLY configurable RISC-V project. -// https://github.com/openhwfoundation/cvw -// -// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at -// -// https://solderpad.org/licenses/SHL-2.1/ -// -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions -// and limitations under the License. -//////////////////////////////////////////////////////////////////////////////////////////////// - -// load code to initialize stack, handle interrupts, terminate - -#include "WALLY-init-lib.h" - -# run-elf.bash find this in project description -main: - li t5, 0x1 - slli t5, t5, 62 - ori t5, t5, 0xF0 - csrs menvcfg, t5 # menvcfg.PBMTE = 1, CBZE, CBCFE, CBIE all 1 - - # Page table root address at 0x80010000; SV39 - li t5, 0x9000000000080010 - csrw satp, t5 - - # sfence.vma x0, x0 - - # switch to supervisor mode - li a0, 1 - ecall - - # - -# Tricky case to cover. I$ miss concurrent with DTLB miss. HPTW has to hit the first -# access in the cache and miss a later one. Trigger this by doing a load that touches -# a page not in the DTLB but where the top-level PTE is already there. Has to happen -# near the end of the 16-instruction I$ line. -# -# Condition Coverage for instance /core/ebu/ebu/ebufsmarb -- -# -# File ../src/ebu/ebufsmarb.sv -#----------------Focused Condition View------------------- -#Line 72 Item 1 ((HREADY & FinalBeatD) & (LSUReq ~& IFUReq)) -#Condition totals: 2 of 4 input terms covered = 50.00% -# -# Input Term Covered Reason for no coverage Hint -# ----------- -------- ----------------------- -------------- -# HREADY Y -# FinalBeatD Y -# LSUReq N '_1' not hit Hit '_1' -# IFUReq N No hits Hit '_0' and '_1' -# -# Rows: Hits FEC Target Non-masking condition(s) -# --------- --------- -------------------- ------------------------- -# Row 1: 2 HREADY_0 ((LSUReq ~& IFUReq) && FinalBeatD) -# Row 2: 14 HREADY_1 ((LSUReq ~& IFUReq) && FinalBeatD) -# Row 3: 1 FinalBeatD_0 ((LSUReq ~& IFUReq) && HREADY) -# Row 4: 14 FinalBeatD_1 ((LSUReq ~& IFUReq) && HREADY) -# Row 5: 14 LSUReq_0 ((HREADY & FinalBeatD) && IFUReq) -# Row 6: ***0*** LSUReq_1 ((HREADY & FinalBeatD) && IFUReq) -# Row 7: ***0*** IFUReq_0 ((HREADY & FinalBeatD) && LSUReq) -# Row 8: ***0*** IFUReq_1 ((HREADY & FinalBeatD) && LSUReq) - - - li a0, 0x80000000 - li a1, 0x81000000 - la a2, pagetableL1Target - addi a2, a2, 0x40 # address of last level of the page table which needs to be removed from the data cache - j label1 - -.align 6 # start on multiple of 64 bytes / 16 instruction cache line -label1: - addi t2, t3, 0x100 # occupy part of cache line - sfence.vma # flush tlb - lw t0, 0x234(a0) # load to get an entry in the DTLB accessing top-level PTE - addi t2, t3, 0x103 # occupy part of cache line - addi t2, t3, 0x104 # occupy part of cache line - addi t2, t3, 0x105 # occupy part of cache line - addi t2, t3, 0x106 # occupy part of cache line - lw t0, 0x234(a1) # trigger DTLB miss - addi t2, t3, 0x107 # occupy part of cache line - addi t2, t3, 0x108 # occupy part of cache line - addi t2, t3, 0x109 # occupy part of cache line - addi t2, t3, 0x10A # occupy part of cache line - addi t2, t3, 0x10C # occupy part of cache line - addi t2, t3, 0x10D # occupy part of cache line - addi t2, t3, 0x10E # occupy part of cache line - cbo.inval (a2) - - addi t2, t3, 0x100 # occupy part of cache line - sfence.vma # flush tlb - lw t0, 0x234(a0) # load to get an entry in the DTLB accessing top-level PTE - addi t2, t3, 0x103 # occupy part of cache line - addi t2, t3, 0x104 # occupy part of cache line - addi t2, t3, 0x105 # occupy part of cache line - addi t2, t3, 0x106 # occupy part of cache line - addi t2, t3, 0x107 # occupy part of cache line - lw t0, 0x234(a1) # trigger DTLB miss - addi t2, t3, 0x108 # occupy part of cache line - addi t2, t3, 0x109 # occupy part of cache line - addi t2, t3, 0x10A # occupy part of cache line - addi t2, t3, 0x10C # occupy part of cache line - addi t2, t3, 0x10D # occupy part of cache line - addi t2, t3, 0x10E # occupy part of cache line - cbo.inval (a2) - - addi t2, t3, 0x100 # occupy part of cache line - sfence.vma # flush tlb - lw t0, 0x234(a0) # load to get an entry in the DTLB accessing top-level PTE - addi t2, t3, 0x103 # occupy part of cache line - addi t2, t3, 0x104 # occupy part of cache line - addi t2, t3, 0x105 # occupy part of cache line - addi t2, t3, 0x106 # occupy part of cache line - addi t2, t3, 0x107 # occupy part of cache line - addi t2, t3, 0x108 # occupy part of cache line - lw t0, 0x234(a1) # trigger DTLB miss - addi t2, t3, 0x109 # occupy part of cache line - addi t2, t3, 0x10A # occupy part of cache line - addi t2, t3, 0x10C # occupy part of cache line - addi t2, t3, 0x10D # occupy part of cache line - addi t2, t3, 0x10E # occupy part of cache line - cbo.inval (a2) - - addi t2, t3, 0x100 # occupy part of cache line - sfence.vma # flush tlb - lw t0, 0x234(a0) # load to get an entry in the DTLB accessing top-level PTE - addi t2, t3, 0x103 # occupy part of cache line - addi t2, t3, 0x104 # occupy part of cache line - addi t2, t3, 0x105 # occupy part of cache line - addi t2, t3, 0x106 # occupy part of cache line - addi t2, t3, 0x107 # occupy part of cache line - addi t2, t3, 0x108 # occupy part of cache line - addi t2, t3, 0x109 # occupy part of cache line - lw t0, 0x234(a1) # trigger DTLB miss - addi t2, t3, 0x10A # occupy part of cache line - addi t2, t3, 0x10C # occupy part of cache line - addi t2, t3, 0x10D # occupy part of cache line - addi t2, t3, 0x10E # occupy part of cache line - cbo.inval (a2) - - addi t2, t3, 0x100 # occupy part of cache line - sfence.vma # flush tlb - lw t0, 0x234(a0) # load to get an entry in the DTLB accessing top-level PTE - addi t2, t3, 0x103 # occupy part of cache line - addi t2, t3, 0x104 # occupy part of cache line - addi t2, t3, 0x105 # occupy part of cache line - addi t2, t3, 0x106 # occupy part of cache line - addi t2, t3, 0x107 # occupy part of cache line - addi t2, t3, 0x108 # occupy part of cache line - addi t2, t3, 0x109 # occupy part of cache line - addi t2, t3, 0x10A # occupy part of cache line - lw t0, 0x234(a1) # trigger DTLB miss - addi t2, t3, 0x10C # occupy part of cache line - addi t2, t3, 0x10D # occupy part of cache line - addi t2, t3, 0x10E # occupy part of cache line - cbo.inval (a2) - - addi t2, t3, 0x100 # occupy part of cache line - sfence.vma # flush tlb - lw t0, 0x234(a0) # load to get an entry in the DTLB accessing top-level PTE - addi t2, t3, 0x103 # occupy part of cache line - addi t2, t3, 0x104 # occupy part of cache line - addi t2, t3, 0x105 # occupy part of cache line - addi t2, t3, 0x106 # occupy part of cache line - addi t2, t3, 0x107 # occupy part of cache line - addi t2, t3, 0x108 # occupy part of cache line - addi t2, t3, 0x109 # occupy part of cache line - addi t2, t3, 0x10A # occupy part of cache line - addi t2, t3, 0x10C # occupy part of cache line - lw t0, 0x234(a1) # trigger DTLB miss - addi t2, t3, 0x10D # occupy part of cache line - addi t2, t3, 0x10E # occupy part of cache line - cbo.inval (a2) - - addi t2, t3, 0x100 # occupy part of cache line - sfence.vma # flush tlb - lw t0, 0x234(a0) # load to get an entry in the DTLB accessing top-level PTE - addi t2, t3, 0x103 # occupy part of cache line - addi t2, t3, 0x104 # occupy part of cache line - addi t2, t3, 0x105 # occupy part of cache line - addi t2, t3, 0x106 # occupy part of cache line - addi t2, t3, 0x107 # occupy part of cache line - addi t2, t3, 0x108 # occupy part of cache line - addi t2, t3, 0x109 # occupy part of cache line - addi t2, t3, 0x10A # occupy part of cache line - addi t2, t3, 0x10C # occupy part of cache line - addi t2, t3, 0x10D # occupy part of cache line - lw t0, 0x234(a1) # trigger DTLB miss - addi t2, t3, 0x10E # occupy part of cache line - cbo.inval (a2) - - addi t2, t3, 0x100 # occupy part of cache line - sfence.vma # flush tlb - lw t0, 0x234(a0) # load to get an entry in the DTLB accessing top-level PTE - addi t2, t3, 0x103 # occupy part of cache line - addi t2, t3, 0x104 # occupy part of cache line - addi t2, t3, 0x105 # occupy part of cache line - addi t2, t3, 0x106 # occupy part of cache line - addi t2, t3, 0x107 # occupy part of cache line - addi t2, t3, 0x108 # occupy part of cache line - addi t2, t3, 0x109 # occupy part of cache line - addi t2, t3, 0x10A # occupy part of cache line - addi t2, t3, 0x10C # occupy part of cache line - addi t2, t3, 0x10D # occupy part of cache line - addi t2, t3, 0x10E # occupy part of cache line - lw t0, 0x234(a1) # trigger DTLB miss - cbo.inval (a2) - - addi t2, t3, 0x100 # occupy part of cache line - sfence.vma # flush tlb - lw t0, 0x234(a0) # load to get an entry in the DTLB accessing top-level PTE - addi t2, t3, 0x103 # occupy part of cache line - addi t2, t3, 0x104 # occupy part of cache line - addi t2, t3, 0x105 # occupy part of cache line - addi t2, t3, 0x106 # occupy part of cache line - addi t2, t3, 0x107 # occupy part of cache line - addi t2, t3, 0x108 # occupy part of cache line - addi t2, t3, 0x109 # occupy part of cache line - addi t2, t3, 0x10A # occupy part of cache line - addi t2, t3, 0x10C # occupy part of cache line - addi t2, t3, 0x10D # occupy part of cache line - addi t2, t3, 0x10E # occupy part of cache line - addi t2, t3, 0x10F # occupy part of cache line - lw t0, 0x234(a1) # trigger DTLB miss - cbo.inval (a2) - - addi t2, t3, 0x100 # occupy part of cache line - sfence.vma # flush tlb - lw t0, 0x234(a0) # load to get an entry in the DTLB accessing top-level PTE - addi t2, t3, 0x103 # occupy part of cache line - addi t2, t3, 0x104 # occupy part of cache line - addi t2, t3, 0x105 # occupy part of cache line - addi t2, t3, 0x106 # occupy part of cache line - addi t2, t3, 0x107 # occupy part of cache line - addi t2, t3, 0x108 # occupy part of cache line - addi t2, t3, 0x109 # occupy part of cache line - addi t2, t3, 0x10A # occupy part of cache line - addi t2, t3, 0x10C # occupy part of cache line - addi t2, t3, 0x10D # occupy part of cache line - addi t2, t3, 0x10E # occupy part of cache line - addi t2, t3, 0x10F # occupy part of cache line - addi t2, t3, 0x110 # occupy part of cache line - lw t0, 0x234(a1) # trigger DTLB miss - cbo.inval (a2) - - # wrap up - li a0, 3 # switch back to machine mode because code at 0x80000000 may not have clean page table entry - ecall - j done - - - -.data - -.align 16 -# root Page table situated at 0x80010000 -pagetable: - .8byte 0x20004401 # 0x00000000-0x80_00000000: PTE at 0x80011000 01 valid - .8byte 0x000000000000100F # misaligned terapage at 0x80_00000000 - -# next page table at 0x80011000 -.align 12 - .8byte 0x000000000000100F # misaligned gigapage at 0x00000000 - .8byte 0x0000000020005801 # PTE for pages at 0x40000000 - .8byte 0x0000000020004801 # - - -# Next page table at 0x80012000 for gigapage at 0x80000000 -.align 12 -pagetableL1Target: - .8byte 0x00000000200000CF # megapage - .8byte 0x0000000020014C0F # for VA starting at 80200000 (misaligned megapage) - .8byte 0x0000000020005001 # for VA starting at 80400000 (bad PBMT pages) - .8byte 0x4000000020004C01 # for VA starting at 80600000 (bad entry: nonleaf PTE can't have PBMT != 0) - .8byte 0x0000000020005401 # for VA starting at 80800000 (testing rwx permissions with cbom/cboz) - .8byte 0x00000000200000CF # megapage - .8byte 0x00000000200000CF # megapage - .8byte 0x00000000200000CF # megapage - .8byte 0x00000000200000CF # megapage - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - .8byte 0x0000000020004C01 - -# Leaf page table at 0x80013000 with NAPOT pages -.align 12 - #80000000 - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - - .8byte 0x800000002000E0CF - .8byte 0x800000002000E0CF - .8byte 0x800000002000E0CF - .8byte 0x800000002000E0CF - - .8byte 0x800000002000E0CF - .8byte 0x800000002000E0CF - -# Leaf page table at 0x80014000 with PBMT pages -.align 12 - #80400000 - .8byte 0x60000000200020CF # reserved entry - -# Leaf page table at 0x80015000 with various permissions for testing CBOM and CBOZ -.align 12 - #80800000 - .8byte 0x00000000200000CF # valid rwx for VA 80800000 - .8byte 0x00000000200000CB # valid r x for VA 80801000 - .8byte 0x00000000200000C3 # valid r for VA 80802000 - .8byte 0x00000000200000C9 # valid x for VA 80803000 - .8byte 0x00000000200000CD # valid wx for VA 80804000 (illegal combination, but used to test tlbcontrol) - .8byte 0x000000002000000F # valid rwx for VA 80805000 for covering ITLB translate and UpdateDA - .8byte 0x20000000200000CF # PBMT=1 for VA 80806000 for covering ITLB BadPBMT - -# Leaf page table at 0x80016000 with NAPOT pages -.align 12 - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF diff --git a/tests/coverage/gshare.S b/tests/coverage/gshare.S deleted file mode 100644 index d49ea6c225..0000000000 --- a/tests/coverage/gshare.S +++ /dev/null @@ -1,277 +0,0 @@ -/////////////////////////////////////////// -// gshare.S -// -// Written: Rose Thompson rose@rosethompson.net -// -// Purpose: basic check that global history and gshare branch npredictors are working as expected. Requires manual inspection. -// TODO: *** Automate checking prediction accuracy. -// -// A component of the CORE-V-WALLY configurable RISC-V project. -// https://github.com/openhwfoundation/cvw -// -// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at -// -// https://solderpad.org/licenses/SHL-2.1/ -// -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions -// and limitations under the License. -//////////////////////////////////////////////////////////////////////////////////////////////// - -// load code to initialize stack, handle interrupts, terminate - -#include "WALLY-init-lib.h" - -# run-elf.bash find this in project description -main: - - addi sp, sp, 8 - sd ra, 0(sp) - jal ra, oneLoopTest - jal ra, global_hist_6_space_test - jal ra, global_hist_4_space_test - jal ra, global_hist_3_space_test - jal ra, global_hist_2_space_test - jal ra, global_hist_1_space_test - jal ra, global_hist_0_space_test - - fence.I - -finished: - j done - - .data - -.section .text -.globl oneLoopTest -.type oneLoopTest, @function -oneLoopTest: - li t1, 0 - li t2, 30 - - mv t3, t1 - mv t4, t2 -oneLoopTest1: - # instruction - addi t3, t3, 1 - li t5, 10 # filler instructions - li t6, 100 - li a0, 1000 - li a1, 10000 - bne t3, t4, oneLoopTest1 # this branch toggles between taken and not taken. - - mv t3, t1 - mv t4, t2 -oneLoopTest2: - # instruction - addi t3, t3, 1 - li t5, 10 # filler instructions - li t6, 100 - li a0, 1000 - bne t3, t4, oneLoopTest2 # this branch toggles between taken and not taken. - - mv t3, t1 - mv t4, t2 -oneLoopTest3: - # instruction - addi t3, t3, 1 - li t5, 10 # filler instructions - li t6, 100 - bne t3, t4, oneLoopTest3 # this branch toggles between taken and not taken. - - mv t3, t1 - mv t4, t2 -oneLoopTest4: - # instruction - addi t3, t3, 1 - li t5, 10 # filler instructions - bne t3, t4, oneLoopTest4 # this branch toggles between taken and not taken. - - mv t3, t1 - mv t4, t2 -oneLoopTest5: - # instruction - addi t3, t3, 1 - bne t3, t4, oneLoopTest5 # this branch toggles between taken and not taken. - - ret - -.section .text -.globl global_hist_6_space_test -.type global_hist_6_space_test, @function -global_hist_6_space_test: - li t1, 1 - li t2, 200 - li t3, 0 - li t4, 1 - -loop_6: - # instruction - addi t3, t3, 1 - addi t3, t3, 1 - addi t3, t3, 1 - addi t3, t3, 1 - addi t3, t3, 1 - addi t3, t3, 1 - beqz t4, zero_6 # this branch toggles between taken and not taken. - li t4, 0 - j one_6 -zero_6: - li t4, 1 - addi t3, t3, 1 - addi t3, t3, 1 - addi t3, t3, 1 - add t1, t1, t4 - -one_6: - addi t3, t3, 1 - addi t3, t3, 1 - addi t3, t3, 1 - addi t3, t3, 1 - addi t2, t2, -1 - bnez t2, loop_6 - - ret - -.section .text -.globl global_hist_4_space_test -.type global_hist_4_space_test, @function -global_hist_4_space_test: - li t1, 1 - li t2, 200 - li t3, 0 - li t4, 1 - -loop_4: - # instruction - addi t3, t3, 1 - addi t3, t3, 1 - addi t3, t3, 1 - addi t3, t3, 1 - beqz t4, zero_4 # this branch toggles between taken and not taken. - li t4, 0 - j one_4 -zero_4: - li t4, 1 - addi t3, t3, 1 - add t1, t1, t4 - -one_4: - addi t3, t3, 1 - addi t2, t2, -1 - bnez t2, loop_4 - - ret - -.section .text -.globl global_hist_3_space_test -.type global_hist_3_space_test, @function -global_hist_3_space_test: - li t1, 1 - li t2, 200 - li t3, 0 - li t4, 1 - -loop_3: - # instruction - addi t3, t3, 1 - addi t3, t3, 1 - addi t3, t3, 1 - addi t3, t3, 1 - beqz t4, zero_3 # this branch toggles between taken and not taken. - li t4, 0 - j one_3 -zero_3: - li t4, 1 - addi t3, t3, 1 - add t1, t1, t4 - -one_3: - addi t3, t3, 1 - addi t2, t2, -1 - bnez t2, loop_3 - - ret - - -.section .text -.globl global_hist_2_space_test -.type global_hist_2_space_test, @function -global_hist_2_space_test: - li t1, 1 - li t2, 200 - li t3, 0 - li t4, 1 - -loop_2: - # instruction - addi t3, t3, 1 - addi t3, t3, 1 - beqz t4, zero_2 # this branch toggles between taken and not taken. - li t4, 0 - j one_2 -zero_2: - li t4, 1 - add t1, t1, t4 - -one_2: - addi t2, t2, -1 - bnez t2, loop_2 - - ret - -.section .text -.globl global_hist_1_space_test -.type global_hist_1_space_test, @function -global_hist_1_space_test: - li t1, 1 - li t2, 200 - li t3, 0 - li t4, 1 - -loop_1: - # instruction - addi t3, t3, 1 - beqz t4, zero_1 # this branch toggles between taken and not taken. - li t4, 0 - j one_1 -zero_1: - li t4, 1 - add t1, t1, t4 - -one_1: - addi t2, t2, -1 - bnez t2, loop_1 - - ret - -.section .text -.globl global_hist_0_space_test -.type global_hist_0_space_test, @function -global_hist_0_space_test: - li t1, 1 - li t2, 200 - li t3, 0 - li t4, 1 - -loop_0: - # instruction - beqz t4, zero_0 # this branch toggles between taken and not taken. - li t4, 0 - j one_0 -zero_0: - li t4, 1 - add t1, t1, t4 - -one_0: - addi t2, t2, -1 - bnez t2, loop_0 - - ret diff --git a/tests/coverage/hptwAccessFault.S b/tests/coverage/hptwAccessFault.S deleted file mode 100644 index 53ea73c03a..0000000000 --- a/tests/coverage/hptwAccessFault.S +++ /dev/null @@ -1,168 +0,0 @@ -/////////////////////////////////////////// -// hptwAccessFault.S -// -// Written: Rose Thompson rose@rosethompson.net -// -// Purpose: Force the HPTW to walk a page table with invalid addresses so that the pma checker -// generate access faults. -// -// A component of the CORE-V-WALLY configurable RISC-V project. -// https://github.com/openhwfoundation/cvw -// -// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at -// -// https://solderpad.org/licenses/SHL-2.1/ -// -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions -// and limitations under the License. -//////////////////////////////////////////////////////////////////////////////////////////////// - -// load code to initialize stack, handle interrupts, terminate - -#include "WALLY-init-lib.h" - -# run-elf.bash find this in project description -main: - - # Page table root address at 0x80010000 - li t5, 0x9000000000080010 - csrw satp, t5 - - # sfence.vma x0, x0 - - # switch to supervisor mode - li a0, 1 - ecall - li t5, 0 - li t2, 0x1000 - li t0, 0x8000001000 - - lw t1, 0(t0) # this load is a valid virtual address, but the page table will access an invalid address so it should cause a load access fault - li t1, 0x00008067 # this store is a valid virtual address, but the page table will access an invalid address so it should cause a store access fault - add t0, t0, t2 - sw t1, 0(t0) - - j jumppoint - -jumppoint: -.align 6 # aligns to cache line size - sw t1, 0(t0) - sw t1, 4(t0) - sw t1, 8(t0) - sw t1, 12(t0) - sw t1, 16(t0) - sw t1, 20(t0) - sw t1, 24(t0) - sw t1, 28(t0) - sw t1, 32(t0) - sw t1, 36(t0) - sw t1, 40(t0) - sw t1, 44(t0) - sw t1, 48(t0) - sw t1, 52(t0) # this one causes a concurrent I$ miss with HPTW access exception (store access exception) - sw t1, 56(t0) - lw t3, 0(t0) - lw t3, 4(t0) - lw t3, 8(t0) - lw t3, 12(t0) - lw t3, 16(t0) - - fence.I - -finished: - j done - -.data - -.align 16 -# Page table situated at 0x80010000 -pagetable: - .8byte 0x20004401 - .8byte 0x30004401 # point to invalid region of physical memory - -.align 12 - .8byte 0x0000004020004801 - .8byte 0x0000000020004801 - .8byte 0x0000000020004801 - - -.align 12 - .8byte 0x0000000020004C01 - -.align 12 - #80000000 - .8byte 0x200000CF - .8byte 0x200004CF - .8byte 0x200008CF - .8byte 0x20000CCF - - .8byte 0x200010CF - .8byte 0x200014CF - .8byte 0x200018CF - .8byte 0x20001CCF - - .8byte 0x200020CF - .8byte 0x200024CF - .8byte 0x200028CF - .8byte 0x20002CCF - - .8byte 0x200030CF - .8byte 0x200034CF - .8byte 0x200038CF - .8byte 0x20003CCF - - .8byte 0x200040CF - .8byte 0x200044CF - .8byte 0x200048CF - .8byte 0x20004CCF - - .8byte 0x200050CF - .8byte 0x200054CF - .8byte 0x200058CF - .8byte 0x20005CCF - - .8byte 0x200060CF - .8byte 0x200064CF - .8byte 0x200068CF - .8byte 0x20006CCF - - .8byte 0x200070CF - .8byte 0x200074CF - .8byte 0x200078CF - .8byte 0x20007CCF - - .8byte 0x200080CF - .8byte 0x200084CF - .8byte 0x200088CF - .8byte 0x20008CCF - - .8byte 0x200090CF - .8byte 0x200094CF - .8byte 0x200098CF - .8byte 0x20009CCF - - .8byte 0x2000A0CF - .8byte 0x2000A4CF - .8byte 0x2000A8CF - .8byte 0x2000ACCF - - .8byte 0x2000B0CF - .8byte 0x2000B4CF - .8byte 0x2000B8CF - .8byte 0x2000BCCF - - .8byte 0x2000C0CF - .8byte 0x2000C4CF - .8byte 0x2000C8CF - .8byte 0x2000CCCF - - .8byte 0x2000D0CF - .8byte 0x2000D4CF diff --git a/tests/coverage/ifuCamlineWrite.S b/tests/coverage/ifuCamlineWrite.S deleted file mode 100644 index 1cba008fdb..0000000000 --- a/tests/coverage/ifuCamlineWrite.S +++ /dev/null @@ -1,147 +0,0 @@ -/////////////////////////////////////////// -// ifuCamlineWrite.S -// -// Written: Miles Cook and Kevin Box 4/17 -// -// Acknowledgements: The pagetable and outline for this test was written by Manuel Mendoza -// and Noah Limpert. -// -// Purpose: Test coverage for TLBCamlines in IFU -// -// A component of the CORE-V-WALLY configurable RISC-V project. -// https://github.com/openhwfoundation/cvw -// -// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at -// -// https://solderpad.org/licenses/SHL-2.1/ -// -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions -// and limitations under the License. -//////////////////////////////////////////////////////////////////////////////////////////////// - -// load code to initialize stack, handle interrupts, terminate - -#include "WALLY-init-lib.h" - -# run-elf.bash find this in project description -main: - # Page table root address at 0x80010000 - li t5, 0x9000000000080010 - csrw satp, t5 - - # switch to supervisor mode - li a0, 1 - ecall - - li t0, 0x80015000 # base addr - - li t2, 0 # i = 0 - li t3, 33 # Max amount of Loops = 32 - -loop: bge t2, t3, finished # exit loop if i >= loops - li t4, 0x1000 - li t1, 0x00008067 # load in jalr - sw t1, 0 (t0) - fence.I - jalr t0 - add t0, t0, t4 - addi t2, t2, 1 - j loop - -finished: - j done - -.data - -.align 16 -# Page table situated at 0x80010000 -pagetable: - .8byte 0x20004401 // old page table was 200040 which just pointed to itself! wrong - -.align 12 - .8byte 0x0000000000000000 - .8byte 0x0000000020004801 - .8byte 0x0000000020004801 - - -.align 12 - .8byte 0x0000000020004C01 - //.8byte 0x00000200800CF// ADD IN THE MEGAPAGE should 3 nibbles of zeros be removed? - -.align 12 - #80000000 - .8byte 0x200000CF - .8byte 0x200004CF - .8byte 0x200008CF - .8byte 0x20000CCF - - .8byte 0x200010CF - .8byte 0x200014CF - .8byte 0x200018CF - .8byte 0x20001CCF - - .8byte 0x200020CF - .8byte 0x200024CF - .8byte 0x200028CF - .8byte 0x20002CCF - - .8byte 0x200030CF - .8byte 0x200034CF - .8byte 0x200038CF - .8byte 0x20003CCF - - .8byte 0x200040CF - .8byte 0x200044CF - .8byte 0x200048CF - .8byte 0x20004CCF - - .8byte 0x200050CF - .8byte 0x200054CF - .8byte 0x200058CF - .8byte 0x20005CCF - - .8byte 0x200060CF - .8byte 0x200064CF - .8byte 0x200068CF - .8byte 0x20006CCF - - .8byte 0x200070CF - .8byte 0x200074CF - .8byte 0x200078CF - .8byte 0x20007CCF - - .8byte 0x200080CF - .8byte 0x200084CF - .8byte 0x200088CF - .8byte 0x20008CCF - - .8byte 0x200090CF - .8byte 0x200094CF - .8byte 0x200098CF - .8byte 0x20009CCF - - .8byte 0x200100CF - .8byte 0x200104CF - .8byte 0x200108CF - .8byte 0x20010CCF - - .8byte 0x200110CF - .8byte 0x200114CF - .8byte 0x200118CF - .8byte 0x20011CCF - - .8byte 0x200120CF - .8byte 0x200124CF - .8byte 0x200128CF - .8byte 0x20012CCF - - .8byte 0x200130CF - .8byte 0x200134CF diff --git a/tests/coverage/lsu.S b/tests/coverage/lsu.S deleted file mode 100644 index 7f1f364378..0000000000 --- a/tests/coverage/lsu.S +++ /dev/null @@ -1,37 +0,0 @@ -/////////////////////////////////////////// -// lsu.S -// -// Written: Kevin Box and Miles Cook kbox@hmc.edu mdcook@hmc.edu 26 March 2023 -// -// Purpose: Test coverage for lsu -// -// A component of the CORE-V-WALLY configurable RISC-V project. -// https://github.com/openhwfoundation/cvw -// -// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at -// -// https://solderpad.org/licenses/SHL-2.1/ -// -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions -// and limitations under the License. -//////////////////////////////////////////////////////////////////////////////////////////////// - -// load code to initialize stack, handle interrupts, terminate -#include "WALLY-init-lib.h" - -main: - - sfence.vma x0, x0 // sfence.vma to assert TLBFlush - - li a0, 0x80000001 # misaligned address - amoadd.w t0, a0, (a0) # amo access to misaligned address - - j done diff --git a/tests/coverage/pmpTOR7.S b/tests/coverage/pmpTOR7.S deleted file mode 100644 index 3df43c4ea0..0000000000 --- a/tests/coverage/pmpTOR7.S +++ /dev/null @@ -1,86 +0,0 @@ -/////////////////////////////////////////// -// pmpTOR7.S -// -// Written: David_Harris@hmc.edu -// -// Purpose: Directed coverage test for PMP entry 7 TOR address decoder. -// -// Target gap: src/mmu/pmpadrdec.sv line 73, focused-expression term -// TORMatch for PMP entry 7, instance .../ifu/immu/immu/pmp/pmpchecker/pmpadrdecs[7]. -// -// pmpadrdec.sv computes (line 58-60): -// PAltPMPAdr = PhysicalAddress < {PMPAdr,2'b00} -// PAgePMPAdrIn = (cascaded ">= previous entry's PMPAdr") -// TORMatch = PAgePMPAdrIn & PAltPMPAdr -// and line 73 selects Match = (AdrMode==TOR) ? TORMatch : ... -// -// For a TOR entry i, PAgePMPAdrIn is the cascaded ">= pmpaddr[i-1]" term, so -// entry 7 matches when pmpaddr6<<2 <= PA < pmpaddr7<<2. Entry 7 had only ever -// been exercised with the checked PA OUTSIDE its TOR range (TORMatch=0). This -// test brackets a block of *instruction* memory with pmpaddr6/pmpaddr7 in TOR -// mode and then fetches/executes instructions whose PA lies inside that range, -// so the IMMU's pmpadrdecs[7] evaluates TORMatch=1. -// -// The decoder is combinational and independent of PMP priority: TORMatch[7] is -// driven for whatever PA the checker is evaluating regardless of which entry -// wins the priorityonehot. Entry 0 (set up by WALLY-init-lib.h to TOR RWX over -// the whole address space) keeps winning priority and granting X, so the fetch -// succeeds in machine mode while entry 7's TORMatch still toggles to 1. -// -// A component of the CORE-V-WALLY configurable RISC-V project. -// https://github.com/openhwfoundation/cvw -// -// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -//////////////////////////////////////////////////////////////////////////////////////////////// - -#include "WALLY-init-lib.h" - -main: - # ------------------------------------------------------------------ - # Program PMP entry 7 as a TOR region that brackets the executable - # block labelled [tor_lo, tor_hi). pmpaddr registers hold addr>>2. - # pmpaddr6 = tor_lo >> 2 (lower bound, cascaded into entry 7) - # pmpaddr7 = tor_hi >> 2 (upper bound of entry 7's TOR range) - # ------------------------------------------------------------------ - la t0, tor_lo - srli t0, t0, 2 - csrw pmpaddr6, t0 # pmpaddr6 = tor_lo>>2 -> entry 7 lower bound - - la t1, tor_hi - srli t1, t1, 2 - csrw pmpaddr7, t1 # pmpaddr7 = tor_hi>>2 -> entry 7 upper bound - - # ------------------------------------------------------------------ - # Configure pmpcfg entry 7 = TOR (A=01), R/W/X, L (locked). - # On RV64 pmpcfg0 holds entries 0..7, one byte each; entry 7 = bits[63:56]. - # Byte value = L(1)<<7 | A(01)<<3 | X(1)<<2 | W(1)<<1 | R(1) - # = 0x80 | 0x08 | 0x07 = 0x8F. - # Read-modify-write pmpcfg0 so the lower entries the init lib set up - # (entry 0 = 0x0F TOR RWX over all space) are preserved. - # ------------------------------------------------------------------ - li t2, 0x8F - slli t2, t2, 56 # place in entry-7 byte position [63:56] - csrr t3, pmpcfg0 # read current pmpcfg0 (preserve entries 0-6) - or t3, t3, t2 # OR in entry-7 = TOR RWX L - csrw pmpcfg0, t3 - - # ------------------------------------------------------------------ - # Now execute instructions whose fetch PA is inside [tor_lo, tor_hi). - # Each fetch drives the IMMU PhysicalAddress through pmpadrdecs[7], where - # pmpaddr6<<2 <= PA < pmpaddr7<<2 ==> TORMatch[7] = 1 (pmpadrdec.sv:60,73) - # Entry 0 still wins priority and grants X, so the fetch is allowed. - # ------------------------------------------------------------------ -.align 3 -tor_lo: # lower bound of TOR region (PMP-bracketed code) - nop - nop - addi t4, x0, 1 - addi t4, t4, 1 - nop - nop -.align 3 -tor_hi: # upper bound: addresses >= here are NOT in entry 7's range - - j done diff --git a/tests/coverage/pmpcfg1.S b/tests/coverage/pmpcfg1.S deleted file mode 100644 index 37dcec14b2..0000000000 --- a/tests/coverage/pmpcfg1.S +++ /dev/null @@ -1,48 +0,0 @@ -// another set of pmpcfg tests. A new file is made because pmpcfg register fields are -// locked forever after writing 1 to the lock bit for the first time. - -// Kevin Wan, kewan@hmc.edu, 4/13/2023 -// This set tests locking the pmpXcfg fields in descending order again, without setting the TOR bits. -// for the other part of the tests, see pmpcfg.S - -#include "WALLY-init-lib.h" -main: - li t0, 0x800 - csrw pmpcfg0, t0 - li t0, 0x8000000 - csrw pmpcfg0, t0 - - li t0, 0x8000000000000000 - csrw pmpcfg2, t0 - li t0, 0x80000000000000 - csrw pmpcfg2, t0 - li t0, 0x800000000000 - csrw pmpcfg2, t0 - li t0, 0x8000000000 - csrw pmpcfg2, t0 - li t0, 0x80000000 - csrw pmpcfg2, t0 - li t0, 0x800000 - csrw pmpcfg2, t0 - li t0, 0x8000 - csrw pmpcfg2, t0 - li t0, 0x80 - csrw pmpcfg2, t0 - li t0, 0x8000000000000000 - csrw pmpcfg0, t0 - li t0, 0x80000000000000 - csrw pmpcfg0, t0 - li t0, 0x800000000000 - csrw pmpcfg0, t0 - li t0, 0x8000000000 - csrw pmpcfg0, t0 - li t0, 0x80000000 - csrw pmpcfg0, t0 - li t0, 0x800000 - csrw pmpcfg0, t0 - li t0, 0x8000 - csrw pmpcfg0, t0 - - - - j done diff --git a/tests/coverage/pmpcfg2.S b/tests/coverage/pmpcfg2.S deleted file mode 100644 index 4e0002e6e4..0000000000 --- a/tests/coverage/pmpcfg2.S +++ /dev/null @@ -1,12 +0,0 @@ -// pmpcfg part 3 -// Kevin Wan, kewan@hmc.edu, 4/18/2023 -// locks each pmpXcfg bit field in order, from X = 15 to X = 0, with the A[1:0] field set to TOR. -// See the next part in pmpcfg1.S - -#include "WALLY-init-lib.h" -main: - li t0, 0x80 - csrw pmpcfg0, t0 - - - j done diff --git a/tests/coverage/pmppriority.S b/tests/coverage/pmppriority.S deleted file mode 100644 index 8a30c6019d..0000000000 --- a/tests/coverage/pmppriority.S +++ /dev/null @@ -1,158 +0,0 @@ -// pmppriority test cases -// Kevin Wan kewan@hmc.edu 4/27/2023 -// want memory ranges to match: -// 1. only the most significant address and none of the lower ones, -// 2. the most significant address and ANY of the lower ones. - - - -#include "WALLY-init-lib.h" -main: - - li t1, 0x21FFFFFF // start at 0x8000000 with a range of 1000000. Address format is set to NAPOT in pmpcfg. - csrw pmpaddr0, t1 - csrw pmpaddr1, t1 - csrw pmpaddr2, t1 - csrw pmpaddr3, t1 - csrw pmpaddr4, t1 - csrw pmpaddr5, t1 - csrw pmpaddr6, t1 - csrw pmpaddr7, t1 - - csrw pmpaddr8, t1 - csrw pmpaddr9, t1 - csrw pmpaddr10, t1 - csrw pmpaddr11, t1 - csrw pmpaddr12, t1 - csrw pmpaddr13, t1 - csrw pmpaddr14, t1 - csrw pmpaddr15, t1 - - - li t0, 0x1F - csrw pmpcfg0, t0 //set to XWR and NAPOT - sw zero, 0(sp) - - li t0, 0x1F00 - csrw pmpcfg0, t0 - sw zero, 0(sp) - - li t0, 0x1F1F - csrw pmpcfg0, t0 - sw zero, 0(sp) - - li t0, 0x1F0000 - csrw pmpcfg0, t0 - sw zero, 0(sp) - - li t0, 0x1F1F1F - csrw pmpcfg0, t0 - sw zero, 0(sp) - - li t0, 0x1F000000 - csrw pmpcfg0, t0 - sw zero, 0(sp) - - li t0, 0x1F1F1F1F - csrw pmpcfg0, t0 - sw zero, 0(sp) - - li t0, 0x1F00000000 - csrw pmpcfg0, t0 - sw zero, 0(sp) - - li t0, 0x1F1F1F1F1F - csrw pmpcfg0, t0 - sw zero, 0(sp) - - li t0, 0x1F0000000000 - csrw pmpcfg0, t0 - sw zero, 0(sp) - - li t0, 0x1F1F1F1F1F1F - csrw pmpcfg0, t0 - sw zero, 0(sp) - - li t0, 0x1F000000000000 - csrw pmpcfg0, t0 - sw zero, 0(sp) - - li t0, 0x1F1F1F1F1F1F1F - csrw pmpcfg0, t0 - sw zero, 0(sp) - - li t0, 0x1F00000000000000 - csrw pmpcfg0, t0 - sw zero, 0(sp) - - li t0, 0x1F1F1F1F1F1F1F1F - csrw pmpcfg0, t0 - sw zero, 0(sp) - - li t0, 0x0 - csrw pmpcfg0, t0 - li t0, 0x1F - csrw pmpcfg2, t0 - sw zero, 0(sp) - - li t0, 0x1F00 - csrw pmpcfg2, t0 - sw zero, 0(sp) - - li t0, 0x1F0000 - csrw pmpcfg2, t0 - sw zero, 0(sp) - - li t0, 0x1F00000000 - csrw pmpcfg2, t0 - sw zero, 0(sp) - - li t0, 0x1F0000000000 - csrw pmpcfg2, t0 - sw zero, 0(sp) - - li t0, 0x1F000000000000 - csrw pmpcfg2, t0 - sw zero, 0(sp) - - li t0, 0x1F00000000000000 - csrw pmpcfg2, t0 - sw zero, 0(sp) - - li t0, 0x1F1F1F1F1F1F1F1F - csrw pmpcfg0, t0 - li t0, 0x1F - csrw pmpcfg2, t0 - sw zero, 0(sp) - - li t0, 0x1F1F - csrw pmpcfg2, t0 - sw zero, 0(sp) - - li t0, 0x1F1F1F - csrw pmpcfg2, t0 - sw zero, 0(sp) - - li t0, 0x1F1F1F1F - csrw pmpcfg2, t0 - sw zero, 0(sp) - - li t0, 0x1F1F1F1F1F - csrw pmpcfg2, t0 - sw zero, 0(sp) - - li t0, 0x1F1F1F1F1F1F - csrw pmpcfg2, t0 - sw zero, 0(sp) - - li t0, 0x1F1F1F1F1F1F1F - csrw pmpcfg2, t0 - sw zero, 0(sp) - - li t0, 0x1F1F1F1F1F1F1F1F - csrw pmpcfg2, t0 - sw zero, 0(sp) - - - - j done diff --git a/tests/coverage/tlbASID.S b/tests/coverage/tlbASID.S deleted file mode 100644 index 56c85e8823..0000000000 --- a/tests/coverage/tlbASID.S +++ /dev/null @@ -1,131 +0,0 @@ -/////////////////////////////////////////// -// tlbASID.S -// -// Written: mmendozamanriquez@hmc.edu 4 April 2023 -// nlimpert@hmc.edu -// -// Purpose: Test coverage for IFU TLB camlines with mismatched ASID values. This file tests odd -// numbered camlines. tlbASID2.S covers even numbered tlb camlines. These two files are identical. -// -// A component of the CORE-V-WALLY configurable RISC-V project. -// https://github.com/openhwfoundation/cvw -// -// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at -// -// https://solderpad.org/licenses/SHL-2.1/ -// -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions -// and limitations under the License. -//////////////////////////////////////////////////////////////////////////////////////////////// - - - -// load code to initialize stack, handle interrupts, terminate - -#include "WALLY-init-lib.h" - -# run-elf.bash find this in project description -main: - # Page table root address at 0x80010000 - li t5, 0x9000000000080080 // try making asid = 0. - csrw satp, t5 - - # sfence.vma x0, x0 - - # switch to supervisor mode - li a0, 1 - ecall - - li t0, 0xC0000000 - - li t2, 0 # i = 0 - li t5, 0 # j = 0 // now use as a counter for new asid loop - li t3, 32 # Max amount of Loops = 32 - -loop: bge t2, t3, finished # exit loop if i >= loops - sfence.vma - li t1, 0x00008067 #load in jalr - sw t1, 0(t0) - fence.I - jalr t0 - li t5, 0x9001000000080080 // try making asid = 1 - csrw satp, t5 - jalr t0 - li t5, 0x9000000000080080 // try making asid = 0 - csrw satp, t5 - li t4, 0x1000 - add t0, t0, t4 - addi t2, t2, 1 - j loop - -finished: - j done - -.data -.align 19 -# level 3 Page table situated at 0x8008 0000, should point to 8008,1000 -pagetable: - .8byte 0x20020401 - -.align 12 // level 2 page table, contains direction to a gigapage - .8byte 0x0 - .8byte 0x0 - .8byte 0x200000CF // gigapage that starts at 8000 0000 goes to C000 0000 - .8byte 0x20020801 // pointer to next page table entry at 8008 2000 - -.align 12 // level 1 page table, points to level 0 page table - .8byte 0x20020C01 - -.align 12 // level 0 page table, points to address C000 0000 // FOR NOW ALL OF THESE GO TO 8 instead of C cause they start with 2 - .8byte 0x200000CF // access xC000 0000 - .8byte 0x200004CF // access xC000 1000 - .8byte 0x200008CF // access xC000 2000 - .8byte 0x20000CCF // access xC000 3000 - - .8byte 0x200010CF // access xC000 4000 - .8byte 0x200014CF - .8byte 0x200018CF - .8byte 0x20001CCF - - .8byte 0x200020CF // access xC000 8000 - .8byte 0x200024CF - .8byte 0x200028CF - .8byte 0x20002CCF - - .8byte 0x200030CF // access xC000 C000 - .8byte 0x200034CF - .8byte 0x200038CF - .8byte 0x20003CCF - - .8byte 0x200040CF // access xC001 0000 - .8byte 0x200044CF - .8byte 0x200048CF - .8byte 0x20004CCF - - .8byte 0x200050CF // access xC001 4000 - .8byte 0x200054CF - .8byte 0x200058CF - .8byte 0x20005CCF - - .8byte 0x200060CF // access xC001 8000 - .8byte 0x200064CF - .8byte 0x200068CF - .8byte 0x20006CCF - - .8byte 0x200070CF // access xC001 C000 - .8byte 0x200074CF - .8byte 0x200078CF - .8byte 0x20007CCF - - .8byte 0x200080CF // access xC002 0000 - .8byte 0x200084CF - .8byte 0x200088CF - .8byte 0x20008CCF diff --git a/tests/coverage/tlbGLB.S b/tests/coverage/tlbGLB.S deleted file mode 100644 index 565fc2692e..0000000000 --- a/tests/coverage/tlbGLB.S +++ /dev/null @@ -1,181 +0,0 @@ -/////////////////////////////////////////// -// tlbGLB.S -// -// Written: mmendozamanriquez@hmc.edu 4 April 2023 -// nlimpert@hmc.edu -// Modified: kevin.j.thomas@okstate.edu May/4/20203 -// -// Purpose: Coverage for the Page Table Entry Global flag check. -// -// A component of the CORE-V-WALLY configurable RISC-V project. -// https://github.com/openhwfoundation/cvw -// -// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at -// -// https://solderpad.org/licenses/SHL-2.1/ -// -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions -// and limitations under the License. -//////////////////////////////////////////////////////////////////////////////////////////////// - - -// load code to initialize stack, handle interrupts, terminate - -#include "WALLY-init-lib.h" - -# run-elf.bash find this in project description -main: - # Page table root address at 0x80010000 - li t5, 0x9000000000080080 // try making asid = 0. - csrw satp, t5 - - # switch to supervisor mode - li a0, 1 - ecall - - li t5, 0 # j = 0, run nASID only once - li t3, 32 //Max amount of Loops = 32 - li t4, 0x1000 //offset between addresses. - li t1, 0x00008067 //load in jalr x0 x1 0 instruction to be stored - -setup: - li t0, 0xC0000000 //starting address - li t2, 0 # i = 0 - beq t5, zero, loop //jump to first loop - -loop2: #jump to each of the addresses in different address space - bge t2, t3, done - jalr t0 //jump to instruction at the virtual address - add t0, t0, t4 //change address for next loop - addi t2, t2, 1 //keep track of number of loops ran - j loop2 - -loop: #store jalr across memory - bge t2, t3, nASID # exit loop if i >= loops - sw t1, 0(t0) //stores this jalr in the virtual address - fence.I //invalidate instruction cache - jalr t0 //jump to instruction at the virtual address - add t0, t0, t4 //change address for next loop - addi t2, t2, 1 //keep track of number of loops ran - j loop - -nASID: #swap to different address space -> jump to each address - li a0, 3 //swap to machine mode - ecall - li t5, 0x9000100000080080 //swap to address space 1 from 0 - csrw satp, t5 - li a0, 1 // change back to supervisor mode. - ecall - li t5, 1 //flag for finished after loops - j setup - - - -.data -.align 19 -# level 3 Page table situated at 0x8008 0000, should point to 8008,1000 -pagetable: - .8byte 0x20020401 - -.align 12 // level 2 page table, contains direction to a gigapageg - .8byte 0x0 - .8byte 0x0 - .8byte 0x200000EF // gigapage that starts at 8000 0000 goes to C000 0000 - .8byte 0x20020821 // pointer to next page table entry at 8008 2000 - -.align 12 // level 1 page table, points to level 0 page table - .8byte 0x20020C21 - -.align 12 // level 0 page table, points to address C000 0000 // FOR NOW ALL OF THESE GO TO 8 instead of C cause they start with 2 - .8byte 0x200000EF // access xC000 0000 - .8byte 0x200004EF // access xC000 1000 - .8byte 0x200008EF // access xC000 2000 - .8byte 0x20000CEF // access xC000 3000 - - .8byte 0x200010EF // access xC000 4000 - .8byte 0x200014EF - .8byte 0x200018EF - .8byte 0x20001CEF - - .8byte 0x200020EF // access xC000 8000 - .8byte 0x200024EF - .8byte 0x200028EF - .8byte 0x20002CEF - - .8byte 0x200030EF // access xC000 C000 - .8byte 0x200034EF - .8byte 0x200038EF - .8byte 0x20003CEF - - .8byte 0x200040EF // access xC001 0000 - .8byte 0x200044EF - .8byte 0x200048EF - .8byte 0x20004CEF - - .8byte 0x200050EF // access xC001 4000 - .8byte 0x200054EF - .8byte 0x200058EF - .8byte 0x20005CEF - - .8byte 0x200060EF // access xC001 8000 - .8byte 0x200064EF - .8byte 0x200068EF - .8byte 0x20006CEF - - .8byte 0x200070EF // access xC001 C000 - .8byte 0x200074eF - .8byte 0x200078EF - .8byte 0x20007CEF - - .8byte 0x200080EF // access xC002 0000 - .8byte 0x200084EF - .8byte 0x200088EF - .8byte 0x20008CEF - - .8byte 0x200010EF // access xC000 4000 - .8byte 0x200014EF - .8byte 0x200018EF - .8byte 0x20001CEF - - .8byte 0x200020EF // access xC000 8000 - .8byte 0x200024EF - .8byte 0x200028EF - .8byte 0x20002CEF - - .8byte 0x200030EF // access xC000 C000 - .8byte 0x200034EF - .8byte 0x200038EF - .8byte 0x20003CEF - - .8byte 0x200040EF // access xC001 0000 - .8byte 0x200044EF - .8byte 0x200048EF - .8byte 0x20004CEF - - .8byte 0x200050EF // access xC001 4000 - .8byte 0x200054EF - .8byte 0x200058EF - .8byte 0x20005CEF - - .8byte 0x200060EF // access xC001 8000 - .8byte 0x200064EF - .8byte 0x200068EF - .8byte 0x20006CEF - - .8byte 0x200070EF // access xC001 C000 - .8byte 0x200074eF - .8byte 0x200078EF - .8byte 0x20007CEF - - .8byte 0x200080EF // access xC002 0000 - .8byte 0x200084EF - .8byte 0x200088EF - .8byte 0x20008CEF diff --git a/tests/coverage/tlbGP.S b/tests/coverage/tlbGP.S deleted file mode 100644 index 3f90b7ec68..0000000000 --- a/tests/coverage/tlbGP.S +++ /dev/null @@ -1,154 +0,0 @@ -/////////////////////////////////////////// -// tlbGP.S -// -// Written: mmendozamanriquez@hmc.edu 4 April 2023 -// nlimpert@hmc.edu -// -// Purpose: Create Page tables and access gigapages -// -// A component of the CORE-V-WALLY configurable RISC-V project. -// https://github.com/openhwfoundation/cvw -// -// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at -// -// https://solderpad.org/licenses/SHL-2.1/ -// -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions -// and limitations under the License. -//////////////////////////////////////////////////////////////////////////////////////////////// - -// load code to initialize stack, handle interrupts, terminate - -#include "WALLY-init-lib.h" - -# run-elf.bash find this in project description -main: - # Page table root address at 0x80010000 - li t5, 0x9000000000080010 - csrw satp, t5 - - # sfence.vma x0, x0 - - # switch to supervisor mode - li a0, 1 - ecall - li t5, 0 - li t0, 0xC0200000 // go to first gigapage - li t4, 0x40000000 // put this outside the loop. - li t2, 0 # i = 0 - li t3, 64 # Max amount of Loops = 16 - -loop: bge t2, t3, finished # exit loop if i >= loops - lw t1, 0(t0) - li t1, 0x00008067 #load in jalr - sw t1, 0(t0) - fence.I - jalr t0 - add t0, t0, t4 - addi t2, t2, 1 - j loop - -finished: - j done - -.data - -.align 16 -# Page table situated at 0x80010000 -pagetable: - .8byte 0x20004401 - -.align 12 - .8byte 0x000000CF //8000 0000 - .8byte 0x100000CF - .8byte 0x200000CF - .8byte 0x200000CF - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - - - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF - .8byte 0x200000CF diff --git a/tests/coverage/tlbMP.S b/tests/coverage/tlbMP.S deleted file mode 100644 index 475c4cdfc0..0000000000 --- a/tests/coverage/tlbMP.S +++ /dev/null @@ -1,199 +0,0 @@ -/////////////////////////////////////////// -// tlbMP.S -// -// Written: mmendozamanriquez@hmc.edu 4 April 2023 -// nlimpert@hmc.edu -// -// Purpose: Test coverage for LSU -// -// A component of the CORE-V-WALLY configurable RISC-V project. -// https://github.com/openhwfoundation/cvw -// -// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at -// -// https://solderpad.org/licenses/SHL-2.1/ -// -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions -// and limitations under the License. -//////////////////////////////////////////////////////////////////////////////////////////////// - -// load code to initialize stack, handle interrupts, terminate - -#include "WALLY-init-lib.h" - -main: - # Page table root address at 0x80010000 - li t5, 0x9000000000080010 - csrw satp, t5 - - # sfence.vma x0, x0 - - # switch to supervisor mode - li a0, 1 - ecall - - li t5, 0 - li t0, 0x84000000 // go to first megapage - li t4, 0x200000 // put this outside the loop. - li t2, 0 # i = 0 - li t3, 32 # Max amount of Loops = 16 - -loop: bge t2, t3, finished # exit loop if i >= loops - li t1, 0x00008067 #load in jalr - sw t1, 0(t0) - fence.I - jalr t0 - add t0, t0, t4 - addi t2, t2, 1 - j loop - -finished: - j done - -.data - -.align 16 -# Page table situated at 0x80010000 -pagetable: - .8byte 0x20004401 - -.align 12 - .8byte 0x0000000020004801 - .8byte 0x0000000020004801 - .8byte 0x0000000020004801 - - -.align 12 // megapages starting at 8000 0000 going to 8480 0000 (32*2 MiB beyond that) - - .8byte 0x200000CF // access 8000,0000 - .8byte 0x200800CF // access 8020,0000 - .8byte 0x201000CF // acesss 8040,0000 - .8byte 0x201800CF // acesss 8060,0000 - - .8byte 0x202000CF // access 8080,0000 - .8byte 0x202800CF // access 80A0,0000 - .8byte 0x203000CF // access 80C0,0000 - .8byte 0x203800CF // access 80E0,0000 - - .8byte 0x204000CF // access 8100,0000 - .8byte 0x204800CF - .8byte 0x205000CF - .8byte 0x205800CF - - .8byte 0x206000CF // access 8180,0000 - .8byte 0x206800CF - .8byte 0x207000CF - .8byte 0x207800CF - - .8byte 0x208000CF // access 8200,0000 - .8byte 0x208800CF - .8byte 0x209000CF - .8byte 0x209800CF - - .8byte 0x20A000CF // access 8280,0000 - .8byte 0x20A800CF - .8byte 0x20B000CF - .8byte 0x20B800CF - - .8byte 0x20C000CF // access 8300,0000 - .8byte 0x20C800CF - .8byte 0x20D000CF - .8byte 0x20D800CF - - .8byte 0x20E000CF // access 8380,0000 - .8byte 0x20E800CF - .8byte 0x20F000CF - .8byte 0x20F800CF - - .8byte 0x200000CF // access 8000,0000 I AM REPEATING PTE TO SAVE TIME. - .8byte 0x200800CF // access 8020,0000 - .8byte 0x201000CF // acesss 8040,0000 - .8byte 0x201800CF // acesss 8060,0000 - - .8byte 0x202000CF // access 8080,0000 - .8byte 0x202800CF // access 80A0,0000 - .8byte 0x203000CF // access 80C0,0000 - .8byte 0x203800CF // access 80E0,0000 - - .8byte 0x204000CF // access 8100,0000 - .8byte 0x204800CF - .8byte 0x205000CF - .8byte 0x205800CF - - .8byte 0x206000CF // access 8180,0000 - .8byte 0x206800CF - .8byte 0x207000CF - .8byte 0x207800CF - - .8byte 0x208000CF // access 8200,0000 - .8byte 0x208800CF - .8byte 0x209000CF - .8byte 0x209800CF - - .8byte 0x20A000CF // access 8280,0000 - .8byte 0x20A800CF - .8byte 0x20B000CF - .8byte 0x20B800CF - - .8byte 0x20C000CF // access 8300,0000 - .8byte 0x20C800CF - .8byte 0x20D000CF - .8byte 0x20D800CF - - .8byte 0x20E000CF // access 8380,0000 - .8byte 0x20E800CF - .8byte 0x20F000CF - .8byte 0x20F800CF - - .8byte 0x20004C01 - // Kilopage entry, for addresses from 8400, 0000 to 841F, FFFF - // point to ... - -.align 12 // should start at 84000000 - .8byte 0x210000CF - .8byte 0x210004CF - .8byte 0x210008CF - .8byte 0x21000CCF - - .8byte 0x210010CF - .8byte 0x210014CF - .8byte 0x210018CF - .8byte 0x21001CCF - - .8byte 0x210020CF - .8byte 0x210024CF - .8byte 0x210028CF - .8byte 0x21002CCF - - .8byte 0x210030CF - .8byte 0x210034CF - .8byte 0x210038CF - .8byte 0x21003CCF - - .8byte 0x210040CF - .8byte 0x210044CF - .8byte 0x210048CF - .8byte 0x21004CCF - - .8byte 0x210050CF - .8byte 0x210054CF - .8byte 0x210058CF - .8byte 0x21005CCF - - .8byte 0x210060CF - .8byte 0x210064CF - .8byte 0x210068CF - .8byte 0x21006CCF - - .8byte 0x210070CF - .8byte 0x210074CF - .8byte 0x210078CF - .8byte 0x21007CCF diff --git a/tests/coverage/tlbTP.S b/tests/coverage/tlbTP.S deleted file mode 100644 index 671f7278c2..0000000000 --- a/tests/coverage/tlbTP.S +++ /dev/null @@ -1,145 +0,0 @@ -/////////////////////////////////////////// -// tlbTP.S -// -// Written: mmendozamanriquez@hmc.edu 4 April 2023 -// nlimpert@hmc.edu -// -// Purpose: Test coverage for LSU -// -// A component of the CORE-V-WALLY configurable RISC-V project. -// https://github.com/openhwfoundation/cvw -// -// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at -// -// https://solderpad.org/licenses/SHL-2.1/ -// -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions -// and limitations under the License. -//////////////////////////////////////////////////////////////////////////////////////////////// - -// load code to initialize stack, handle interrupts, terminate - -#include "WALLY-init-lib.h" - -# run-elf.bash find this in project description -main: - # Page table root address at 0x80010000 - li t5, 0x9000000000080010 - csrw satp, t5 - - # sfence.vma x0, x0 - - # switch to supervisor mode - li a0, 1 - ecall - li t5, 0 - li t0, 0x80000000 // go to first gigapage - li t4, 0x8000000000 // put this outside the loop. - li t2, 0 # i = 0 - li t3, 64 # run through 64 PTEs - -loop: bge t2, t3, finished # exit loop if i >= loops - li t1, 0x00008067 - sw t1, 0(t0) - fence.I - jalr t0 - add t0, t0, t4 - addi t2, t2, 1 - j loop - - -finished: - j done - -.data - -.align 16 -# Page table situated at 0x80010000 -pagetable: - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF - .8byte 0x0CF From f43313328a5a999a56e91ecd87c3728023478917 Mon Sep 17 00:00:00 2001 From: David Harris Date: Fri, 9 Oct 2026 17:20:21 -0700 Subject: [PATCH 4/9] pmp, pmpcfg, pmpadrdecs, csrwrites, priv, wfitimeout: check results They passed by reaching done. Each now logs every trap and checks the log against the traps it expects (cause, mepc and mtval), and checks the CSR values it writes: PMP configuration and address WARL and lock behavior, including the TOR lock of the previous pmpaddr; PMP access faults and the fetches a locked region allows; envcfg legalization; stimecmp and mstatus after privileged instructions; and the wfi timeout. pmpadrdecs aligns its scratch word so that the supervisor AMO it expects to fault really does, and wfitimeout adds a supervisor wfi that must time out. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: David Harris --- tests/coverage/csrwrites.S | 143 +++++++++++++++++++++++-- tests/coverage/pmp.S | 194 ++++++++++++++++++++++++++++++++++ tests/coverage/pmpadrdecs.S | 154 ++++++++++++++++++++++++++- tests/coverage/pmpcfg.S | 152 +++++++++++++++++++++++++++ tests/coverage/priv.S | 202 +++++++++++++++++++++++++++++++++++- tests/coverage/wfitimeout.S | 112 ++++++++++++++++++++ 6 files changed, 948 insertions(+), 9 deletions(-) diff --git a/tests/coverage/csrwrites.S b/tests/coverage/csrwrites.S index f5a85d3a15..26e7f86cec 100644 --- a/tests/coverage/csrwrites.S +++ b/tests/coverage/csrwrites.S @@ -23,49 +23,180 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. //////////////////////////////////////////////////////////////////////////////////////////////// +// +// Checks: stimecmp, satp, stvec and sscratch read back what was written; menvcfg and senvcfg +// legalize an all-ones-but-FIOM write to the fields rv64gc implements (STCE, PBMTE, ADUE, CBZE, +// CBCFE, CBIE); each FIOM set and clear reads back (failure indices 1-10). Writing the read-only +// time CSR is an illegal instruction, and each ecall reports the privilege mode the previous one +// switched to (8 = U, 9 = S, 11 = M), checked in a trap log (indices 0x100-0x1FF). FIOM itself has +// no visible effect on Wally because its fences do nothing. // load code to initialize stack, handle interrupts, terminate #include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" + +// menvcfg: STCE, PBMTE, ADUE, CBZE, CBCFE, CBIE = 11 +#define MENVCFG_LEGAL 0xE0000000000000F0 +// senvcfg: CBZE, CBCFE, CBIE = 11 +#define SENVCFG_LEGAL 0xF0 + +// CHECK_CSR csr, expected, index: fail with index unless the CSR reads expected +.macro CHECK_CSR csr, expected, index + csrr a1, \csr + li a2, \expected + SC_CHECK_EQ a1, a2, \index +.endm main: + la t0, log_trap + csrw mtvec, t0 + li t0, -5 csrw stimecmp, t0 # initialize so ImperasDV agrees - csrrw t0, stimecmp, t0 - csrrw t0, satp, zero - csrrw t0, stvec, zero - csrrw t0, sscratch, zero + csrrw a1, stimecmp, t0 + li a2, -5 + SC_CHECK_EQ a1, a2, 1 + csrrw a1, satp, zero + csrrw a1, stvec, zero + csrrw a1, sscratch, zero + CHECK_CSR stimecmp, -5, 2 + CHECK_CSR satp, 0, 3 + CHECK_CSR stvec, 0, 4 + CHECK_CSR sscratch, 0, 5 li t0, -2 - csrrw t1, menvcfg, t0 - csrrw t2, senvcfg, t0 + csrrw a3, menvcfg, t0 + csrrw a4, senvcfg, t0 + CHECK_CSR menvcfg, MENVCFG_LEGAL, 6 + CHECK_CSR senvcfg, SENVCFG_LEGAL, 7 # Test writing to TIME CSR +ill_time: csrw time, zero # testing FIOM with different privilege modes # setting environment config (to both 1 and 0) in each privilege mode csrsi menvcfg, 1 + CHECK_CSR menvcfg, MENVCFG_LEGAL | 1, 8 li a0, 1 +ecall_m1: ecall # enter supervisor mode li a0, 0 +ecall_s1: ecall # enter user mode li a0, 1 +ecall_u1: ecall # enter supervisor mode csrsi senvcfg, 1 + CHECK_CSR senvcfg, SENVCFG_LEGAL | 1, 9 li a0, 0 +ecall_s2: ecall # enter user mode li a0, 3 +ecall_u2: ecall # enter machine mode csrci menvcfg, 1 + CHECK_CSR menvcfg, MENVCFG_LEGAL, 10 li a0, 1 +ecall_m2: ecall # enter supervisor mode li a0, 0 +ecall_s3: ecall # enter user mode + li a0, 3 +ecall_u3: + ecall # back to machine mode to check the trap log + jal check_traps + SC_PASS + j done +// Trap log: log_trap records mcause, mepc and mtval of every trap, then continues in the +// WALLY-init-lib.h handler, which handles ecalls and skips other faulting instructions. check_traps +// compares the log with expected_traps, failing with index 0x100 + 3n + k for field k (0 = mcause, +// 1 = mepc, 2 = mtval) of trap n, or 0x1FF if the number of traps differs. +.equ TRAPLOG_MAX, 16 +.align 4 +log_trap: + csrrw t0, mscratch, t0 # t0 = trap stack pointer, mscratch = saved t0 + sd t1, -16(t0) + sd t2, -24(t0) + sd t3, -32(t0) + la t1, traplog + ld t2, 0(t1) + addi t2, t2, 1 # count the trap + sd t2, 0(t1) + li t1, TRAPLOG_MAX + bgtu t2, t1, 1f # log full: count only + slli t3, t2, 4 + slli t2, t2, 3 + add t2, t2, t3 + la t1, traplog - 16 + add t1, t1, t2 # trap n (from 1) goes at traplog + 24n - 16 + csrr t2, mcause + sd t2, 0(t1) + csrr t2, mepc + sd t2, 8(t1) + csrr t2, mtval + sd t2, 16(t1) +1: ld t3, -32(t0) + ld t2, -24(t0) + ld t1, -16(t0) + csrrw t0, mscratch, t0 # restore t0 and the trap stack pointer + j trap_handler + +check_traps: + la a1, traplog + 8 + la a2, expected_traps + ld a3, 0(a2) + addi a2, a2, 8 + slli a4, a3, 1 + add a3, a3, a4 # fields to compare + li a4, 0x100 +1: beqz a3, 2f + ld a5, 0(a1) # actual + ld a6, 0(a2) # expected + bne a5, a6, 3f + addi a1, a1, 8 + addi a2, a2, 8 + addi a3, a3, -1 + addi a4, a4, 1 + j 1b +2: la a1, traplog # and no extra traps + ld a5, 0(a1) + la a2, expected_traps + ld a6, 0(a2) + li a4, 0x1FF + bne a5, a6, 3f + ret +3: la t0, selfcheck_record # record the mismatch as SC_CHECK_EQ does + li t1, 2 + sd t1, 0(t0) + sd a4, 8(t0) + sd zero, 16(t0) + sd a6, 24(t0) + sd a5, 32(t0) + SC_REPORT_FAIL j done + +.section .data +.align 3 +traplog: # number of traps, then mcause, mepc and mtval of each + .fill 1 + 3*TRAPLOG_MAX, 8, 0 +expected_traps: # number of traps, then mcause, mepc and mtval of each + .dword (2f - 1f) / 24 +1: .dword 2, ill_time, 0xc0101073 # illegal instruction: time is read-only + .dword 11, ecall_m1, 0 # ecall from M + .dword 9, ecall_s1, 0 # ecall from S + .dword 8, ecall_u1, 0 # ecall from U + .dword 9, ecall_s2, 0 + .dword 8, ecall_u2, 0 + .dword 11, ecall_m2, 0 + .dword 9, ecall_s3, 0 + .dword 8, ecall_u3, 0 +2: diff --git a/tests/coverage/pmp.S b/tests/coverage/pmp.S index 3a020ce370..e9a9134c0f 100644 --- a/tests/coverage/pmp.S +++ b/tests/coverage/pmp.S @@ -8,8 +8,35 @@ #include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" + +// Checks (rv64gc: 16 entries, G = 4, so NA4 is not selectable and NAPOT/TOR addresses are rounded +// to 64-byte grains): the pmpcfg read after the first configuration (failure index 1), pmpcfg0 +// and pmpcfg2 after each configuration, showing that locked entries ignore every later write +// while unlocked ones take it (10-41), and every pmpaddr at the end, showing that locked entries +// kept their address (50-65). At the end every entry is locked, so the regions bind M-mode too: +// entry 1 is TOR [0x80000100, 0x800001C0) and entries 3-15 are NAPOT [0x80000400, 0x80000500), +// all RW without X. Stores and loads in both regions work (70-71), fetches inside them raise an +// instruction access fault with mtval = the address, and fetches just outside them run (trap log, +// indices 0x100-0x1FF). + +// CHECK_CSR csr, expected, index: fail with index unless the CSR reads expected +.macro CHECK_CSR csr, expected, index + csrr a1, \csr + li a2, \expected + SC_CHECK_EQ a1, a2, \index +.endm + +// FETCH adr: call adr; log_trap returns to ra if the fetch faults +.macro FETCH adr + li a1, \adr + jalr ra, 0(a1) +.endm main: + la t0, log_trap + csrw mtvec, t0 + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // BEGIN Configuration and Testing Starting at Register: 0 @@ -105,6 +132,8 @@ csrw pmpaddr15, t5 li t4, 1806234828062034819 csrw pmpcfg0, t4 csrr t5, pmpcfg0 +li a2, 0x190109019b838b83 # NA4 (0x93, 0x11) is not selectable: the A field stays OFF +SC_CHECK_EQ t5, a2, 1 # write pmpcfg2, output 0x181008001c140c04 li t4, 1733894653101739012 @@ -112,6 +141,8 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x190109019b838b83, 10 +CHECK_CSR pmpcfg2, 0x180008001c040c04, 11 // END Configuration and Testing Starting at Register: 0 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -214,6 +245,8 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x1909019b9b838b83, 12 +CHECK_CSR pmpcfg2, 0x1808001c1c0c0419, 13 // END Configuration and Testing Starting at Register: 1 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -316,6 +349,8 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x09019b9b9b838b83, 14 +CHECK_CSR pmpcfg2, 0x08001c1c0c041919, 15 // END Configuration and Testing Starting at Register: 2 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -418,6 +453,8 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x019b9b9b9b838b83, 16 +CHECK_CSR pmpcfg2, 0x001c1c0c04191909, 17 // END Configuration and Testing Starting at Register: 3 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -520,6 +557,8 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x9b9b9b9b9b838b83, 18 +CHECK_CSR pmpcfg2, 0x1c1c0c0419190901, 19 // END Configuration and Testing Starting at Register: 4 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -622,6 +661,8 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x9b9b9b9b9b838b83, 20 +CHECK_CSR pmpcfg2, 0x1c0c04191909019b, 21 // END Configuration and Testing Starting at Register: 5 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -724,6 +765,8 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x9b9b9b9b9b838b83, 22 +CHECK_CSR pmpcfg2, 0x0c04191909019b9b, 23 // END Configuration and Testing Starting at Register: 6 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -826,6 +869,8 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x9b9b9b9b9b838b83, 24 +CHECK_CSR pmpcfg2, 0x04191909019b9b9b, 25 // END Configuration and Testing Starting at Register: 7 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -929,6 +974,8 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x9b9b9b9b9b838b83, 26 +CHECK_CSR pmpcfg2, 0x191909019b9b9b9b, 27 // END Configuration and Testing Starting at Register: 8 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1031,6 +1078,8 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x9b9b9b9b9b838b83, 28 +CHECK_CSR pmpcfg2, 0x1909019b9b9b9b9b, 29 // END Configuration and Testing Starting at Register: 9 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1133,6 +1182,8 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x9b9b9b9b9b838b83, 30 +CHECK_CSR pmpcfg2, 0x09019b9b9b9b9b9b, 31 // END Configuration and Testing Starting at Register: 10 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1235,6 +1286,8 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x9b9b9b9b9b838b83, 32 +CHECK_CSR pmpcfg2, 0x019b9b9b9b9b9b9b, 33 // END Configuration and Testing Starting at Register: 11 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1337,6 +1390,8 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x9b9b9b9b9b838b83, 34 +CHECK_CSR pmpcfg2, 0x9b9b9b9b9b9b9b9b, 35 // END Configuration and Testing Starting at Register: 12 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1439,6 +1494,8 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x9b9b9b9b9b838b83, 36 +CHECK_CSR pmpcfg2, 0x9b9b9b9b9b9b9b9b, 37 // END Configuration and Testing Starting at Register: 13 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1541,6 +1598,8 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x9b9b9b9b9b838b83, 38 +CHECK_CSR pmpcfg2, 0x9b9b9b9b9b9b9b9b, 39 // END Configuration and Testing Starting at Register: 14 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1643,9 +1702,144 @@ csrw pmpcfg2, t4 // Testing +CHECK_CSR pmpcfg0, 0x9b9b9b9b9b838b83, 40 +CHECK_CSR pmpcfg2, 0x9b9b9b9b9b9b9b9b, 41 // END Configuration and Testing Starting at Register: 15 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// + # Every entry is locked now, so no address changed after its entry (or the next TOR entry) locked + CHECK_CSR pmpaddr0, 0x20000040, 50 + CHECK_CSR pmpaddr1, 0x20000070, 51 + CHECK_CSR pmpaddr2, 0x200000b0, 52 + CHECK_CSR pmpaddr3, 0x2000011f, 53 + CHECK_CSR pmpaddr4, 0x2000011f, 54 + CHECK_CSR pmpaddr5, 0x2000011f, 55 + CHECK_CSR pmpaddr6, 0x2000011f, 56 + CHECK_CSR pmpaddr7, 0x2000011f, 57 + CHECK_CSR pmpaddr8, 0x2000011f, 58 + CHECK_CSR pmpaddr9, 0x2000011f, 59 + CHECK_CSR pmpaddr10, 0x2000011f, 60 + CHECK_CSR pmpaddr11, 0x2000011f, 61 + CHECK_CSR pmpaddr12, 0x2000011f, 62 + CHECK_CSR pmpaddr13, 0x2000011f, 63 + CHECK_CSR pmpaddr14, 0x2000011f, 64 + CHECK_CSR pmpaddr15, 0x2000011f, 65 + + # The locked regions are RW for M-mode: stores and loads work + li a1, 0x80000140 # entry 1, TOR + li a2, 0x13579BDF + sw a2, 0(a1) + lw a3, 0(a1) + SC_CHECK_EQ a3, a2, 70 + li a1, 0x80000480 # entries 3-15, NAPOT + li a2, 0x2468ACE0 + sw a2, 0(a1) + lw a3, 0(a1) + SC_CHECK_EQ a3, a2, 71 + + # They have no X: put a ret at each address to fetch, then fetch inside and just outside them + li a2, 0x00008067 # ret + .irp adr, 0x800001BC, 0x800001C0, 0x800003FC, 0x80000400, 0x800004FC, 0x80000500 + li a1, \adr + sw a2, 0(a1) + .endr + fence.i + FETCH 0x800001BC # last word of the TOR region: fault + FETCH 0x800001C0 # TOR top is exclusive: runs + FETCH 0x800003FC # below the NAPOT region: runs + FETCH 0x80000400 # first word of the NAPOT region: fault + FETCH 0x800004FC # last word of the NAPOT region: fault + FETCH 0x80000500 # above the NAPOT region: runs + + jal check_traps + SC_PASS + j done +// Trap log: log_trap records mcause, mepc and mtval of every trap. An instruction access fault +// (from FETCH) returns to ra; anything else continues in the WALLY-init-lib.h handler. +// check_traps compares the log with expected_traps, failing with index 0x100 + 3n + k for field k +// (0 = mcause, 1 = mepc, 2 = mtval) of trap n, or 0x1FF if the number of traps differs. +.equ TRAPLOG_MAX, 16 +.align 4 +log_trap: + csrrw t0, mscratch, t0 # t0 = trap stack pointer, mscratch = saved t0 + sd t1, -16(t0) + sd t2, -24(t0) + sd t3, -32(t0) + la t1, traplog + ld t2, 0(t1) + addi t2, t2, 1 # count the trap + sd t2, 0(t1) + li t1, TRAPLOG_MAX + bgtu t2, t1, 1f # log full: count only + slli t3, t2, 4 + slli t2, t2, 3 + add t2, t2, t3 + la t1, traplog - 16 + add t1, t1, t2 # trap n (from 1) goes at traplog + 24n - 16 + csrr t2, mcause + sd t2, 0(t1) + csrr t2, mepc + sd t2, 8(t1) + csrr t2, mtval + sd t2, 16(t1) +1: csrr t2, mcause + li t1, 1 # instruction access fault? + bne t2, t1, 2f + csrw mepc, ra # return from the FETCH + ld t3, -32(t0) + ld t2, -24(t0) + ld t1, -16(t0) + csrrw t0, mscratch, t0 + mret +2: ld t3, -32(t0) + ld t2, -24(t0) + ld t1, -16(t0) + csrrw t0, mscratch, t0 # restore t0 and the trap stack pointer + j trap_handler + +check_traps: + la a1, traplog + 8 + la a2, expected_traps + ld a3, 0(a2) + addi a2, a2, 8 + slli a4, a3, 1 + add a3, a3, a4 # fields to compare + li a4, 0x100 +1: beqz a3, 2f + ld a5, 0(a1) # actual + ld a6, 0(a2) # expected + bne a5, a6, 3f + addi a1, a1, 8 + addi a2, a2, 8 + addi a3, a3, -1 + addi a4, a4, 1 + j 1b +2: la a1, traplog # and no extra traps + ld a5, 0(a1) + la a2, expected_traps + ld a6, 0(a2) + li a4, 0x1FF + bne a5, a6, 3f + ret +3: la t0, selfcheck_record # record the mismatch as SC_CHECK_EQ does + li t1, 2 + sd t1, 0(t0) + sd a4, 8(t0) + sd zero, 16(t0) + sd a6, 24(t0) + sd a5, 32(t0) + SC_REPORT_FAIL j done + +.section .data +.align 3 +traplog: # number of traps, then mcause, mepc and mtval of each + .fill 1 + 3*TRAPLOG_MAX, 8, 0 +expected_traps: # number of traps, then mcause, mepc and mtval of each + .dword (2f - 1f) / 24 +1: .dword 1, 0x800001BC, 0x800001BC # instruction access fault: TOR region without X + .dword 1, 0x80000400, 0x80000400 # instruction access fault: NAPOT region without X + .dword 1, 0x800004FC, 0x800004FC +2: diff --git a/tests/coverage/pmpadrdecs.S b/tests/coverage/pmpadrdecs.S index 22c29ca53d..4723a4b4e0 100644 --- a/tests/coverage/pmpadrdecs.S +++ b/tests/coverage/pmpadrdecs.S @@ -1,23 +1,55 @@ // pmpadrdecs // Liam Chalk, lchalk@hmc.edu, 4/27/2023 // Setting AdrMode to 2 or 3 for pmpadrdecs[0-4] +// +// Checks (rv64gc: 16 entries, G = 4, so NA4 is not selectable and a TOR region is 64-byte aligned): +// - pmpcfg writes read back legalized: A = NA4 keeps the old A field (indices 1-4, 7-8) +// - pmpaddr reads back with the low G bits zero in OFF and TOR modes (5-6, 10-25 and the +// second check of each TOR step) +// - each TOR step's pmpcfg and top-of-range pmpaddr read back (30-51); the jumps to pmpjump run +// in M-mode with no lock bits, so they must not fault +// - the S-mode AMO to a TOR region with X only raises a store/AMO access fault, not a load +// access fault, with mtval = the address, and leaves memory unchanged (60 and the trap log) +// - a trap log holds exactly the expected traps (indices 0x100-0x1FF) #include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" + +// CHECK_CSR csr, expected, index: fail with index unless the CSR reads expected +.macro CHECK_CSR csr, expected, index + csrr a1, \csr + li a2, \expected + SC_CHECK_EQ a1, a2, \index +.endm + +// CHECK_CSR_REG csr, reg, index: fail with index unless the CSR reads reg with its low 4 bits (G) clear +.macro CHECK_CSR_REG csr, reg, index + csrr a1, \csr + andi a2, \reg, -16 + SC_CHECK_EQ a1, a2, \index +.endm + main: + la t0, log_trap + csrw mtvec, t0 # Writing values to pmpcfg0 to change AdrMode to 2 or 3 # pmpadrdec[0] li t0, 0x0000000010 csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0x08, 1 # entry 0 keeps TOR from WALLY-init-lib.h # pmpadrdec[1] li t0, 0x0000001800 csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0x1800, 2 # pmpadrdec[2] li t0, 0x0000180000 csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0x180000, 3 # pmpadrdec[4] li t0, 0x1000000000 csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0, 4 # entry 4 keeps OFF # test hitting each region in NA4 mode for DMMU li t0, 0x20000000 # address 0x80000000 @@ -25,9 +57,13 @@ main: csrw pmpaddr14, t0 csrw pmpaddr13, t0 csrw pmpaddr12, t0 + CHECK_CSR pmpaddr15, 0x20000000, 5 + CHECK_CSR pmpaddr12, 0x20000000, 6 li t0, 0x1717171717171717 # every region is NA4 XWR csrw pmpcfg0, t0 csrw pmpcfg2, t0 + CHECK_CSR pmpcfg0, 0x0707070707070707, 7 # NA4 is not selectable, so every region stays OFF + CHECK_CSR pmpcfg2, 0x0707070707070707, 8 # test hitting region in NA4 mode for IMMU @@ -49,6 +85,9 @@ main: csrw pmpaddr2, t1 csrw pmpaddr1, t1 csrw pmpaddr0, t1 + .irp n, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 + CHECK_CSR_REG pmpaddr\n, t1, 10+\n + .endr jalr t0 # test hitting region in TOR mode for IMMU @@ -60,6 +99,8 @@ main: csrw pmpcfg2, 0 csrw pmpaddr0, t2 jalr t0 + CHECK_CSR pmpcfg0, 0x0F, 30 + CHECK_CSR_REG pmpaddr0, t2, 31 # region 1 TOR at pmpjump li t3, 0x0F00 # TOR XWR @@ -68,6 +109,8 @@ main: csrw pmpaddr0, t1 csrw pmpaddr1, t2 jalr t0 + CHECK_CSR pmpcfg0, 0x0F00, 32 + CHECK_CSR_REG pmpaddr1, t2, 33 # region 1 TOR at pmpjump li t3, 0x0F00 # TOR XWR @@ -76,6 +119,8 @@ main: csrw pmpaddr0, t1 csrw pmpaddr1, t2 jalr t0 + CHECK_CSR pmpcfg0, 0x0F00, 34 + CHECK_CSR_REG pmpaddr1, t2, 35 # region 8 TOR at pmpjump li t3, 0x0F # TOR XWR @@ -84,6 +129,8 @@ main: csrw pmpaddr7, t1 csrw pmpaddr8, t2 jalr t0 + CHECK_CSR pmpcfg2, 0x0F, 36 + CHECK_CSR_REG pmpaddr8, t2, 37 # region 9 TOR at pmpjump li t3, 0x0F00 # TOR XWR @@ -92,6 +139,8 @@ main: csrw pmpaddr8, t1 csrw pmpaddr9, t2 jalr t0 + CHECK_CSR pmpcfg2, 0x0F00, 38 + CHECK_CSR_REG pmpaddr9, t2, 39 # region 10 TOR at pmpjump li t3, 0x0F0000 # TOR XWR @@ -100,6 +149,8 @@ main: csrw pmpaddr9, t1 csrw pmpaddr10, t2 jalr t0 + CHECK_CSR pmpcfg2, 0x0F0000, 40 + CHECK_CSR_REG pmpaddr10, t2, 41 # region 11 TOR at pmpjump li t3, 0x0F000000 # TOR XWR @@ -108,6 +159,8 @@ main: csrw pmpaddr10, t1 csrw pmpaddr11, t2 jalr t0 + CHECK_CSR pmpcfg2, 0x0F000000, 42 + CHECK_CSR_REG pmpaddr11, t2, 43 # region 12 TOR at pmpjump li t3, 0x0F00000000 # TOR XWR @@ -116,6 +169,8 @@ main: csrw pmpaddr11, t1 csrw pmpaddr12, t2 jalr t0 + CHECK_CSR pmpcfg2, 0x0F00000000, 44 + CHECK_CSR_REG pmpaddr12, t2, 45 # region 13 TOR at pmpjump li t3, 0x0F0000000000 # TOR XWR @@ -124,6 +179,8 @@ main: csrw pmpaddr12, t1 csrw pmpaddr13, t2 jalr t0 + CHECK_CSR pmpcfg2, 0x0F0000000000, 46 + CHECK_CSR_REG pmpaddr13, t2, 47 # region 14 TOR at pmpjump li t3, 0x0F000000000000 # TOR XWR @@ -132,6 +189,8 @@ main: csrw pmpaddr13, t1 csrw pmpaddr14, t2 jalr t0 + CHECK_CSR pmpcfg2, 0x0F000000000000, 48 + CHECK_CSR_REG pmpaddr14, t2, 49 # region 15 TOR at pmpjump li t3, 0x0F00000000000000 # TOR XWR @@ -140,6 +199,8 @@ main: csrw pmpaddr14, t1 csrw pmpaddr15, t2 jalr t0 + CHECK_CSR pmpcfg2, 0x0F00000000000000, 50 + CHECK_CSR_REG pmpaddr15, t2, 51 @@ -164,20 +225,109 @@ main: # switch to supervisor mode li a0, 1 +ecall_m1: ecall # test the AMO +amo: amoswap.w t1, zero, (s0) # attempt amo; should get store but not load access fault because this region is X only li a0, 3 +ecall_s1: ecall # return to M mode + lw a1, 0(s0) + li a2, 0x13579BDF + SC_CHECK_EQ a1, a2, 60 # the faulting AMO left memory unchanged + + jal check_traps + SC_PASS j done .align 2 pmpjump: ret -.align 2 +.align 6 # scratch is the last word of a 64-byte PMP grain, so the 4-byte +.skip 60 # TOR region 1 rounds out to the whole grain rather than to nothing scratch: - .word 0 + .word 0x13579BDF + +// Trap log: log_trap records mcause, mepc and mtval of every trap, then continues in the +// WALLY-init-lib.h handler, which handles ecalls and skips other faulting instructions. check_traps +// compares the log with expected_traps, failing with index 0x100 + 3n + k for field k (0 = mcause, +// 1 = mepc, 2 = mtval) of trap n, or 0x1FF if the number of traps differs. +.equ TRAPLOG_MAX, 16 +.align 4 +log_trap: + csrrw t0, mscratch, t0 # t0 = trap stack pointer, mscratch = saved t0 + sd t1, -16(t0) + sd t2, -24(t0) + sd t3, -32(t0) + la t1, traplog + ld t2, 0(t1) + addi t2, t2, 1 # count the trap + sd t2, 0(t1) + li t1, TRAPLOG_MAX + bgtu t2, t1, 1f # log full: count only + slli t3, t2, 4 + slli t2, t2, 3 + add t2, t2, t3 + la t1, traplog - 16 + add t1, t1, t2 # trap n (from 1) goes at traplog + 24n - 16 + csrr t2, mcause + sd t2, 0(t1) + csrr t2, mepc + sd t2, 8(t1) + csrr t2, mtval + sd t2, 16(t1) +1: ld t3, -32(t0) + ld t2, -24(t0) + ld t1, -16(t0) + csrrw t0, mscratch, t0 # restore t0 and the trap stack pointer + j trap_handler + +check_traps: + la a1, traplog + 8 + la a2, expected_traps + ld a3, 0(a2) + addi a2, a2, 8 + slli a4, a3, 1 + add a3, a3, a4 # fields to compare + li a4, 0x100 +1: beqz a3, 2f + ld a5, 0(a1) # actual + ld a6, 0(a2) # expected + bne a5, a6, 3f + addi a1, a1, 8 + addi a2, a2, 8 + addi a3, a3, -1 + addi a4, a4, 1 + j 1b +2: la a1, traplog # and no extra traps + ld a5, 0(a1) + la a2, expected_traps + ld a6, 0(a2) + li a4, 0x1FF + bne a5, a6, 3f + ret +3: la t0, selfcheck_record # record the mismatch as SC_CHECK_EQ does + li t1, 2 + sd t1, 0(t0) + sd a4, 8(t0) + sd zero, 16(t0) + sd a6, 24(t0) + sd a5, 32(t0) + SC_REPORT_FAIL + j done + +.section .data +.align 3 +traplog: # number of traps, then mcause, mepc and mtval of each + .fill 1 + 3*TRAPLOG_MAX, 8, 0 +expected_traps: # number of traps, then mcause, mepc and mtval of each + .dword (2f - 1f) / 24 +1: .dword 11, ecall_m1, 0 # ecall from M + .dword 7, amo, scratch # store/AMO access fault: region 1 has X only + .dword 9, ecall_s1, 0 # ecall from S +2: diff --git a/tests/coverage/pmpcfg.S b/tests/coverage/pmpcfg.S index 1013439b76..c91c912510 100644 --- a/tests/coverage/pmpcfg.S +++ b/tests/coverage/pmpcfg.S @@ -3,104 +3,256 @@ // Liam Chalk, lchalk@hmc.edu, 4/25/2023 // locks each pmpXcfg bit field in order, from X = 15 to X = 0, with the A[1:0] field set to TOR. // See the next part in pmpcfg1.S +// +// Checks (rv64gc: 16 entries, G = 4, so NA4 is not selectable): +// - every pmpcfg write reads back legalized: A = NA4 keeps the old A field, and the bytes of +// locked entries ignore the write (failure indices 1-24) +// - pmpcfg1 and pmpcfg3 do not exist on RV64, so writing them is an illegal instruction +// - after the lock sequence, pmpaddr0 ignores writes because entry 1 is locked TOR although +// entry 0 itself is unlocked, pmpaddr1 and pmpaddr15 ignore writes because their entries are +// locked, and entry 0's cfg byte still accepts writes while the locked bytes beside it do +// not (indices 50-56) +// - the trap log holds exactly the four illegal instructions (indices 0x100-0x1FF) #include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" + +// CHECK_CSR csr, expected, index: fail with index unless the CSR reads expected +.macro CHECK_CSR csr, expected, index + csrr a1, \csr + li a2, \expected + SC_CHECK_EQ a1, a2, \index +.endm + main: + la t0, log_trap + csrw mtvec, t0 li t0, 0x90000000 csrw pmpaddr0, t0 li t0, 0x00000017 csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0x0F, 1 # entry 0 keeps TOR from WALLY-init-lib.h: NA4 not selectable li t0, 0x90000000 csrw pmpaddr2, t0 li t0, 0x00000017 csrw pmpcfg2, t0 + CHECK_CSR pmpcfg2, 0x07, 2 # entry 8 keeps OFF li t0, 0x90000000 csrw pmpaddr0, t0 li t0, 0x00001700 csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0x0700, 3 li t0, 0x90000000 csrw pmpaddr0, t0 li t0, 0x00001700 +ill_cfg1a: csrw pmpcfg1, t0 li t0, 0x90000000 csrw pmpaddr0, t0 li t0, 0x00001700 csrw pmpcfg2, t0 + CHECK_CSR pmpcfg2, 0x0700, 4 li t0, 0x90000000 csrw pmpaddr0, t0 li t0, 0x00001700 +ill_cfg3a: csrw pmpcfg3, t0 li t0, 0x90000000 csrw pmpaddr1, t0 li t0, 0x00001700 +ill_cfg1b: csrw pmpcfg1, t0 li t0, 0x90000000 csrw pmpaddr2, t0 li t0, 0x00001700 csrw pmpcfg2, t0 + CHECK_CSR pmpcfg2, 0x0700, 5 li t0, 0x90000000 csrw pmpaddr3, t0 li t0, 0x00001700 +ill_cfg3b: csrw pmpcfg3, t0 li t0, 0x90000000 csrw pmpaddr0, t0 li t0, 0x00170000 csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0x070000, 6 li t0, 0x90000000 csrw pmpaddr2, t0 li t0, 0x00170000 csrw pmpcfg2, t0 + CHECK_CSR pmpcfg2, 0x070000, 7 li t0, 0x90000000 csrw pmpaddr0, t0 li t0, 0x17000000 csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0x07000000, 8 li t0, 0x90000000 csrw pmpaddr2, t0 li t0, 0x17000000 csrw pmpcfg2, t0 + CHECK_CSR pmpcfg2, 0x07000000, 9 + # Lock entries 15 down to 1 as TOR with no permissions. The other bytes of each write are 0, + # so the read back shows that the entries locked earlier ignored them. Every TOR region is + # empty (pmpaddr0-3 = 0x90000000, pmpaddr4-15 = 0), so the locked entries deny nothing. li t0, 0x8800000000000000 csrw pmpcfg2, t0 + CHECK_CSR pmpcfg2, 0x8800000000000000, 10 li t0, 0x88000000000000 csrw pmpcfg2, t0 + CHECK_CSR pmpcfg2, 0x8888000000000000, 11 li t0, 0x880000000000 csrw pmpcfg2, t0 + CHECK_CSR pmpcfg2, 0x8888880000000000, 12 li t0, 0x8800000000 csrw pmpcfg2, t0 + CHECK_CSR pmpcfg2, 0x8888888800000000, 13 li t0, 0x88000000 csrw pmpcfg2, t0 + CHECK_CSR pmpcfg2, 0x8888888888000000, 14 li t0, 0x880000 csrw pmpcfg2, t0 + CHECK_CSR pmpcfg2, 0x8888888888880000, 15 li t0, 0x8800 csrw pmpcfg2, t0 + CHECK_CSR pmpcfg2, 0x8888888888888800, 16 li t0, 0x88 csrw pmpcfg2, t0 + CHECK_CSR pmpcfg2, 0x8888888888888888, 17 li t0, 0x8800000000000000 csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0x8800000000000000, 18 li t0, 0x88000000000000 csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0x8888000000000000, 19 li t0, 0x880000000000 csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0x8888880000000000, 20 li t0, 0x8800000000 csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0x8888888800000000, 21 li t0, 0x88000000 csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0x8888888888000000, 22 li t0, 0x880000 csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0x8888888888880000, 23 li t0, 0x8800 csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0x8888888888888800, 24 + + # Locked addresses and entries ignore writes + li t0, 0x12345670 + csrw pmpaddr0, t0 # entry 0 is unlocked, but entry 1 is locked TOR + CHECK_CSR pmpaddr0, 0x90000000, 50 + csrw pmpaddr1, t0 # entry 1 is locked + CHECK_CSR pmpaddr1, 0x90000000, 51 + csrw pmpaddr15, t0 # entry 15 is locked + CHECK_CSR pmpaddr15, 0, 52 + li t0, -1 + csrw pmpcfg2, t0 # entries 8-15 are all locked + CHECK_CSR pmpcfg2, 0x8888888888888888, 53 + li t0, 0x0F # TOR RWX for entry 0; try to clear entries 1-7 + csrw pmpcfg0, t0 + CHECK_CSR pmpcfg0, 0x888888888888880F, 54 + csrw pmpcfg0, zero # entry 0 is still unlocked + CHECK_CSR pmpcfg0, 0x8888888888888800, 55 + CHECK_CSR pmpaddr2, 0x90000000, 56 + jal check_traps + SC_PASS j done + +// Trap log: log_trap records mcause, mepc and mtval of every trap, then continues in the +// WALLY-init-lib.h handler, which handles ecalls and skips other faulting instructions. check_traps +// compares the log with expected_traps, failing with index 0x100 + 3n + k for field k (0 = mcause, +// 1 = mepc, 2 = mtval) of trap n, or 0x1FF if the number of traps differs. +.equ TRAPLOG_MAX, 16 +.align 4 +log_trap: + csrrw t0, mscratch, t0 # t0 = trap stack pointer, mscratch = saved t0 + sd t1, -16(t0) + sd t2, -24(t0) + sd t3, -32(t0) + la t1, traplog + ld t2, 0(t1) + addi t2, t2, 1 # count the trap + sd t2, 0(t1) + li t1, TRAPLOG_MAX + bgtu t2, t1, 1f # log full: count only + slli t3, t2, 4 + slli t2, t2, 3 + add t2, t2, t3 + la t1, traplog - 16 + add t1, t1, t2 # trap n (from 1) goes at traplog + 24n - 16 + csrr t2, mcause + sd t2, 0(t1) + csrr t2, mepc + sd t2, 8(t1) + csrr t2, mtval + sd t2, 16(t1) +1: ld t3, -32(t0) + ld t2, -24(t0) + ld t1, -16(t0) + csrrw t0, mscratch, t0 # restore t0 and the trap stack pointer + j trap_handler + +check_traps: + la a1, traplog + 8 + la a2, expected_traps + ld a3, 0(a2) + addi a2, a2, 8 + slli a4, a3, 1 + add a3, a3, a4 # fields to compare + li a4, 0x100 +1: beqz a3, 2f + ld a5, 0(a1) # actual + ld a6, 0(a2) # expected + bne a5, a6, 3f + addi a1, a1, 8 + addi a2, a2, 8 + addi a3, a3, -1 + addi a4, a4, 1 + j 1b +2: la a1, traplog # and no extra traps + ld a5, 0(a1) + la a2, expected_traps + ld a6, 0(a2) + li a4, 0x1FF + bne a5, a6, 3f + ret +3: la t0, selfcheck_record # record the mismatch as SC_CHECK_EQ does + li t1, 2 + sd t1, 0(t0) + sd a4, 8(t0) + sd zero, 16(t0) + sd a6, 24(t0) + sd a5, 32(t0) + SC_REPORT_FAIL + j done + +.section .data +.align 3 +traplog: # number of traps, then mcause, mepc and mtval of each + .fill 1 + 3*TRAPLOG_MAX, 8, 0 +expected_traps: # number of traps, then mcause, mepc and mtval of each + .dword (2f - 1f) / 24 +1: .dword 2, ill_cfg1a, 0x3a129073 # illegal instruction: pmpcfg1 does not exist on RV64 + .dword 2, ill_cfg3a, 0x3a329073 + .dword 2, ill_cfg1b, 0x3a129073 + .dword 2, ill_cfg3b, 0x3a329073 +2: diff --git a/tests/coverage/priv.S b/tests/coverage/priv.S index a70b3e3e5d..c65502a59e 100644 --- a/tests/coverage/priv.S +++ b/tests/coverage/priv.S @@ -5,6 +5,14 @@ // // Purpose: Test coverage for EBU // +// Checks: a trap log records mcause, mepc and mtval of every trap and must match expected_traps +// exactly: each illegal privileged instruction or CSR access traps with cause 2 at that instruction +// with the instruction bits in mtval, each ecall reports the privilege mode it came from (8 = U, +// 9 = S, 11 = M, which also checks that sret from M-mode returned to U through sstatus.SPP), and +// nothing else traps, including no interrupt (failure indices 0x100-0x1FF). stimecmp keeps its +// value across the illegal S-mode writes and takes the legal ones (indices 1-4 and 6), and the +// final mret to M-mode with MPRV = 1 leaves MPRV set and MPP = U (index 5). +// // A component of the CORE-V-WALLY configurable RISC-V project. // https://github.com/openhwfoundation/cvw // @@ -26,8 +34,19 @@ // load code to initialize stack, handle interrupts, terminate #include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" + +// CHECK_CSR csr, expected, index: fail with index unless the CSR reads expected +.macro CHECK_CSR csr, expected, index + csrr a1, \csr + li a2, \expected + SC_CHECK_EQ a1, a2, \index +.endm main: + la t0, log_trap + csrw mtvec, t0 + # Tests sret in machine mode la t1, sretdone csrw sepc, t1 @@ -37,28 +56,36 @@ sretdone: # switch to user mode li a0, 0 +trap01: ecall +trap02: sret #should be treated as illegal instruction +trap03: mret #mret in user mode and should be illegal # switch to supervisor mode li a0, 1 +trap04: ecall # Test read to stimecmp fails when MCOUNTEREN_TM is not set li t1, -3 +trap05: csrw stimecmp, t1 +trap06: csrr t0, stimecmp # satp write with mstatus.TVM = 1 bseti t0, zero, 20 +trap07: csrs mstatus, t0 csrw satp, zero # STIMECMP from S mode # 1st is when MENVCFG_STCE is cleared li a0, 3 +trap08: ecall # starts in M-mode li t1, -3 csrw stimecmp, t1 # sets stimecmp to large value to prevent it from interrupting immediately @@ -68,22 +95,32 @@ sretdone: csrs sie, t0 # enables sie.stie csrw menvcfg, x0 li a0, 1 +trap09: ecall # enter S-mode +trap10: csrw stimecmp, zero li a0, 3 +trap11: ecall # in M-mode + CHECK_CSR stimecmp, -3, 1 # the illegal S-mode write left stimecmp unchanged li t0, 32 csrs mip, t0 li a0, 1 +trap12: ecall # in S-mode and expects stimer interrupt to occur li a0, 3 +trap13: ecall # return to M-mode csrsi mcounteren, 2 # mcounteren_tm = 1 li a0, 1 +trap14: ecall # supervisor mode again +trap15: csrw stimecmp, zero li a0, 3 +trap16: ecall # machine mode again + CHECK_CSR stimecmp, -3, 2 # still illegal with mcounteren.TM = 1 while menvcfg.STCE = 0 # STIMECMP from S mode # 2nd is when MENVCFG_STCE is set @@ -92,16 +129,23 @@ sretdone: slli t0, t0, 63 csrw menvcfg, t0 li a0, 1 +trap17: ecall # enter S-mode +trap18: csrw stimecmp, zero li a0, 3 +trap19: ecall # return to M-mode + CHECK_CSR stimecmp, -3, 3 # still illegal with menvcfg.STCE = 1 while mcounteren.TM = 0 csrsi mcounteren, 2 # mcounteren_tm = 1 li a0, 1 +trap20: ecall # supervisor mode again csrw stimecmp, zero li a0, 3 +trap21: ecall # machine mode again + CHECK_CSR stimecmp, 0, 4 # legal with both set # writes to FCSR li t0, 1 @@ -112,6 +156,7 @@ sretdone: # switch to supervisor mode li a0, 1 +trap22: ecall # Test write to STVAL, SCAUSE, SEPC, and STIMECMP CSRs @@ -126,6 +171,7 @@ sretdone: # Switch to machine mode li a0, 3 +trap23: ecall # Write to MCOUNTINHIBIT CSR @@ -135,6 +181,7 @@ sretdone: # Base address is 2816 (MHPMCOUNTERBASE) # There are 32 HPMCOUNTER registers csrw 2816, t0 +trap24: csrw 2817, t0 csrw 2818, t0 csrw 2819, t0 @@ -168,6 +215,7 @@ sretdone: csrw 2847, t0 # Testing the HPMCOUNTERM performance counter: reading +trap25: csrr t0, 2817 # Test writes to pmp address registers @@ -199,6 +247,7 @@ sretdone: # CSRC MCOUNTEREN Register # Go to machine mode li a0, 3 +trap26: ecall # Activate HPM3 li t0, -1 @@ -207,16 +256,19 @@ sretdone: # Go to supervisor li a0, 1 +trap27: ecall #try to write to HPMs csrw 333, t0 #go to user mode li a0, 0 +trap28: ecall csrr t0, hpmcounter22 # setting registers bits to 0 li a0, 3 # back to machine mode +trap29: ecall li t0, 0 csrw mcounteren, t0 @@ -226,18 +278,22 @@ sretdone: #go to user mode li a0, 0 +trap30: ecall #set status TVM to 0 by writing to bit 20 of mstatus as 0 #bseti t0, zero, 20 +trap31: sfence.vma zero, zero # Go to supervisor mode li a0, 1 +trap32: ecall sfence.vma zero, zero li a0, 3 +trap33: ecall # Write to satp when status.TVM is 1 from machine mode @@ -252,6 +308,7 @@ sretdone: # Go to machine mode li a0, 3 +trap34: ecall # Set SCOUNTEREN to all 0s, MCOUNTEREN to all 1s @@ -263,6 +320,7 @@ sretdone: # Go to supervisor mode li a0, 1 +trap35: ecall # try to read from HPM22 @@ -270,19 +328,23 @@ sretdone: # go to user mode li a0, 0 +trap36: ecall +trap37: csrr t0, hpmcounter22 #getting the mpp and mstatus Mpriv condition met #go to machine mode li a0, 3 +trap38: ecall # set bit 17 of mstatus to enable STATUS_MPRV bseti t1, zero, 17 csrs mstatus, t1 li a0, 3 +trap39: ecall # set bit 21 of mstatus to 0 to disable STATUS_TW @@ -294,27 +356,33 @@ sretdone: # go to user mode li a0, 0 +trap40: ecall +trap41: wfi # Test uncovered privdec instructions li a0, 3 +trap42: ecall # exercise sfence.inval.ir instruction sfence.inval.ir # exercise sret with rs1 not 0 +trap43: .word 0x102F8073 # illegal ebreak with nonzero rs1 +trap44: .word 0x00110073 # cover mret when mpp = 3 and mprv = 1 li a0, 3 +trap45: ecall # enter machine mode bseti t0, zero, 17 csrs mstatus, t0 # set MPRV @@ -326,4 +394,136 @@ sretdone: mret -finished: j done +finished: + # mret to M-mode leaves MPRV set and sets MPP to U, the least-privileged supported mode + csrr a1, mstatus + li a2, 0x21800 # MPRV | MPP + and a1, a1, a2 + li a2, 0x20000 # MPRV = 1, MPP = U + SC_CHECK_EQ a1, a2, 5 + CHECK_CSR stimecmp, -1, 6 # written from S-mode (CSR 333) once TM and STCE were both set + + jal check_traps + SC_PASS + j done + +// Trap log: log_trap records mcause, mepc and mtval of every trap, then continues in the +// WALLY-init-lib.h handler, which handles ecalls and skips other faulting instructions. check_traps +// compares the log with expected_traps, failing with index 0x100 + 3n + k for field k (0 = mcause, +// 1 = mepc, 2 = mtval) of trap n, or 0x1FF if the number of traps differs. +.equ TRAPLOG_MAX, 64 +.align 4 +log_trap: + csrrw t0, mscratch, t0 # t0 = trap stack pointer, mscratch = saved t0 + sd t1, -16(t0) + sd t2, -24(t0) + sd t3, -32(t0) + la t1, traplog + ld t2, 0(t1) + addi t2, t2, 1 # count the trap + sd t2, 0(t1) + li t1, TRAPLOG_MAX + bgtu t2, t1, 1f # log full: count only + slli t3, t2, 4 + slli t2, t2, 3 + add t2, t2, t3 + la t1, traplog - 16 + add t1, t1, t2 # trap n (from 1) goes at traplog + 24n - 16 + csrr t2, mcause + sd t2, 0(t1) + csrr t2, mepc + sd t2, 8(t1) + csrr t2, mtval + sd t2, 16(t1) +1: ld t3, -32(t0) + ld t2, -24(t0) + ld t1, -16(t0) + csrrw t0, mscratch, t0 # restore t0 and the trap stack pointer + j trap_handler + +check_traps: + la a1, traplog + 8 + la a2, expected_traps + ld a3, 0(a2) + addi a2, a2, 8 + slli a4, a3, 1 + add a3, a3, a4 # fields to compare + li a4, 0x100 +1: beqz a3, 2f + ld a5, 0(a1) # actual + ld a6, 0(a2) # expected + bne a5, a6, 3f + addi a1, a1, 8 + addi a2, a2, 8 + addi a3, a3, -1 + addi a4, a4, 1 + j 1b +2: la a1, traplog # and no extra traps + ld a5, 0(a1) + la a2, expected_traps + ld a6, 0(a2) + li a4, 0x1FF + bne a5, a6, 3f + ret +3: la t0, selfcheck_record # record the mismatch as SC_CHECK_EQ does + li t1, 2 + sd t1, 0(t0) + sd a4, 8(t0) + sd zero, 16(t0) + sd a6, 24(t0) + sd a5, 32(t0) + SC_REPORT_FAIL + j done + +.section .data +.align 3 +traplog: # number of traps, then mcause, mepc and mtval of each + .fill 1 + 3*TRAPLOG_MAX, 8, 0 +expected_traps: # number of traps, then mcause, mepc and mtval of each + .dword (2f - 1f) / 24 +1: .dword 8, trap01, 0 # ecall from U: sret in M-mode returned to U (SPP = U) + .dword 2, trap02, 0x10200073 # sret in U-mode + .dword 2, trap03, 0x30200073 # mret in U-mode + .dword 8, trap04, 0 # ecall from U + .dword 2, trap05, 0x14d31073 # stimecmp write from S with menvcfg.STCE = 0 + .dword 2, trap06, 0x14d022f3 # stimecmp read from S + .dword 2, trap07, 0x3002a073 # mstatus write from S (so TVM stays 0) + .dword 9, trap08, 0 # ecall from S + .dword 11, trap09, 0 # ecall from M + .dword 2, trap10, 0x14d01073 # stimecmp write from S, STCE = 0 + .dword 9, trap11, 0 + .dword 11, trap12, 0 # no supervisor timer interrupt: mie.STIE = 0 + .dword 9, trap13, 0 + .dword 11, trap14, 0 + .dword 2, trap15, 0x14d01073 # stimecmp write from S, TM = 1 but STCE = 0 + .dword 9, trap16, 0 + .dword 11, trap17, 0 + .dword 2, trap18, 0x14d01073 # stimecmp write from S, STCE = 1 but TM = 0 + .dword 9, trap19, 0 + .dword 11, trap20, 0 + .dword 9, trap21, 0 # (stimecmp write from S with TM = STCE = 1 is legal) + .dword 11, trap22, 0 + .dword 9, trap23, 0 # (S-mode CSR writes are legal) + .dword 2, trap24, 0xb0129073 # CSR 0xB01 does not exist + .dword 2, trap25, 0xb01022f3 # CSR 0xB01 read + .dword 11, trap26, 0 + .dword 11, trap27, 0 + .dword 9, trap28, 0 # (stimecmp write as CSR 333 from S is legal) + .dword 8, trap29, 0 # (hpmcounter22 read from U with mcounteren and scounteren set is legal) + .dword 11, trap30, 0 + .dword 2, trap31, 0x12000073 # sfence.vma in U-mode + .dword 8, trap32, 0 + .dword 9, trap33, 0 # (sfence.vma in S-mode with TVM = 0 is legal) + .dword 11, trap34, 0 # (satp write in M-mode with TVM = 1 is legal) + .dword 11, trap35, 0 + .dword 9, trap36, 0 # (hpmcounter22 read from S with only mcounteren set is legal) + .dword 2, trap37, 0xc16022f3 # hpmcounter22 read from U with scounteren clear + .dword 8, trap38, 0 + .dword 11, trap39, 0 + .dword 11, trap40, 0 + .dword 2, trap41, 0x10500073 # wfi in U-mode times out + .dword 8, trap42, 0 + .dword 2, trap43, 0x102f8073 # sret with rs1 != 0 (sfence.inval.ir before it is legal in M) + .dword 2, trap44, 0x110073 # ebreak with rs1 != 0 + .dword 11, trap45, 0 +2: diff --git a/tests/coverage/wfitimeout.S b/tests/coverage/wfitimeout.S index 6d07a58512..278265d6d5 100644 --- a/tests/coverage/wfitimeout.S +++ b/tests/coverage/wfitimeout.S @@ -24,6 +24,13 @@ // 0x14000 (81920) cycles guarantees the counter saturates before the interrupt // wakes WFI. // +// Checks: the M-mode WFI raised no illegal instruction and woke only for the +// machine timer interrupt, taken with mepc at the instruction after the WFI, +// once MTIME reached the deadline (failure index 1). For contrast, a WFI in +// S-mode with TW=1 and no interrupt pending must time out to an illegal +// instruction exception at the WFI. A trap log checks the sequence of traps +// (indices 0x100-0x1FF). +// // A component of the CORE-V-WALLY configurable RISC-V project. // https://github.com/openhwfoundation/cvw // @@ -45,8 +52,12 @@ // load code to initialize stack, handle interrupts, terminate #include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" main: + la t0, log_trap + csrw mtvec, t0 + # Set mstatus.TW (bit 21). TW must not affect WFI legality in M-mode. li t0, 0x200000 csrs mstatus, t0 @@ -58,12 +69,113 @@ main: add t2, t2, t3 # MTIMECMP = MTIME + offset li t1, 0x02004000 # MTIMECMP register in CLINT sd t2, 0(t1) + mv s0, t2 # keep the deadline for the check # Wait in M-mode. The watchdog counter saturates while WFI is still stalled, # but WFITimeoutM stays 0 because we are in M-mode, so no illegal instruction # is raised. The hart wakes when the timer interrupt fires. wfi +wfi_m_next: nop # slack for the interrupt-return mepc adjustment in WALLY-init-lib.h nop + # The timer interrupt woke the hart, so MTIME has reached the deadline + li t1, 0x0200BFF8 + ld a1, 0(t1) + sltu a1, a1, s0 # 1 if MTIME < deadline + SC_CHECK_EQ a1, zero, 1 + + # In S-mode, TW=1 makes a WFI that does not complete within the time limit an illegal + # instruction. The interrupt handler pushed mtimecmp to the maximum, so nothing wakes it. + li a0, 1 +ecall_m1: + ecall # enter supervisor mode +wfi_s: + wfi + li a0, 3 +ecall_s1: + ecall # back to machine mode + + jal check_traps + SC_PASS j done + +// Trap log: log_trap records mcause, mepc and mtval of every trap, then continues in the +// WALLY-init-lib.h handler, which handles ecalls and skips other faulting instructions. check_traps +// compares the log with expected_traps, failing with index 0x100 + 3n + k for field k (0 = mcause, +// 1 = mepc, 2 = mtval) of trap n, or 0x1FF if the number of traps differs. +.equ TRAPLOG_MAX, 16 +.align 4 +log_trap: + csrrw t0, mscratch, t0 # t0 = trap stack pointer, mscratch = saved t0 + sd t1, -16(t0) + sd t2, -24(t0) + sd t3, -32(t0) + la t1, traplog + ld t2, 0(t1) + addi t2, t2, 1 # count the trap + sd t2, 0(t1) + li t1, TRAPLOG_MAX + bgtu t2, t1, 1f # log full: count only + slli t3, t2, 4 + slli t2, t2, 3 + add t2, t2, t3 + la t1, traplog - 16 + add t1, t1, t2 # trap n (from 1) goes at traplog + 24n - 16 + csrr t2, mcause + sd t2, 0(t1) + csrr t2, mepc + sd t2, 8(t1) + csrr t2, mtval + sd t2, 16(t1) +1: ld t3, -32(t0) + ld t2, -24(t0) + ld t1, -16(t0) + csrrw t0, mscratch, t0 # restore t0 and the trap stack pointer + j trap_handler + +check_traps: + la a1, traplog + 8 + la a2, expected_traps + ld a3, 0(a2) + addi a2, a2, 8 + slli a4, a3, 1 + add a3, a3, a4 # fields to compare + li a4, 0x100 +1: beqz a3, 2f + ld a5, 0(a1) # actual + ld a6, 0(a2) # expected + bne a5, a6, 3f + addi a1, a1, 8 + addi a2, a2, 8 + addi a3, a3, -1 + addi a4, a4, 1 + j 1b +2: la a1, traplog # and no extra traps + ld a5, 0(a1) + la a2, expected_traps + ld a6, 0(a2) + li a4, 0x1FF + bne a5, a6, 3f + ret +3: la t0, selfcheck_record # record the mismatch as SC_CHECK_EQ does + li t1, 2 + sd t1, 0(t0) + sd a4, 8(t0) + sd zero, 16(t0) + sd a6, 24(t0) + sd a5, 32(t0) + SC_REPORT_FAIL + j done + +.section .data +.align 3 +traplog: # number of traps, then mcause, mepc and mtval of each + .fill 1 + 3*TRAPLOG_MAX, 8, 0 +expected_traps: # number of traps, then mcause, mepc and mtval of each + .dword (2f - 1f) / 24 +1: .dword 0x8000000000000007, wfi_m_next, 0 # machine timer interrupt after the M-mode WFI retires + .dword 11, ecall_m1, 0 # ecall from M + .dword 2, wfi_s, 0x10500073 # S-mode WFI timed out: illegal instruction + .dword 9, ecall_s1, 0 # ecall from S +2: From 840e8d71ee1c4085962d27161fde28c84dab4431 Mon Sep 17 00:00:00 2001 From: David Harris Date: Fri, 9 Oct 2026 17:20:21 -0700 Subject: [PATCH 5/9] tlbASIDMISS, tlbGLBASID, tlbGLBHIT, tlbNAPOT, tlbmisc, vm64check: check results They passed by reaching done. Each page they map now holds code or data that identifies it, so every fetch and load through a TLB entry checks that it reached the right physical page: tlbASIDMISS gives the second ASID its own pages, so a hit under the wrong ASID is caught; tlbGLBASID repoints the global PTEs before each full sfence.vma, so a global entry that survives it is caught; tlbNAPOT checks each NAPOT translation and the page faults on ill-formed NAPOT PTEs. tlbmisc checks its traps and the values of its loads, AMO, cbo.zero and A-bit update, and vm64check, now on WALLY-init-lib.h, checks that each non-canonical jump takes an instruction access fault. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: David Harris --- tests/coverage/tlbASIDMISS.S | 330 +++++++++++++++++----- tests/coverage/tlbGLBASID.S | 249 +++++++++++------ tests/coverage/tlbGLBHIT.S | 206 +++++++++----- tests/coverage/tlbNAPOT.S | 316 ++++++++++++++------- tests/coverage/tlbmisc.S | 528 +++++++++++++++++++++++++---------- tests/coverage/vm64check.S | 118 +++----- 6 files changed, 1202 insertions(+), 545 deletions(-) diff --git a/tests/coverage/tlbASIDMISS.S b/tests/coverage/tlbASIDMISS.S index 751e5c7d78..c0fe1b3072 100644 --- a/tests/coverage/tlbASIDMISS.S +++ b/tests/coverage/tlbASIDMISS.S @@ -19,6 +19,19 @@ // entry that is looked up under a mismatched ASID. The running code lives on the *global* // gigapage (L2[2]=0x..EF) so it keeps fetching across the ASID switch. // +// Self-checking (WALLY-selfcheck.h). The two ASIDs have their own page tables, which map +// the VA pages to different physical pages: ASID 0 to set A (0x80000000 + p * 0x1000) and +// ASID 1 to set B (0x80040000 + p * 0x1000). Every physical page holds the code +// "addi a5, x0, ID; ret", ID = (set << 6) | p, preloaded in machine mode, and each fill +// stores it again with WRITTEN (0x80) added. The fill fetch must run the code just stored +// (exact ID), and the refetch under the other ASID must run its own set's page (ID without +// WRITTEN), so a stale entry of the other ASID that wrongly hits is caught. At the end, +// machine mode checks that every page of both sets holds its own code with WRITTEN, so no +// store went to a page of the wrong set. The pointer PTEs are non-global: a global +// pointer would make the leaves below it global, and global mappings must be the same in +// every address space. Failure indices: 1 fill fetch, 2 refetch under the other ASID, +// 3 set A contents, 4 set B contents, 0xF0 any trap but an ecall. +// // A component of the CORE-V-WALLY configurable RISC-V project. // https://github.com/openhwfoundation/cvw // @@ -42,72 +55,11 @@ #include "WALLY-init-lib.h" -# run-elf.bash find this in project description -main: - # Page table root address at 0x80080000, ASID = 0 (SV48) - li t5, 0x9000000000080080 - csrw satp, t5 - - # switch to supervisor mode - li a0, 1 - ecall - - # A non-global re-fetch MISSES (MatchASID_0) and then installs a new-ASID entry, evicting per the - # bit-PLRU. So MatchASID_0 only fires for whichever camline still holds the stale entry at probe - # time, and the running code's global gigapage pins one slot -- which camline gets caught depends on - # the fill/refetch alignment. To reach EVERY camline (slot 30 is the lone holdout in the merged - # report), sweep the distinct-page count across rounds and alternate the ASID pair each round. - li t4, 0x1000 # offset between pages - li t1, 0x00008067 # "ret" (jalr x0, x1, 0) instruction word - li s2, 0 # ASID toggle (0/1) for the fill phase - li s3, 24 # current distinct-page count (swept) - li s4, 49 # one past the largest distinct-page count - -outer: - bge s3, s4, finished - - # build satp values: base (MODE=SV48, PPN=0x80080) with ASID = s2 (fill) and 1-s2 (refetch) - li s0, 0x9000000000080080 - slli t5, s2, 44 # fill-phase ASID in satp[59:44] - or t5, s0, t5 # satp for fill phase - xori s1, s2, 1 - slli t6, s1, 44 # refetch-phase ASID - or t6, s0, t6 # satp for refetch phase - - # fill s3 distinct non-global pages under the fill ASID (store a ret, then fetch+execute it) - csrw satp, t5 - li t0, 0xC0000000 - li t2, 0 -fill: - bge t2, s3, swap - sw t1, 0(t0) # DTLB fill - fence.i - jalr t0 # ITLB fill: non-global entry, Key_ASID = fill ASID - add t0, t0, t4 - addi t2, t2, 1 - j fill -swap: - csrw satp, t6 # switch ASID, NO sfence: the non-global entries stay resident - li t0, 0xC0000000 - li t2, 0 -refetch: - bge t2, s3, nextround - jalr t0 # ITLB lookup: Key_ASID != SATP_ASID and PTE_G=0 => MatchASID_0 - add t0, t0, t4 - addi t2, t2, 1 - j refetch - -nextround: - xori s2, s2, 1 # alternate the ASID pair - addi s3, s3, 1 # next distinct-page count - j outer - -finished: - j done - +// The page tables come before WALLY-selfcheck.h so that its data does not move them off the +// addresses that satp and the pointer PTEs name (0x80080000-0x80087FFF). .data .align 19 -# level 3 Page table situated at 0x80080000, points to 0x80081000 +# ASID 0: level 3 Page table situated at 0x80080000, points to 0x80081000 pagetable: .8byte 0x20020401 @@ -115,10 +67,10 @@ pagetable: .8byte 0x0 .8byte 0x0 .8byte 0x200000EF // global gigapage 8000 0000 - C000 0000: holds the running test code - .8byte 0x20020821 // pointer to next page table entry at 8008 2000 + .8byte 0x20020801 // pointer to next page table entry at 8008 2000 (non-global) .align 12 // level 1 page table, points to level 0 page table - .8byte 0x20020C21 + .8byte 0x20020C01 // non-global .align 12 // level 0 page table, contains 64 NON-GLOBAL (G=0, 0x..CF) kilopages .8byte 0x200000CF // access xC000 0000 @@ -205,3 +157,249 @@ pagetable: .8byte 0x200084CF .8byte 0x200088CF .8byte 0x20008CCF + +# ASID 1: the same structure at 0x80084000, with the kilopages at VA 0xC0000000 mapping set B +.align 12 // level 3 page table at 0x80084000, points to 0x80085000 + .8byte 0x20021401 + +.align 12 // level 2 page table + .8byte 0x0 + .8byte 0x0 + .8byte 0x200000EF // the same global gigapage 8000 0000 - C000 0000 + .8byte 0x20021801 // pointer to the level 1 page table at 8008 6000 (non-global) + +.align 12 // level 1 page table, points to level 0 page table at 8008 7000 + .8byte 0x20021C01 // non-global + +.align 12 // level 0 page table: VA page i -> set B page p (0x80040000 + p * 0x1000), non-global + .8byte 0x200100CF // access xC000 0000 -> 0x80040000 + .8byte 0x200104CF + .8byte 0x200108CF + .8byte 0x20010CCF + + .8byte 0x200110CF // access xC000 4000 -> 0x80044000 + .8byte 0x200114CF + .8byte 0x200118CF + .8byte 0x20011CCF + + .8byte 0x200120CF // access xC000 8000 -> 0x80048000 + .8byte 0x200124CF + .8byte 0x200128CF + .8byte 0x20012CCF + + .8byte 0x200130CF // access xC000 C000 -> 0x8004C000 + .8byte 0x200134CF + .8byte 0x200138CF + .8byte 0x20013CCF + + .8byte 0x200140CF // access xC001 0000 -> 0x80050000 + .8byte 0x200144CF + .8byte 0x200148CF + .8byte 0x20014CCF + + .8byte 0x200150CF // access xC001 4000 -> 0x80054000 + .8byte 0x200154CF + .8byte 0x200158CF + .8byte 0x20015CCF + + .8byte 0x200160CF // access xC001 8000 -> 0x80058000 + .8byte 0x200164CF + .8byte 0x200168CF + .8byte 0x20016CCF + + .8byte 0x200170CF // access xC001 C000 -> 0x8005C000 + .8byte 0x200174CF + .8byte 0x200178CF + .8byte 0x20017CCF + + .8byte 0x200180CF // access xC002 0000 -> 0x80060000 + .8byte 0x200184CF + .8byte 0x200188CF + .8byte 0x20018CCF + + .8byte 0x200110CF // access xC002 4000 -> 0x80044000 + .8byte 0x200114CF + .8byte 0x200118CF + .8byte 0x20011CCF + + .8byte 0x200120CF // access xC002 8000 -> 0x80048000 + .8byte 0x200124CF + .8byte 0x200128CF + .8byte 0x20012CCF + + .8byte 0x200130CF // access xC002 C000 -> 0x8004C000 + .8byte 0x200134CF + .8byte 0x200138CF + .8byte 0x20013CCF + + .8byte 0x200140CF // access xC003 0000 -> 0x80050000 + .8byte 0x200144CF + .8byte 0x200148CF + .8byte 0x20014CCF + + .8byte 0x200150CF // access xC003 4000 -> 0x80054000 + .8byte 0x200154CF + .8byte 0x200158CF + .8byte 0x20015CCF + + .8byte 0x200160CF // access xC003 8000 -> 0x80058000 + .8byte 0x200164CF + .8byte 0x200168CF + .8byte 0x20016CCF + + .8byte 0x200170CF // access xC003 C000 -> 0x8005C000 + .8byte 0x200174CF + .8byte 0x200178CF + .8byte 0x20017CCF + + .8byte 0x200180CF // access xC004 0000 -> 0x80060000 + .8byte 0x200184CF + .8byte 0x200188CF + .8byte 0x20018CCF + +#include "WALLY-selfcheck.h" + +// VA page i (VA 0xC0000000 + i * 0x1000) maps physical page p of the ASID's set, with p = i for +// i < 36 and p = i - 32 for the entries 36-67 that repeat pages 4-35. +#define NPAGES 36 +#define WRITTEN 0x80 // added to the ID of the code a fill stores +#define RETADDI 0x0000806700000793 // "ret" << 32 | "addi a5, x0, 0": the code stored in each page + +// PAGEOF p, i: p = the physical page number that VA page i maps +.macro PAGEOF p, i + sltiu \p, \i, NPAGES + xori \p, \p, 1 + slli \p, \p, 5 + sub \p, \i, \p +.endm + +// CODEWORD dst, id: dst = "addi a5, x0, id; ret" with id in a register (s11 = RETADDI) +.macro CODEWORD dst, id + slli \dst, \id, 20 + or \dst, \dst, s11 +.endm + +// PRELOAD adr, first, id: store the code with ID id | p at adr + p * 0x1000 for p = first-35 (clobbers a1-a4) +.macro PRELOAD adr, first, id + li a1, \adr + \first * 0x1000 + li a2, \first +1: ori a3, a2, \id + CODEWORD a4, a3 + sd a4, 0(a1) + li a3, 0x1000 + add a1, a1, a3 + addi a2, a2, 1 + li a3, NPAGES + blt a2, a3, 1b +.endm + +// SWEEP adr, id, index: check that adr + p * 0x1000 holds the code with ID id | p for p = 0-35 +.macro SWEEP adr, id, index + li t0, \adr + li t2, 0 +1: ori a3, t2, \id + CODEWORD a4, a3 + ld a5, 0(t0) + SC_CHECK_EQ_ADR a5, a4, t0, \index + add t0, t0, t4 + addi t2, t2, 1 + li a6, NPAGES + blt t2, a6, 1b +.endm + +# run-elf.bash find this in project description +main: + SC_STRICT_TRAPS # fail on any trap but an ecall + li s11, RETADDI + + # Preload the code of every physical page that a refetch can reach before a fill stores it. + # Set A pages 0 and 1 are left alone (they hold the entry code and tohost); the first fill + # stores them before any refetch under ASID 0. + PRELOAD 0x80000000, 2, 0 << 6 + PRELOAD 0x80040000, 0, 1 << 6 + fence.i + + # Page table root address at 0x80080000, ASID = 0 (SV48) + li t5, 0x9000000000080080 + csrw satp, t5 + + # switch to supervisor mode + li a0, 1 + ecall + + # A non-global re-fetch MISSES (MatchASID_0) and then installs a new-ASID entry, evicting per the + # bit-PLRU. So MatchASID_0 only fires for whichever camline still holds the stale entry at probe + # time, and the running code's global gigapage pins one slot -- which camline gets caught depends on + # the fill/refetch alignment. To reach EVERY camline (slot 30 is the lone holdout in the merged + # report), sweep the distinct-page count across rounds and alternate the ASID pair each round. + li t4, 0x1000 # offset between pages + li s2, 0 # ASID toggle (0/1) for the fill phase + li s3, 24 # current distinct-page count (swept) + li s4, 49 # one past the largest distinct-page count + +outer: + bge s3, s4, finished + + # build satp values: base (MODE=SV48, PPN=0x80080) with ASID = s2 (fill) and 1-s2 (refetch); + # ASID 1 uses the page table at 0x80084000 (PPN + 4) + li s0, 0x9000000000080080 + slli t5, s2, 44 # fill-phase ASID in satp[59:44] + or t5, s0, t5 # satp for fill phase + slli a6, s2, 2 + add t5, t5, a6 + xori s1, s2, 1 + slli t6, s1, 44 # refetch-phase ASID + or t6, s0, t6 # satp for refetch phase + slli a6, s1, 2 + add t6, t6, a6 + slli s5, s2, 6 + ori s5, s5, WRITTEN # ID fields of the code the fill stores + slli s6, s1, 6 # ID field of the refetch ASID's set + + # fill s3 distinct non-global pages under the fill ASID (store code, then fetch+execute it) + csrw satp, t5 + li t0, 0xC0000000 + li t2, 0 +fill: + bge t2, s3, swap + PAGEOF a3, t2 + or a3, a3, s5 # a3 = ID of the code this page must run + CODEWORD t1, a3 + sd t1, 0(t0) # DTLB fill + fence.i + li a5, -1 + jalr t0 # ITLB fill: non-global entry, Key_ASID = fill ASID + SC_CHECK_EQ_ADR a5, a3, t0, 1 + add t0, t0, t4 + addi t2, t2, 1 + j fill +swap: + csrw satp, t6 # switch ASID, NO sfence: the non-global entries stay resident + li t0, 0xC0000000 + li t2, 0 +refetch: + bge t2, s3, nextround + PAGEOF a3, t2 + or a3, a3, s6 # the refetch ASID's page, whether or not a fill has stored it yet + li a5, -1 + jalr t0 # ITLB lookup: Key_ASID != SATP_ASID and PTE_G=0 => MatchASID_0 + andi a7, a5, ~WRITTEN + SC_CHECK_EQ_ADR a7, a3, t0, 2 + add t0, t0, t4 + addi t2, t2, 1 + j refetch + +nextround: + xori s2, s2, 1 # alternate the ASID pair + addi s3, s3, 1 # next distinct-page count + j outer + + # ---- every page of both sets holds its own code, stored by the last fill of its ASID ---- +finished: + li a0, 3 + ecall # machine mode: read the physical pages directly + SWEEP 0x80000000, WRITTEN | (0 << 6), 3 + SWEEP 0x80040000, WRITTEN | (1 << 6), 4 + + SC_PASS + j done diff --git a/tests/coverage/tlbGLBASID.S b/tests/coverage/tlbGLBASID.S index dc16c6c86a..b3583a593a 100644 --- a/tests/coverage/tlbGLBASID.S +++ b/tests/coverage/tlbGLBASID.S @@ -15,6 +15,17 @@ // tlbGLB.S installs global entries but never issues sfence.vma; tlbASID.S issues sfence.vma // but with no global entry resident. Page table reused from tlbGLB.S. // +// Self-checking (WALLY-selfcheck.h). Each fill stores "addi a5, x0, ID; ret" into its +// page, where ID = (round << 6) | the physical page the VA maps, and checks that the fetch +// right after runs that code. The global PTEs are repointed from physical pages set A +// (0x80000000 + p * 0x1000) to set B (0x80040000 + p * 0x1000) before the first full +// sfence.vma and back before the second, so a global ITLB entry that survives a full fence +// fetches the other set's code and a global DTLB entry that survives stores into the other +// set, leaving the fetched page without this round's code. The ASID-scoped fences may keep +// the global entries, so the mapping does not change across them. At the end, machine mode +// checks that set A holds round 4's code and set B round 3's. Failure indices: 1-4 fill +// fetch in rounds 1-4, 5 set A contents, 6 set B contents, 0xF0 any trap but an ecall. +// // A component of the CORE-V-WALLY configurable RISC-V project. // https://github.com/openhwfoundation/cvw // @@ -39,87 +50,8 @@ #include "WALLY-init-lib.h" -# run-elf.bash find this in project description -main: - # Page table root address at 0x80080000, ASID = 0 (SV48) - li t5, 0x9000000000080080 - csrw satp, t5 - - # switch to supervisor mode - li a0, 1 - ecall - - li t3, 48 # distinct pages per fill (> TLB entries so replacement churns every line) - li t4, 0x1000 # offset between pages - li t1, 0x00008067 # "ret" (jalr x0, x1, 0) instruction word - - # Each round fills every ITLB and DTLB entry with global (G=1) pages (store a ret into each page - # => DTLB fill; fetch/execute it => ITLB fill) and then issues an sfence.vma while those global - # entries are resident. We alternate full and ASID-scoped fences across four rounds so that, - # regardless of replacement order, every camline holds a global entry during both a - # TLBFlushAll=1 fence (covers the TLBFlushAll_1 row) and a TLBFlushAll=0 fence (covers the - # PTE_G_1 and TLBFlushAll_0 rows) of: assign ShouldFlush = TLBFlush & (~PTE_G | TLBFlushAll); - - # ---- round 1: fill, then FULL sfence.vma (rs2 = x0 => TLBFlushAll = 1) ---- - li t0, 0xC0000000 - li t2, 0 -fillA: - bge t2, t3, flushA - sw t1, 0(t0) - fence.i - jalr t0 - add t0, t0, t4 - addi t2, t2, 1 - j fillA -flushA: - sfence.vma - - # ---- round 2: fill, then ASID-scoped sfence.vma (rs2 = t2 != x0 => TLBFlushAll = 0) ---- - li t0, 0xC0000000 - li t2, 0 -fillB: - bge t2, t3, flushB - sw t1, 0(t0) - fence.i - jalr t0 - add t0, t0, t4 - addi t2, t2, 1 - j fillB -flushB: - li t2, 1 - sfence.vma x0, t2 - - # ---- round 3: fill, then FULL sfence.vma ---- - li t0, 0xC0000000 - li t2, 0 -fillC: - bge t2, t3, flushC - sw t1, 0(t0) - fence.i - jalr t0 - add t0, t0, t4 - addi t2, t2, 1 - j fillC -flushC: - sfence.vma - - # ---- round 4: fill, then ASID-scoped sfence.vma ---- - li t0, 0xC0000000 - li t2, 0 -fillD: - bge t2, t3, flushD - sw t1, 0(t0) - fence.i - jalr t0 - add t0, t0, t4 - addi t2, t2, 1 - j fillD -flushD: - li t2, 1 - sfence.vma x0, t2 - - j done - +// The page tables come before WALLY-selfcheck.h so that its data does not move them off the +// addresses that satp and the pointer PTEs name (0x80080000-0x80083FFF). .data .align 19 # level 3 Page table situated at 0x80080000, points to 0x80081000 @@ -136,6 +68,7 @@ pagetable: .8byte 0x20020C21 .align 12 // level 0 page table, contains 64 global kilopages +pt_l0: .8byte 0x200000EF // access xC000 0000 .8byte 0x200004EF // access xC000 1000 .8byte 0x200008EF // access xC000 2000 @@ -220,3 +153,157 @@ pagetable: .8byte 0x200084EF .8byte 0x200088EF .8byte 0x20008CEF + +#include "WALLY-selfcheck.h" + +// VA page i (VA 0xC0000000 + i * 0x1000) maps physical page p of the current set, with p = i for +// i < 36 and p = i - 32 for the entries 36-67 that repeat pages 4-35. +#define NPAGES 36 +#define SETA 0x80000 // first PPN of physical page set A (the PTEs above) +#define SETB 0x80040 // first PPN of physical page set B +#define RETADDI 0x0000806700000793 // "ret" << 32 | "addi a5, x0, 0": the code stored in each page + +// PAGEOF p, i: p = the physical page number that VA page i maps +.macro PAGEOF p, i + sltiu \p, \i, NPAGES + xori \p, \p, 1 + slli \p, \p, 5 + sub \p, \i, \p +.endm + +// CODEWORD dst, id: dst = "addi a5, x0, id; ret" with id in a register (s11 = RETADDI) +.macro CODEWORD dst, id + slli \dst, \id, 20 + or \dst, \dst, s11 +.endm + +// FILLCHECK index: the fill loop body for the page at VA t0 (VA page t2): store the code with ID +// s10 | p into it, fetch and run it, and check that it ran +.macro FILLCHECK index + PAGEOF a3, t2 + or a3, a3, s10 # a3 = ID of the code this page must run + CODEWORD t1, a3 + sd t1, 0(t0) # store the code into the page (DTLB fill) + fence.i + li a5, -1 + jalr t0 # fetch and run it (ITLB fill) + SC_CHECK_EQ_ADR a5, a3, t0, \index +.endm + +// REMAP set: point the global PTEs of VA pages 0-47 at the physical pages of a set (first PPN set) +.macro REMAP set + la a1, pt_l0 + li a2, 0 +1: PAGEOF a3, a2 + li a4, \set + add a3, a3, a4 + slli a3, a3, 10 + ori a3, a3, 0xEF # V R W X G A D + sd a3, 0(a1) + addi a1, a1, 8 + addi a2, a2, 1 + blt a2, t3, 1b +.endm + +// SWEEP set, round, index: check that each physical page of a set holds the code of a round +.macro SWEEP set, round, index + li t0, \set << 12 + li t2, 0 + li s10, \round << 6 +1: or a3, t2, s10 + CODEWORD a4, a3 + ld a5, 0(t0) + SC_CHECK_EQ_ADR a5, a4, t0, \index + add t0, t0, t4 + addi t2, t2, 1 + li a6, NPAGES + blt t2, a6, 1b +.endm + +# run-elf.bash find this in project description +main: + SC_STRICT_TRAPS # fail on any trap but an ecall + li s11, RETADDI + + # Page table root address at 0x80080000, ASID = 0 (SV48) + li t5, 0x9000000000080080 + csrw satp, t5 + + # switch to supervisor mode + li a0, 1 + ecall + + li t3, 48 # distinct pages per fill (> TLB entries so replacement churns every line) + li t4, 0x1000 # offset between pages + + # Each round fills every ITLB and DTLB entry with global (G=1) pages (store code into each page + # => DTLB fill; fetch/execute it => ITLB fill) and then issues an sfence.vma while those global + # entries are resident. We alternate full and ASID-scoped fences across four rounds so that, + # regardless of replacement order, every camline holds a global entry during both a + # TLBFlushAll=1 fence (covers the TLBFlushAll_1 row) and a TLBFlushAll=0 fence (covers the + # PTE_G_1 and TLBFlushAll_0 rows) of: assign ShouldFlush = TLBFlush & (~PTE_G | TLBFlushAll); + + # ---- round 1: fill (set A), then FULL sfence.vma (rs2 = x0 => TLBFlushAll = 1) ---- + li s10, 1 << 6 + li t0, 0xC0000000 + li t2, 0 +fillA: + bge t2, t3, flushA + FILLCHECK 1 + add t0, t0, t4 + addi t2, t2, 1 + j fillA +flushA: + REMAP SETB # the full fence must drop the set A global entries + sfence.vma + + # ---- round 2: fill (set B), then ASID-scoped sfence.vma (rs2 = t2 != x0 => TLBFlushAll = 0) ---- + li s10, 2 << 6 + li t0, 0xC0000000 + li t2, 0 +fillB: + bge t2, t3, flushB + FILLCHECK 2 + add t0, t0, t4 + addi t2, t2, 1 + j fillB +flushB: + li t2, 1 + sfence.vma x0, t2 + + # ---- round 3: fill (set B, which the ASID-scoped fence may have kept), then FULL sfence.vma ---- + li s10, 3 << 6 + li t0, 0xC0000000 + li t2, 0 +fillC: + bge t2, t3, flushC + FILLCHECK 3 + add t0, t0, t4 + addi t2, t2, 1 + j fillC +flushC: + REMAP SETA # the full fence must drop the set B global entries + sfence.vma + + # ---- round 4: fill (set A), then ASID-scoped sfence.vma ---- + li s10, 4 << 6 + li t0, 0xC0000000 + li t2, 0 +fillD: + bge t2, t3, flushD + FILLCHECK 4 + add t0, t0, t4 + addi t2, t2, 1 + j fillD +flushD: + li t2, 1 + sfence.vma x0, t2 + + # ---- the stores went to the physical pages their PTEs named: set A holds round 4's code, set B round 3's ---- + li a0, 3 + ecall # machine mode: read the physical pages directly + SWEEP SETA, 4, 5 + SWEEP SETB, 3, 6 + + SC_PASS + j done diff --git a/tests/coverage/tlbGLBHIT.S b/tests/coverage/tlbGLBHIT.S index 33d25ca744..9f77646872 100644 --- a/tests/coverage/tlbGLBHIT.S +++ b/tests/coverage/tlbGLBHIT.S @@ -18,6 +18,15 @@ // ASID pair so that, regardless of replacement order, slot 2 holds a global entry that is // hit under a mismatched ASID. // +// Self-checking (WALLY-selfcheck.h). Each fill stores "addi a5, x0, ID; ret" into its +// page, where ID = (round << 6) | the physical page the VA maps, so every fetch (the fill +// and the refetch under the other ASID) checks that it ran the code of the right physical +// page from the current round. At the end, machine mode reads the 36 physical pages back +// to check that every store landed in the page its PTE names. Whether the refetch hits +// the global entry or walks again is not architecturally visible; either way the mapping +// must be the same. Failure indices: 1/3 fill fetch (rounds 1/2), 2/4 refetch under the +// other ASID, 5 physical page contents, 0xF0 any trap but an ecall. +// // A component of the CORE-V-WALLY configurable RISC-V project. // https://github.com/openhwfoundation/cvw // @@ -41,74 +50,8 @@ #include "WALLY-init-lib.h" -# run-elf.bash find this in project description -main: - # Page table root address at 0x80080000, ASID = 0 (SV48) - li t5, 0x9000000000080080 - csrw satp, t5 - - # switch to supervisor mode - li a0, 1 - ecall - - li t3, 48 # distinct pages per fill (> TLB entries so every line churns) - li t4, 0x1000 # offset between pages - li t1, 0x00008067 # "ret" (jalr x0, x1, 0) instruction word - - # ---- round 1: fill global pages under ASID=0, then re-fetch under ASID=1 ---- - li t5, 0x9000000000080080 # ASID = 0 - csrw satp, t5 - li t0, 0xC0000000 - li t2, 0 -fill0: - bge t2, t3, swap0 - sw t1, 0(t0) # store a ret into the page (DTLB fill) - fence.i - jalr t0 # fetch+execute it (ITLB fill, global entry, Key_ASID=0) - add t0, t0, t4 - addi t2, t2, 1 - j fill0 -swap0: - li t5, 0x9001000000080080 # switch to ASID=1, NO sfence: global entries stay resident - csrw satp, t5 - li t0, 0xC0000000 - li t2, 0 -refetch0: - bge t2, t3, round2 - jalr t0 # ITLB hit on global entry (Key_ASID=0) under SATP_ASID=1 => PTE_G_1 row - add t0, t0, t4 - addi t2, t2, 1 - j refetch0 - - # ---- round 2: fill global pages under ASID=1, then re-fetch under ASID=0 ---- -round2: - li t5, 0x9001000000080080 # ASID = 1 - csrw satp, t5 - li t0, 0xC0000000 - li t2, 0 -fill1: - bge t2, t3, swap1 - sw t1, 0(t0) - fence.i - jalr t0 # ITLB fill, global entry, Key_ASID=1 - add t0, t0, t4 - addi t2, t2, 1 - j fill1 -swap1: - li t5, 0x9000000000080080 # switch to ASID=0, NO sfence - csrw satp, t5 - li t0, 0xC0000000 - li t2, 0 -refetch1: - bge t2, t3, finished - jalr t0 # ITLB hit on global entry (Key_ASID=1) under SATP_ASID=0 => PTE_G_1 row - add t0, t0, t4 - addi t2, t2, 1 - j refetch1 - -finished: - j done - +// The page tables come before WALLY-selfcheck.h so that its data does not move them off the +// addresses that satp and the pointer PTEs name (0x80080000-0x80083FFF). .data .align 19 # level 3 Page table situated at 0x80080000, points to 0x80081000 @@ -209,3 +152,130 @@ pagetable: .8byte 0x200084EF .8byte 0x200088EF .8byte 0x20008CEF + +#include "WALLY-selfcheck.h" + +// VA page i (VA 0xC0000000 + i * 0x1000) maps physical page p (PA 0x80000000 + p * 0x1000), with +// p = i for i < 36 and p = i - 32 for the entries 36-67 that repeat pages 4-35. +#define NPAGES 36 +#define RETADDI 0x0000806700000793 // "ret" << 32 | "addi a5, x0, 0": the code stored in each page + +// PAGEOF p, i: p = the physical page number that VA page i maps +.macro PAGEOF p, i + sltiu \p, \i, NPAGES + xori \p, \p, 1 + slli \p, \p, 5 + sub \p, \i, \p +.endm + +// CODEWORD dst, id: dst = "addi a5, x0, id; ret" with id in a register (s11 = RETADDI) +.macro CODEWORD dst, id + slli \dst, \id, 20 + or \dst, \dst, s11 +.endm + +# run-elf.bash find this in project description +main: + SC_STRICT_TRAPS # fail on any trap but an ecall + li s11, RETADDI + + # Page table root address at 0x80080000, ASID = 0 (SV48) + li t5, 0x9000000000080080 + csrw satp, t5 + + # switch to supervisor mode + li a0, 1 + ecall + + li t3, 48 # distinct pages per fill (> TLB entries so every line churns) + li t4, 0x1000 # offset between pages + + # ---- round 1: fill global pages under ASID=0, then re-fetch under ASID=1 ---- + li s10, 1 << 6 # round field of the ID + li t5, 0x9000000000080080 # ASID = 0 + csrw satp, t5 + li t0, 0xC0000000 + li t2, 0 +fill0: + bge t2, t3, swap0 + PAGEOF a3, t2 + or a3, a3, s10 # a3 = ID of the code this page must run + CODEWORD t1, a3 + sd t1, 0(t0) # store the code into the page (DTLB fill) + fence.i + li a5, -1 + jalr t0 # fetch+execute it (ITLB fill, global entry, Key_ASID=0) + SC_CHECK_EQ_ADR a5, a3, t0, 1 + add t0, t0, t4 + addi t2, t2, 1 + j fill0 +swap0: + li t5, 0x9001000000080080 # switch to ASID=1, NO sfence: global entries stay resident + csrw satp, t5 + li t0, 0xC0000000 + li t2, 0 +refetch0: + bge t2, t3, round2 + PAGEOF a3, t2 + or a3, a3, s10 + li a5, -1 + jalr t0 # ITLB hit on global entry (Key_ASID=0) under SATP_ASID=1 => PTE_G_1 row + SC_CHECK_EQ_ADR a5, a3, t0, 2 + add t0, t0, t4 + addi t2, t2, 1 + j refetch0 + + # ---- round 2: fill global pages under ASID=1, then re-fetch under ASID=0 ---- +round2: + li s10, 2 << 6 + li t5, 0x9001000000080080 # ASID = 1 + csrw satp, t5 + li t0, 0xC0000000 + li t2, 0 +fill1: + bge t2, t3, swap1 + PAGEOF a3, t2 + or a3, a3, s10 + CODEWORD t1, a3 + sd t1, 0(t0) + fence.i + li a5, -1 + jalr t0 # ITLB fill, global entry, Key_ASID=1 + SC_CHECK_EQ_ADR a5, a3, t0, 3 + add t0, t0, t4 + addi t2, t2, 1 + j fill1 +swap1: + li t5, 0x9000000000080080 # switch to ASID=0, NO sfence + csrw satp, t5 + li t0, 0xC0000000 + li t2, 0 +refetch1: + bge t2, t3, finished + PAGEOF a3, t2 + or a3, a3, s10 + li a5, -1 + jalr t0 # ITLB hit on global entry (Key_ASID=1) under SATP_ASID=0 => PTE_G_1 row + SC_CHECK_EQ_ADR a5, a3, t0, 4 + add t0, t0, t4 + addi t2, t2, 1 + j refetch1 + + # ---- every physical page holds the code that round 2 stored through its mapping ---- +finished: + li a0, 3 + ecall # machine mode: read the physical pages directly + li t0, 0x80000000 + li t2, 0 + li a6, NPAGES +sweep: + or a3, t2, s10 + CODEWORD a4, a3 + ld a5, 0(t0) + SC_CHECK_EQ_ADR a5, a4, t0, 5 + add t0, t0, t4 + addi t2, t2, 1 + blt t2, a6, sweep + + SC_PASS + j done diff --git a/tests/coverage/tlbNAPOT.S b/tests/coverage/tlbNAPOT.S index 14b64ed100..06638a6305 100644 --- a/tests/coverage/tlbNAPOT.S +++ b/tests/coverage/tlbNAPOT.S @@ -6,6 +6,20 @@ // Adapted David_Harris@hmc.edu 8/29/23 to exercise NAPOT huge pages // // Purpose: Test coverage for LSU NAPOT +// Self-checking (WALLY-selfcheck.h). Each looptest iteration stores "c.li a5, i; c.ret" +// (i = the iteration) through its VA, loads it back, and runs it, so on the pages that +// translate it checks that the load returns the word stored and that the fetch runs it +// (a5 = i: the ITLB maps the VA to the physical page the DTLB stored into). On the +// ill-formed NAPOT pages (PPN[3:0] != 1000 with N = 1) the store, load and fetch must each +// take a page fault (15, 13, 12) at the right instruction with mtval = the VA, and the load +// and the code must not write their registers; rec_default and rec_ipf count the faults +// that match (s9) and the ones that do not (s10), and every other trap but an ecall fails. +// At the end, machine mode checks the physical pages: the 32 pages the 4 KiB step stored +// into (0x80016000-0x80035000, through the NAPOT mappings of 0x80216000-0x80235000) and the +// two pages the 2 MiB steps store into (0x80015240 through the NAPOT pages, +// 0x80018240 through the non-NAPOT pages). +// Failure indices: 1 load value, 2 fetched code, 3/4 page faults matched/mismatched per +// looptest call, 5 physical pages, 0xF0 any other trap. // // A component of the CORE-V-WALLY configurable RISC-V project. // https://github.com/openhwfoundation/cvw @@ -30,109 +44,8 @@ #include "WALLY-init-lib.h" -# run-elf.bash find this in project description -main: - li t5, 0x1 - slli t5, t5, 62 - csrs menvcfg, t5 - # Page table root address at 0x80010000; SV48 - li t5, 0x9000000000080010 - csrw satp, t5 - - # sfence.vma x0, x0 - - # switch to supervisor mode - li a0, 1 - ecall - - li t4, 0x200000 # address step size - li a2, 0x80215240 # Test NAPOT pages - jal a1, looptest - li a2, 0xC0215240 # Test ill-formed NAPOT pages - jal a1, looptest - li a2, 0x40215240 # Test properly formed pages with 1 in PPN[3] that are not NAPOT - jal a1, looptest - li t4, 0x1000 # address step size - li a2, 0x80216000 # Test NAPOT pages - jal a1, looptest - - sfence.vma - li t4, 0x200000 # address step size - li a2, 0x80215240 # Test NAPOT pages - jal a1, looptest - li a2, 0xC0215240 # Test ill-formed NAPOT pages - jal a1, looptest - li a2, 0x40215240 # Test properly formed pages with 1 in PPN[3] that are not NAPOT - jal a1, looptest - - li a0, 3 # switch back to machine mode because code at 0x80000000 may not have clean page table entry - ecall - j done - -looptest: - mv t0, a2 # base address - li t2, 0 # i = 0 - li t3, 32 # Max amount of Loops = 32 - li t5, 0x8082 # return instruction opcode - -loop: bge t2, t3, finished # exit loop if i >= loops - sw t5, 0(t0) # store a return at this address to exercise DTLB - lw t1, 0(t0) # read it back - fence.i # synchronize with I$ - jal changetoipfhandler # set up trap handler to return from instruction page fault if necessary - jalr ra, t0 # jump to the return statement to exercise the ITLB - jal changetodefaulthandler - add t0, t0, t4 - addi t2, t2, 1 - j loop - -finished: - jr a1 - -changetoipfhandler: - li a0, 3 - ecall # switch to machine mode - la a0, ipf_handler - csrw mtvec, a0 # point to new handler - li a0, 1 - ecall # switch back to supervisor mode - ret - -changetodefaulthandler: - li a0, 3 - ecall # switch to machine mode - la a0, trap_handler - csrw mtvec, a0 # point to new handler - li a0, 1 - ecall # switch back to supervisor mode - ret - -instructionpagefaulthandler: - csrw mepc, ra # go back to calling function - mret - -.align 4 # trap handlers must be aligned to multiple of 4 -ipf_handler: - # Load trap handler stack pointer tp - csrrw tp, mscratch, tp # swap MSCRATCH and tp - sd t0, 0(tp) # Save t0 and t1 on the stack - sd t1, -8(tp) - csrr t0, mcause # Check the cause - li t1, 8 # is it an ecall trap? - andi t0, t0, 0xFC # if CAUSE = 8, 9, or 11 - beq t0, t1, ecall # yes, take ecall - csrr t0, mcause - li t1, 12 # is it an instruction page fault - beq t0, t1, ipf # yes, return to calling function - j trap_return - -ipf: - csrw mepc, ra # return to calling function - ld t1, -8(tp) # restore t1 and t0 - ld t0, 0(tp) - csrrw tp, mscratch, tp # restore tp - mret # return from trap - +// The page tables come before WALLY-selfcheck.h so that its data does not move them off the +// addresses that satp and the pointer PTEs name (0x80010000-0x80017FFF). .data .align 16 @@ -437,3 +350,200 @@ pagetable: .8byte 0x00000000200060CF .8byte 0x000000002000A0CF .8byte 0x000000002000E0CF + +// The 4 KiB-step looptest call stores code at the start of every page from 0x80016000 to 0x80035000. +// That includes 0x80019000, where this code section starts, so keep its first word unused (and keep +// the code under 4 KiB so that no other page start in that range holds code). +.section .text.main +napot_pad: + .word 0 + +#include "WALLY-selfcheck.h" + +#define CLIRET 0x80824781 // "c.ret" << 16 | "c.li a5, 0"; c.li a5, i adds i << 2 for i = 0-31 + +// CALLTEST base, faults: run looptest from VA base; faults = 1 if every access there must page fault +.macro CALLTEST base, faults + li a3, \faults + li s9, 0 + li s10, 0 + li a2, \base + jal a1, looptest + li s7, 96 * \faults # a store, load and fetch fault in each of 32 iterations + SC_CHECK_EQ s9, s7, 3 + SC_CHECK_EQ s10, zero, 4 +.endm + +# run-elf.bash find this in project description +main: + la t5, rec_default + csrw mtvec, t5 + li s6, CLIRET + li t5, 0x1 + slli t5, t5, 62 + csrs menvcfg, t5 + # Page table root address at 0x80010000; SV48 + li t5, 0x9000000000080010 + csrw satp, t5 + + # sfence.vma x0, x0 + + # switch to supervisor mode + li a0, 1 + ecall + + li t4, 0x200000 # address step size + CALLTEST 0x80215240, 0 # Test NAPOT pages + CALLTEST 0xC0215240, 1 # Test ill-formed NAPOT pages + CALLTEST 0x40215240, 0 # Test properly formed pages with 1 in PPN[3] that are not NAPOT + li t4, 0x1000 # address step size + CALLTEST 0x80216000, 0 # Test NAPOT pages + + sfence.vma + li t4, 0x200000 # address step size + CALLTEST 0x80215240, 0 # Test NAPOT pages + CALLTEST 0xC0215240, 1 # Test ill-formed NAPOT pages + CALLTEST 0x40215240, 0 # Test properly formed pages with 1 in PPN[3] that are not NAPOT + + li a0, 3 # switch back to machine mode because code at 0x80000000 may not have clean page table entry + ecall + + # The physical pages hold the code each mapping stored: the 4 KiB step's 32 pages, and the last + # iteration's code at the address that every 2 MiB step's VA maps to + li t0, 0x80016000 + li t2, 0 + li t3, 32 +1: slli t5, t2, 2 + add t5, t5, s6 + lwu t1, 0(t0) + SC_CHECK_EQ_ADR t1, t5, t0, 5 + li t5, 0x1000 + add t0, t0, t5 + addi t2, t2, 1 + blt t2, t3, 1b + li t5, CLIRET + (31 << 2) + li t0, 0x80015240 + lwu t1, 0(t0) + SC_CHECK_EQ_ADR t1, t5, t0, 5 + li t0, 0x80018240 + lwu t1, 0(t0) + SC_CHECK_EQ_ADR t1, t5, t0, 5 + + SC_PASS + j done + +looptest: + mv t0, a2 # base address + li t2, 0 # i = 0 + li t3, 32 # Max amount of Loops = 32 + +loop: bge t2, t3, finished # exit loop if i >= loops + slli t5, t2, 2 + add t5, t5, s6 # "c.li a5, i; c.ret" + li t1, -1 +napot_sw: + sw t5, 0(t0) # store code at this address to exercise DTLB +napot_lw: + lw t1, 0(t0) # read it back + fence.i # synchronize with I$ + jal changetoipfhandler # set up trap handler to return from instruction page fault if necessary + li a5, -1 + jalr ra, t0 # jump to the code to exercise the ITLB + jal changetodefaulthandler + li s4, -1 # a faulting load and the code leave t1 and a5 alone + mv s5, s4 + bnez a3, 1f + addiw s4, t5, 0 # the load returns the word stored, sign-extended + mv s5, t2 # the code sets a5 = i +1: SC_CHECK_EQ_ADR t1, s4, t0, 1 + SC_CHECK_EQ_ADR a5, s5, t0, 2 + add t0, t0, t4 + addi t2, t2, 1 + j loop + +finished: + jr a1 + +changetoipfhandler: + li a0, 3 + ecall # switch to machine mode + la a0, rec_ipf + csrw mtvec, a0 # point to new handler + li a0, 1 + ecall # switch back to supervisor mode + ret + +changetodefaulthandler: + li a0, 3 + ecall # switch to machine mode + la a0, rec_default + csrw mtvec, a0 # point to new handler + li a0, 1 + ecall # switch back to supervisor mode + ret + +instructionpagefaulthandler: + csrw mepc, ra # go back to calling function + mret + +// rec_default: mtvec while looptest's store and load run. A store (15) or load (13) page fault must come from +// napot_sw / napot_lw with mtval = the VA in t0: count it in s9 if so and in s10 if not, then let the library +// handler skip the instruction. Everything else goes to sc_trap_handler, which passes ecalls on to the library +// handler and fails the test on any other trap. s7, s8 and s11 are the handlers' scratch registers. +.align 4 +rec_default: + csrr s7, mcause + li s8, 13 + la s11, napot_lw + beq s7, s8, 1f + li s8, 15 + la s11, napot_sw + beq s7, s8, 1f + j sc_trap_handler +1: csrr s7, mepc + bne s7, s11, 2f + csrr s7, mtval + bne s7, t0, 2f + addi s9, s9, 1 + j trap_handler +2: addi s10, s10, 1 + j trap_handler + +// rec_ipf: mtvec while looptest's jalr runs. An instruction page fault (12) must have mepc = mtval = the VA in +// t0: count it, then ipf_handler returns to ra. Everything else goes to sc_trap_handler. +.align 4 +rec_ipf: + csrr s7, mcause + li s8, 12 + beq s7, s8, 1f + j sc_trap_handler +1: csrr s7, mepc + bne s7, t0, 2f + csrr s7, mtval + bne s7, t0, 2f + addi s9, s9, 1 + j ipf_handler +2: addi s10, s10, 1 + j ipf_handler + +.align 4 # trap handlers must be aligned to multiple of 4 +ipf_handler: + # Load trap handler stack pointer tp + csrrw tp, mscratch, tp # swap MSCRATCH and tp + sd t0, 0(tp) # Save t0 and t1 on the stack + sd t1, -8(tp) + csrr t0, mcause # Check the cause + li t1, 8 # is it an ecall trap? + andi t0, t0, 0xFC # if CAUSE = 8, 9, or 11 + beq t0, t1, ecall # yes, take ecall + csrr t0, mcause + li t1, 12 # is it an instruction page fault + beq t0, t1, ipf # yes, return to calling function + j trap_return + +ipf: + csrw mepc, ra # return to calling function + ld t1, -8(tp) # restore t1 and t0 + ld t0, 0(tp) + csrrw tp, mscratch, tp # restore tp + mret # return from trap diff --git a/tests/coverage/tlbmisc.S b/tests/coverage/tlbmisc.S index 3b22033c51..576ae602cf 100644 --- a/tests/coverage/tlbmisc.S +++ b/tests/coverage/tlbmisc.S @@ -4,6 +4,18 @@ // Written David_Harris@hmc.edu 1/1/24 // // Purpose: Test coverage for other TLB issues +// Self-checking (WALLY-selfcheck.h). rec_ipf and rec_default log mcause, mepc and mtval +// of every trap but an ecall in traplog before the test's handlers act on it, and at the +// end, in machine mode, the log must match expected_traps: each fault the test provokes +// (misaligned superpages, VA extension, reserved bits, Svpbmt, A = 0, W without R, PTE +// and page access faults, CBO permissions with and without MXR) has the expected cause, +// instruction and mtval, and no other trap was taken. The test also checks the values of +// the accesses that translate: the load through the 1 GiB page at VA 0x2_0000_0000 +// returns the word stored at PA 0x80000000, the AMO through the Svpbmt IO page returns +// the value placed at its physical address, cbo.zero through the IO page and through the +// read-write-execute page zeroes the physical lines, Svadu sets A in the megapage PTE that +// the fetch from 0x80E00000 used, and the fault at 0x80807000 leaves its A = 0 PTE alone. +// Failure indices: 1 trap count, 2 trap log entry (address = log entry), 3-8 values. // // A component of the CORE-V-WALLY configurable RISC-V project. // https://github.com/openhwfoundation/cvw @@ -28,8 +40,177 @@ #include "WALLY-init-lib.h" +// The page tables come before WALLY-selfcheck.h so that its data does not move them off the +// addresses that satp and the PTEs name (0x80010000-0x80015FFF). +.data + +.align 16 +# root Page table situated at 0x80010000 +pagetable: + .8byte 0x20004401 # VA 0x00000000-0x7F_FFFFFFFF: PTE at 0x80011000 01 valid + .8byte 0x00000000000010CF # misaligned terapage at 0x80_00000000 + .8byte 0x00000000000000CF # access fault terapage at 0x100_00000000 + .8byte 0x4000000020004401 # Bad PBMT at VA 0x180_00000000 + +# next page table at 0x80011000 +.align 12 + .8byte 0x00000000000010CF # misaligned gigapage at 0x00000000 + .8byte 0x0000000020005801 # PTE for pages at 0x40000000 pointing to 0x80150000 + .8byte 0x0000000020004801 # gigapage at 0x80000000 pointing to 0x80120000 + .8byte 0x0000000000000001 # gigapage at VA 0xC0000000 causes access fault + .8byte 0x0 + .8byte 0x0 + .8byte 0x0 + .8byte 0x0 +SpecialPage: + .8byte 0x00000000200000CF # 0x2_0000_0000 1GiB page1 + + +# Next page table at 0x80012000 for gigapage at 0x80000000 +.align 12 + .8byte 0x0000000020004C01 # for VA starting at 80000000 (pointer to NAPOT 64 KiB pages Page table at 80013000) + .8byte 0x0000000020014CCF # for VA starting at 80200000 (misaligned megapage) + .8byte 0x0000000020005001 # for VA starting at 80400000 (bad PBMT pages page table at 0x80014000) + .8byte 0x4000000020004C01 # for VA starting at 80600000 (bad entry: nonleaf PTE can't have PBMT != 0) + .8byte 0x0000000020005401 # for VA starting at 80800000 (testing rwx permissions with cbom/cboz . page table at 0x80015000) + .8byte 0x0000000020004C01 # for VA starting at 80A00000 (pointer to NAPOT 64 KiB pages like at 80000000. page table at 0x80013000) + .8byte 0x0F00000020004CCF # for VA starting at 80C00000 (bad reserved field in bits 60:54) + .8byte 0x000000002000000F # for VA starting at 80E00000 (megapage not dirty or accessed) + .8byte 0x0000000000004C01 # for VA starting at 81000000 (nonleaf pointing to unimplemented memory causes access fault) + .8byte 0x4000000020004C01 # for VA starting at 81200000 (nonleaf with PBMT nonzero causes page fault) + .8byte 0x00000000000000CF # for VA starting at 81400000 (megapage with access fault) + .8byte 0x0000000020004C09 # for VA starting at 81600000 (executable and not readable) + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + .8byte 0x0000000020004CC1 + +# Leaf page table at 0x80013000 with 64 KiB NAPOT pages +.align 12 + #80000000 + .8byte 0xA0000000200020CF + .8byte 0xA0000000200020CF + .8byte 0xA0000000200020CF + .8byte 0xA0000000200020CF + + .8byte 0xA0000000200020CF + .8byte 0xA0000000200020CF + .8byte 0xA0000000200020CF + .8byte 0xA0000000200020CF + + .8byte 0xA0000000200020CF + .8byte 0xA0000000200020CF + .8byte 0xA0000000200020CF + .8byte 0xA0000000200020CF + + .8byte 0xA0000000200020CF + .8byte 0xA0000000200020CF + .8byte 0xA0000000200020CF + .8byte 0xA0000000200020CF + + .8byte 0x80000000200060CF + .8byte 0x80000000200060CF + .8byte 0x80000000200060CF + .8byte 0x80000000200060CF + + .8byte 0x80000000200060CF + .8byte 0x80000000200060CF + .8byte 0x80000000200060CF + .8byte 0x80000000200060CF + + .8byte 0x80000000200060CF + .8byte 0x80000000200060CF + .8byte 0x80000000200060CF + .8byte 0x80000000200060CF + + .8byte 0x80000000200060CF + .8byte 0x80000000200060CF + .8byte 0x80000000200060CF + .8byte 0x80000000200060CF + + .8byte 0x800000002000A0CF + .8byte 0x800000002000A0CF + .8byte 0x800000002000A0CF + .8byte 0x800000002000A0CF + + .8byte 0x800000002000A0CF + .8byte 0x800000002000A0CF + .8byte 0x800000002000A0CF + .8byte 0x800000002000A0CF + + .8byte 0x800000002000A0CF + .8byte 0x800000002000A0CF + .8byte 0x800000002000A0CF + .8byte 0x800000002000A0CF + + .8byte 0x800000002000A0CF + .8byte 0x800000002000A0CF + .8byte 0x800000002000A0CF + .8byte 0x800000002000A0CF + + .8byte 0x800000002000E0CF + .8byte 0x800000002000E0CF + .8byte 0x800000002000E0CF + .8byte 0x800000002000E0CF + + .8byte 0x800000002000E0CF + .8byte 0x800000002000E0CF + +# Leaf page table at 0x80014000 with PBMT pages +.align 12 + #80400000 + .8byte 0x60000000200020CF # reserved entry VA 80400000 + .8byte 0x40000000201000CF # non-cache non-idempotent VA 80401000 + +# Leaf page table at 0x80015000 with various permissions for testing CBOM and CBOZ +.align 12 + #80800000 + .8byte 0x00000000200000CF # valid rwx for VA 80800000 + .8byte 0x00000000200000CB # valid r x for VA 80801000 + .8byte 0x00000000200000C3 # valid r for VA 80802000 + .8byte 0x00000000200000C9 # valid x for VA 80803000 + .8byte 0x00000000200000CD # valid wx for VA 80804000 (illegal combination, but used to test tlbcontrol) + .8byte 0x00000000200000CF # valid rwx for VA 80805000 for covering ITLB translate + .8byte 0x20000000200000CF # PBMT=1 for VA 80806000 for covering ITLB BadPBMT + .8byte 0x000000002000000F # valid rwx for VA 80807000 for covering UpdateDA + +#include "WALLY-selfcheck.h" + +#define NTRAPS 42 // traps expected (see expected_traps) +#define LOGMAX 64 // traps logged; more are only counted +#define AMOINIT 0x5A5A1234 // at PA 0x80400000 for the AMO through the Svpbmt IO page + # run-elf.bash find this in project description main: + la t5, rec_default + csrw mtvec, t5 + + # give the word at PA 0x80400000 a known value in memory (not just in the D$), since the AMO + # through the IO page reaches memory directly + li t0, 0x80400000 + li t1, AMOINIT + sw t1, 0(t0) + cbo.flush (t0) + li t5, 0x1 slli t5, t5, 62 ori t5, t5, 0xF0 @@ -92,14 +273,16 @@ main: # page has PBMT = 3 (reserved) li t0, 0x80400000 +tm_lw_pbmt3: lw t1, 0(t0) # read from page +tm_sw_pbmt3: sw t1, 0(t0) # write to page jalr ra, t0 # jump to page # AMO at page has PBMT = 2 or 1 (uncached) li t0, 0x80401000 li t1, 10 - amoadd.w t1, t1, (t0) + amoadd.w s3, t1, (t0) # s3 = old value (checked at the end) la t2, SpecialPage li t0, 0x200000000 # an address to a specific 1 GiB page @@ -133,7 +316,7 @@ ConcurrentICacheMissDTLBMiss: nop nop nop - lw t1, 0(t0) + lw s2, 0(t0) # s2 = word at PA 0x80000000 (checked at the end) # write a cacheline length (512 bits) to memory in uncached region li t0, 0x80401000 @@ -141,14 +324,18 @@ ConcurrentICacheMissDTLBMiss: # Nonleaf PTE has PBMT != 0 causes a page fault during page walking. H li t0, 0x80600000 +tm_lw_nlpbmt: lw t1, 0(t0) # read from page +tm_sw_nlpbmt: sw t1, 0(t0) # write to page jalr ra, t0 # jump to page sfence.vma li t0, 0x18000000000 +tm_lw_toppbmt: lw t1, 0(t0) # read from page +tm_sw_toppbmt: sw t1, 0(t0) # write to page jalr ra, t0 # jump to page @@ -166,6 +353,7 @@ ConcurrentICacheMissDTLBMiss: # Access page with R=0 W = 1 li t0, 0x80804000 li t1, 0x8067 # ret +tm_sw_wonly: sw t1, 0(t0) # have something to return from jalr ra, t0 @@ -187,37 +375,49 @@ ConcurrentICacheMissDTLBMiss: # Load and AMO operation on page table entry that causes access fault li t0, 0x81000000 +tm_lw_ptaf: lw t1, 0(t0) sfence.vma +tm_amo_ptaf: amoadd.w t0, t0, 0(t0) # Access fault on top level PTE li t0, 0x1000000000 +tm_lw_top1: lw t1, 0(t0) # Bad PBMT on top level PTE li t0, 0x1800000000 +tm_lw_top2: lw t1, 0(t0) # Access fault on megapage li t0, 0x81400000 +tm_lw_megaaf: lw t1, 0(t0) # Access fault walking page tables at megapage level li t0, 0xC0000000 +tm_lw_walkaf: lw t1, 0(t0) # AMO operation on page table entry that causes page fault due to malformed PBMT li t0, 0x81200000 jalr t0 # Attempt to fetch instruction from address causing faulty page walk +tm_lw_nlpbmt2: lw t1, 0(t0) sfence.vma +tm_amo_nlpbmt2: amoadd.w t0, t0, 0(t0) # point top-level page table to an illegal address and verify it faults li t0, 0x9000000000070000 # trap handler at non-existing memory location csrw satp, t0 # should cause trap + # Wally's ITLB still holds this code's translation, so sfence.vma is fetched and the next fetch, + # which walks the new page table, takes the access fault. (Walking for the sfence.vma fetch, + # which would fault there instead, is also legal.) sfence.vma +tm_nop_badroot: nop @@ -226,6 +426,7 @@ ConcurrentICacheMissDTLBMiss: # uncachable AMO access li t0, 0x80401000 # PBMT sets as uncachable +tm_amo_io: amoadd.w t0, t0, 0(t0) # exercise CBOM instructions with various permissions @@ -233,16 +434,22 @@ ConcurrentICacheMissDTLBMiss: cbo.zero (t0) cbo.clean (t0) li t0, 0x80801000 +tm_cboz_rx: cbo.zero (t0) cbo.clean (t0) li t0, 0x80802000 +tm_cboz_r: cbo.zero (t0) cbo.clean (t0) li t0, 0x80803000 +tm_cboz_x: cbo.zero (t0) +tm_cboc_x: cbo.clean (t0) li t0, 0x80804000 +tm_cboz_wx: cbo.zero (t0) +tm_cboc_wx: cbo.clean (t0) # set mstatus.MXR @@ -259,20 +466,26 @@ ConcurrentICacheMissDTLBMiss: cbo.zero (t0) cbo.clean (t0) li t0, 0x80801000 +tm_cboz_rx_mxr: cbo.zero (t0) cbo.clean (t0) li t0, 0x80802000 +tm_cboz_r_mxr: cbo.zero (t0) cbo.clean (t0) li t0, 0x80803000 +tm_cboz_x_mxr: cbo.zero (t0) cbo.clean (t0) li t0, 0x80804000 +tm_cboz_wx_mxr: cbo.zero (t0) +tm_cboc_wx_mxr: cbo.clean (t0) # attempt read from read disabled, execute enabled PTE with MXR set li t0, 0x81600000 +tm_lw_xonly: lw t1, 0(t0) # read from page # clear mstatus.MXR @@ -290,6 +503,56 @@ ConcurrentICacheMissDTLBMiss: # wrap up li a0, 3 # switch back to machine mode because code at 0x80000000 may not have clean page table entry ecall + + //////////////////////////////////////////////////////////////////////////////////////////// + // Checks, in machine mode + //////////////////////////////////////////////////////////////////////////////////////////// + // Every trap but an ecall matched expected_traps, in order + la a1, traplog + ld t2, 0(a1) + li t3, NTRAPS + SC_CHECK_EQ t2, t3, 1 + addi a1, a1, 8 + la a2, expected_traps + li a3, 3 * NTRAPS +1: ld t2, 0(a1) + ld t3, 0(a2) + SC_CHECK_EQ_ADR t2, t3, a1, 2 + addi a1, a1, 8 + addi a2, a2, 8 + addi a3, a3, -1 + bnez a3, 1b + // The load through the 1 GiB page at VA 0x2_0000_0000 read PA 0x80000000, which held c.ret then + li t3, 0x8082 + SC_CHECK_EQ s2, t3, 3 + // The AMO through the Svpbmt IO page at VA 0x80401000 read PA 0x80400000 + li t3, AMOINIT + SC_CHECK_EQ s3, t3, 4 + // cbo.zero through the IO page zeroed the line at PA 0x80400000 in memory, and cbo.zero through + // the read-write-execute page at VA 0x80800000 zeroed the line at PA 0x80000000 + li a1, 0x80400000 + li a2, 0x80000000 + li a3, 8 +2: ld t2, 0(a1) + SC_CHECK_EQ_ADR t2, zero, a1, 5 + ld t2, 0(a2) + SC_CHECK_EQ_ADR t2, zero, a2, 6 + addi a1, a1, 8 + addi a2, a2, 8 + addi a3, a3, -1 + bnez a3, 2b + // Svadu set A in the megapage PTE of VA 0x80E00000 (level-1 table at 0x80012000, entry 7) + li a1, 0x80012038 + ld t2, 0(a1) + li t3, 0x000000002000004F + SC_CHECK_EQ_ADR t2, t3, a1, 7 + // The fetch from VA 0x80807000 with ADUE = 0 faulted and left A = 0 (leaf table at 0x80015000, entry 7) + li a1, 0x80015038 + ld t2, 0(a1) + li t3, 0x000000002000000F + SC_CHECK_EQ_ADR t2, t3, a1, 8 + + SC_PASS j done backandforth: @@ -298,7 +561,7 @@ backandforth: changetoipfhandler: li a0, 3 ecall # switch to machine mode - la a0, ipf_handler + la a0, rec_ipf csrw mtvec, a0 # point to new handler li a0, 1 ecall # switch back to supervisor mode @@ -307,7 +570,7 @@ changetoipfhandler: changetodefaulthandler: li a0, 3 ecall # switch to machine mode - la a0, trap_handler + la a0, rec_default csrw mtvec, a0 # point to new handler li a0, 1 ecall # switch back to supervisor mode @@ -317,6 +580,51 @@ instructionpagefaulthandler: csrw mepc, ra # go back to calling function mret +// REC_TRAP target: log mcause, mepc and mtval of a trap other than an ecall in traplog (the first LOGMAX +// of them; all are counted), then go on to the test's handler, target, with every register unchanged +.macro REC_TRAP target + csrrw t0, mscratch, t0 # t0 = trap stack pointer, mscratch = saved t0 + sd t1, -16(t0) + sd t2, -24(t0) + sd t3, -32(t0) + csrr t1, mcause + li t2, 8 # ecall from U, S or M: not logged + beq t1, t2, 2f + li t2, 9 + beq t1, t2, 2f + li t2, 11 + beq t1, t2, 2f + la t2, traplog + ld t3, 0(t2) + addi t3, t3, 1 + sd t3, 0(t2) # count every trap + li t1, LOGMAX + bgtu t3, t1, 2f # log the first LOGMAX + slli t1, t3, 4 + slli t3, t3, 3 + add t1, t1, t3 + add t1, t1, t2 # traplog + 24 * count: entry count - 1 starts at -16(t1) + csrr t3, mcause + sd t3, -16(t1) + csrr t3, mepc + sd t3, -8(t1) + csrr t3, mtval + sd t3, 0(t1) +2: ld t3, -32(t0) + ld t2, -24(t0) + ld t1, -16(t0) + csrrw t0, mscratch, t0 # restore t0 and the trap stack pointer + j \target +.endm + +.align 4 # trap handlers must be aligned to multiple of 16 +rec_ipf: # mtvec while ipf_handler is selected + REC_TRAP ipf_handler + +.align 4 +rec_default: # mtvec while the library handler is selected + REC_TRAP trap_handler + .align 4 # trap handlers must be aligned to multiple of 16 ipf_handler: # Load trap handler stack pointer tp @@ -348,152 +656,66 @@ fixsatptraphandler: mret .data - -.align 16 -# root Page table situated at 0x80010000 -pagetable: - .8byte 0x20004401 # VA 0x00000000-0x7F_FFFFFFFF: PTE at 0x80011000 01 valid - .8byte 0x00000000000010CF # misaligned terapage at 0x80_00000000 - .8byte 0x00000000000000CF # access fault terapage at 0x100_00000000 - .8byte 0x4000000020004401 # Bad PBMT at VA 0x180_00000000 - -# next page table at 0x80011000 -.align 12 - .8byte 0x00000000000010CF # misaligned gigapage at 0x00000000 - .8byte 0x0000000020005801 # PTE for pages at 0x40000000 pointing to 0x80150000 - .8byte 0x0000000020004801 # gigapage at 0x80000000 pointing to 0x80120000 - .8byte 0x0000000000000001 # gigapage at VA 0xC0000000 causes access fault - .8byte 0x0 - .8byte 0x0 - .8byte 0x0 - .8byte 0x0 -SpecialPage: - .8byte 0x00000000200000CF # 0x2_0000_0000 1GiB page1 - - -# Next page table at 0x80012000 for gigapage at 0x80000000 -.align 12 - .8byte 0x0000000020004C01 # for VA starting at 80000000 (pointer to NAPOT 64 KiB pages Page table at 80013000) - .8byte 0x0000000020014CCF # for VA starting at 80200000 (misaligned megapage) - .8byte 0x0000000020005001 # for VA starting at 80400000 (bad PBMT pages page table at 0x80014000) - .8byte 0x4000000020004C01 # for VA starting at 80600000 (bad entry: nonleaf PTE can't have PBMT != 0) - .8byte 0x0000000020005401 # for VA starting at 80800000 (testing rwx permissions with cbom/cboz . page table at 0x80015000) - .8byte 0x0000000020004C01 # for VA starting at 80A00000 (pointer to NAPOT 64 KiB pages like at 80000000. page table at 0x80013000) - .8byte 0x0F00000020004CCF # for VA starting at 80C00000 (bad reserved field in bits 60:54) - .8byte 0x000000002000000F # for VA starting at 80E00000 (megapage not dirty or accessed) - .8byte 0x0000000000004C01 # for VA starting at 81000000 (nonleaf pointing to unimplemented memory causes access fault) - .8byte 0x4000000020004C01 # for VA starting at 81200000 (nonleaf with PBMT nonzero causes page fault) - .8byte 0x00000000000000CF # for VA starting at 81400000 (megapage with access fault) - .8byte 0x0000000020004C09 # for VA starting at 81600000 (executable and not readable) - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - .8byte 0x0000000020004CC1 - -# Leaf page table at 0x80013000 with 64 KiB NAPOT pages -.align 12 - #80000000 - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - .8byte 0xA0000000200020CF - - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - .8byte 0x80000000200060CF - - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - .8byte 0x800000002000A0CF - - .8byte 0x800000002000E0CF - .8byte 0x800000002000E0CF - .8byte 0x800000002000E0CF - .8byte 0x800000002000E0CF - - .8byte 0x800000002000E0CF - .8byte 0x800000002000E0CF - -# Leaf page table at 0x80014000 with PBMT pages -.align 12 - #80400000 - .8byte 0x60000000200020CF # reserved entry VA 80400000 - .8byte 0x40000000201000CF # non-cache non-idempotent VA 80401000 - -# Leaf page table at 0x80015000 with various permissions for testing CBOM and CBOZ -.align 12 - #80800000 - .8byte 0x00000000200000CF # valid rwx for VA 80800000 - .8byte 0x00000000200000CB # valid r x for VA 80801000 - .8byte 0x00000000200000C3 # valid r for VA 80802000 - .8byte 0x00000000200000C9 # valid x for VA 80803000 - .8byte 0x00000000200000CD # valid wx for VA 80804000 (illegal combination, but used to test tlbcontrol) - .8byte 0x00000000200000CF # valid rwx for VA 80805000 for covering ITLB translate - .8byte 0x20000000200000CF # PBMT=1 for VA 80806000 for covering ITLB BadPBMT - .8byte 0x000000002000000F # valid rwx for VA 80807000 for covering UpdateDA +.align 12 // a page of its own, clear of the page tables above +traplog: // number of traps but ecalls, then mcause, mepc, mtval of each + .fill 1 + 3 * LOGMAX, 8, 0 + +// mcause, mepc, mtval of each trap but an ecall, in order +expected_traps: + // instruction page faults: misaligned terapage, gigapage, megapage; VA extension (2); reserved PTE bits + .dword 12, 0x8000000000, 0x8000000000 + .dword 12, 0x0, 0x0 + .dword 12, 0x80200000, 0x80200000 + .dword 12, 0x7FFFFFFF80000000, 0x7FFFFFFF80000000 + .dword 12, 0x8000000080C00000, 0x8000000080C00000 + .dword 12, 0x80C00000, 0x80C00000 + // leaf with PBMT = 3: load, store and fetch page faults + .dword 13, tm_lw_pbmt3, 0x80400000 + .dword 15, tm_sw_pbmt3, 0x80400000 + .dword 12, 0x80400000, 0x80400000 + // nonleaf PTE with PBMT != 0 at level 1, then at the top level + .dword 13, tm_lw_nlpbmt, 0x80600000 + .dword 15, tm_sw_nlpbmt, 0x80600000 + .dword 12, 0x80600000, 0x80600000 + .dword 13, tm_lw_toppbmt, 0x18000000000 + .dword 15, tm_sw_toppbmt, 0x18000000000 + .dword 12, 0x18000000000, 0x18000000000 + // fetch from a page with A = 0 and ADUE = 0 + .dword 12, 0x80807000, 0x80807000 + // W without R: store and fetch page faults + .dword 15, tm_sw_wonly, 0x80804000 + .dword 12, 0x80804000, 0x80804000 + // PBMT = 1 with menvcfg.PBMTE = 0 + .dword 12, 0x80806000, 0x80806000 + // nonleaf PTE pointing to unimplemented memory: load and AMO access faults + .dword 5, tm_lw_ptaf, 0x81000000 + .dword 7, tm_amo_ptaf, 0x81000000 + // invalid PTEs below VA 0x1000000000 and 0x1800000000: load page faults + .dword 13, tm_lw_top1, 0x1000000000 + .dword 13, tm_lw_top2, 0x1800000000 + // megapage at PA 0: load access fault; level-1 pointer to PA 0: access fault on the walk + .dword 5, tm_lw_megaaf, 0x81400000 + .dword 5, tm_lw_walkaf, 0xC0000000 + // nonleaf PTE with PBMT != 0: fetch, load and AMO page faults + .dword 12, 0x81200000, 0x81200000 + .dword 13, tm_lw_nlpbmt2, 0x81200000 + .dword 15, tm_amo_nlpbmt2, 0x81200000 + // root page table in unimplemented memory: instruction access fault on the walk + .dword 1, tm_nop_badroot, tm_nop_badroot + // AMO to the IO page now that PBMTE = 0 + .dword 15, tm_amo_io, 0x80401000 + // CBOs: cbo.zero needs W; cbo.clean needs R (or X with MXR); W without R is reserved + .dword 15, tm_cboz_rx, 0x80801000 + .dword 15, tm_cboz_r, 0x80802000 + .dword 15, tm_cboz_x, 0x80803000 + .dword 15, tm_cboc_x, 0x80803000 + .dword 15, tm_cboz_wx, 0x80804000 + .dword 15, tm_cboc_wx, 0x80804000 + // the same with MXR = 1, where cbo.clean of the execute-only page succeeds + .dword 15, tm_cboz_rx_mxr, 0x80801000 + .dword 15, tm_cboz_r_mxr, 0x80802000 + .dword 15, tm_cboz_x_mxr, 0x80803000 + .dword 15, tm_cboz_wx_mxr, 0x80804000 + .dword 15, tm_cboc_wx_mxr, 0x80804000 + // load from an execute-only misaligned megapage + .dword 13, tm_lw_xonly, 0x81600000 diff --git a/tests/coverage/vm64check.S b/tests/coverage/vm64check.S index cf6ba105cc..b73c529c8b 100644 --- a/tests/coverage/vm64check.S +++ b/tests/coverage/vm64check.S @@ -4,6 +4,12 @@ // Written: David_Harris@hmc.edu 7 April 2023 // // Purpose: vm64check coverage +// Self-checking (WALLY-selfcheck.h). Every jump runs in machine mode, so its target is +// used untranslated and lies outside every memory region: the fetch must take an +// instruction access fault (mcause 1) with mepc = mtval = the target. vmtrap records +// mcause, mepc and mtval in s10, s9 and s11 and returns to ra; each check compares +// them. Every other trap but an ecall fails the test. Failure indices: 10 * jump number +// + 1 mcause, + 2 mepc, + 3 mtval; 0xF0 any other trap. // // A component of the CORE-V-WALLY configurable RISC-V project. // https://github.com/openhwfoundation/cvw @@ -30,19 +36,22 @@ // assign eq_63_47 = &(VAdr[63:47]) | ~|(VAdr[63:47]); // assign UpperBitsUnequal = SV39Mode ? ~(eq_63_47 & eq_46_38) : ~eq_63_47; -.section .text.init -.global rvtest_entry_point +#include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" -rvtest_entry_point: - la sp, topofstack # Initialize stack pointer (not used) +// FAULTED n: the jump to the address in t0 took an instruction access fault there (n = jump number) +.macro FAULTED n + li t2, 1 + SC_CHECK_EQ s10, t2, 10 * \n + 1 + SC_CHECK_EQ s9, t0, 10 * \n + 2 + SC_CHECK_EQ s11, t0, 10 * \n + 3 + li s10, -1 # no trap recorded for the next jump yet +.endm - # Set up interrupts - la t0, trap_handler - csrw mtvec, t0 # Initialize MTVEC to trap_handler - # set up PMP so user and supervisor mode can access full address space - csrw pmpcfg0, 0xF # configure PMP0 to TOR RWX - li t0, 0xFFFFFFFF - csrw pmpaddr0, t0 # configure PMP0 top of range to 0xFFFFFFFF to allow all 32-bit addresses +main: + la t0, vmtrap + csrw mtvec, t0 + li s10, -1 # SATP in non-39 mode csrw satp, zero @@ -55,26 +64,31 @@ check1: jalr t0 check2: + FAULTED 1 li t0, 0xFFFFFFF1800F0000 # unimplemented memory with upper and lower all one la ra, check3 jalr t0 check3: + FAULTED 2 li t0, 0xFFF81001800F0000 # unimplemented memory with upper all one, lower mixed la ra, check4 jalr t0 check4: + FAULTED 3 li t0, 0x03001001800F0000 # unimplemented memory with upper mixed, lower mixed la ra, check5 jalr t0 check5: + FAULTED 4 li t0, 0x00001001800F0000 # unimplemented memory with upper all zero, lower mixed la ra, check11 jalr t0 check11: + FAULTED 5 # SATP in SV39 mode li t0, 0x8000000000000000 csrw satp, t0 @@ -85,21 +99,25 @@ check11: jalr t0 check12: + FAULTED 6 li t0, 0xFFFFFFF1800F0000 # unimplemented memory with upper and lower all one la ra, check13 jalr t0 check13: + FAULTED 7 li t0, 0xFFF81001800F0000 # unimplemented memory with upper all one, lower mixed la ra, check14 jalr t0 check14: + FAULTED 8 li t0, 0x03001001800F0000 # unimplemented memory with upper mixed, lower mixed la ra, check15 jalr t0 check15: + FAULTED 9 li t0, 0x00001001800F0000 # unimplemented memory with upper all zero, lower mixed la ra, check15a jalr t0 @@ -107,85 +125,37 @@ check15: # The checks above leave three SV39 FEC terms in vm64check uncovered because no # VA exercises these exact upper-bit field patterns. Add them explicitly: check15a: # all0_55_47_0: bits[63:56]=0, bits[55:47]!=0 (bit47), bits[46:38]=0 + FAULTED 10 li t0, 0x00008000800F0000 la ra, check15b jalr t0 check15b: # all1_46_38_0: bits[63:47]=all1, bits[46:38]!=all1 (=0) + FAULTED 11 li t0, 0xFFFF8000800F0000 la ra, check15c jalr t0 check15c: # all1_55_47_0: bits[63:56]=all1, bits[55:47]!=all1 (=0), bits[46:38]=all1 + FAULTED 12 li t0, 0xFF007FC0800F0000 la ra, check16 jalr t0 check16: + FAULTED 13 + SC_PASS + j done -write_tohost: - la t1, tohost - li t0, 1 # 1 for success, 3 for failure - sd t0, 0(t1) # send success code - -self_loop: - j self_loop # wait - +// Instruction access faults: record mcause in s10, mepc in s9 and mtval in s11 and return to ra. Everything +// else goes to sc_trap_handler, which passes ecalls on to the WALLY-init-lib.h handler and fails the test on +// any other trap. .align 4 # trap handlers must be aligned to multiple of 4 -trap_handler: +vmtrap: + csrr s10, mcause + addi s8, s10, -1 + bnez s8, sc_trap_handler + csrr s9, mepc + csrr s11, mtval csrw mepc, ra # return to address in ra mret - -.section .tohost -tohost: # write to HTIF - .dword 0 -fromhost: - .dword 0 - - -# Initialize stack with room for 512 bytes -.bss - .space 512 -topofstack: - - - - - - j done - - lw t1, 0(t0) - li t0, 0xFFFFFFFF80000000 - lw t1, 0(t0) - li t1, 0xFFF8000080000000 - lw t1, 0(t0) - li t1, 0x1000000080000000 - lw t1, 0(t0) - li t1, 0x0000010080000000 - lw t1, 0(t0) - li t0, 0x8000000000000000 - csrw satp, t0 # SV39 mode - li t0, 0x0000000080000000 - lw t1, 0(t0) - li t0, 0xFFFFFFFF80000000 - lw t1, 0(t0) - li t1, 0xFFF8000080000000 - lw t1, 0(t0) - li t1, 0x1000000080000000 - lw t1, 0(t0) - li t1, 0x0000010080000000 - lw t1, 0(t0) - li t0, 0x9000000000000000 - csrw satp, t0 # SV48 mode - li t0, 0x0000000080000000 - lw t1, 0(t0) - li t0, 0xFFFFFFFF80000000 - lw t1, 0(t0) - li t1, 0xFFF8000080000000 - lw t1, 0(t0) - li t1, 0x1000000080000000 - lw t1, 0(t0) - li t1, 0x0000010080000000 - lw t1, 0(t0) - li t0, 0x0000000000000000 - csrw satp, t0 # disable virtual memory From 0f151f62a8f528e04c7bbe28c0395889348622f6 Mon Sep 17 00:00:00 2001 From: David Harris Date: Fri, 9 Oct 2026 17:26:26 -0700 Subject: [PATCH 6/9] cacheInval, dcache1, fpu, fpuReservedRM, ieu, ifu: check results They passed by reaching done. Each now checks its results: cacheInval checks that the timer interrupt lands in the fence.i burst and adds a self-modifying code check after it; dcache1 stores distinct values and reads them back after the flushes and through evictions; fpu checks every legal result and fflags and that each reserved encoding raises illegal instruction at its own address; fpuReservedRM checks that a dynamic-rounding add with frm 5 or 6 traps and changes nothing; ieu and ifu check their integer, bit-manipulation and compressed results and every expected trap. None of the timed sequences changed. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: David Harris --- tests/coverage/cacheInval.S | 58 +++++++- tests/coverage/dcache1.S | 83 +++++++++-- tests/coverage/fpu.S | 246 ++++++++++++++++++++++++--------- tests/coverage/fpuReservedRM.S | 51 ++++++- tests/coverage/ieu.S | 168 ++++++++++++++++------ tests/coverage/ifu.S | 108 ++++++++++++--- 6 files changed, 571 insertions(+), 143 deletions(-) diff --git a/tests/coverage/cacheInval.S b/tests/coverage/cacheInval.S index 6fe675a1e2..3abfeaa031 100644 --- a/tests/coverage/cacheInval.S +++ b/tests/coverage/cacheInval.S @@ -24,6 +24,11 @@ // LRU/valid state is invalidated, and the burst is long enough to absorb timer // jitter. // +// Checks: the D$ priming loads return the stored values; the timer interrupt was taken +// (mcause) on a fence.i inside the burst (mepc); and self-modifying code works: a function +// run once (so it is in the I$) is overwritten with a store, and after fence.i the next +// call runs the new instruction. +// // A component of the CORE-V-WALLY configurable RISC-V project. // https://github.com/openhwfoundation/cvw // @@ -44,6 +49,7 @@ //////////////////////////////////////////////////////////////////////////////////////////////// #include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" main: // Prime the instruction cache by executing code spread across several @@ -56,12 +62,14 @@ main: fence.i // back-to-back fence.i // Prime the data cache with valid lines (for context; D$ InvalidateCache is tied 0). + // Each line holds its own address, checked after the burst. li t0, 0x80108000 - sd zero, 0(t0) - ld t1, 0(t0) + sd t0, 0(t0) + ld a1, 0(t0) li t0, 0x80109000 - sd zero, 0(t0) - ld t1, 0(t0) + sd t0, 0(t0) + ld a2, 0(t0) + csrw mcause, zero // the check after the burst reads the cause of the timer interrupt // ---- Arm the machine timer to fire during the fence.i burst ---- // CLINT: MTIME @ 0x0200BFF8, MTIMECMP @ 0x02004000. MTIE and global MIE @@ -77,9 +85,11 @@ main: // Memory stage asserting InvalidateICacheM; with the timer armed to fire inside this // window, the trap (TrapM -> FlushW) is taken while a fence.i is in M, hitting // InvalidateFlushStage_1 (InvalidateCache=1 & InvalidateFlushStage=1). +burst_start: .rept 256 fence.i .endr +burst_end: // The trap_handler in WALLY-init-lib.h disables the timer (mtimecmp = -1) and // returns into the middle of the burst, which then runs to completion. @@ -89,6 +99,40 @@ main: jal primer fence.i + // ---- Checks, after the timing-sensitive sequence ---- + li t5, 0x80108000 + SC_CHECK_EQ a1, t5, 1 + li t5, 0x80109000 + SC_CHECK_EQ a2, t5, 2 + + // The machine timer interrupt was taken, on a fence.i in the burst. The library handler returns + // past the interrupted instruction, so mepc is its address + 4. + csrr a3, mcause + li t5, 0x8000000000000007 + SC_CHECK_EQ a3, t5, 3 + csrr a3, mepc + addi a3, a3, -4 + la t5, burst_start + bltu a3, t5, 1f + la t5, burst_end + bltu a3, t5, 2f +1: SC_FAIL 4 +2: + + // Self-modifying code. smc_fn returns a0 = 1. Run it so its line is in the I$, then overwrite its + // first instruction with addi a0, x0, 2. After fence.i, the next call must run the new instruction. + jal smc_fn + li t5, 1 + SC_CHECK_EQ a0, t5, 5 + la a3, smc_fn + li a4, 0x00200513 // addi a0, x0, 2 + sw a4, 0(a3) + fence.i + jal smc_fn + li t5, 2 + SC_CHECK_EQ a0, t5, 6 + + SC_PASS j done // Subroutine spread over several .align'd cache lines so executing it fills @@ -111,3 +155,9 @@ primer2: nop nop jr t6 + +// Function rewritten by the self-modifying code check, on its own cache line +.align 6 +smc_fn: + .word 0x00100513 // addi a0, x0, 1 (a 32-bit encoding, so one word store replaces it) + ret diff --git a/tests/coverage/dcache1.S b/tests/coverage/dcache1.S index d54326d0d2..b98d39622c 100644 --- a/tests/coverage/dcache1.S +++ b/tests/coverage/dcache1.S @@ -1,5 +1,37 @@ +/////////////////////////////////////////// +// dcache1.S +// +// Written: avercruysse@hmc.edu 18 April 2023 +// +// Purpose: Test coverage for D$: for each way, trigger a CacheDataMem write enable while chip enable +// is low, then flush each way's line with cbo.flush. The four way tests were generated by +// dcache1.py; the flushes and the checks were added by hand afterwards. +// Each way test stores two different values to its line, all four lines in the same set. +// The cbo.flush instructions write the lines back and invalidate them, so reading them +// afterwards fetches them from memory and checks both the second store and the writebacks. +// Finally five lines of one set, one more than the ways, are stored and read back, so the +// lines are evicted and refetched, and every value must survive. +// +// A component of the CORE-V-WALLY configurable RISC-V project. +// https://github.com/openhwfoundation/cvw +// +// Copyright (C) 2021-26 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +//////////////////////////////////////////////////////////////////////////////////////////////// + #include "WALLY-init-lib.h" + #include "WALLY-selfcheck.h" main: + // Values stored by the way tests: the first store of way test n stores s(2n), the second s(2n+1) + li s2, 0x1111111111111111 + li s3, 0x2222222222222222 + li s4, 0x3333333333333333 + li s5, 0x4444444444444444 + li s6, 0x5555555555555555 + li s7, 0x6666666666666666 + li s8, 0x7777777777777777 + li s9, 0x8888888888888888 // start way test #1 li t0, 0x80100000 .align 6 @@ -16,8 +48,8 @@ main: .word 0x00000013 .word 0x00000013 .word 0x00000013 - sd zero, 0(t0) - sd zero, 0(t0) + sd s2, 0(t0) + sd s3, 0(t0) .word 0x00000013 .word 0x00000013 // start way test #2 @@ -36,8 +68,8 @@ main: .word 0x00000013 .word 0x00000013 .word 0x00000013 - sd zero, 0(t0) - sd zero, 0(t0) + sd s4, 0(t0) + sd s5, 0(t0) .word 0x00000013 .word 0x00000013 // start way test #3 @@ -56,8 +88,8 @@ main: .word 0x00000013 .word 0x00000013 .word 0x00000013 - sd zero, 0(t0) - sd zero, 0(t0) + sd s6, 0(t0) + sd s7, 0(t0) .word 0x00000013 .word 0x00000013 // start way test #4 @@ -76,8 +108,8 @@ main: .word 0x00000013 .word 0x00000013 .word 0x00000013 - sd zero, 0(t0) - sd zero, 0(t0) + sd s8, 0(t0) + sd s9, 0(t0) .word 0x00000013 .word 0x00000013 @@ -91,4 +123,39 @@ main: li t0, 0x80103000 cbo.flush (t0) +# The flushed lines are no longer in the cache, so these loads read memory: each must return the +# second value its way test stored + li a1, 0x80100000 + ld a0, 0(a1) + SC_CHECK_EQ_ADR a0, s3, a1, 1 + li a1, 0x80101000 + ld a0, 0(a1) + SC_CHECK_EQ_ADR a0, s5, a1, 2 + li a1, 0x80102000 + ld a0, 0(a1) + SC_CHECK_EQ_ADR a0, s7, a1, 3 + li a1, 0x80103000 + ld a0, 0(a1) + SC_CHECK_EQ_ADR a0, s9, a1, 4 + +# Evictions: store to five lines in the same set (4 KiB apart), one more than the four ways, each line +# holding its own address, then read them back. Lines are evicted and refetched along the way. + li a1, 0x80104000 + li a2, 0x1000 + li a3, 5 +1: + sd a1, 0(a1) + add a1, a1, a2 + addi a3, a3, -1 + bnez a3, 1b + li a1, 0x80104000 + li a3, 5 +2: + ld a0, 0(a1) + SC_CHECK_EQ_ADR a0, a1, a1, 5 + add a1, a1, a2 + addi a3, a3, -1 + bnez a3, 2b + + SC_PASS j done diff --git a/tests/coverage/fpu.S b/tests/coverage/fpu.S index ed401eef72..b85094eaa7 100644 --- a/tests/coverage/fpu.S +++ b/tests/coverage/fpu.S @@ -4,6 +4,9 @@ // Written: David_Harris@hmc.edu 28 March 2023 // // Purpose: Test coverage for FPU +// Checks the result (all 64 bits, so NaN-boxing too) and the exception flags of each +// legal operation, and that each illegal instruction raises an illegal instruction +// exception at its own address. The flags are read and cleared after each check. // // A component of the CORE-V-WALLY configurable RISC-V project. // https://github.com/openhwfoundation/cvw @@ -26,6 +29,44 @@ // load code to initialize stack, handle interrupts, terminate #include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" + +// CHECKF freg, val, index: check all 64 bits of an FP register (a single or half is NaN-boxed) +.macro CHECKF freg, val, index + fmv.x.d t2, \freg + li t3, \val + SC_CHECK_EQ t2, t3, \index +.endm + +// CHECKX reg, val, index: check an integer register +.macro CHECKX reg, val, index + li t3, \val + SC_CHECK_EQ \reg, t3, \index +.endm + +// FLAGS val, index: check the accrued exception flags (NV=0x10 DZ=0x08 OF=0x04 UF=0x02 NX=0x01), then clear them +.macro FLAGS val, index + csrrw t2, fflags, zero + li t3, \val + SC_CHECK_EQ t2, t3, \index +.endm + +// TRAP cause, index, insn: run insn and check mcause = cause afterwards (0: no trap). When it traps, +// also check that it trapped at its own address; the library handler returns past it, so mepc = its +// address + 4. +.macro TRAP cause, index, insn:vararg + csrw mcause, zero +1: + \insn + csrr t2, mcause + li t3, \cause + SC_CHECK_EQ t2, t3, \index +.if \cause + csrr t2, mepc + la t3, 1b + 4 + SC_CHECK_EQ t2, t3, \index + 1 +.endif +.endm main: @@ -45,85 +86,140 @@ main: # zfa instructions (because Zfa tests aren't running yet) fli.d fs0, 16 fcvtmod.w.d t0, fs0, rtz + // Checks for the block above, kept after it so its instruction sequence is unchanged + CHECKF f2, 0xffffffff3f800000, 1 // sqrt(1) = 1 + CHECKX t1, 0x20, 3 // fclass: positive subnormal + CHECKX t0, 16, 4 // fcvtmod.w.d(16.0) = 16 + FLAGS 0, 5 // all exact; fsqrt's Y (f0, from the rs2 field) = 0 must not raise divide by zero # fminm.d fs1, fs0, fs0 # fmaxm.d fs1, fs0, fs0 # fltq.d t0, fs1, ft0 # fleq.d t0, fs1, ft0 - fcvt.d.q fs1, fs0 - fcvt.h.q fs1, fs0 - fcvt.s.q fs1, fs0 + TRAP 2, 6, fcvt.d.q fs1, fs0 // Q is not supported + TRAP 2, 8, fcvt.h.q fs1, fs0 + TRAP 2, 10, fcvt.s.q fs1, fs0 # round for now because these tests are excluded from Zfa until rounding is implemented + # fs0 holds the double 16.0, which is not a NaN-boxed single or half, so those read it as the canonical NaN fround.s fs1, fs0 - froundnx.s fs1, fs0 - fround.d fs1, fs0 - froundnx.d fs1, fs0 - fround.h fs1, fs0 - froundnx.h fs1, fs0 - fround.s fs1, fs0 - froundnx.s fs1, fs0 + froundnx.s fs2, fs0 + fround.d fs3, fs0 + froundnx.d fs4, fs0 + fround.h fs5, fs0 + froundnx.h fs6, fs0 + fround.s fs7, fs0 + froundnx.s fs8, fs0 + CHECKF fs1, 0xffffffff7fc00000, 12 + CHECKF fs2, 0xffffffff7fc00000, 13 + CHECKF fs3, 0x4030000000000000, 14 + CHECKF fs4, 0x4030000000000000, 15 + CHECKF fs5, 0xffffffffffff7e00, 16 + CHECKF fs6, 0xffffffffffff7e00, 17 + CHECKF fs7, 0xffffffff7fc00000, 18 + CHECKF fs8, 0xffffffff7fc00000, 19 + FLAGS 0, 20 // quiet NaNs and an integral value: no flags, not even inexact #Result Sign Test Coverage la t0, TestData2 flw ft0, 0(t0) flw ft1, 4(t0) fadd.s ft2, ft0, ft1 #Adds coverage for inf as arg for FADD + CHECKF ft2, 0xffffffff7f800000, 21 // 1 + inf = inf + FLAGS 0, 22 flw ft2, 4(t0) fmsub.s ft3, ft0, ft1, ft2 #Adds coverage for fmaAs or Z Sign Bit + CHECKF ft3, 0xffffffff7fc00000, 23 // 1 * inf - inf = canonical NaN + FLAGS 0x10, 24 // invalid #Adds Coverage for Flag fmaAs, fmaPs, YSNaN, ZSNaN fmadd.s ft3, ft0, ft1, ft2 + CHECKF ft3, 0xffffffff7f800000, 25 // 1 * inf + inf = inf + FLAGS 0, 26 flw ft0, 8(t0) fmadd.s ft3, ft0, ft1, ft2 + CHECKF ft3, 0xffffffff7fc00000, 27 // -1 * inf + inf = canonical NaN + FLAGS 0x10, 28 flw ft1, 12(t0) fmadd.s ft3, ft0, ft1, ft2 + CHECKF ft3, 0xffffffff7fc00000, 29 // -1 * sNaN + inf + FLAGS 0x10, 30 // signaling NaN operand flw ft2, 12(t0) flw ft1, 4(t0) fmadd.s ft3, ft0, ft1, ft2 + CHECKF ft3, 0xffffffff7fc00000, 31 // -1 * inf + sNaN + FLAGS 0x10, 32 #Add Coverage for round lsbRes flw ft0, 16(t0) flw ft1, 4(t0) fmadd.s ft3, ft0, ft1, ft2 + CHECKF ft3, 0xffffffff7fc00000, 33 // 0x3fffffff * inf + sNaN + FLAGS 0x10, 34 #Fix BadNaNBox test on unpackinput Z la t0, TestData2 flw ft3, 0(t0) flw ft4, 0(t0) fadd.s ft5, ft3, ft4 + CHECKF ft5, 0xffffffff40000000, 35 // 1 + 1 = 2 + FLAGS 0, 36 - # Test legal instructions not covered elsewhere - flq ft0, 0(a0) - flh ft0, 8(a0) - fsq ft0, 0(a0) - fsh ft0, 8(a0) + # Test legal instructions not covered elsewhere. Q (flq, fsq) is not supported. a0 = 0, and there + # is no memory at address 8, so flh and fsh take access faults. + li a0, 0 // as from reset + TRAP 2, 37, flq ft0, 0(a0) + TRAP 5, 39, flh ft0, 8(a0) // load access fault + TRAP 2, 41, fsq ft0, 0(a0) + TRAP 7, 43, fsh ft0, 8(a0) // store/AMO access fault # Tests for fpu/fctrl.sv fcvt.h.s ft1, ft0 - fcvt.q.s ft2, ft0 + CHECKF ft1, 0xffffffffffff4000, 45 // ft0 = 0x3fffffff = 2 - 2^-23 rounds to 2.0 + FLAGS 0x01, 46 // inexact + TRAP 2, 47, fcvt.q.s ft2, ft0 fcvt.h.w ft3, a0 - fcvt.h.wu ft3, a0 - fcvt.h.l ft3, a0 - fcvt.h.lu ft3, a0 - fcvt.w.h a0, ft3 - fcvt.wu.h a0, ft3 - fcvt.l.h a0, ft3 - fcvt.lu.h a0, ft3 - fcvt.q.w ft3, a0 - fcvt.q.wu ft3, a0 - fcvt.q.l ft3, a0 - fcvt.q.lu ft3, a0 - fcvt.w.q a0, ft3 - fcvt.wu.q a0, ft3 - fcvt.l.q a0, ft3 - fcvt.lu.q a0, ft3 + fcvt.h.wu ft6, a0 + fcvt.h.l ft7, a0 + fcvt.h.lu ft8, a0 + li a1, -1 // sentinels, so the checks see the conversions write 0 + li a2, -1 + li a3, -1 + li a4, -1 + fcvt.w.h a1, ft3 + fcvt.wu.h a2, ft3 + fcvt.l.h a3, ft3 + fcvt.lu.h a4, ft3 + CHECKF ft3, 0xffffffffffff0000, 49 // integer 0 converts to +0.0 half + CHECKF ft6, 0xffffffffffff0000, 50 + CHECKF ft7, 0xffffffffffff0000, 51 + CHECKF ft8, 0xffffffffffff0000, 52 + CHECKX a1, 0, 53 // and back to integer 0 + CHECKX a2, 0, 54 + CHECKX a3, 0, 55 + CHECKX a4, 0, 56 + FLAGS 0, 57 + TRAP 2, 58, fcvt.q.w ft3, a0 + TRAP 2, 60, fcvt.q.wu ft3, a0 + TRAP 2, 62, fcvt.q.l ft3, a0 + TRAP 2, 64, fcvt.q.lu ft3, a0 + TRAP 2, 66, fcvt.w.q a0, ft3 + TRAP 2, 68, fcvt.wu.q a0, ft3 + TRAP 2, 70, fcvt.l.q a0, ft3 + TRAP 2, 72, fcvt.lu.q a0, ft3 fcvt.l.s a0, ft0 - fcvt.lu.s a0, ft0 + fcvt.lu.s a5, ft0 + CHECKX a0, 2, 74 // 2 - 2^-23 rounds to 2 (rne) + CHECKX a5, 2, 75 + FLAGS 0x01, 76 // inexact + li t0, 0x80004004 // a fixed value (TestData2's address when the test was written) fcvt.s.l ft0, t0 - fcvt.s.lu ft0, t0 + fcvt.s.lu ft9, t0 + CHECKF ft0, 0xffffffff4f000040, 77 // 2^31 + 16388 rounds to 2^31 + 64 * 256 (ulp 256) + CHECKF ft9, 0xffffffff4f000040, 78 + FLAGS 0x01, 79 // Tests verifying that half and quad floating point conversion instructions are not supported by rv64gc # fcvt.h.d ft3, ft0 // Somehow this instruction is taking the route on line 124 @@ -138,7 +234,10 @@ main: la t0, TestData3 fld ft2, 0(t0) // bad NaN-boxed number fmadd.h ft1, ft2, ft2, ft2 // Test NaN boxing - fmadd.s ft1, ft2, ft2, ft2 // Test NaN boxing + fmadd.s ft10, ft2, ft2, ft2 // Test NaN boxing + CHECKF ft1, 0xffffffffffff7e00, 80 // improperly boxed operands read as the canonical NaN + CHECKF ft10, 0xffffffff7fc00000, 81 + FLAGS 0, 82 // quiet NaNs: no flags // fdivsqrt: test busy->idle transition caused by a FlushE while divider is busy (when interrupt arrives) // This code doesn't actually trigger a busy->idle transition because the pending timer interrupt doesn't occur until the division finishes. @@ -158,42 +257,49 @@ main: nop fdiv.s ft2, ft1, ft0 # should get interrupted, triggering a flush csrci mstatus, 0b1000 # disable interrupts with mstatus.MIE + // The interrupt may or may not catch the fdiv (the library handler returns past the interrupted + // instruction), so its result and inexact flag depend on timing and are not checked. Clear the flags. + csrw fflags, zero # Completing branch coverage in fctrl.sv - .word 0x38007553 // Testing the all False case for 119 - funct7 under, op = 101 0011 - .word 0x40000053 // Line 145 All False Test case - illegal instruction? - .word 0xd0400053 // Line 156 All False Test case - illegal instruction? - .word 0xc0400053 // Line 162 All False Test case - illegal instruction? - .word 0xd2400053 // Line 168 All False Test case - illegal instruction? - .word 0xc2400053 // Line 174 All False Test case - illegal instruction? + TRAP 2, 83, .word 0x38007553 // Testing the all False case for 119 - funct7 under, op = 101 0011 + TRAP 2, 85, .word 0x40000053 // Line 145 All False Test case - illegal instruction? + TRAP 2, 87, .word 0xd0400053 // Line 156 All False Test case - illegal instruction? + TRAP 2, 89, .word 0xc0400053 // Line 162 All False Test case - illegal instruction? + TRAP 2, 91, .word 0xd2400053 // Line 168 All False Test case - illegal instruction? + TRAP 2, 93, .word 0xc2400053 // Line 174 All False Test case - illegal instruction? # Increasing conditional coverage in fctrl.sv - .word 0xc5000007 // Attempting to toggle (Op7 != 7) to 0 on line 97 in fctrl, not sure what instruction this works out to - .word 0xe0101053 // toggling (Rs2D == 0) to 0 on line 139 in fctrl. Illegal Intsr (like fclass but incorrect rs2) - .word 0xe0100053 // toggling (Rs2D == 0) to 0 on line 141 in fctrl. Illegal Intsr (like fmv but incorrect rs2) - .word 0x40D00053 // toggling (Rs2D[4:2] == 0) to 0 on line 145 in fctrl. - .word 0x40300053 // toggling SupportFmt2 to 0 on line 145 in fctrl. - .word 0x42100053 // toggling (Rs2D[1:0] != 1) to 0 on line 147 in fctrl. Illegal Instr - .word 0xf0100053 // toggling (Rs2D == 0) to 0 on line 143 in fctrl. Illegal Instr + TRAP 2, 95, .word 0xc5000007 // Attempting to toggle (Op7 != 7) to 0 on line 97 in fctrl; a vector load, and V is not supported + TRAP 2, 97, .word 0xe0101053 // toggling (Rs2D == 0) to 0 on line 139 in fctrl. Illegal Intsr (like fclass but incorrect rs2) + TRAP 2, 99, .word 0xe0100053 // toggling (Rs2D == 0) to 0 on line 141 in fctrl. Illegal Intsr (like fmv but incorrect rs2) + TRAP 2, 101, .word 0x40D00053 // toggling (Rs2D[4:2] == 0) to 0 on line 145 in fctrl. + TRAP 2, 103, .word 0x40300053 // toggling SupportFmt2 to 0 on line 145 in fctrl (fcvt.s.q, and Q is not supported) + TRAP 2, 105, .word 0x42100053 // toggling (Rs2D[1:0] != 1) to 0 on line 147 in fctrl. Illegal Instr + TRAP 0, 107, .word 0xf0100053 // toggling (Rs2D == 0) to 0 on line 143 in fctrl. Legal: Zfa fli.s f0, -1.0 + CHECKF f0, 0xffffffffbf800000, 108 # Test illegal instructions are detected - .word 0x00000007 // illegal floating-point load (bad Funct3) - .word 0x00000027 // illegal floating-point store (bad Funct3) - .word 0x58F00053 // illegal fsqrt (bad Rs2D) - .word 0x20007053 // illegal fsgnj (bad Funct3) - .word 0x28007053 // illegal fmin/max (bad Funct3) - .word 0xA0007053 // illegal fcmp (bad Funct3) - .word 0xE0007053 // illegal fclass/fmv (bad Funct3) - .word 0xF0007053 // illegal fmv (bad Funct3) - .word 0x43007053 // illegal fcvt.d.* (bad Rs2D) - .word 0x42207053 // illegal fcvt.d.* (bad Rs2D[1]) - .word 0xD5F00053 // illegal fcvt.h.* (bad Rs2D) - .word 0xC5F00053 // illegal fcvt.*.h (bad Rs2D) - .word 0x04000043 // illegal fmadd.h (h not supported) - .word 0xC2800053 // illegal fcvtmod.w.d with rm rne - .word 0xF0101053 // illegal fli with Funct3D not 0 - .word 0xF0400053 // illegal fli with Rs2D not 1 - .word 0x44200053 // illegal instruction for f.cvt.h.h + TRAP 2, 109, .word 0x00000007 // illegal floating-point load (bad Funct3) + TRAP 2, 111, .word 0x00000027 // illegal floating-point store (bad Funct3) + TRAP 2, 113, .word 0x58F00053 // illegal fsqrt (bad Rs2D) + TRAP 2, 115, .word 0x20007053 // illegal fsgnj (bad Funct3) + TRAP 2, 117, .word 0x28007053 // illegal fmin/max (bad Funct3) + TRAP 2, 119, .word 0xA0007053 // illegal fcmp (bad Funct3) + TRAP 2, 121, .word 0xE0007053 // illegal fclass/fmv (bad Funct3) + TRAP 2, 123, .word 0xF0007053 // illegal fmv (bad Funct3) + TRAP 2, 125, .word 0x43007053 // illegal fcvt.d.* (bad Rs2D) + TRAP 0, 127, .word 0x42207053 // fcvt.d.* with Rs2D[1] set: legal fcvt.d.h f0, f0 (Zfh, dynamic rounding) + CHECKF f0, 0x7ff8000000000000, 128 // f0 (the single -1.0) is not a NaN-boxed half: canonical NaN + TRAP 2, 129, .word 0xD5F00053 // illegal fcvt.h.* (bad Rs2D) + TRAP 2, 131, .word 0xC5F00053 // illegal fcvt.*.h (bad Rs2D) + TRAP 0, 133, .word 0x04000043 // fmadd.h f0, f0, f0, f0, rne: legal (Zfh) + CHECKF f0, 0xffffffffffff7e00, 134 // f0 (a double NaN) is not a NaN-boxed half: canonical NaN + TRAP 2, 135, .word 0xC2800053 // illegal fcvtmod.w.d with rm rne + TRAP 2, 137, .word 0xF0101053 // illegal fli with Funct3D not 0 + TRAP 2, 139, .word 0xF0400053 // illegal fli with Rs2D not 1 + TRAP 2, 141, .word 0x44200053 // illegal instruction for f.cvt.h.h + FLAGS 0, 143 // illegal instructions and quiet NaN operations raise no flags // Test divide by zero with rounding mode toward zero li t0, 1 @@ -202,11 +308,19 @@ main: fcvt.s.w ft1, t0 fcvt.s.w ft2, zero fdiv.s ft3, ft1, ft2 + CHECKF ft1, 0xffffffff4e7e0000, 144 // 0x3f800000 = 1065353216 = 2^29 * 1.984375 converts exactly + CHECKF ft2, 0xffffffff00000000, 145 + CHECKF ft3, 0xffffffff7f800000, 146 // x / 0 = +inf in every rounding mode + FLAGS 0x08, 147 // divide by zero # Test floating point convert to integer and using result - fcvt.w.s t0, f0 - add t1, t0, t0 + fcvt.w.s a1, f0 + add a2, a1, a1 + CHECKX a1, 0x7fffffff, 148 // f0 = 0xffffffff_ffff7e00 is a NaN-boxed single NaN: 2^31 - 1 + CHECKX a2, 0xfffffffe, 149 + FLAGS 0x10, 150 // invalid + SC_PASS j done .section .data diff --git a/tests/coverage/fpuReservedRM.S b/tests/coverage/fpuReservedRM.S index af4b4c26aa..32d1cb3b8c 100644 --- a/tests/coverage/fpuReservedRM.S +++ b/tests/coverage/fpuReservedRM.S @@ -18,6 +18,10 @@ // uses dynamic rounding (funct3 = 0b111). This makes SupportedRM false, // which marks the instruction illegal. The library trap handler advances // mepc past the illegal instruction so the test continues to "done". +// The test checks that each reserved-mode instruction raised an illegal +// instruction exception at its own address without writing its destination +// or fflags, that frm holds the value written, and that the legal adds before +// and after give 1 + 1 = 2 exactly. // // A component of the CORE-V-WALLY configurable RISC-V project. // https://github.com/openhwfoundation/cvw @@ -40,6 +44,38 @@ // load code to initialize stack, handle interrupts, terminate #include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" + +// CHECKF freg, val, index: check all 64 bits of freg (a single is NaN-boxed) +.macro CHECKF freg, val, index + fmv.x.d t2, \freg + li t3, \val + SC_CHECK_EQ t2, t3, \index +.endm + +// CHECKCSR csr, val, index: check that a CSR reads val +.macro CHECKCSR csr, val, index + csrr t2, \csr + li t3, \val + SC_CHECK_EQ t2, t3, \index +.endm + +// ILLEGAL_DYN index: f3 = sentinel, then a dynamic-rounding fadd.s, which frm makes reserved. It must +// raise an illegal instruction exception (mcause 2) at its own address (the library trap handler +// returns past it, so mepc = its address + 4) and leave f3 and fflags unchanged. +.macro ILLEGAL_DYN index + li t2, 0x5555aaaa5555aaaa + fmv.d.x f3, t2 + csrw mcause, zero +1: + fadd.s f3, f1, f2, dyn // funct3=0b111 (dyn); reserved because FRM_REGW is 5 or 6 -> illegal + CHECKCSR mcause, 2, \index + csrr t2, mepc + la t3, 1b + 4 + SC_CHECK_EQ t2, t3, \index + 1 + CHECKF f3, 0x5555aaaa5555aaaa, \index + 2 + CHECKCSR fflags, 0, \index + 3 +.endm main: @@ -49,21 +85,30 @@ main: # A couple of legal FP ops to keep FS live and the FPU exercised fli.s f1, 1 fli.s f2, 1 - fadd.s f3, f1, f2 # legal add with implicit (static) rounding + fadd.s f3, f1, f2 # legal add with frm = 0 (rne) + CHECKF f3, 0xffffffff40000000, 1 # 1 + 1 = 2.0, NaN-boxed + CHECKCSR fflags, 0, 2 # exact # ---- Set frm = 5 (reserved), then issue a dynamic-rounding OP-FP op ---- li t0, 5 csrw frm, t0 # frm (CSR 0x002) = 0b101 -> fcsr[7:5] = 5 - fadd.s f3, f1, f2, dyn # funct3=0b111 (dyn); reserved because FRM_REGW==5 -> illegal + CHECKCSR frm, 5, 3 # frm holds the reserved value; only its use by a dyn op is illegal + ILLEGAL_DYN 4 # ---- Set frm = 6 (reserved), then issue a dynamic-rounding OP-FP op ---- li t0, 6 csrw frm, t0 # frm (CSR 0x002) = 0b110 -> fcsr[7:5] = 6 - fadd.s f3, f1, f2, dyn # funct3=0b111 (dyn); reserved because FRM_REGW==6 -> illegal + CHECKCSR frm, 6, 8 + ILLEGAL_DYN 9 # Restore a legal rounding mode and do a legal op so FPU still works li t0, 0 csrw frm, t0 # frm = rne + li t2, 0x5555aaaa5555aaaa + fmv.d.x f3, t2 # sentinel, so the check sees the add write f3 fadd.s f3, f1, f2 + CHECKF f3, 0xffffffff40000000, 13 + CHECKCSR fcsr, 0, 14 # frm = 0 and no flags + SC_PASS j done diff --git a/tests/coverage/ieu.S b/tests/coverage/ieu.S index a071f1c6e3..3a709d66ac 100644 --- a/tests/coverage/ieu.S +++ b/tests/coverage/ieu.S @@ -4,6 +4,9 @@ // Written: David_Harris@hmc.edu 21 March 2023 // // Purpose: Test coverage for IEU +// Checks the divide, clz, and store conditional results, that the prefetch hints do +// not trap, and the cause and address of the trap each illegal or faulting instruction +// raises, in machine mode and (through record_trap) in user mode. // // A component of the CORE-V-WALLY configurable RISC-V project. // https://github.com/openhwfoundation/cvw @@ -26,83 +29,164 @@ // load code to initialize stack, handle interrupts, terminate #include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" + +// EXPECT cause, index, insn: run insn and check that it raised exactly one trap, with mcause = cause, +// at its own address. record_trap counts traps and saves mcause and mepc in lasttrap, so this works +// in user mode, which cannot read mcause. Clobbers s2, t2-t4. +.macro EXPECT cause, index, insn:vararg + la t2, lasttrap + ld s2, 16(t2) # trap count before +1: + \insn + la t2, lasttrap + ld t3, 16(t2) + sub t3, t3, s2 + li t4, 1 + SC_CHECK_EQ t3, t4, \index + ld t3, 0(t2) + li t4, \cause + SC_CHECK_EQ t3, t4, \index + 1 + ld t3, 8(t2) + la t4, 1b + SC_CHECK_EQ t3, t4, \index + 2 +.endm + +// NOTRAP index, insn: run insn and check that it did not trap +.macro NOTRAP index, insn:vararg + la t2, lasttrap + ld s2, 16(t2) + \insn + la t2, lasttrap + ld t3, 16(t2) + SC_CHECK_EQ t3, s2, \index +.endm main: + la t0, record_trap + csrw mtvec, t0 # Division test (having trouble with buildroot) li x11, 0x384000 li x12, 0x1c2000 divuw x9, x11, x12 + li t2, 2 + SC_CHECK_EQ x9, t2, 1 # 0x384000 / 0x1c2000 = 2 # Test clz with all bits being 0 li t0, 0 - clz t1, t0 + clz a2, t0 li t0, -1 - clz t1, t0 + clz a3, t0 li t0, 1 - clz t1, t0 + clz a4, t0 + li t2, 64 + SC_CHECK_EQ a2, t2, 2 # clz(0) = XLEN + SC_CHECK_EQ a3, zero, 3 # clz(-1) = 0 + li t2, 63 + SC_CHECK_EQ a4, t2, 4 # clz(1) = 63 # Test forwarding from store conditional mv a0, sp lr.w t0, 0(a0) sc.w t0, a1, 0(a0) - addi t0, t0, 1 + addi a5, t0, 1 # uses the sc.w result in the next instruction + li t2, 1 + SC_CHECK_EQ a5, t2, 5 # sc.w succeeded (wrote 0), and the addi saw it + lw t3, 0(a0) + SC_CHECK_EQ t3, a1, 6 # sc.w stored a1 (x11, 0x384000 from the division test) - # test prefetch Hints (ori with destination x0) + # test prefetch Hints (ori with destination x0); hints must not trap + la t2, lasttrap + ld s2, 16(t2) ori x0, x0, 0 ori x0, x0, 1 ori x0, x0, 2 ori x0, x0, 3 + la t2, lasttrap + ld t3, 16(t2) + SC_CHECK_EQ t3, s2, 7 - - # Test illegal instructions are detected - .word 0x80000033 // illegal R-type instruction - .word 0x00007003 // illegal Load instruction - .word 0x80005013 // illegal I-type instruction: srli: op = 0010011, funct3 = 101, funct7 = 1000000 - .word 0x00000000 // illegal instruction - .word 0x0000701B // Illegal IW instruction - .word 0x00004023 // Illegal store instruction - .word 0x0400003B // Illegal RW or MulDivW instruction - .word 0x00007067 // Illegal JALR instruction - .word 0x00002063 // Illegal branch instruction - .word 0x60F01013 // Illegal BMU sign extend / count instruction - .word 0x60801013 // Illegal BMU sign extend / count instruction - .word 0x60301013 // Illegal BMU sign extend / count instruction - .word 0x6BF05013 // Illegal BMU similar to rev8 - .word 0x69805013 // Illegal BMU similar to rev8 - .word 0x28F05013 // Illegal BMU similar to or.c - .word 0x60F0101B // Illegal BMU similar to count word - .word 0x6080101B // Illegal BMU similar to count word - .word 0x6030101B // Illegal BMU similar to count word - .word 0x0000202F // Illegal similar to LR - .word 0x1010202F // Illegal similar to LR - .word 0x00402003 // illegal similar to CMO - .word 0x00202003 // illegal similar to CMO - .word 0xFF00302F // illegal Atomic instruction - .word 0xFF00402F // illegal Atomic instruction - .word 0x00000873 // illegal CSR instruction - .word 0x31bf1f93 // illegal aes64ksli1 instruction + # Test illegal instructions are detected (mcause 2). Some of these encodings are legal + # instructions that fault on address 0 instead, as noted. + EXPECT 2, 10, .word 0x80000033 // illegal R-type instruction + EXPECT 2, 13, .word 0x00007003 // illegal Load instruction + EXPECT 2, 16, .word 0x80005013 // illegal I-type instruction: srli: op = 0010011, funct3 = 101, funct7 = 1000000 + // illegal instruction 0x00000000: two 16-bit all-zero instructions, each illegal + EXPECT 2, 19, .2byte 0x0000 + EXPECT 2, 22, .2byte 0x0000 + EXPECT 2, 25, .word 0x0000701B // Illegal IW instruction + EXPECT 2, 28, .word 0x00004023 // Illegal store instruction + EXPECT 2, 31, .word 0x0400003B // Illegal RW or MulDivW instruction + EXPECT 2, 34, .word 0x00007067 // Illegal JALR instruction + EXPECT 2, 37, .word 0x00002063 // Illegal branch instruction + EXPECT 2, 40, .word 0x60F01013 // Illegal BMU sign extend / count instruction + EXPECT 2, 43, .word 0x60801013 // Illegal BMU sign extend / count instruction + EXPECT 2, 46, .word 0x60301013 // Illegal BMU sign extend / count instruction + EXPECT 2, 49, .word 0x6BF05013 // Illegal BMU similar to rev8 + EXPECT 2, 52, .word 0x69805013 // Illegal BMU similar to rev8 + EXPECT 2, 55, .word 0x28F05013 // Illegal BMU similar to or.c + EXPECT 2, 58, .word 0x60F0101B // Illegal BMU similar to count word + EXPECT 2, 61, .word 0x6080101B // Illegal BMU similar to count word + EXPECT 2, 64, .word 0x6030101B // Illegal BMU similar to count word + EXPECT 7, 67, .word 0x0000202F // amoadd.w x0, x0, (x0): legal, but no memory at 0 -> store/AMO access fault + EXPECT 2, 70, .word 0x1010202F // Illegal similar to LR (lr.w with rs2 != 0) + EXPECT 5, 73, .word 0x00402003 // lw x0, 4(x0): legal, but no memory at 4 -> load access fault + EXPECT 5, 76, .word 0x00202003 // lw x0, 2(x0): misaligned loads are supported (Zicclsm) -> load access fault + EXPECT 2, 79, .word 0xFF00302F // illegal Atomic instruction + EXPECT 2, 82, .word 0xFF00402F // illegal Atomic instruction + EXPECT 2, 85, .word 0x00000873 // illegal CSR instruction (ecall encoding with rd != 0) + EXPECT 2, 88, .word 0x31bf1f93 // illegal aes64ksli1 instruction (rnum = 0xB > 0xA) # Illegal CMO instructions because envcfg is 0 and system is in user Mode li a0, 0 ecall # switch to user mode - cbo.inval (x1) - cbo.clean (x1) - cbo.flush (x1) - cbo.zero (x1) + EXPECT 2, 100, cbo.inval 0(x1) + EXPECT 2, 103, cbo.clean 0(x1) + EXPECT 2, 106, cbo.flush 0(x1) + EXPECT 2, 109, cbo.zero 0(x1) li a0, 3 ecall # switch back to machine mode li x1, 0x50 - csrw menvcfg, x1 + csrw menvcfg, x1 # CBCFE = 1, CBIE = 01 (cbo.inval performs a flush); CBZE = 0 csrw senvcfg, x1 + li x3, 0 # x3 (gp) is 0 from reset; set it explicitly since cbo.clean uses it as the address li a0, 0 ecall # switch to user mode - cbo.inval (x2) - cbo.clean (x3) - cbo.flush (x1) + NOTRAP 112, cbo.inval 0(x2) # sp is ordinary RAM + EXPECT 7, 113, cbo.clean 0(x3) # no memory at 0 -> store/AMO access fault + EXPECT 7, 116, cbo.flush 0(x1) # no memory at 0x50 -> store/AMO access fault + # Every trap is accounted for: 27 in the illegal list above, 4 + 2 cbo traps, and 3 ecalls + la t2, lasttrap + ld t3, 16(t2) + li t4, 36 + SC_CHECK_EQ t3, t4, 120 + SC_PASS + j done +// Record mcause and mepc in lasttrap and count the trap, then continue in the library trap handler +.align 4 +record_trap: + csrrw t0, mscratch, t0 # t0 = trap stack pointer, mscratch = saved t0 + sd t1, -16(t0) + sd t2, -24(t0) + la t1, lasttrap + csrr t2, mcause + sd t2, 0(t1) + csrr t2, mepc + sd t2, 8(t1) + ld t2, 16(t1) + addi t2, t2, 1 + sd t2, 16(t1) + ld t2, -24(t0) + ld t1, -16(t0) + csrrw t0, mscratch, t0 # restore t0 and the trap stack pointer + j trap_handler - j done +.section .data +.align 3 +lasttrap: # mcause, mepc, and number of traps + .fill 3, 8, 0 diff --git a/tests/coverage/ifu.S b/tests/coverage/ifu.S index ba20fab3a5..e25acd1b6b 100644 --- a/tests/coverage/ifu.S +++ b/tests/coverage/ifu.S @@ -4,6 +4,10 @@ // Written: sriley@g.hmc.edu 28 March 2023 // // Purpose: Test coverage for IFU +// Checks the values loaded and stored by the compressed FP and Zcb loads and stores +// and computed by c.mul and the Zcb extend/not instructions, that each reserved +// compressed encoding raises an illegal instruction exception at its own address, +// and that the c.slli HINT does not trap. // // A component of the CORE-V-WALLY configurable RISC-V project. // https://github.com/openhwfoundation/cvw @@ -26,47 +30,108 @@ // load code to initialize stack, handle interrupts, terminate #include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" + +// CHECKX reg, val, index: check an integer register +.macro CHECKX reg, val, index + li t2, \val + SC_CHECK_EQ \reg, t2, \index +.endm + +// CHECKF freg, val, index: check all 64 bits of an FP register +.macro CHECKF freg, val, index + fmv.x.d t3, \freg + li t2, \val + SC_CHECK_EQ t3, t2, \index +.endm + +// RES16 encoding, index: execute a reserved 16-bit encoding and check that it raised an illegal +// instruction exception (mcause = 2) at its own address. The library trap handler advances mepc past +// the 2-byte instruction and returns, leaving mcause and mepc for the check. +.macro RES16 encoding, index + csrw mcause, zero +1: + .2byte \encoding + csrr t3, mcause + CHECKX t3, 2, \index + csrr t3, mepc + la t2, 1b + 2 + SC_CHECK_EQ t3, t2, \index + 1 +.endm main: # turn floating point on li t0, 0x2000 csrs mstatus, t0 + # Data for the loads and stores below. Memory at sp starts as 0x0123456789ABCDEF; c.fsd stores fs1 + # and c.fsdsp stores f1, so every load and store leaves a different value. + li t2, 0x0123456789ABCDEF + sd t2, 0(sp) + li t2, 0xFEDCBA9876543210 + fmv.d.x fs1, t2 + li t2, 0x1122334455669988 + fmv.d.x f1, t2 + li a3, 0x5A // stored by c.sb + li a4, 0x3CC3 // stored by c.sh + # calling compressed floating point load double instruction //.hword 0x2000 // CL type compressed floating-point ld-->funct3,imm,rs1',imm,rd',op // binary version 0000 0000 0000 0000 0010 0000 0000 0000 mv s0, sp c.fld fs0, 0(s0) // Previously uncovered instructions - c.fsd fs0, 0(s0) - .hword 0x2002 // c.fldsp fs0, 0 - .hword 0xA002 // c.fsdsp fs0, 0 - .hword 0x9C41 // line 134 Illegal compressed instruction + c.fsd fs1, 0(s0) + .hword 0x2002 // c.fldsp f0, 0 + .hword 0xA006 // c.fsdsp f1, 0 + .hword 0x9C41 // c.mul s0, s0 (Zcb): s0 = sp * sp + mul t2, sp, sp + SC_CHECK_EQ s0, t2, 1 + CHECKF fs0, 0x0123456789ABCDEF, 2 // c.fld read the initial value + CHECKF f0, 0xFEDCBA9876543210, 3 // c.fldsp read what c.fsd stored + ld t3, 0(sp) + CHECKX t3, 0x1122334455669988, 4 // c.fsdsp stored f1 # Zcb coverage tests mv s0, sp - c.lbu s1, 0(s0) // exercise c.lbu - c.lh s1, 0(s0) // exercise c.lh + c.lbu a1, 0(s0) // exercise c.lbu + c.lh a2, 0(s0) // exercise c.lh c.lhu s1, 0(s0) // exercise c.lhu - c.sb s1, 0(s0) // exercise c.sb - c.sh s1, 0(s0) // exercise c.sh - - .hword 0x2005 // line 110 - .hword 0x6101 // line 114 - .hword 0x6201 // line 115 - .hword 0x0202 // Illegal compressed instruction with op = 10, Instr[15:13] = 000, c.slli x4, 0. Line 151 illegal instruction - .hword 0x4002 // Illegal compressed instruction with op = 10, Instr[15:13] = 010, c.lwsp zero, 0. Line 158 illegal instruction - .hword 0x8C44 // Illegal compressed instruction with op = 00, Instr[15:10] = 100011, Instr[6] = 1 and 0's everywhere else. Line 119 illegal instruction - .hword 0x9C00 // Illegal compressed instruction with op = 00, Instr[15:10] = 100111, and 0's everywhere else. Line 119 illegal instruction - - li s0, 0xFF + c.sb a3, 0(s0) // exercise c.sb + lbu t3, 0(s0) + CHECKX t3, 0x5A, 5 + c.sh a4, 0(s0) // exercise c.sh + CHECKX a1, 0x88, 6 + CHECKX a2, 0xFFFFFFFFFFFF9988, 7 // sign-extended + CHECKX s1, 0x9988, 8 + ld t3, 0(s0) + CHECKX t3, 0x1122334455663CC3, 9 // c.sh replaced the c.sb byte and the next one + + RES16 0x2005, 10 // line 110: c.addiw with rd = 0 is reserved + RES16 0x6101, 12 // line 114: c.addi16sp with imm = 0 is reserved + RES16 0x6201, 14 // line 115: c.lui with imm = 0 is reserved + csrw mcause, zero + .hword 0x0202 // c.slli x4, 0 is a HINT, so it must not trap (op = 10, Instr[15:13] = 000) + csrr t3, mcause + SC_CHECK_EQ t3, zero, 16 + RES16 0x4002, 17 // Illegal compressed instruction with op = 10, Instr[15:13] = 010, c.lwsp zero, 0. Line 158 illegal instruction + RES16 0x8C44, 19 // Illegal compressed instruction with op = 00, Instr[15:10] = 100011, Instr[6] = 1 and 0's everywhere else. Line 119 illegal instruction + RES16 0x9C00, 21 // Illegal compressed instruction with op = 00, Instr[15:10] = 100111, and 0's everywhere else. Line 119 illegal instruction + + li s0, 0xA5A5A5A5A5A5A5FF // upper bits set so c.zext.b changes the value c.zext.b s0 // exercise c.zext.b + CHECKX s0, 0xFF, 23 c.sext.b s0 // exercise c.sext.b + CHECKX s0, 0xFFFFFFFFFFFFFFFF, 24 c.zext.h s0 // exercise c.zext.h + CHECKX s0, 0xFFFF, 25 c.sext.h s0 // exercise c.sext.h + CHECKX s0, 0xFFFFFFFFFFFFFFFF, 26 c.zext.w s0 // exercise c.zext.w + CHECKX s0, 0xFFFFFFFF, 27 c.not s0 // exercise c.not + CHECKX s0, 0xFFFFFFFF00000000, 28 - .hword 0x9C7D // Reserved instruction from line 187 with op = 01, Instr[15:10] = 100111, Instr[6:5] = 11, and 0's everywhere else + RES16 0x9C7D, 29 // Reserved instruction from line 187 with op = 01, Instr[15:10] = 100111, Instr[6:5] = 11, and 0's everywhere else # exercise all the cache ways j way0code @@ -75,7 +140,10 @@ main: .align 12 way0code: jal way1code - fence.i +1: fence.i + la t2, 1b + SC_CHECK_EQ ra, t2, 31 // the chain of jumps through the other ways returned here + SC_PASS j done .align 12 From 764670b145cab0b580678625e87e4ac7660bf921 Mon Sep 17 00:00:00 2001 From: David Harris Date: Fri, 16 Oct 2026 03:32:42 -0700 Subject: [PATCH 7/9] Restore gshare.S as a self-checking branch predictor example Rose uses gshare.S to show how the global history and gshare predictors behave, so keep it in tests/coverage, where it is built and run with the other coverage tests. It now checks each loop's results and fails on an unexpected trap. The comments describe what each loop shows. Co-Authored-By: Claude Opus 5.5 Signed-off-by: David Harris --- tests/coverage/gshare.S | 298 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 tests/coverage/gshare.S diff --git a/tests/coverage/gshare.S b/tests/coverage/gshare.S new file mode 100644 index 0000000000..9c3cecf9ad --- /dev/null +++ b/tests/coverage/gshare.S @@ -0,0 +1,298 @@ +/////////////////////////////////////////// +// gshare.S +// +// Written: Rose Thompson rose@rosethompson.net +// +// Purpose: Simple examples of how the global history and gshare branch predictors behave, useful for +// watching the predictor in a waveform. oneLoopTest runs five 30-iteration loops whose bodies +// shrink from six instructions to one. Each global_hist_N_space_test runs 200 iterations of a +// loop containing a branch that alternates between taken and not taken, with a different +// number of instructions around the alternating branch in each test, so the branches land in +// the global history at different spacings. A global history predictor should learn the +// alternating pattern after a few iterations. +// The test checks each loop's architectural results; it does not check prediction accuracy. +// +// A component of the CORE-V-WALLY configurable RISC-V project. +// https://github.com/openhwfoundation/cvw +// +// Copyright (C) 2021-26 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +//////////////////////////////////////////////////////////////////////////////////////////////// + +// load code to initialize stack, handle interrupts, terminate +#include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" + +// CHECK_HIST index: check the global_hist results, t1 = 101 and t3 = a0 (checks index and index+1). +// The values are copied first because SC_CHECK_EQ uses t0 and t1. +.macro CHECK_HIST index + mv a2, t1 + mv a3, t3 + li a1, 101 + SC_CHECK_EQ a2, a1, \index + SC_CHECK_EQ a3, a0, (\index + 1) +.endm + +main: + SC_STRICT_TRAPS + + # Each subroutine leaves its alternating-branch count in t1 and its instruction count in t3. + # In the global_hist tests the alternating branch is taken on the 100 even iterations, which + # each add 1 to t1, so t1 ends at 101. + jal ra, oneLoopTest + li a1, 30 + SC_CHECK_EQ t3, a1, 1 + jal ra, global_hist_6_space_test + li a0, 2300 # 200 iterations x 10, plus 3 on each of the 100 taken iterations + CHECK_HIST 2 + jal ra, global_hist_4_space_test + li a0, 1100 # 200 x 5, plus 1 on each taken iteration + CHECK_HIST 4 + jal ra, global_hist_3_space_test + li a0, 1100 + CHECK_HIST 6 + jal ra, global_hist_2_space_test + li a0, 400 # 200 x 2 + CHECK_HIST 8 + jal ra, global_hist_1_space_test + li a0, 200 + CHECK_HIST 10 + jal ra, global_hist_0_space_test + li a0, 0 + CHECK_HIST 12 + + fence.I + +finished: + SC_PASS + j done + + .data + +.section .text +.globl oneLoopTest +.type oneLoopTest, @function +oneLoopTest: + li t1, 0 + li t2, 30 + + mv t3, t1 + mv t4, t2 +oneLoopTest1: + # instruction + addi t3, t3, 1 + li t5, 10 # filler instructions + li t6, 100 + li a0, 1000 + li a1, 10000 + bne t3, t4, oneLoopTest1 # loop branch: taken 29 times, then not taken + + mv t3, t1 + mv t4, t2 +oneLoopTest2: + # instruction + addi t3, t3, 1 + li t5, 10 # filler instructions + li t6, 100 + li a0, 1000 + bne t3, t4, oneLoopTest2 # loop branch: taken 29 times, then not taken + + mv t3, t1 + mv t4, t2 +oneLoopTest3: + # instruction + addi t3, t3, 1 + li t5, 10 # filler instructions + li t6, 100 + bne t3, t4, oneLoopTest3 # loop branch: taken 29 times, then not taken + + mv t3, t1 + mv t4, t2 +oneLoopTest4: + # instruction + addi t3, t3, 1 + li t5, 10 # filler instructions + bne t3, t4, oneLoopTest4 # loop branch: taken 29 times, then not taken + + mv t3, t1 + mv t4, t2 +oneLoopTest5: + # instruction + addi t3, t3, 1 + bne t3, t4, oneLoopTest5 # loop branch: taken 29 times, then not taken + + ret + +.section .text +.globl global_hist_6_space_test +.type global_hist_6_space_test, @function +global_hist_6_space_test: + li t1, 1 + li t2, 200 + li t3, 0 + li t4, 1 + +loop_6: + # instruction + addi t3, t3, 1 + addi t3, t3, 1 + addi t3, t3, 1 + addi t3, t3, 1 + addi t3, t3, 1 + addi t3, t3, 1 + beqz t4, zero_6 # this branch toggles between taken and not taken. + li t4, 0 + j one_6 +zero_6: + li t4, 1 + addi t3, t3, 1 + addi t3, t3, 1 + addi t3, t3, 1 + add t1, t1, t4 + +one_6: + addi t3, t3, 1 + addi t3, t3, 1 + addi t3, t3, 1 + addi t3, t3, 1 + addi t2, t2, -1 + bnez t2, loop_6 + + ret + +.section .text +.globl global_hist_4_space_test +.type global_hist_4_space_test, @function +global_hist_4_space_test: + li t1, 1 + li t2, 200 + li t3, 0 + li t4, 1 + +loop_4: + # instruction + addi t3, t3, 1 + addi t3, t3, 1 + addi t3, t3, 1 + addi t3, t3, 1 + beqz t4, zero_4 # this branch toggles between taken and not taken. + li t4, 0 + j one_4 +zero_4: + li t4, 1 + addi t3, t3, 1 + add t1, t1, t4 + +one_4: + addi t3, t3, 1 + addi t2, t2, -1 + bnez t2, loop_4 + + ret + +.section .text +.globl global_hist_3_space_test +.type global_hist_3_space_test, @function +global_hist_3_space_test: + li t1, 1 + li t2, 200 + li t3, 0 + li t4, 1 + +loop_3: + # instruction + addi t3, t3, 1 + addi t3, t3, 1 + addi t3, t3, 1 + addi t3, t3, 1 + beqz t4, zero_3 # this branch toggles between taken and not taken. + li t4, 0 + j one_3 +zero_3: + li t4, 1 + addi t3, t3, 1 + add t1, t1, t4 + +one_3: + addi t3, t3, 1 + addi t2, t2, -1 + bnez t2, loop_3 + + ret + + +.section .text +.globl global_hist_2_space_test +.type global_hist_2_space_test, @function +global_hist_2_space_test: + li t1, 1 + li t2, 200 + li t3, 0 + li t4, 1 + +loop_2: + # instruction + addi t3, t3, 1 + addi t3, t3, 1 + beqz t4, zero_2 # this branch toggles between taken and not taken. + li t4, 0 + j one_2 +zero_2: + li t4, 1 + add t1, t1, t4 + +one_2: + addi t2, t2, -1 + bnez t2, loop_2 + + ret + +.section .text +.globl global_hist_1_space_test +.type global_hist_1_space_test, @function +global_hist_1_space_test: + li t1, 1 + li t2, 200 + li t3, 0 + li t4, 1 + +loop_1: + # instruction + addi t3, t3, 1 + beqz t4, zero_1 # this branch toggles between taken and not taken. + li t4, 0 + j one_1 +zero_1: + li t4, 1 + add t1, t1, t4 + +one_1: + addi t2, t2, -1 + bnez t2, loop_1 + + ret + +.section .text +.globl global_hist_0_space_test +.type global_hist_0_space_test, @function +global_hist_0_space_test: + li t1, 1 + li t2, 200 + li t3, 0 + li t4, 1 + +loop_0: + # instruction + beqz t4, zero_0 # this branch toggles between taken and not taken. + li t4, 0 + j one_0 +zero_0: + li t4, 1 + add t1, t1, t4 + +one_0: + addi t2, t2, -1 + bnez t2, loop_0 + + ret From 68fdcabf2343073b4b0cf557fbab71858c10bc79 Mon Sep 17 00:00:00 2001 From: David Harris Date: Fri, 16 Oct 2026 03:45:06 -0700 Subject: [PATCH 8/9] Coverage exclusions: follow the ifu, tlbcontrol and privdec changes regression-wally --ccov stopped at the exclusion step: two GetLineNum patterns still named ~ITLBMissF, which ifu.sv now spells ~ITLBMissOrUpdateRawF. The ITLB TLBMiss exclusion named row 3, but with UpperBitsUnequal added Questa lists TLBAccess_0 as row 5. The wfi rework left two rows of TWTimeoutM uncovered: wfiM_0 can't occur with the wait counter saturated, since the counter clears whenever M advances, so it is excluded; STATUS_TW_0 in S mode is reached by the new wfiSnoTW test, an S-mode wfi with mstatus.TW = 0 that waits past the counter limit for a timer interrupt. Co-Authored-By: Claude Opus 5.5 --- sim/questa/coverage-exclusions-rv64gc.do | 10 ++- tests/coverage/wfiSnoTW.S | 95 ++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 tests/coverage/wfiSnoTW.S diff --git a/sim/questa/coverage-exclusions-rv64gc.do b/sim/questa/coverage-exclusions-rv64gc.do index d43bb4fca0..9bb46ce52c 100644 --- a/sim/questa/coverage-exclusions-rv64gc.do +++ b/sim/questa/coverage-exclusions-rv64gc.do @@ -436,8 +436,9 @@ set line [GetLineNum ${SRC}/mmu/tlb/tlbcontrol.sv "ReadAccess \\| WriteAccess"] coverage exclude -scope /core/ifu/immu/immu/tlb/tlb/tlbcontrol -fecexprrow $line 1 3-6 set line [GetLineNum ${SRC}/mmu/tlb/tlbcontrol.sv "CAMHit & TLBAccess"] coverage exclude -scope /core/ifu/immu/immu/tlb/tlb/tlbcontrol -fecexprrow $line 3 +# Questa orders the TLBMiss terms CAMHit, UpperBitsUnequal, TLBAccess, Translate: row 5 is TLBAccess_0 set line [GetLineNum ${SRC}/mmu/tlb/tlbcontrol.sv "~CAMHit & TLBAccess"] -coverage exclude -scope /core/ifu/immu/immu/tlb/tlb/tlbcontrol -fecexprrow $line 3 +coverage exclude -scope /core/ifu/immu/immu/tlb/tlb/tlbcontrol -fecexprrow $line 5 # IMMU only makes word-sized accesses (Size = 2'b10), so the byte, halfword and doubleword arms of the # misalignment case never run, and the fetch address bit 0 is always 0 (row 4, VAdr[0]_1, of the word arm). @@ -485,9 +486,9 @@ coverage exclude -scope /core/ifu/immu/immu/pmp/pmpchecker -fecexprrow [GetLineN coverage exclude -scope /core/ifu/immu/immu/pmp/pmpchecker -fecexprrow [GetLineNum ${SRC}/mmu/pmpchecker.sv "exclusion-tag: immu-pmpcboaccess"] 2 4 # No irom -set line [GetLineNum ${SRC}/ifu/ifu.sv "~ITLBMissF & ~CacheableF & ~SelIROM"] +set line [GetLineNum ${SRC}/ifu/ifu.sv "~ITLBMissOrUpdateRawF & ~CacheableF & ~SelIROM"] coverage exclude -scope /core/ifu -feccondrow $line 6 -set line [GetLineNum ${SRC}/ifu/ifu.sv "~ITLBMissF & CacheableF & ~SelIROM"] +set line [GetLineNum ${SRC}/ifu/ifu.sv "~ITLBMissOrUpdateRawF & CacheableF & ~SelIROM"] coverage exclude -scope /core/ifu -feccondrow $line 4 # no DTIM @@ -618,6 +619,9 @@ coverage exclude -scope /core/hzu -fecexprrow [GetLineNum ${SRC}/hazard/hazard.s # Instruction Misaligned never asserted because compressed instructions are accepted coverage exclude -scope /core/priv/priv/trap -fecexprrow [GetLineNum ${SRC}/privileged/trap.sv "assign ExceptionM"] 2 +# The wait counter clears whenever M advances and M holds the same instruction while it is stalled, so the counter +# can only reach its limit with the wfi still in M: wfiM_0 (row 1) never sees the counter's top bit set. +coverage exclude -scope /core/priv/priv/pmd -fecexprrow [GetLineNum ${SRC}/privileged/privdec.sv "assign TWTimeoutM"] 1 # Attempting to access fflags, frm, fcsr with mstatus.FS = 0 traps, so checking for (STATUS_FS != 2'b00) # before enabling writes to these CSRs is redundant and uncoverable diff --git a/tests/coverage/wfiSnoTW.S b/tests/coverage/wfiSnoTW.S new file mode 100644 index 0000000000..050ecb3045 --- /dev/null +++ b/tests/coverage/wfiSnoTW.S @@ -0,0 +1,95 @@ +/////////////////////////////////////////// +// wfiSnoTW.S +// +// Written David_Harris@hmc.edu 10/16/26 +// +// Purpose: A wfi in S mode with mstatus.TW = 0 has no time limit. It waits past the point where the +// wait counter saturates (2^WFI_TIMEOUT_BIT cycles) and must still end in the interrupt that +// wakes it (mcause 0x8000000000000005, mepc = wfi+4), not an illegal-instruction trap. +// +// The supervisor timer interrupt (Sstc stimecmp) is not delegated, so it is enabled in S mode and +// traps to M. +// +// A component of the CORE-V-WALLY configurable RISC-V project. +// https://github.com/openhwfoundation/cvw +// +// Copyright (C) 2021-26 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +//////////////////////////////////////////////////////////////////////////////////////////////// + +#include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" + +.option norvc # the handler skips 4-byte instructions + +main: + la t0, ti_handler + csrw mtvec, t0 + csrw mie, zero + csrw mideleg, zero # STI is taken in M, so it is enabled in S mode + li t0, 1 + slli t0, t0, 63 + csrs menvcfg, t0 # menvcfg.STCE = 1: stimecmp drives STIP + csrsi mcounteren, 2 # mcounteren.TM = 1: S mode can read time and write stimecmp + li t0, -1 + csrw stimecmp, t0 + li t0, 0x20 + csrw mie, t0 # mie.STIE = 1 + li t0, 0x200000 + csrc mstatus, t0 # mstatus.TW = 0 + li a0, 1 + ecall # S mode + + la s3, wfi_instr + csrr t2, time + li t3, 0x18000 # well past the 2^16-cycle wait limit + add t2, t2, t3 + csrw stimecmp, t2 +wfi_instr: + wfi + nop + la t0, trap_rec + ld a1, 0(t0) + ld a2, 8(t0) + li t2, 0x8000000000000005 + SC_CHECK_EQ a1, t2, 2 # the supervisor timer interrupt, not an illegal-instruction trap + addi t2, s3, 4 + SC_CHECK_EQ a2, t2, 3 # taken after the wfi waited, so mepc = wfi+4 + SC_PASS + j done + +.align 4 +ti_handler: + csrrw tp, mscratch, tp # tp = trap stack + sd t0, 0(tp) + sd t1, -8(tp) + csrr t0, mcause + li t1, 8 + bge t0, t1, ti_ecall # ecalls (8, 9, 11) go on to trap_handler + la t1, trap_rec + sd t0, 0(t1) # record mcause and mepc + csrr t0, mepc + sd t0, 8(t1) + li t1, -1 + csrw stimecmp, t1 # clear STIP + csrr t1, mcause + bltz t1, ti_ret # interrupt: return to mepc + addi t0, t0, 4 + csrw mepc, t0 # exception: skip the wfi +ti_ret: + ld t1, -8(tp) + ld t0, 0(tp) + csrrw tp, mscratch, tp + mret +ti_ecall: + ld t1, -8(tp) + ld t0, 0(tp) + csrrw tp, mscratch, tp + j trap_handler + +.section .data +.align 3 +trap_rec: + .dword 0 # mcause + .dword 0 # mepc From 3fa4ef3125f640dc3287c315d3fdf180d18ecd55 Mon Sep 17 00:00:00 2001 From: David Harris Date: Fri, 16 Oct 2026 04:46:02 -0700 Subject: [PATCH 9/9] Add tlbOverlap: exercise the TLB Overlap check on every line The Overlap check that invalidates TLB lines overlapping a new entry had rows no test reached on any ITLB or DTLB line: a NAPOT leaf in the line's 64 KiB region or in another region of its megapage, a global leaf under another ASID, and an Sv57 petapage leaf that differs from the line only in VPN[3]. tlbOverlap fills all 32 lines of each TLB with 4 KiB pages before each case, sized to the replacement policy, and checks every access's translation. Co-Authored-By: Claude Opus 5.5 --- tests/coverage/tlbOverlap.S | 338 ++++++++++++++++++++++++++++++++++++ 1 file changed, 338 insertions(+) create mode 100644 tests/coverage/tlbOverlap.S diff --git a/tests/coverage/tlbOverlap.S b/tests/coverage/tlbOverlap.S new file mode 100644 index 0000000000..a16e393f27 --- /dev/null +++ b/tests/coverage/tlbOverlap.S @@ -0,0 +1,338 @@ +/////////////////////////////////////////// +// tlbOverlap.S +// +// Written David_Harris@hmc.edu 10/16/26 +// +// Purpose: Exercise the Overlap check of every ITLB and DTLB line: while each line holds a valid entry, the +// walker reads a PTE that may cover the same address, for each way the check can match: +// A. a 64 KiB NAPOT leaf in the 64 KiB region of the line's 4 KiB page (the line is invalidated) +// B. a NAPOT leaf in the same megapage but another 64 KiB region (the line stays) +// C. a global gigapage leaf read under another ASID, in the line's gigapage (the line is invalidated) +// D. an Sv57 petapage leaf for a VA that shares the line's VPN[4] but not its VPN[3] (likewise) +// The TLB replaces the first line not recently used, and clears the recently-used bits once every line +// has been used. The access replayed after a miss hits the line just written, so when that write +// cleared the bits, the line is marked again and the next 31 writes pass over it. 62 misses to +// distinct pages therefore fill every line. In part A, each NAPOT walk's write evicts a line that +// may hold a page of a region not walked yet, so part A runs twice, with 62 and 64 pages, which +// leaves the replacement pointer in different places. An SFENCE.VMA does not change the bits. +// The DTLB is reached by M-mode loads with mstatus.MPRV = 1 and MPP = S; the ITLB by entering S mode at +// the page, which runs addi a1, x0, ; ecall. Every access checks the page it reached, through +// the tag stored in that physical page (loads) or the page number the code returns (fetches). +// The DTLB sees the walker's PTE addresses during a walk, so its lines are checked against the leaf +// when the load replays: part C ends in a global gigapage leaf and part D in a petapage leaf. +// Where a part rewrites a PTE without an SFENCE.VMA (the NAPOT PTEs of part A, the petapage leaf of +// part D), the next access has not been cached and the old and new PTEs map it to the same physical +// page, so either translation gives the same result. Under ASID 2 (part C) the address is mapped +// only by the global gigapage. +// +// A component of the CORE-V-WALLY configurable RISC-V project. +// https://github.com/openhwfoundation/cvw +// +// Copyright (C) 2021-26 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +//////////////////////////////////////////////////////////////////////////////////////////////// + +#include "WALLY-init-lib.h" +#include "WALLY-selfcheck.h" + +#define VA_MA 0x40000000 // megapage A: VPN[2] = 1, VPN[1] = 0 +#define VA_MB 0x40200000 // megapage B: VPN[2] = 1, VPN[1] = 1 +#define VA57_A 0x0001008040200000 // Sv57: VPN[4] = 1, VPN[3] = 1, then megapage B +#define VA57_HI 0x0001000000000000 // Sv57: VPN[4] = 1; the petapage maps VA57_HI + x to x +#define TAG 0x5A5A0000 // physical page j holds TAG + j at offset 8 +#define PTE_RWX 0xCF // V R W X A D +#define PTE_PTR 0x01 // non-leaf +#define PTE_G 0x20 +#define NVICT 62 // misses that fill every line + +// rd = PTE pointing to the table at label, with extra flag bits +.macro PTR rd, label, flags + la \rd, \label + srli \rd, \rd, 12 + slli \rd, \rd, 10 + ori \rd, \rd, PTE_PTR | \flags +.endm + +// satp = mode | asid | root +.macro SATP mode, asid, root + la t0, \root + srli t0, t0, 12 + li t1, (\mode << 60) | (\asid << 44) + or t0, t0, t1 + csrw satp, t0 +.endm + +// access pages first..last of base: page p maps to physical page p & 31 +.macro ACCESS_RANGE base, first, last + li s1, \first +1: li a2, \base + slli t0, s1, 12 + add a2, a2, t0 + andi a3, s1, 31 + jal ra, access + addi s1, s1, 1 + li t0, \last + ble s1, t0, 1b +.endm + +// access page p of base, expecting physical page phys +.macro ACCESS_ONE base, p, phys + li a2, \base + (\p << 12) + li a3, \phys + jal ra, access +.endm + +// a2 = base + physical address of page 16, expecting page 16 +.macro ACCESS_PHYS16 base + la a2, vpages + (16 << 12) + li t0, \base + add a2, a2, t0 + li a3, 16 + jal ra, access +.endm + +main: + SC_STRICT_TRAPS + li s0, 0 # DTLB + jal ra, sequence + la t0, itlb_handler + csrw mtvec, t0 + li s0, 1 # ITLB + jal ra, sequence + SC_STRICT_TRAPS + SC_PASS + j done + +// Run parts A-D on the TLB s0 selects +sequence: + mv s10, ra + jal ra, init_tables + SATP 8, 1, root1 # Sv39, ASID 1 + sfence.vma + ACCESS_RANGE VA_MB, 16, 47 + + # A. NAPOT leaves in the 64 KiB regions of the lines' pages + li a6, NVICT + jal ra, partA + li a6, NVICT + 2 + jal ra, partA + + # B. a NAPOT leaf in another 64 KiB region of the lines' megapage + sfence.vma + ACCESS_RANGE VA_MB, 16, 16 + NVICT - 1 # regions 1-4 of megapage B + ACCESS_ONE VA_MB, 0, 0 # region 0 is a NAPOT page + + # C. a global gigapage leaf under another ASID, in the lines' gigapage + sfence.vma + ACCESS_RANGE VA_MB, 16, 16 + NVICT - 1 + SATP 8, 2, root2 # Sv39, ASID 2 + ACCESS_PHYS16 (VA_MA - 0x80000000) # the gigapage maps VA_MA + x to 0x80000000 + x + + # D. an Sv57 petapage leaf for a VA with the lines' VPN[4] and another VPN[3] + SATP 10, 3, root57 # Sv57, ASID 3 + sfence.vma + ACCESS_RANGE VA57_A, 16, 16 + NVICT - 1 + li t0, PTE_RWX # root57[1] becomes a petapage on physical address 0; no SFENCE.VMA + la t1, root57 + sd t0, 8(t1) + ACCESS_PHYS16 VA57_HI # VPN[3] = 0: through the petapage, or the old gigapage, to page 16 + + csrw satp, zero + sfence.vma + mv ra, s10 + ret + +// Part A with a6 pages: pages 1-15 of the 64 KiB regions of megapage A in turn, then a NAPOT walk in each +partA: + mv s8, ra + la a0, LA + li a1, 0 + li a5, 80 + jal ra, plain # regions 0-4 of megapage A as 4 KiB pages + sfence.vma + li s2, 0 # pages accessed + li s3, 0 # region base page +2: addi s1, s3, 1 +3: li a2, VA_MA + slli t0, s1, 12 + add a2, a2, t0 + andi a3, s1, 31 + jal ra, access + addi s2, s2, 1 + beq s2, a6, 4f + addi s1, s1, 1 + addi t0, s3, 16 + blt s1, t0, 3b + addi s3, s3, 16 + j 2b +4: la a0, LA # region r becomes a NAPOT page on physical pages 16 * (r & 1) + 0-15 + li s3, 0 +5: andi a1, s3, 16 + slli t0, s3, 3 + add a0, a0, t0 + jal ra, napot + la a0, LA + addi s3, s3, 16 + li t0, 80 + blt s3, t0, 5b + li s3, 0 +6: li a2, VA_MA # each region's first page misses and reads its NAPOT PTE + slli t0, s3, 12 + add a2, a2, t0 + andi a3, s3, 31 + jal ra, access + addi s3, s3, 16 + li t0, 80 + blt s3, t0, 6b + mv ra, s8 + ret + +// Access VA a2 through the TLB s0 selects and check that it reached physical page a3. Clobbers t0-t2, a1, s11. +access: + li t0, 0x1800 + csrc mstatus, t0 + li t0, 0x800 + csrs mstatus, t0 # mstatus.MPP = S + bnez s0, access_fetch + li t0, 0x20000 + csrs mstatus, t0 # mstatus.MPRV = 1: the load translates as in S mode + ld t2, 8(a2) + csrc mstatus, t0 + li t1, TAG + add t1, t1, a3 + SC_CHECK_EQ_ADR t2, t1, a2, 1 + ret +access_fetch: + la s11, access_back # itlb_handler returns here + csrw mepc, a2 + mret # S mode at a2: addi a1, x0, ; ecall +access_back: + SC_CHECK_EQ_ADR a1, a3, a2, 2 + ret + +// The page's ecall from S mode ends the fetch access; any other trap fails the test +.align 4 +itlb_handler: + csrr a4, mcause + li a5, 9 + bne a4, a5, 1f + jr s11 +1: la t0, trap_handler + csrw mtvec, t0 + SC_CHECK_EQ a4, a5, 3 + +// Write 16 NAPOT PTEs at a0 mapping a 64 KiB region to physical pages a1-a1+15 +napot: + la t0, vpages + srli t0, t0, 12 + add t0, t0, a1 + ori t0, t0, 8 # PPN[3:0] = 1000 + slli t0, t0, 10 + li t1, 1 + slli t1, t1, 63 # N + or t0, t0, t1 + ori t0, t0, PTE_RWX + li t1, 0 +1: slli t2, t1, 3 + add t2, t2, a0 + sd t0, 0(t2) + addi t1, t1, 1 + li t2, 16 + blt t1, t2, 1b + ret + +// Build the page tables +// Sv39: root1[1] -> T1; T1[0] -> LA (megapage A), T1[1] -> LB (megapage B) +// root2[1] = global gigapage on 0x80000000 +// Sv57: root57[1] -> L3; L3[1] -> T2; T2[1] -> T1 (the lines' pages) +// L3[0] -> T2B; T2B[2] = gigapage on 0x80000000 (the old translation of part D's last access) +// LB: pages 0-15 are a NAPOT page on physical pages 0-15; pages 16-95 map to physical page p & 31 +// (partA writes LA) +init_tables: + mv s9, ra + PTR t0, T1, 0 + la t1, root1 + sd t0, 8(t1) + li t0, (0x80000000 >> 2) | PTE_G | PTE_RWX + la t1, root2 + sd t0, 8(t1) + PTR t0, LA, 0 + la t1, T1 + sd t0, 0(t1) + PTR t0, LB, 0 + sd t0, 8(t1) + PTR t0, L3, 0 + la t1, root57 + sd t0, 8(t1) + PTR t0, T2, 0 + la t1, L3 + sd t0, 8(t1) + PTR t0, T2B, 0 + sd t0, 0(t1) + PTR t0, T1, 0 + la t1, T2 + sd t0, 8(t1) + li t0, (0x80000000 >> 2) | PTE_RWX + la t1, T2B + sd t0, 16(t1) + la a0, LB + li a1, 0 + jal ra, napot + la a0, LB + li a1, 16 + li a5, 96 + jal ra, plain + mv ra, s9 + ret + +// Write 4 KiB PTEs for pages a1 to a5-1 of the table at a0, page p mapping to physical page p & 31 +plain: + la t1, vpages + srli t1, t1, 12 +1: andi t0, a1, 31 + add t0, t0, t1 + slli t0, t0, 10 + ori t0, t0, PTE_RWX + slli t2, a1, 3 + add t2, t2, a0 + sd t0, 0(t2) + addi a1, a1, 1 + blt a1, a5, 1b + ret + +.data +// 32 physical pages: page j starts with addi a1, x0, j; ecall and holds TAG + j at offset 8 +.align 16 +vpages: +.option push +.option norvc +.set vp, 0 +.rept 32 + addi a1, x0, vp + ecall + .dword TAG + vp + .fill 4096 - 16, 1, 0 +.set vp, vp + 1 +.endr +.option pop + +.align 12 +root1: + .fill 512, 8, 0 +root2: + .fill 512, 8, 0 +root57: + .fill 512, 8, 0 +L3: + .fill 512, 8, 0 +T2: + .fill 512, 8, 0 +T2B: + .fill 512, 8, 0 +T1: + .fill 512, 8, 0 +LA: + .fill 512, 8, 0 +LB: + .fill 512, 8, 0