diff --git a/sim/questa/coverage-exclusions-rv64gc.do b/sim/questa/coverage-exclusions-rv64gc.do index d43bb4fca0..bc293eb76b 100644 --- a/sim/questa/coverage-exclusions-rv64gc.do +++ b/sim/questa/coverage-exclusions-rv64gc.do @@ -292,10 +292,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 @@ -436,8 +438,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 +488,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 +621,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/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/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/cbofaulthit.S b/tests/coverage/cbofaulthit.S new file mode 100644 index 0000000000..677d4d7a9a --- /dev/null +++ b/tests/coverage/cbofaulthit.S @@ -0,0 +1,149 @@ +/////////////////////////////////////////// +// 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. 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. 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 + + // 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, 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 +// 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/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/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/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/fetchperiph.S b/tests/coverage/fetchperiph.S new file mode 100644 index 0000000000..4ac97d89cc --- /dev/null +++ b/tests/coverage/fetchperiph.S @@ -0,0 +1,60 @@ +/////////////////////////////////////////// +// 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" +#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 + + 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/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/gshare.S b/tests/coverage/gshare.S index d49ea6c225..9c3cecf9ad 100644 --- a/tests/coverage/gshare.S +++ b/tests/coverage/gshare.S @@ -3,49 +3,70 @@ // // 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. +// 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-24 Harvey Mudd College & Oklahoma State University +// Copyright (C) 2021-26 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" +#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 -# run-elf.bash find this in project description main: + SC_STRICT_TRAPS - addi sp, sp, 8 - sd ra, 0(sp) + # 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: - j done + SC_PASS + j done .data @@ -65,7 +86,7 @@ oneLoopTest1: li t6, 100 li a0, 1000 li a1, 10000 - bne t3, t4, oneLoopTest1 # this branch toggles between taken and not taken. + bne t3, t4, oneLoopTest1 # loop branch: taken 29 times, then not taken mv t3, t1 mv t4, t2 @@ -75,7 +96,7 @@ oneLoopTest2: li t5, 10 # filler instructions li t6, 100 li a0, 1000 - bne t3, t4, oneLoopTest2 # this branch toggles between taken and not taken. + bne t3, t4, oneLoopTest2 # loop branch: taken 29 times, then not taken mv t3, t1 mv t4, t2 @@ -84,7 +105,7 @@ oneLoopTest3: addi t3, t3, 1 li t5, 10 # filler instructions li t6, 100 - bne t3, t4, oneLoopTest3 # this branch toggles between taken and not taken. + bne t3, t4, oneLoopTest3 # loop branch: taken 29 times, then not taken mv t3, t1 mv t4, t2 @@ -92,14 +113,14 @@ oneLoopTest4: # instruction addi t3, t3, 1 li t5, 10 # filler instructions - bne t3, t4, oneLoopTest4 # this branch toggles between taken and not taken. + 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 # this branch toggles between taken and not taken. + bne t3, t4, oneLoopTest5 # loop branch: taken 29 times, then not taken 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/icachebusbusy.S b/tests/coverage/icachebusbusy.S new file mode 100644 index 0000000000..d411fdfa80 --- /dev/null +++ b/tests/coverage/icachebusbusy.S @@ -0,0 +1,54 @@ +/////////////////////////////////////////// +// 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. +// 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 + +.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 + + // 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 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 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/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/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/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/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/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/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/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/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/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/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/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/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 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 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 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 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: