Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions bin/wsim
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ def isDesignUpToDate(target):
return True


def targetMtime(target):
return target.stat().st_mtime if target.exists() else None


def runSim(args, flags, prefix):
if args.sim == "questa":
runQuesta(args, flags, prefix)
Expand Down Expand Up @@ -220,8 +224,12 @@ def runQuesta(args, flags, prefix):
# Phase 1: Compile (only once per config/flags/params/defines combo)
print(f"Running Questa on {args.config} {args.testsuite}")
wkdir_path = WALLY / "sim" / "questa" / wkdir
target = wkdir_path / "testbenchopt"
target_mtime = targetMtime(target)
needs_compile = target_mtime is None or not isDesignUpToDate(target)
with compileLock("questa", f"{args.config}_{args.tb}{'_' + bhash if bhash else ''}"):
if not isDesignUpToDate(wkdir_path / "testbenchopt"):
current_mtime = targetMtime(target)
if needs_compile and current_mtime == target_mtime:
compile_cmd = f'do wally-compile.do {args.config} {args.tb} {wkdir} {compile_flags}'
compile_cmd = f'cd {sim_dir}; {prefix} vsim -c -do "{compile_cmd}"'
if os.system(compile_cmd):
Expand Down Expand Up @@ -252,8 +260,11 @@ def runVerilator(args):
f' DEFINE_ARGS="{args.define}"'
f' BUILD_HASH="{bhash}"'
)
target_mtime = targetMtime(binary)
needs_compile = target_mtime is None or not isDesignUpToDate(binary)
with compileLock("verilator", f"{args.config}_{args.tb}{'_' + bhash if bhash else ''}"):
if not isDesignUpToDate(binary):
current_mtime = targetMtime(binary)
if needs_compile and current_mtime == target_mtime:
if os.system(compile_cmd):
return

Expand Down Expand Up @@ -283,8 +294,11 @@ def runVCS(args, flags, prefix):
compile_flags += f' --define "{args.define}"'

# Phase 1: Compile (only once per config/flags/params/defines combo)
target_mtime = targetMtime(binary)
needs_compile = target_mtime is None or not isDesignUpToDate(binary)
with compileLock("vcs", f"{args.config}_{args.tb}{'_' + bhash if bhash else ''}"):
if not isDesignUpToDate(binary):
current_mtime = targetMtime(binary)
if needs_compile and current_mtime == target_mtime:
compile_cmd = f'cd {sim_dir}; {prefix} ./run_vcs compile {args.config} {args.testsuite} --wkdir {wkdir} {compile_flags}'
if os.system(compile_cmd):
return
Expand Down
4 changes: 4 additions & 0 deletions testbench/testbench.sv
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ module testbench;
"arch64pmp": if (P.PMP_ENTRIES > 0) tests = arch64pmp;
"arch64vm_sv39": if (P.SV39_SUPPORTED) tests = arch64vm_sv39;
"arch64vm_sv48": if (P.SV48_SUPPORTED) tests = arch64vm_sv48;
"arch64vm_sv48_a": if (P.SV48_SUPPORTED) tests = arch64vm_sv48_a;
"arch64vm_sv48_b": if (P.SV48_SUPPORTED) tests = arch64vm_sv48_b;
"arch64vm_sv39_isolate": if (P.SV39_SUPPORTED) tests = arch64vm_sv39_isolate;
"arch64vm_sv48_mxr_isolate": if (P.SV48_SUPPORTED) tests = arch64vm_sv48_mxr_isolate;
"arch64vm_sv57": if (P.SV57_SUPPORTED) tests = arch64vm_sv57;
endcase
end else begin // RV32
Expand Down
30 changes: 29 additions & 1 deletion testbench/tests.vh
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ string coverage64gc[] = '{
"tlbNAPOT",
"tlbASID",
"tlbGLB",
"tlbGLBASID",
"tlbGLBHIT",
"tlbMP",
"tlbGP",
"tlbTP",
Expand All @@ -73,7 +75,11 @@ string coverage64gc[] = '{
"pmppriority",
"pmpcbo",
"pmpadrdecs",
"btbthrash"
"btbthrash",
"fpuReservedRM",
"decompReserved",
"pmpTOR7",
"cacheInval"
};

