MAVERIC Core 2.0 is an open-source, 5-stage pipelined RISC-V processor written in SystemVerilog. Earlier progress is logged at https://github.com/olzhasnurman/maveric_core.git
This repository contains the design (RTL), verification environment, test infrastructure, and helper scripts needed to simulate the core, run the official RISC-V test suites against it, and check its behaviour both against the Dromajo golden reference model (per-instruction co-simulation) and the Spike commit log (trace comparison).
- ISA: 64-bit RISC-V — RV64IMAC (the RV64I base plus the
Mmultiply/divide extension, theAatomic extension, theCcompressed-instruction extension, and theW-variant integer ops) with theZicsrcontrol/status registers, theZifenceiinstruction-fetch fence (FENCE/FENCE.I), theZicntrbase counters (cycle/time/instret), and theSstcsupervisor timer-compare extension (stimecmp).misareportsRV64ACIMSU. - Data / address width: 64-bit addressing, 32-bit instructions (16-bit
compressed encodings are expanded by
rtl/decompressor.svin decode). - Pipeline depth: 5 stages — Fetch, Decode, Execute, Memory, Write-Back —
where each stage registers its outputs (fetch owns the IF/ID boundary
register, decode ID/EX, execute EX/MEM, memory MEM/WB; write-back consumes
the registered payload directly), and stage boundaries carry a ready/valid
handshake:
valid(inside the payload structs ofrtl/pipeline_stage_pkg.sv) marks a real instruction vs a bubble,readyis the hazard-unit backpressure. - Register file: 32 × 64-bit integer registers with synchronous write and asynchronous read ports.
- Privilege levels: all three modes — machine (M), supervisor (S), and
user (U) — with per-mode CSR access checking, trap delegation
(
medeleg/mideleg), and bothmretandsretreturns. - Virtual memory: Sv39 address translation through a hardware page-table
walker and split 4-entry instruction/data TLBs, plus
SFENCE.VMA. - Protection: 16-entry PMP (physical memory protection) checked on every instruction fetch and load/store.
- Traps / interrupts: synchronous exceptions (environment calls from U/S/M,
breakpoint, illegal instruction, misaligned address, access fault, page
fault) with correct priority when several arrive at once, and asynchronous
machine/supervisor timer and software interrupts delivered by an on-chip
CLINT and the
Sstcstimecmpcomparator. - Configuration: widths, CSR addresses, privilege encodings, and trap causes
are centralised in
rtl/maveric_pkg.sv(maveric_pkg+csr_pkg).
The core is a classic in-order 5-stage pipeline (IF → ID → EX → MEM → WB) with one instruction issued per cycle when no hazard is present.
- Fetch (IF) —
rtl/fetch_stage.svdrives the PC, consults the I-cache, and asks the branch predictor for a next-PC override on taken branches / hit-in-BTB jumps. When translation is active the PC is first looked up in the ITLB (rtl/itlb.sv); the resulting physical address is screened by the fetch-side PMP checker (rtl/pmp_check.sv) before it reaches the I-cache. The predicted target, predicted direction, and BTB way flow down the pipeline alongside the instruction so that EX can validate them and drive training updates back to the predictor. - Decode (ID) —
rtl/decode_stage.svsplits the 32-bit instruction into fields viainstr_decoder, derives all control signals from thecontrol_unit(which wrapsmain_decoder+alu_decoderand also raises theis_mdu_op/is_mdu_word_opflags for the M extension), reads GPRs fromregister_file, and sign-/zero-extends the immediate throughextend_imm. The decoder also recognises the privileged instructions (mret,sret,sfence.vma) and flags them for the back-end. - Execute (EX) —
rtl/execute_stage.svhouses the 64-bitalu, the forwarding mux tree (three-way: no-forward / EX-MEM / MEM-WB), and branch resolution. It also hosts the multi-cycle MDU (rtl/mdu.sv, wrappingmultiplier.sv+divider.sv) for the M extension and the CSR file (rtl/csr_file.sv) that servicesZicsrreads/writes, tracks the current privilege mode, and performs trap entry/return. A misprediction detected here drivesbranch_mispred_exec_o, which flushes IF + ID and retargets the PC. - Memory (MEM) —
rtl/memory_stage.svaccesses the D-cache for loads and stores. Data addresses are translated by the DTLB (rtl/dtlb.sv) and screened by the LSU-side PMP checker (rtl/pmp_check_lsu.sv). Store widths are encoded as00=SB,01=SH,10=SW,11=SD;mem_exc_detect.svflags misaligned accesses as load/storeaddr_maexceptions. Loads are re-aligned and sign-/zero-extended byload_mux. This stage also hosts the memory-mapped CLINT (rtl/clint.sv) and the AMO ALU (rtl/amo_alu.sv) that computes read-modify-write results for the atomic extension (see Atomic (A) Extension below), muxing the CLINT register reads back into the load path. - Write-Back (WB) —
rtl/write_back_stage.svselects between ALU result, load data, PC+4 (for JAL/JALR) and immediate sources, then commits to the register file on the next rising edge. Trap commit,mret/sret, andsfence.vmaside effects take effect from here.
rtl/hazard_unit.sv (instantiated inside datapath) centralises all
pipeline-control policy:
- RAW forwarding: each source register has a 2-bit select —
10forwards from EX/MEM,01from MEM/WB,00uses the regfile read. Bothrs1andrs2are handled independently, prioritising the younger producer (EX/MEM over MEM/WB). - Load-use interlock: when an EX-stage load's destination matches a decode-stage source, the unit stalls IF/ID and bubbles EX for one cycle.
- Flushes: a branch misprediction in EX flushes ID and EX; the front-end
is redirected to the correct target computed by
adderin EX. - Cache stalls:
stall_cache_ifrom the cache FSM freezes every stage during an I-cache or D-cache miss. - MMU stalls: a TLB miss hands the pipeline to the page-table walker;
mmu_stall_i(data side) andmmu_stall_icache_i(fetch side) hold the affected stages until the walk completes and the TLB is refilled.
rtl/branch_pred_unit.sv couples a Branch Target Buffer and a Branch
History Table so that taken branches / indirect jumps can be resolved in IF
without waiting for EX.
- BTB (
rtl/btb.sv) — 4-way set-associative, 16 sets by default (SET_COUNT=16,N=4). Each entry stores a 60-bit branch instruction address (tag), the 64-bit target, and a valid bit. The BTB also returns the way that was hit so that EX can update the correct entry on a mispredict. - BHT (
rtl/bht.sv) — 64-entry table of 2-bit saturating counters (00=strongly not-taken,11=strongly taken). The counter is updated in EX based on actual branch outcome; IF consults it in parallel with the BTB lookup. - Resolution: EX compares the predicted direction and target against
the resolved values; on a mismatch
branch_mispred_exec_otriggers a front-end flush and BTB/BHT training write-back. - Accuracy reporting: every run prints total branches and
mispredictions (see
test/tb/check.c), so prediction quality can be tracked per test.
Both caches share a 512-bit refill line by default (configurable at
elaboration time via BLOCK_WIDTH).
- I-cache (
rtl/icache.sv) — direct-mapped, 16 blocks by default, read-only from the pipeline's perspective; refilled by the cache FSM on a miss and fully invalidated on aFENCE.I(see Instruction-Fetch Fence below). - D-cache (
rtl/dcache.sv) — 4-way set-associative, write-back / write-allocate, with a dirty bit per way. On an eviction of a dirty way the FSM transitions through theWRITE_BACKstate before re-allocating. The MMU page-table walker shares the D-cache port, so page-table entries are cached like ordinary data. - Cache FSM (
rtl/cache_fsm.sv) — a controller whose core flow isIDLE → ALLOCATE_I → IDLE,IDLE → ALLOCATE_D → IDLE, andIDLE → WRITE_BACK → ALLOCATE_D → IDLE, plus aWB_FENCEI → WB_FENCEI_DONEpath that drains every dirty D-cache line on aFENCE.I(see Instruction-Fetch Fence below). Data misses take priority over instruction misses to keep the pipeline from deadlocking on a load that sits behind a fetch. - Reconfigurability:
run_tests.py -vmust be paired with-s,-g, or-a. It sweepsBLOCK_WIDTHfrom 128 b to 1024 b andSET_COUNTfrom 2 to 16, regenerating the performance numbers for every combination. D-cache associativity is fixed atN=4(the design is not parameterized for other widths), so the sweep holds it at the saved default.
- The core speaks AXI4-Lite as a master through
rtl/axi4_lite_master.sv(instantiated intopnext to the cache/MMIO arbitration FSM), which splits intoaxi4_lite_master_read.svandaxi4_lite_master_write.sv. A matching slave pair (axi4_lite_slave_read.sv,axi4_lite_slave_write.sv) lives in thetest_envsimulation wrapper. - Default widths: 64-bit address, 32-bit data per beat. Cache lines are
streamed as
BLOCK_WIDTH / 32beats bycache_data_transfer.sv, which also generates the beat counter and assertscount_doneto close the transaction. - External memory is modelled by
rtl/mem_simulated.sv, which loads the test image from a$readmemh-style hex file produced byscripts/disasm2mem.py. Accesses above the MMIO base are routed to the device window instead of the memory array, and UART writes are forwarded out of the simulation through thepmem_writeDPI-C hook (test/tb/pmem_write.c) intoMAVERIC_PMEM_WRITE_FILE.
rtl/csr_file.sv implements the full M/S/U privilege machinery: it tracks
the current privilege mode, owns every CSR, and performs trap entry/return.
CSR addresses, privilege encodings, and trap-cause codes come from csr_pkg
in rtl/maveric_pkg.sv.
- Machine-level CSRs: information registers (
mvendorid,marchid,mimpid,mhartid, all read-as-zero), trap setup (mstatus,misa,medeleg,mideleg,mie,mtvec,mcounteren), trap handling (mscratch,mepc,mcause,mtval,mip), configuration (menvcfgwith writableSTCEandCDEbits), memory protection (two 64-bit PMP configuration registers at0x3A0/0x3A2pluspmpaddr0–pmpaddr15), and counters (mcycle,minstret,mcountinhibit). - Supervisor-level CSRs:
sstatus,sie,stvec,scounteren,scountinhibit,sscratch,sepc,scause,stval,sip,stimecmp(Sstc), andsatp. Thes*views are the architecturally required subsets of their machine counterparts. - Unprivileged CSRs:
cycle,time(mirrored from the CLINTmtime), andinstret, with reads from S/U mode gated bymcounteren/scounterenas the spec requires. - Access checking: a CSR access from an insufficient privilege level, a
write to a read-only CSR, or an access to an unimplemented address raises
an illegal-instruction exception, so OS-style privilege-separation code
behaves as on real hardware.
scountinhibitaccessibility is additionally gated bymenvcfg.CDE. - Trap entry and delegation: on an exception or interrupt the file
latches
xepc/xcause/xtvaland redirects the front-end to themtvecorstvechandler, choosing the destination privilege level viamedeleg/mideleg.mretandsretrestore the saved context (including themstatus.MPRVdrop onmretto a less-privileged mode). When several exceptions arrive in the same cycle the architecturally defined priority order picks the survivor. - Interrupts: machine timer and software interrupts come from the CLINT
(
mtimecmp/msip); the supervisor timer interrupt is generated by theSstccomparisonmtime >= stimecmp(enabled bymenvcfg.STCE); the supervisor software interrupt is raised by writingsip.SSIP. Pending bits are masked bymie/sieand themstatusglobal-enable bits, andmidelegroutes supervisor interrupts to S-mode. External-interrupt cause codes are defined, but no PLIC is integrated yet.
The core translates addresses with the Sv39 scheme (39-bit virtual, 3-level
page tables) whenever the effective privilege level is S or U and
satp.MODE = 8:
- ITLB / DTLB (
rtl/itlb.sv,rtl/dtlb.sv) — a 4-entry fully associative TLB in front of each cache. Entries are tagged with the VPN and thesatpASID and carry the PPN plus the R/W/X/U/A/D permission bits, so a hit checks permissions in the same cycle it translates. - Page-table walker (
rtl/mmu_ptw.sv) — a single hardware PTW shared by both TLBs. On a TLB miss it stalls the pipeline, walks the three page-table levels through the D-cache port (so PTEs are cached), refills the missing TLB entry, and re-runs the access. PTE permission violations — includingmstatus.MXR/mstatus.SUMchecks — surface as instruction / load / store page faults (causes 12 / 13 / 15) on the faulting instruction. - Effective privilege: loads and stores honour
mstatus.MPRV, i.e. whenMPRV=1in M-mode the LSU translates and protection-checks with the privilege inmstatus.MPP, while instruction fetches always use the true current mode. SFENCE.VMA— decoded in ID and committed in WB, it invalidates both TLBs so page-table updates andsatpswitches take effect.
- 16 PMP entries, programmed through two 64-bit configuration CSRs
(
0x3A0,0x3A2) andpmpaddr0–pmpaddr15.rtl/pmp_range.svinside the CSR file pre-decodes every entry into a physical-address range; supported address-matching modes areOFF,TOR, andNA4(NAPOTis decoded but not implemented yet). The lock (L) bit enforces entries on M-mode as well. - Checks on both paths:
rtl/pmp_check.svscreens every instruction fetch andrtl/pmp_check_lsu.svevery load/store/AMO, after translation, against the decoded ranges. A violation raises the matching access-fault exception — cause 1 (fetch), 5 (load), or 7 (store/AMO).
rtl/perf_counters.sv tracks microarchitectural statistics for reporting,
while the architectural counters live in the CSR file: mcycle and
minstret count in hardware (suppressible per-counter via
mcountinhibit / scountinhibit), and the unprivileged cycle / time /
instret views are exposed to lower privilege levels under
mcounteren / scounteren control.
- CLINT (
rtl/clint.sv) — the Core Local Interruptor, instantiated in the memory stage. It exposesMSIP(0x0000, machine software interrupt pending),MTIMECMP(0x4000), and the free-runningMTIME(0xBFF8), and raisestimer_irq/software_irqback into the CSR file. - MMIO routing: stores to the device window (e.g. the UART) are issued by
the memory stage and carried out over AXI4-Lite by the cache/MMIO
arbitration FSM in
toprather than to the cached memory array, so console output and CLINT register traffic stay coherent with the golden model during co-simulation.
The core implements the RV64A atomic instructions end-to-end:
- Decode:
main_decoderrecognises the atomic opcode (0101111) as its own instruction class and, from the fullfunc7, raises theatomic_lr/atomic_sc/atomic_amo_opflags, the acquire/release bits (aq/rl), and a 5-bitatomic_alu_op.alu_decoderadds ALU op101, which bypassesrs1so the effective address arrives unmodified at MEM. - AMO ALU (
rtl/amo_alu.sv): in the memory stage the read-modify-write atomics (amoswap,amoadd,amoxor,amoand,amoor,amomin[u],amomax[u], in both.wand.dwidths) combine the loaded value withrs2; the result is written back into the cache while the original loaded value is returned tord. Word variants sign-extend the 32-bit result. - LR/SC reservation (
rtl/dcache.sv):LRrecords a reservation over the accessed word / double-word;SCsucceeds only while that reservation is still valid — writing memory and returning0— and otherwise fails, returning1without writing. Any store landing in the reserved range clears the reservation. - Faults: an atomic that targets the MMIO or CLINT window raises an access
fault — cause
5(load / AMO) or7(store / SC) — instead of touching the device.
The core implements the Zifencei fences end-to-end:
- Decode:
main_decoderrecognises the fence opcode (0001111) and raisesfenceionly forFENCE.I(func3[0]); a plainFENCEretires as a NOP because the in-order pipeline already preserves memory ordering. - Dirty write-back:
FENCE.Imust make prior stores visible to instruction fetch, so in the memory stage the D-cache (rtl/dcache.sv) walks every set and way and writes each dirty line back to main memory. The cache FSM drives this multi-cycle drain through its dedicatedWB_FENCEI/WB_FENCEI_DONEstates. - I-cache invalidation: once the write-back walk completes, the I-cache's
valid bits are cleared (
invalidate_i), forcing the next fetch to re-read instructions from the now-coherent memory. - Front-end redirect & stall:
rtl/hazard_unit.svstalls IF/ID/EX and flushes ID + EX while the walk runs (fencei_wb_start); the fetch stage then redirects the PC to the instruction following the fence (pc_fencei_mem) so execution resumes with a freshly invalidated I-cache.
rtl/datapath.sv is the complete core: the five pipeline stages (each
registering its output boundary), the shared MMU page-table walker, the
effective-privilege / translation enable logic, plus the two controllers —
hazard_unit (stall / flush / forward policy, including MMU stalls) and
cache_fsm (cache-miss and AXI transaction control).
rtl/top.sv wraps the datapath with the memory-side plumbing: the
cache_data_transfer beat streamer, the AXI4-Lite master, and the
cache/MMIO arbitration FSM. Its external interface is the AXI4-Lite master
channel plus the axi_start_read_o / axi_start_write_o sideband strobes
that kick off the environment's AXI slave in the same cycle as the master
(the master holds AW/AR_VALID for a single cycle only).
The testbench wrapper rtl/test_env.sv wires top to the AXI4-Lite slave
and mem_simulated so that Verilator simulations can run complete programs
end-to-end.
rtl/ SystemVerilog source for the core, MMU/PMP, caches, CLINT, and AXI interface
test/tb/ C/C++ Verilator testbench, Dromajo cosim, trace/self-check helpers
test/tests/ Prebuilt test binaries
scripts/ Test catalog and flow helpers: ELF→disasm, disasm→mem, Spike trace comparison
tools/snippy/ Snippy configuration and test-generation script
tools/dromajo/ Dromajo golden-model submodule (built into libdromajo_cosim.a)
results/ Auto-populated with pass/fail and performance results
run_tests.py Top-level driver that verilates, builds, runs, and grades tests
Key test/tb/ helpers: tb_test_env.cpp (Verilator harness),
dromajo_cosim.cpp (Dromajo co-simulation bridge), check.c (self-check),
log_trace.c (commit-log emitter), report_perf.c (performance dump), and
pmem_write.c (UART/MMIO write sink).
Verification combines up to three independent checks: Dromajo
co-simulation that lock-steps the DUT against a golden model every retired
instruction, a self-check on the architectural end-state, and a
trace-compare against Spike's commit log. By default every applicable
check must agree for a run to be reported PASS; the test catalog marks a
handful of tests as cosim-only or no-tracecomp (see below), and the
--cosim-only / --no-cosim / --no-tracecomp flags let any run opt in or
out per check.
The catalog currently holds 485 tests across seven suites (37 AM,
83 riscv-arch-test, 102 riscv-tests physical, 86 riscv-tests virtual,
108 Snippy, 9 custom, 60 xv6), and the full matrix passes: every applicable
self-check and trace-compare reports PASS, with the remainder N/A
(random Snippy programs) or skipped by design.
- AM — Abstract Machine tests, small hand-written programs that cover ISA corner cases and elementary library routines from NJU-ProjectN/am-kernels.
- riscv-tests — the classic per-instruction regression suite from the
RISC-V community from
riscv-software-src/riscv-tests,
in two builds: the
rv-tests-pgroup runs the physical-memory (-p) binaries and therv-tests-vgroup the virtual-memory (-v) binaries, which boot into Sv39 paging and exercise the MMU on every access. Both cover the RV64UI base, RV64UM (M), and RV64UA (A— theamo*andlrsctests); the physical group additionally runs the RV64MI machine-mode tests (misaligned loads/stores,ma_addr,sbreak,scall,zicntr,instret_overflow) and the RV64SI supervisor-mode tests (s-csr,s-dirty,s-icache-alias,s-sbreak,s-scall). - riscv-arch-test — the official RISC-V architectural compliance suite from riscv/riscv-arch-test.
- Snippy — randomly generated programs produced by LLVM Snippy from
syntacore/snippy via
tools/snippy/snippy_gen_tests.pyusinglayout_base.yaml. Each snippet spans 10 functions arranged in 2 call-graph layers and is drawn from the full RV64I + M + A + C (+Zifencei) histogram — AMOs in both plain and.aqrlencodings; LR/SC are deliberately excluded because reservation-set invalidation is implementation-defined and legitimately differs between the RTL, Dromajo, and Spike. Per-instruction tests run 500 instructions, the four mix tests (load-store,compressed,atomics,simple) 1000. Every test is seeded with the CRC-32 of its own name, so regeneration is reproducible. - Custom — local hand-written regressions exercising the privileged and
interrupt features, including the CSR tests (
custom-csr-test,custom-csr-test-2),custom-ebreak-mret, the CLINT interrupt suite (custom-clint-msi-test,custom-clint-mti-test,custom-clint-msi-mti,custom-clint-mti-irq-regwrite), andcustom-rtthread, which boots the RT-Thread RTOS on the core. Theamgroup likewise addsam-yield-os, a cooperative-scheduling smoke test.
Test groups and runner names are defined in scripts/test_catalog.py, which
also tags the tests that are checked by Dromajo cosim only (e.g.
custom-rtthread, custom-clint-mti-irq-regwrite, am-yield-os) or that
skip Spike tracecomp (the remaining CLINT tests and custom-csr-test-2),
because their interrupt timing or random scheduling does not line up with a
single deterministic Spike trace.
- RTL simulator: Verilator. The wrapper in
test/tb/tb_test_env.cppdrivestest_env, toggles the clock, and holds reset for the first 100 cycles. Simulation terminates when the program reachesECALL/EBREAKor hitsMAX_SIM_TIME. - Golden reference (trace): Spike (
riscv-isa-sim) is launched byscripts/tracecomp.pywithspike -d --log-commitsand--isa=rv64imafv_zicntr_zihpm— a superset of what the core implements. Only instructions the DUT actually executes are compared, so the superset is safe. - Golden reference (co-simulation): Dromajo (
tools/dromajo, built aslibdromajo_cosim.a) runs alongside the RTL.test/tb/dromajo_cosim.cppinitialises the model with the same ELF, and the write-back stage calls thedromajo_stepDPI-C function once per retired instruction to advance the model and compare PC, instruction, destination register, andmstatus(with only theMPPfield masked). A separatedromajo_raise_traphook registers a pending interrupt so the model takes it before the next comparison; synchronous exceptions are left for the model to raise itself on the faulting instruction. Any mismatch fails the run and is surfaced viadromajo_has_error.
Dromajo is vendored as a git submodule and must be checked out and built once before co-simulation can run:
git submodule update --init --recursive
cd tools/dromajo && mkdir -p build && cd build
cmake -DCMAKE_BUILD_TYPE=Release .. && makeThis produces libdromajo_cosim.a and dromajo_cosim.h, which the test
driver links into the Verilator harness.
The DUT emits one line per retiring instruction through
test/tb/log_trace.c:
PC: 0x<pc>, INSTR: 0x<opcode>, REG x<rd>: 0x<value>, MEM 0x<addr>: 0x<data>
Register-write, memory-read, and memory-write fields are only printed when
the corresponding enable is asserted, giving the same shape Spike's
--log-commits produces and making a line-by-line diff meaningful. CSR
writes append their own field, c<addr>_<name>: 0x<value>, with the
recognised M-mode and S-mode CSRs printed by name. Atomic ops emit both a
register-write and a memory-write field on the same line; tracecomp.py
parses Spike's matching mem <addr> mem <addr> <value> form so the two
traces stay aligned (and so a zero-rd AMO is not mis-read as a hex value).
ECALL / EBREAK retire as tagged ecall / ebreak lines and normally
end the trace. Under -C (continue-after-trap) the trace instead ends at
the first self-loop jump (j .) or — for riscv-tests — at the committing
store to tohost (0x80001000), which is the same event that stops Spike,
so both traces cover the identical retirement window.
test/tb/check.c is called once the simulation finishes and inspects:
a0(return code):0→ PASS,1→ FAIL, anything else → undefined.mcause: reported by name for the full implemented set — environment calls from U/S/M and breakpoint for a standard exit, plus illegal instruction, instruction/load/store misalignment, access faults (PMP), page faults (MMU), and the machine/supervisor timer and software interrupt codes.- Branch-predictor counters (
branch_total,branch_mispred) are read out and accuracy is printed next to the pass/fail verdict, so predictor regressions show up immediately in the results log.
Snippy tests intentionally exercise random behaviour, so their self-check status is reported as Not Applicable — they rely on trace-compare instead.
scripts/tracecomp.py automates the reference run:
- Spawns Spike in interactive commit-log mode on the same ELF the RTL is executing.
- Streams Spike's log, stopping as soon as
ecall,ebreak, orexception trapappears — this keeps the two traces bounded to the same retirement window. - Strips interactive shell noise (
(spike),>>>>, banner lines) and parses each commit into a dict of PC / instruction / register / value / memory address / memory value. - Writes the normalised Spike log to
spike_log_trace/<test>-log-trace.log; the RTL log lands inlog_trace/<test>-log-trace.log. run_tests.pythen runsdiffbetween the two; the first mismatch (up to ten lines) is kept intemp.txtfor debugging, and the test is flaggedTracecomp: FAIL.
run_tests.py coverage flags must be paired with a test-running command,
for example python3 run_tests.py -a --coverage-all (or -s ... -cl,
-g ... -ct). The driver re-verilates with coverage enabled, runs the
selected tests, and then invokes verilator_coverage to merge and
annotate the per-test .dat files into coverage_annotated/. Per-test
coverage files are written to cov/ so that cache-parameter sweeps keep
their data separated.
Regular test runs keep successful Verilator stdout/stderr quiet so the
pass/fail output stays compact. Add -w / --warnings to any test-running
command when you want to see Verilator warnings and a Verilator warnings: <count> summary. The -L / --lint-module operation runs a standalone
lint-only check for one RTL module and always prints the lint output and
warning count.
Alongside the pass/fail log, each run records microarchitectural
statistics into results/perf_result.txt: retired-instruction count,
total cycles, IPC, stall breakdown by source (load-use, cache-miss,
branch-flush), and the branch-predictor hit rate. Combined with
-v this is what lets the repository track the performance
impact of cache geometry changes.
- Verilator (with
--traceand--coveragesupport) - A RISC-V GNU toolchain (for the scripts that manipulate ELF/disassembly)
- Spike (
riscv-isa-sim) for reference traces - Dromajo (the
tools/dromajosubmodule) built with CMake for co-simulation - Python 3, GCC, Make, CMake
The run_tests.py driver handles Verilator compilation, simulation, Dromajo
co-simulation, Spike comparison, and result aggregation. Invoked with no
operation flag it runs the default CLINT interrupt suite.
For normal -s, -g, and -a runs, the script uses the current saved
defaults from rtl/test_env.sv (BLOCK_WIDTH) and rtl/dcache.sv
(SET_COUNT and associativity N). A -v sweep temporarily overrides
BLOCK_WIDTH and SET_COUNT only — associativity N stays at its saved
default (4) because the D-cache is not parameterized for other widths — then
the original defaults are restored at the end of the run.
# Run the default CLINT interrupt suite (no operation flag)
python3 run_tests.py
# List every available test, grouped by suite with a per-group summary
python3 run_tests.py -l
# Run the full test matrix
python3 run_tests.py -a
# Run a single test and dump a waveform
python3 run_tests.py -s <test_name> -t
# Run only Dromajo co-simulation (skip self-check and Spike tracecomp)
python3 run_tests.py -s <test_name> --cosim-only
# Run without Dromajo co-simulation, keeping self-check and Spike tracecomp
python3 run_tests.py -s <test_name> --no-cosim
# Run without Spike trace logging/comparison (cosim + self-check still run)
python3 run_tests.py -s <test_name> --no-tracecomp
# Run every test past the ecall/ebreak trap instead of finishing on it
python3 run_tests.py -a -C
# Show Verilator warnings and the warning count during a test build
python3 run_tests.py -s <test_name> -w
# Run a group: am | rv-arch-test | rv-tests-p | rv-tests-v | snippy | custom
python3 run_tests.py -g rv-tests-v
# Lint one RTL module with Verilator
python3 run_tests.py -L <module_name>
# Sweep BLOCK_WIDTH and SET_COUNT (associativity held at N=4) for one test
python3 run_tests.py -s <test_name> -v
# Sweep the same cache parameters for a test group
python3 run_tests.py -g rv-tests-p -v
# Sweep the full test matrix
python3 run_tests.py -a -v
# Generate line + toggle coverage
python3 run_tests.py -a --coverage-all
# Remove generated build, trace, coverage, and prepared test artifacts
python3 run_tests.py -c
# Tidy the tree before a commit
python3 run_tests.py -pPass/fail summaries are written to results/result.txt and per-test
performance numbers (IPC, stall counts, etc.) to results/perf_result.txt.
For -v runs, both files also include cache-configuration headers showing
BLOCK_WIDTH, SET_COUNT, and associativity (fixed at 4) for each sweep
point.