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
55 changes: 0 additions & 55 deletions .github/workflows/ci-compile.yml

This file was deleted.

151 changes: 151 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
name: CI

on:
workflow_dispatch:
push:
paths:
- '**.hpp'
- '**.cpp'
- '**.h'
- '**.c'
- '**.S'
- '**CMakeLists.txt'
- '.github/workflows/**'
- 'conanfile.py'
pull_request:
paths:
- '**.hpp'
- '**.cpp'
- '**.h'
- '**.c'
- '**.S'
- '**CMakeLists.txt'
- '.github/workflows/**'
- 'conanfile.py'

jobs:
cpp-compliance:
name: C++ Std Compliance (C++${{ matrix.cpp_std }})
runs-on: ubuntu-24.04
strategy:
matrix:
cpp_std: [17, 20]
steps:
- uses: actions/checkout@v4

- name: Update submodules
run: git submodule update --init --recursive

- name: Cache Conan
uses: actions/cache@v4
with:
path: ~/.conan2
key: conan-${{ runner.os }}-unit-cpp${{ matrix.cpp_std }}-${{ hashFiles('conanfile.py') }}

- name: Install dependencies
run: |
sudo apt-get install -y g++ python3-pip cmake llvm-19-dev
pip3 install conan
cmake --version

- name: Configure
run: cmake --preset Release -B build -DCMAKE_CXX_STANDARD=${{ matrix.cpp_std }} -DWITH_LLVM=ON -DWITH_SYSTEM_LLVM=ON

- name: Build
run: cmake --build build -j

- name: Smoke test
run: ./build/riscv-sim -h

# NOTE: .so filename is tied to VERSION in top-level CMakeLists.txt - update both together
- name: Upload binary
if: matrix.cpp_std == 20
uses: actions/upload-artifact@v4
with:
name: riscv-sim
path: |
build/riscv-sim
build/libdbt-rise-riscv.so.2.1.0

pmp-tests:
name: PMP Functional Tests
runs-on: ubuntu-24.04
needs: cpp-compliance
steps:
- uses: actions/checkout@v4

- name: Install RISC-V toolchain
run: sudo apt-get install -y gcc-riscv64-unknown-elf

- name: Download riscv-sim binary
uses: actions/download-artifact@v4
with:
name: riscv-sim

- name: Make binary executable
run: chmod +x riscv-sim

- name: Build PMP CSR test firmware
run: |
riscv64-unknown-elf-gcc -nostdlib -march=rv64gc -mabi=lp64 \
-Wl,-Ttext=0x10000,--no-dynamic-linker \
-o pmp_csr_test \
contrib/fw/pmp-csr-test/pmp_csr_test.S

- name: rv64gc_m CSR test - interp (no PMP, expect exit 2)
run: |
LD_LIBRARY_PATH=. ./riscv-sim -f pmp_csr_test --isa rv64gc_m --backend interp || rc=$?
[ "${rc:-0}" -eq 2 ]

- name: rv64gc_mp CSR test - interp (with PMP)
run: LD_LIBRARY_PATH=. ./riscv-sim -f pmp_csr_test --isa rv64gc_mp --backend interp

- name: Build PMP enforcement test firmware
run: |
riscv64-unknown-elf-gcc -nostdlib -march=rv64gc -mabi=lp64 \
-Wl,-Ttext=0x10000,--no-dynamic-linker \
-o pmp_enforce_test \
contrib/fw/pmp-enforce-test/pmp_enforce_test.S

- name: rv64gc_mp enforcement test - interp
run: LD_LIBRARY_PATH=. ./riscv-sim -f pmp_enforce_test --isa rv64gc_mp --backend interp

- name: Build PMP shift test firmware
run: |
riscv64-unknown-elf-gcc -nostdlib -march=rv64gc -mabi=lp64 \
-Wl,-Ttext=0x10000,--no-dynamic-linker \
-o pmp_shift_test \
contrib/fw/pmp-shift-test/pmp_shift_test.S