string buildroot[] = '{
Expand Down Expand Up @@ -446,6 +452,28 @@ string arch64vm_sv48[] = '{
"rv64i_m/vm_pmp/src/sv48/sv48_pmp_on_pte_U_mode.S"
};

string arch64vm_sv48_a[] = '{
`RISCVARCHTEST,
"rv64i_m/vm_sv48/src/sv48_res_global_pte_U_mode.S",
"rv64i_m/vm_sv48/src/sv48_pte_reserved_field_S_mode.S"
};

string arch64vm_sv48_b[] = '{
`RISCVARCHTEST,
"rv64i_m/vm_sv48/src/sv48_pte_reserved_field_S_mode.S",
"rv64i_m/vm_sv48/src/sv48_res_global_pte_U_mode.S"
};

string arch64vm_sv39_isolate[] = '{
`RISCVARCHTEST,
"rv64i_m/vm_sv39/src/vm_VA_all_zeros_S_mode.S"
};

string arch64vm_sv48_mxr_isolate[] = '{
`RISCVARCHTEST,
"rv64i_m/vm_sv48/src/sv48_mxr_S_mode.S"
};

string arch64vm_sv57[] = '{
`RISCVARCHTEST,
//"rv64i_m/vm_sv57/src/sv57_A_and_D_S_mode.S", // Disable until fixed; Might be due to Issue#1538 ***TODO: Zain
Expand Down
113 changes: 113 additions & 0 deletions tests/coverage/cacheInval.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
///////////////////////////////////////////
// cacheInval.S
//
// Written: David_Harris@hmc.edu 29 May 2026
//
// Purpose: Coverage test for the shared cache invalidate path.
// Targets the FEC condition (InvalidateCache & ~InvalidateFlushStage)
// in src/cache/cacheLRU.sv (~140), cachefsm.sv (~128), cacheway.sv (~155).
//
// For the instruction cache, InvalidateCache is driven by InvalidateICacheM
// (asserted by fence.i while it is in the Memory stage) and InvalidateFlushStage
// is driven by FlushW. The previously uncovered FEC row is InvalidateFlushStage_1:
// InvalidateCache=1 while InvalidateFlushStage=1. A plain fence.i only flushes
// D/E/M (CSRWriteFenceM) and never asserts FlushW, so it always hits the _0 row.
// FlushW is asserted by FlushWCause = TrapM. A trap (machine timer interrupt) is
// asynchronous and attaches to whatever instruction occupies the Memory stage when
// it is taken; if that instruction is a fence.i, then InvalidateICacheM=1 and
// TrapM=1 (hence FlushW=1) in the same cycle, producing the unhit polarity.
//
// Strategy: arm the machine timer to fire very soon, then execute a long burst of
// back-to-back fence.i instructions so that the interrupt is overwhelmingly likely
// to be recognized while a fence.i sits in the Memory stage. The fence.i burst is
// preceded by cache priming (loads + branches) so the icache has valid lines whose
// LRU/valid state is invalidated, and the burst is long enough to absorb timer
// jitter.
//
// A component of the CORE-V-WALLY configurable RISC-V project.
// https://github.com/openhwgroup/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:
// Prime the instruction cache by executing code spread across several
// cache lines, then a fence.i, repeated, so the invalidate path operates
// on valid/LRU state (covers the InvalidateCache=1 / InvalidateFlushStage=0 rows).
jal primer
fence.i
jal primer
fence.i
fence.i // back-to-back fence.i

// Prime the data cache with valid lines (for context; D$ InvalidateCache is tied 0).
li t0, 0x80108000
sd zero, 0(t0)
ld t1, 0(t0)
li t0, 0x80109000
sd zero, 0(t0)
ld t1, 0(t0)

// ---- Arm the machine timer to fire during the fence.i burst ----
// CLINT: MTIME @ 0x0200BFF8, MTIMECMP @ 0x02004000. MTIE and global MIE
// are already enabled by WALLY-init-lib.h.
li t2, 0x0200BFF8 // address of MTIME
ld t3, 0(t2) // current time
addi t3, t3, 8 // small delta so the interrupt fires shortly into the burst
li t4, 0x02004000 // address of MTIMECMP
sd t3, 0(t4) // arm timer interrupt

// ---- fence.i burst ----
// A long run of back-to-back fence.i. Each fence.i spends multiple cycles in the
// 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).
.rept 256
fence.i
.endr

// 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.

// A few more fence.i interleaved with control flow for good measure.
fence.i
jal primer
fence.i

j done

// Subroutine spread over several .align'd cache lines so executing it fills
// multiple icache sets/ways before invalidation.
.align 6
primer:
nop
nop
nop
nop
jal t6, primer2
nop
nop
ret

.align 6
primer2:
nop
nop
nop
nop
jr t6
46 changes: 46 additions & 0 deletions tests/coverage/decompReserved.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
///////////////////////////////////////////
// decompReserved.S
//
// Written: david_harris@hmc.edu 29 May 2026
//
// Purpose: Coverage for the compressed-instruction decoder (src/ifu/decompress.sv).
// Closes the uncovered branch at decompress.sv:164 ("All False") and the
// condition (rds1 != 5'b0)_0 in the c.jr decode:
// 5'b10100: if (instr16[12]==0) if (rs2==0) if (rds1 != 5'b0) ... // c.jr
// c.jr requires rs1 != 0; the rs1==0 form (instr 0x8002) is a RESERVED
// encoding that no compiler emits, so the rds1==0 / all-false path was never
// exercised. Emitting the raw 16-bit reserved instruction drives the decoder
// down that path (it raises an illegal-instruction trap, which the harness
// trap handler skips by advancing mepc past the 2-byte instruction).
//
// A component of the CORE-V-WALLY configurable RISC-V project.
// https://github.com/openhwgroup/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:
# c.jr x0 : funct3=100, instr16[12]=0, rs1=00000, rs2=00000, op=10 => 0x8002
# This is the RESERVED c.jr encoding (c.jr is only defined for rs1 != 0).
# The decompressor evaluates `if (rds1 != 5'b0)` at decompress.sv:164 with rds1==0,
# taking the previously-uncovered all-false branch / (rds1!=0)_0 condition.
# It decodes to an illegal instruction; the trap handler advances mepc by 2 and returns.
.2byte 0x8002

j done
69 changes: 69 additions & 0 deletions tests/coverage/fpuReservedRM.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
///////////////////////////////////////////
// fpuReservedRM.S
//
// Written: david_harris@hmc.edu
//
// Purpose: Directed coverage test for src/fpu/fctrl.sv line 98.
// Hits the focused-expression input terms (FRM_REGW == 5)_1 and
// (FRM_REGW == 6)_1 in the SupportedRM assignment.
//
// fctrl.sv:98:
// assign SupportedRM = ~(Funct3D == 3'b101 | Funct3D == 3'b110 |
// (Funct3D == 3'b111 & (FRM_REGW == 3'b101 |
// FRM_REGW == 3'b110 | FRM_REGW == 3'b111))) | ...
//
// To make (FRM_REGW == 5) and (FRM_REGW == 6) each evaluate true while
// the rest of the SupportedRM term is exercised, we set frm (fcsr[7:5])
// to the reserved values 5 then 6, and execute an OP-FP instruction that
// 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".
//
// A component of the CORE-V-WALLY configurable RISC-V project.
// https://github.com/openhwgroup/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:

bseti t0, zero, 14 # mstatus.FS = 0b01 (turn on FPU)
csrs mstatus, t0

# 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

# ---- 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

# ---- 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

# Restore a legal rounding mode and do a legal op so FPU still works
li t0, 0
csrw frm, t0 # frm = rne
fadd.s f3, f1, f2

j done
Loading