- name: rv64gc_mp shift test - interp
run: LD_LIBRARY_PATH=. ./riscv-sim -f pmp_shift_test --isa rv64gc_mp --backend interp

- name: Build PMP upper-cfg test firmware
run: |
riscv64-unknown-elf-gcc -nostdlib -march=rv64gc -mabi=lp64 \
-Wl,-Ttext=0x10000,--no-dynamic-linker \
-o pmp_upper_cfg_test \
contrib/fw/pmp-upper-cfg-test/pmp_upper_cfg_test.S

- name: rv64gc_mp upper-cfg test - interp
run: LD_LIBRARY_PATH=. ./riscv-sim -f pmp_upper_cfg_test --isa rv64gc_mp --backend interp

- name: Build PMP cfg2 test firmware
run: |
riscv64-unknown-elf-gcc -nostdlib -march=rv64gc -mabi=lp64 \
-Wl,-Ttext=0x10000,--no-dynamic-linker \
-o pmp_cfg2_test \
contrib/fw/pmp-cfg2-test/pmp_cfg2_test.S

- name: rv64gc_mp cfg2 test - interp
run: LD_LIBRARY_PATH=. ./riscv-sim -f pmp_cfg2_test --isa rv64gc_mp --backend interp

- name: Build PMP TOR test firmware
run: |
riscv64-unknown-elf-gcc -nostdlib -march=rv64gc -mabi=lp64 \
-Wl,-Ttext=0x10000,--no-dynamic-linker \
-o pmp_tor_test \
contrib/fw/pmp-tor-test/pmp_tor_test.S

- name: rv64gc_mp TOR test - interp
run: LD_LIBRARY_PATH=. ./riscv-sim -f pmp_tor_test --isa rv64gc_mp --backend interp
18 changes: 18 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@ if(OPTIMIZE_FOR_NATIVE AND COMPILER_SUPPORTS_MARCH_NATIVE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
endif()

option(WITH_SYSTEM_LLVM "Use system-installed LLVM (llvm-dev from apt) instead of Conan-built" OFF)
set(LLVM_MAJOR_VERSION "19" CACHE STRING "System LLVM major version when WITH_SYSTEM_LLVM=ON")
if(WITH_SYSTEM_LLVM)
set(LLVM_DIR "/usr/lib/llvm-${LLVM_MAJOR_VERSION}/lib/cmake/llvm" CACHE PATH "" FORCE)
find_package(LLVM REQUIRED CONFIG BYPASS_PROVIDER)
message(STATUS "Found system LLVM ${LLVM_VERSION}")
if(NOT TARGET llvm-core::llvm-core)
add_library(llvm-core::llvm-core INTERFACE IMPORTED GLOBAL)
target_include_directories(llvm-core::llvm-core INTERFACE ${LLVM_INCLUDE_DIRS})
target_compile_definitions(llvm-core::llvm-core INTERFACE ${LLVM_DEFINITIONS})
target_link_libraries(llvm-core::llvm-core INTERFACE LLVM-${LLVM_VERSION_MAJOR})
endif()
if(NOT WITH_LLVM)
set(WITH_LLVM ON CACHE BOOL "Build LLVM backend" FORCE)
endif()
endif()

include(FetchContent)
if(NOT TARGET dbt-rise-core)
FetchContent_Declare(
Expand Down Expand Up @@ -70,6 +87,7 @@ set(LIB_SOURCES
src/vm/interp/vm_rv64i.cpp
src/vm/interp/vm_rv64imac.cpp
src/vm/interp/vm_rv64gc.cpp
src/vm/interp/vm_rv64gc_mp.cpp
src/vm/interp/vm_rv64gcv.cpp
src/iss/debugger/csr_names.cpp
src/iss/semihosting/semihosting.cpp
Expand Down
29 changes: 29 additions & 0 deletions contrib/fw/pmp-cfg2-test/pmp_cfg2_test.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Verify pmpcfg2 (entries 8-15) is accessible as a CSR on RV64.
// On RV64 valid pmpcfg CSRs are pmpcfg0 (entries 0-7) and pmpcfg2 (entries 8-15).
//
// Pass: pmpcfg2 readback == written value -> j . (exit 0)
// Fail: pmpcfg2 write traps OR readback wrong -> semihosting SYS_EXIT (exit 2)

.section .text
.globl _start
_start:
la t0, fail
csrw mtvec, t0

// 0x18 per byte = NAPOT mode, no RWX, no lock; survives 0x9f byte mask
li t0, 0x1818181818181818
csrw pmpcfg2, t0
csrr t1, pmpcfg2
bne t0, t1, fail

j . // pass: ISS detects j . and exits 0

fail:
li a0, 0x18
.option push
.option norvc
slli zero, zero, 0x1f
ebreak
srai zero, zero, 7
.option pop
j .
32 changes: 32 additions & 0 deletions contrib/fw/pmp-csr-test/pmp_csr_test.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Verify pmpaddr0 is accessible as a CSR (no illegal-instruction trap on read/write).
// Pass: j . detected as JUMP_TO_SELF by ISS, exits 0.
// Fail: semihosting SYS_EXIT sequence, ISS exits non-zero.

.section .text
.globl _start
_start:
// Install fail handler: any unexpected trap before the ecall path means the CSR
// is not available or behaves incorrectly
la t0, fail
csrw mtvec, t0

// Write a distinctive value to pmpaddr0 and verify the readback matches
li t0, 0xDEAD
csrw pmpaddr0, t0
csrr t1, pmpaddr0
bne t0, t1, fail

j . // pass: ISS detects j . and exits 0

fail:
// Semihosting SYS_EXIT: the ISS checks for slli+ebreak+srai and intercepts
// before dispatching to mtvec. Must use 4-byte ebreak (not c.ebreak) so the
// check lands at the correct offsets (-4 and +4 from the ebreak address).
li a0, 0x18 // SYS_EXIT
.option push
.option norvc // force 4-byte ebreak (0x00100073), not 2-byte c.ebreak
slli zero, zero, 0x1f
ebreak
srai zero, zero, 7
.option pop
j . // fallback if semihosting is not configured
52 changes: 52 additions & 0 deletions contrib/fw/pmp-enforce-test/pmp_enforce_test.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// PMP enforcement test - entry 0 (pmpaddr0 + pmpcfg0 byte 0).
// Tests that pmpcfg naming bugs 1+2 in pmp.h are fixed:
// Bug 1: read_pmpcfg reads pmpaddr[] instead of pmpcfg[]
// Bug 2: write_pmpcfg writes pmpaddr[] instead of pmpcfg[]
//
// Pass: PMP denies load -> load fault fires -> j . (exit 0)
// Fail: any CSR trap, OR load succeeds without fault -> semihosting SYS_EXIT (exit 2)

.macro semihosting_fail
li a0, 0x18
.option push
.option norvc
slli zero, zero, 0x1f
ebreak
srai zero, zero, 7
.option pop
j .
.endm

.section .text
.globl _start
_start:
// phase1_trap: CSR write trapped - PMP not available
la t0, phase1_trap
csrw mtvec, t0

// Configure pmpaddr0 = test_word >> 2 (NA4: covers exactly 4 bytes at test_word)
la t5, test_word
srli t1, t5, 2
csrw pmpaddr0, t1

// pmpcfg0 byte 0 = 0x90 = NA4 (0x10) | L (0x80), no R/W/X permissions
li t1, 0x90
csrw pmpcfg0, t1

// phase2_trap: load fault fired - PMP enforcement active = PASS
la t0, phase2_trap
csrw mtvec, t0

lw t1, 0(t5) // should fault: PMP denies read

semihosting_fail // no fault: enforcement did not fire -> FAIL

phase1_trap:
semihosting_fail // CSR trapped: PMP unavailable -> FAIL

phase2_trap:
j . // load denied as expected -> PASS

.align 2
test_word:
.word 0xCAFECAFE
Loading
Loading