aboutsummaryrefslogtreecommitdiff
path: root/rv64cpu/tb/unit/tb_csr.cc
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-11-07 03:30:17 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-11-07 03:31:03 +0800
commit2a4c290c55b683352a16747432a7fc4635597cc6 (patch)
treea150caea2ade12af4c5dcd70f09bacd3318f4d43 /rv64cpu/tb/unit/tb_csr.cc
parente0948e3fc10029ba4bb4d48d7b5daae94fe575b9 (diff)
downloadrcc-project-2a4c290c55b683352a16747432a7fc4635597cc6.tar.gz
rcc-project-2a4c290c55b683352a16747432a7fc4635597cc6.tar.xz
refactor: reset software and test directories
Diffstat (limited to 'rv64cpu/tb/unit/tb_csr.cc')
-rw-r--r--rv64cpu/tb/unit/tb_csr.cc770
1 files changed, 0 insertions, 770 deletions
diff --git a/rv64cpu/tb/unit/tb_csr.cc b/rv64cpu/tb/unit/tb_csr.cc
deleted file mode 100644
index e86ad9b..0000000
--- a/rv64cpu/tb/unit/tb_csr.cc
+++ /dev/null
@@ -1,770 +0,0 @@
-// Richard CPU
-// Copyright (C) 2026 Richard Qin
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-// File path: tb/unit/tb_csr.cc
-// Description: CSR unit testbench
-
-#include <array>
-#include <cassert>
-#include <cstdint>
-#include <iostream>
-#include <verilated.h>
-
-#include "Vcsr_unit.h"
-
-namespace {
-constexpr uint32_t CSR_OP_WRITE = 0b001;
-constexpr uint32_t CSR_OP_SET = 0b010;
-constexpr uint32_t CSR_OP_CLEAR = 0b011;
-
-constexpr uint16_t CSR_MSTATUS = 0x300;
-constexpr uint16_t CSR_MISA = 0x301;
-constexpr uint16_t CSR_MEDELEG = 0x302;
-constexpr uint16_t CSR_MIDELEG = 0x303;
-constexpr uint16_t CSR_MIE = 0x304;
-constexpr uint16_t CSR_MTVEC = 0x305;
-constexpr uint16_t CSR_MCOUNTEREN= 0x306;
-constexpr uint16_t CSR_MENVCFG = 0x30A;
-constexpr uint16_t CSR_MSTATEEN0 = 0x30C;
-constexpr uint16_t CSR_MCOUNTINHIBIT = 0x320;
-constexpr uint16_t CSR_MSCRATCH = 0x340;
-constexpr uint16_t CSR_MEPC = 0x341;
-constexpr uint16_t CSR_MCAUSE = 0x342;
-constexpr uint16_t CSR_MTVAL = 0x343;
-constexpr uint16_t CSR_MIP = 0x344;
-constexpr uint16_t CSR_MCYCLE = 0xB00;
-constexpr uint16_t CSR_MINSTRET = 0xB02;
-constexpr uint16_t CSR_SSTATUS = 0x100;
-constexpr uint16_t CSR_SIE = 0x104;
-constexpr uint16_t CSR_STVEC = 0x105;
-constexpr uint16_t CSR_SCOUNTEREN= 0x106;
-constexpr uint16_t CSR_SENVCFG = 0x10A;
-constexpr uint16_t CSR_SSCRATCH = 0x140;
-constexpr uint16_t CSR_SEPC = 0x141;
-constexpr uint16_t CSR_SCAUSE = 0x142;
-constexpr uint16_t CSR_STVAL = 0x143;
-constexpr uint16_t CSR_SIP = 0x144;
-constexpr uint16_t CSR_SATP = 0x180;
-constexpr uint16_t CSR_CYCLE = 0xC00;
-constexpr uint16_t CSR_INSTRET = 0xC02;
-constexpr uint16_t CSR_FFLAGS = 0x001;
-constexpr uint16_t CSR_FRM = 0x002;
-constexpr uint16_t CSR_FCSR = 0x003;
-constexpr uint16_t CSR_MVENDORID = 0xF11;
-
-constexpr uint64_t EXC_INST_ILLEGAL = 2ULL;
-constexpr uint64_t EXC_ECALL_FROM_U = 8ULL;
-constexpr uint64_t EXC_ECALL_FROM_S = 9ULL;
-constexpr uint64_t EXC_LOAD_FAULT = 5ULL;
-constexpr uint64_t EXC_STORE_FAULT = 7ULL;
-
-constexpr uint64_t INTERRUPT_BIT = 1ULL << 63;
-constexpr uint64_t INTERRUPT_M_SOFTWARE = 3ULL;
-constexpr uint64_t INTERRUPT_M_TIMER = 7ULL;
-constexpr uint64_t INTERRUPT_M_EXTERNAL = 11ULL;
-constexpr uint64_t INTERRUPT_S_SOFTWARE = 1ULL;
-constexpr uint64_t INTERRUPT_S_TIMER = 5ULL;
-constexpr uint64_t INTERRUPT_S_EXTERNAL = 9ULL;
-
-constexpr int MSTATUS_MIE_BIT = 3;
-constexpr int MSTATUS_MPIE_BIT = 7;
-constexpr int MSTATUS_MPP_LO = 11;
-constexpr int MSTATUS_MPP_HI = 12;
-constexpr int MSTATUS_TVM_BIT = 20;
-constexpr int MSTATUS_TW_BIT = 21;
-constexpr int MSTATUS_TSR_BIT = 22;
-constexpr int MSTATUS_UXL_LO = 32;
-constexpr int MSTATUS_SXL_LO = 34;
-constexpr int SSTATUS_SIE_BIT = 1;
-constexpr int SSTATUS_SPIE_BIT = 5;
-constexpr int SSTATUS_SPP_BIT = 8;
-constexpr int MCOUNTINHIBIT_CY_BIT = 0;
-constexpr int MCOUNTINHIBIT_IR_BIT = 2;
-constexpr int STATEEN0_ENVCFG_BIT = 62;
-
-constexpr uint64_t STATUS_XLEN_64 = 2ULL;
-constexpr uint64_t MSTATUS_RESET_VALUE = (STATUS_XLEN_64 << MSTATUS_UXL_LO) |
- (STATUS_XLEN_64 << MSTATUS_SXL_LO);
-constexpr uint64_t SSTATUS_RESET_VALUE = (STATUS_XLEN_64 << MSTATUS_UXL_LO);
-
-constexpr uint64_t PRIV_ENCODE_U = 0ULL;
-constexpr uint64_t PRIV_ENCODE_S = 1ULL;
-constexpr uint64_t PRIV_ENCODE_M = 3ULL;
-constexpr uint64_t MEDELEG_WRITABLE_MASK =
- (1ULL << EXC_ECALL_FROM_U) |
- (1ULL << EXC_ECALL_FROM_S) |
- (1ULL << EXC_LOAD_FAULT) |
- (1ULL << EXC_STORE_FAULT);
-constexpr uint64_t MEDELEG_READ_ONLY_ONE_MASK = (1ULL << EXC_INST_ILLEGAL);
-constexpr uint64_t MIDELEG_WRITABLE_MASK =
- (1ULL << INTERRUPT_S_SOFTWARE) |
- (1ULL << INTERRUPT_S_TIMER) |
- (1ULL << INTERRUPT_S_EXTERNAL);
-constexpr uint64_t MCOUNTINHIBIT_WRITABLE_MASK =
- (1ULL << MCOUNTINHIBIT_CY_BIT) |
- (1ULL << MCOUNTINHIBIT_IR_BIT) |
- (0x1FFFFFFFULL << 3);
-constexpr uint64_t COUNTEREN_WRITABLE_MASK = 0xFFFFFFFFULL;
-}
-
-void tick(Vcsr_unit* dut) {
- dut->clk = 0;
- dut->eval();
- dut->clk = 1;
- dut->eval();
- dut->clk = 0;
- dut->eval();
-}
-
-void drive_defaults(Vcsr_unit* dut) {
- dut->clk = 0;
- dut->rst = 0;
- dut->csr_req = 0;
- dut->csr_op = 0;
- dut->csr_addr = 0;
- dut->csr_rs1_data = 0;
- dut->trap_ctrl_trigger = 0;
- dut->trap_ctrl_to_s_mode = 0;
- dut->trap_ctrl_mepc = 0;
- dut->trap_ctrl_mcause = 0;
- dut->trap_ctrl_mtval = 0;
- dut->trap_ctrl_mret_en = 0;
- dut->trap_ctrl_sret_en = 0;
- dut->m_timer_interrupt = 0;
- dut->m_software_interrupt = 0;
- dut->m_external_interrupt = 0;
- dut->s_timer_interrupt = 0;
- dut->s_software_interrupt = 0;
- dut->s_external_interrupt = 0;
-}
-
-void apply_reset(Vcsr_unit* dut) {
- dut->rst = 1;
- tick(dut);
- dut->rst = 0;
- tick(dut);
-}
-
-void clear_trap_lines(Vcsr_unit* dut) {
- dut->trap_ctrl_trigger = 0;
- dut->trap_ctrl_to_s_mode = 0;
- dut->trap_ctrl_mepc = 0;
- dut->trap_ctrl_mcause = 0;
- dut->trap_ctrl_mtval = 0;
- dut->trap_ctrl_mret_en = 0;
- dut->trap_ctrl_sret_en = 0;
-}
-
-void trigger_trap(
- Vcsr_unit* dut,
- uint64_t mepc,
- uint64_t mcause,
- uint64_t mtval,
- bool to_s_mode
-) {
- dut->trap_ctrl_trigger = 1;
- dut->trap_ctrl_mepc = mepc;
- dut->trap_ctrl_mcause = mcause;
- dut->trap_ctrl_mtval = mtval;
- dut->trap_ctrl_to_s_mode = to_s_mode ? 1 : 0;
- tick(dut);
- dut->trap_ctrl_trigger = 0;
- dut->trap_ctrl_mepc = 0;
- dut->trap_ctrl_mcause = 0;
- dut->trap_ctrl_mtval = 0;
- dut->trap_ctrl_to_s_mode = 0;
-}
-
-void trigger_interrupt(Vcsr_unit* dut, uint64_t mepc, uint64_t interrupt_cause) {
- trigger_trap(dut, mepc, (INTERRUPT_BIT | interrupt_cause), 0ULL, false);
-}
-
-template <typename Fn>
-void run_csr_scenario(Vcsr_unit* dut, const char* name, Fn&& body) {
- clear_trap_lines(dut);
- std::cout << "[RUN] CSR scenario: " << name << std::endl;
- body(dut);
- std::cout << "[PASS] CSR scenario: " << name << std::endl;
-}
-
-uint64_t read_csr(Vcsr_unit* dut, uint16_t addr) {
- dut->csr_addr = addr;
- dut->csr_req = 0;
- dut->csr_op = 0;
- dut->csr_rs1_data = 0;
- dut->eval();
- return dut->csr_rdata;
-}
-
-void csr_op(Vcsr_unit* dut, uint16_t addr, uint32_t op, uint64_t data) {
- dut->csr_addr = addr;
- dut->csr_op = op;
- dut->csr_rs1_data = data;
- dut->csr_req = 1;
- tick(dut);
- dut->csr_req = 0;
- dut->csr_op = 0;
- dut->csr_rs1_data = 0;
- dut->csr_addr = 0;
-}
-
-void advance_cycles(Vcsr_unit* dut, int cycles) {
- for (int i = 0; i < cycles; ++i) {
- tick(dut);
- }
-}
-
-void force_priv_mode_via_mret(Vcsr_unit* dut, uint64_t encoded_mode) {
- if (dut->csr_priv_mode != PRIV_ENCODE_M) {
- trigger_trap(dut, 0x100ULL, EXC_INST_ILLEGAL, 0ULL, false);
- }
- uint64_t mstatus = read_csr(dut, CSR_MSTATUS);
- mstatus &= ~(0x3ULL << MSTATUS_MPP_LO);
- mstatus |= (encoded_mode << MSTATUS_MPP_LO);
- csr_op(dut, CSR_MSTATUS, CSR_OP_WRITE, mstatus);
- dut->trap_ctrl_mret_en = 1;
- tick(dut);
- dut->trap_ctrl_mret_en = 0;
-}
-
-uint64_t arbitrate_trap_priority(
- bool ext_external,
- bool ext_software,
- bool ext_timer,
- bool has_sync_exception,
- uint64_t sync_cause,
- bool& chose_interrupt
-) {
- if (ext_external) {
- chose_interrupt = true;
- return INTERRUPT_BIT | INTERRUPT_M_EXTERNAL;
- }
- if (ext_software) {
- chose_interrupt = true;
- return INTERRUPT_BIT | INTERRUPT_M_SOFTWARE;
- }
- if (ext_timer) {
- chose_interrupt = true;
- return INTERRUPT_BIT | INTERRUPT_M_TIMER;
- }
- chose_interrupt = false;
- return has_sync_exception ? sync_cause : 0ULL;
-}
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Vcsr_unit* dut = new Vcsr_unit;
-
- drive_defaults(dut);
- apply_reset(dut);
- clear_trap_lines(dut);
-
- std::cout << "----------------------------------\n";
- std::cout << " CSR Unit Testbench\n";
- std::cout << "----------------------------------\n";
-
- // Reset sanity
- assert(read_csr(dut, CSR_MSTATUS) == MSTATUS_RESET_VALUE);
- assert(read_csr(dut, CSR_MIE) == 0);
- assert(read_csr(dut, CSR_MEPC) == 0);
- assert(read_csr(dut, CSR_SSTATUS) == SSTATUS_RESET_VALUE);
- assert(read_csr(dut, CSR_SATP) == 0);
- std::cout << "[PASS] Reset clears CSR state" << std::endl;
-
- // CSRRW basic write
- csr_op(dut, CSR_MTVEC, CSR_OP_WRITE, 0x1020ULL);
- assert(read_csr(dut, CSR_MTVEC) == 0x1020ULL);
- csr_op(dut, CSR_MSCRATCH, CSR_OP_WRITE, 0xCAFEBABEULL);
- assert(read_csr(dut, CSR_MSCRATCH) == 0xCAFEBABEULL);
- std::cout << "[PASS] CSRRW updates mutable CSRs" << std::endl;
-
- // CSRRS / CSRRC behavior
- csr_op(dut, CSR_MIE, CSR_OP_WRITE, 0);
- csr_op(dut, CSR_MIE, CSR_OP_SET, (1ULL << INTERRUPT_M_SOFTWARE) | (1ULL << INTERRUPT_M_TIMER));
- assert(read_csr(dut, CSR_MIE) == ((1ULL << INTERRUPT_M_SOFTWARE) | (1ULL << INTERRUPT_M_TIMER)));
- csr_op(dut, CSR_MIE, CSR_OP_CLEAR, (1ULL << INTERRUPT_M_SOFTWARE));
- assert(read_csr(dut, CSR_MIE) == (1ULL << INTERRUPT_M_TIMER));
- csr_op(dut, CSR_MIE, CSR_OP_SET, 0x0ULL);
- assert(read_csr(dut, CSR_MIE) == (1ULL << INTERRUPT_M_TIMER));
- std::cout << "[PASS] CSRRS/CSRRC honor masks and ignore zero data" << std::endl;
-
- csr_op(
- dut,
- CSR_MSTATUS,
- CSR_OP_WRITE,
- (1ULL << MSTATUS_TVM_BIT) | (1ULL << MSTATUS_TW_BIT) | (1ULL << MSTATUS_TSR_BIT)
- );
- assert((read_csr(dut, CSR_MSTATUS) & ((1ULL << MSTATUS_TVM_BIT) |
- (1ULL << MSTATUS_TW_BIT) |
- (1ULL << MSTATUS_TSR_BIT))) == 0ULL);
- std::cout << "[PASS] Unsupported mstatus trap-control bits are read-zero" << std::endl;
-
- // Hardwired WARL CSRs may ignore writes without taking an illegal-instruction trap.
- const uint64_t misa_ro_snapshot = read_csr(dut, CSR_MISA);
- csr_op(dut, CSR_MISA, CSR_OP_WRITE, 0ULL);
- assert(read_csr(dut, CSR_MISA) == misa_ro_snapshot);
- std::cout << "[PASS] Hardwired WARL CSRs ignore unsupported writes" << std::endl;
-
- // Machine counters are writable through machine CSR addresses; user aliases are read-only.
- csr_op(dut, CSR_MCYCLE, CSR_OP_WRITE, 0x40ULL);
- assert(read_csr(dut, CSR_MCYCLE) == 0x40ULL);
- csr_op(dut, CSR_CYCLE, CSR_OP_WRITE, 0x100ULL);
- assert(read_csr(dut, CSR_MCYCLE) == 0x41ULL);
- advance_cycles(dut, 5);
- assert(read_csr(dut, CSR_MCYCLE) == 0x46ULL);
- const uint64_t instret_before = read_csr(dut, CSR_MINSTRET);
- advance_cycles(dut, 3);
- assert(read_csr(dut, CSR_MINSTRET) == instret_before + 3);
- std::cout << "[PASS] Cycle/instret counters tick correctly" << std::endl;
-
- csr_op(dut, CSR_MCOUNTINHIBIT, CSR_OP_WRITE, ~0ULL);
- assert(read_csr(dut, CSR_MCOUNTINHIBIT) == MCOUNTINHIBIT_WRITABLE_MASK);
- const uint64_t inhibited_cycle = read_csr(dut, CSR_MCYCLE);
- const uint64_t inhibited_instret = read_csr(dut, CSR_MINSTRET);
- advance_cycles(dut, 3);
- assert(read_csr(dut, CSR_MCYCLE) == inhibited_cycle);
- assert(read_csr(dut, CSR_MINSTRET) == inhibited_instret);
- csr_op(dut, CSR_MCOUNTINHIBIT, CSR_OP_WRITE, 0ULL);
- advance_cycles(dut, 1);
- assert(read_csr(dut, CSR_MCYCLE) == inhibited_cycle + 1);
- assert(read_csr(dut, CSR_MINSTRET) == inhibited_instret + 1);
- std::cout << "[PASS] mcountinhibit controls machine counters" << std::endl;
-
- csr_op(dut, CSR_MCOUNTEREN, CSR_OP_WRITE, ~0ULL);
- assert(read_csr(dut, CSR_MCOUNTEREN) == COUNTEREN_WRITABLE_MASK);
- csr_op(dut, CSR_SCOUNTEREN, CSR_OP_WRITE, ~0ULL);
- assert(read_csr(dut, CSR_SCOUNTEREN) == COUNTEREN_WRITABLE_MASK);
- std::cout << "[PASS] counteren CSRs keep only implemented counter bits" << std::endl;
-
- // Trap should freeze minstret for that cycle
- uint64_t mstatus_cfg = read_csr(dut, CSR_MSTATUS) | (1ULL << MSTATUS_MIE_BIT);
- csr_op(dut, CSR_MSTATUS, CSR_OP_WRITE, mstatus_cfg);
- const uint64_t instret_pre_trap = read_csr(dut, CSR_MINSTRET);
- trigger_trap(dut, 0xDEADULL, EXC_INST_ILLEGAL, 0xDEADULL, false);
- assert(read_csr(dut, CSR_MINSTRET) == instret_pre_trap);
- advance_cycles(dut, 1);
- assert(read_csr(dut, CSR_MINSTRET) == instret_pre_trap + 1);
- std::cout << "[PASS] Trap cycle does not increment minstret" << std::endl;
-
- // Trap bookkeeping for illegal instruction
- assert(read_csr(dut, CSR_MEPC) == 0xDEADULL);
- assert(read_csr(dut, CSR_MCAUSE) == EXC_INST_ILLEGAL);
- assert(read_csr(dut, CSR_MTVAL) == 0xDEADULL);
- const uint64_t mstatus_after_trap = read_csr(dut, CSR_MSTATUS);
- assert(((mstatus_after_trap >> MSTATUS_MIE_BIT) & 1ULL) == 0ULL);
- assert(((mstatus_after_trap >> MSTATUS_MPIE_BIT) & 1ULL) == 1ULL);
- assert(((mstatus_after_trap >> MSTATUS_MPP_LO) & 0x3ULL) == PRIV_ENCODE_M);
- std::cout << "[PASS] Illegal trap updates mepc/mcause/mtval/mstatus" << std::endl;
-
- // mret restore flow
- dut->trap_ctrl_mret_en = 1;
- tick(dut);
- dut->trap_ctrl_mret_en = 0;
- const uint64_t mstatus_after_mret_reset = read_csr(dut, CSR_MSTATUS);
- assert(((mstatus_after_mret_reset >> MSTATUS_MIE_BIT) & 1ULL) == 1ULL);
- assert(((mstatus_after_mret_reset >> MSTATUS_MPIE_BIT) & 1ULL) == 1ULL);
- assert(((mstatus_after_mret_reset >> MSTATUS_MPP_LO) & 0x3ULL) == PRIV_ENCODE_U);
- std::cout << "[PASS] mret restores interrupt enable bits" << std::endl;
-
- // sret restore flow
- uint64_t sstatus_cfg = (1ULL << SSTATUS_SIE_BIT) | (1ULL << SSTATUS_SPP_BIT);
- csr_op(dut, CSR_SSTATUS, CSR_OP_WRITE, sstatus_cfg);
- dut->trap_ctrl_sret_en = 1;
- tick(dut);
- dut->trap_ctrl_sret_en = 0;
- const uint64_t sstatus_after_sret_reset = read_csr(dut, CSR_SSTATUS);
- assert(((sstatus_after_sret_reset >> SSTATUS_SIE_BIT) & 1ULL) == 0ULL);
- assert(((sstatus_after_sret_reset >> SSTATUS_SPIE_BIT) & 1ULL) == 1ULL);
- assert(((sstatus_after_sret_reset >> SSTATUS_SPP_BIT) & 1ULL) == 0ULL);
- std::cout << "[PASS] sret updates supervisor status bits" << std::endl;
-
- // Ecall causes depend on current privilege mode
- force_priv_mode_via_mret(dut, PRIV_ENCODE_S);
- trigger_trap(dut, 0x600ULL, EXC_ECALL_FROM_S, 0ULL, false);
- assert(read_csr(dut, CSR_MCAUSE) == EXC_ECALL_FROM_S);
- assert(read_csr(dut, CSR_MTVAL) == 0ULL);
- assert(read_csr(dut, CSR_MEPC) == 0x600ULL);
- assert(((read_csr(dut, CSR_MSTATUS) >> MSTATUS_MPP_LO) & 0x3ULL) == PRIV_ENCODE_S);
-
- force_priv_mode_via_mret(dut, PRIV_ENCODE_U);
- trigger_trap(dut, 0x700ULL, EXC_ECALL_FROM_U, 0ULL, false);
- assert(read_csr(dut, CSR_MCAUSE) == EXC_ECALL_FROM_U);
- assert(read_csr(dut, CSR_MTVAL) == 0ULL);
- assert(read_csr(dut, CSR_MEPC) == 0x700ULL);
- assert(((read_csr(dut, CSR_MSTATUS) >> MSTATUS_MPP_LO) & 0x3ULL) == PRIV_ENCODE_U);
- std::cout << "[PASS] Ecall encodes mcause per privilege" << std::endl;
-
- // Trap delegation from U to S via medeleg
- csr_op(dut, CSR_MEDELEG, CSR_OP_WRITE, 0ULL);
- csr_op(dut, CSR_MEDELEG, CSR_OP_SET, (1ULL << EXC_ECALL_FROM_U));
- csr_op(dut, CSR_STVEC, CSR_OP_WRITE, 0x4000ULL);
- assert(read_csr(dut, CSR_MEDELEG) & (1ULL << EXC_ECALL_FROM_U));
- force_priv_mode_via_mret(dut, PRIV_ENCODE_U);
- assert(dut->csr_priv_mode == PRIV_ENCODE_U);
- trigger_trap(dut, 0x900ULL, EXC_ECALL_FROM_U, 0ULL, true);
- assert(dut->csr_trap_to_s_mode);
- assert(dut->csr_trap_vector == 0x4000ULL);
- assert(read_csr(dut, CSR_SEPC) == 0x900ULL);
- assert(read_csr(dut, CSR_SCAUSE) == EXC_ECALL_FROM_U);
- assert(read_csr(dut, CSR_STVAL) == 0ULL);
- assert(dut->csr_priv_mode == PRIV_ENCODE_S);
- std::cout << "[PASS] medeleg routes ecall to supervisor" << std::endl;
- force_priv_mode_via_mret(dut, PRIV_ENCODE_M);
-
- csr_op(dut, CSR_MEDELEG, CSR_OP_WRITE, ~0ULL);
- assert((read_csr(dut, CSR_MEDELEG) & MEDELEG_WRITABLE_MASK) == MEDELEG_WRITABLE_MASK);
- assert((read_csr(dut, CSR_MEDELEG) & MEDELEG_READ_ONLY_ONE_MASK) == MEDELEG_READ_ONLY_ONE_MASK);
- assert((read_csr(dut, CSR_MEDELEG) & (1ULL << 11)) == 0ULL);
- csr_op(dut, CSR_MEDELEG, CSR_OP_WRITE, 0ULL);
- assert((read_csr(dut, CSR_MEDELEG) & MEDELEG_READ_ONLY_ONE_MASK) == MEDELEG_READ_ONLY_ONE_MASK);
- csr_op(dut, CSR_MIDELEG, CSR_OP_WRITE, ~0ULL);
- assert((read_csr(dut, CSR_MIDELEG) & MIDELEG_WRITABLE_MASK) == MIDELEG_WRITABLE_MASK);
- assert((read_csr(dut, CSR_MIDELEG) & (1ULL << INTERRUPT_M_TIMER)) == 0ULL);
- std::cout << "[PASS] Delegation CSRs expose only writable standard bits" << std::endl;
-
- // Basic supervisor CSRs touching for completeness
- const uint64_t s_interrupt_mask = (1ULL << INTERRUPT_S_SOFTWARE) |
- (1ULL << INTERRUPT_S_TIMER) |
- (1ULL << INTERRUPT_S_EXTERNAL);
- csr_op(dut, CSR_SIE, CSR_OP_WRITE, ~0ULL);
- csr_op(dut, CSR_SSCRATCH, CSR_OP_WRITE, 0xAAULL);
- csr_op(dut, CSR_SEPC, CSR_OP_WRITE, 0x1234ULL);
- csr_op(dut, CSR_SCAUSE, CSR_OP_WRITE, 0x21ULL);
- csr_op(dut, CSR_SIP, CSR_OP_WRITE, ~0ULL);
- csr_op(dut, CSR_SATP, CSR_OP_WRITE, 0x8000000000000000ULL);
- assert(read_csr(dut, CSR_SIE) == s_interrupt_mask);
- assert(read_csr(dut, CSR_SSCRATCH) == 0xAAULL);
- assert(read_csr(dut, CSR_SEPC) == 0x1234ULL);
- assert(read_csr(dut, CSR_SCAUSE) == 0x21ULL);
- assert(read_csr(dut, CSR_SIP) == s_interrupt_mask);
- assert(read_csr(dut, CSR_SATP) == 0x8000000000000000ULL);
- std::cout << "[PASS] Supervisor space CSR read/write" << std::endl;
-
- csr_op(dut, CSR_MIE, CSR_OP_WRITE, 0ULL);
- csr_op(dut, CSR_SIE, CSR_OP_WRITE, (1ULL << INTERRUPT_S_TIMER));
- assert(read_csr(dut, CSR_SIE) == (1ULL << INTERRUPT_S_TIMER));
- assert(read_csr(dut, CSR_MIE) == (1ULL << INTERRUPT_S_TIMER));
-
- csr_op(dut, CSR_MIP, CSR_OP_WRITE, 0ULL);
- dut->s_timer_interrupt = 1;
- dut->eval();
- assert(read_csr(dut, CSR_SIP) == (1ULL << INTERRUPT_S_TIMER));
- assert(read_csr(dut, CSR_MIP) == (1ULL << INTERRUPT_S_TIMER));
- dut->s_timer_interrupt = 0;
-
- dut->m_external_interrupt = 1;
- dut->eval();
- assert((read_csr(dut, CSR_MIP) & (1ULL << INTERRUPT_M_EXTERNAL)) != 0ULL);
- assert((read_csr(dut, CSR_SIP) & (1ULL << INTERRUPT_M_EXTERNAL)) == 0ULL);
- dut->m_external_interrupt = 0;
- std::cout << "[PASS] Interrupt pending and enable CSRs expose delegated views" << std::endl;
-
- // Writing SSTATUS should mirror MSTATUS subset bits
- csr_op(dut, CSR_SSTATUS, CSR_OP_WRITE, (1ULL << SSTATUS_SIE_BIT));
- assert(((read_csr(dut, CSR_MSTATUS) >> SSTATUS_SIE_BIT) & 1ULL) == 1ULL);
- csr_op(dut, CSR_SSTATUS, CSR_OP_WRITE, 0ULL);
- assert(((read_csr(dut, CSR_MSTATUS) >> SSTATUS_SIE_BIT) & 1ULL) == 0ULL);
- std::cout << "[PASS] SSTATUS write mirrors MSTATUS" << std::endl;
-
- // Floating-point CSR block
- csr_op(dut, CSR_FFLAGS, CSR_OP_WRITE, 0x1FULL);
- assert((read_csr(dut, CSR_FFLAGS) & 0x1FULL) == 0x1FULL);
- csr_op(dut, CSR_FRM, CSR_OP_WRITE, 0x7ULL);
- assert((read_csr(dut, CSR_FRM) & 0x7ULL) == 0x7ULL);
- csr_op(dut, CSR_FCSR, CSR_OP_WRITE, 0xABULL);
- assert((read_csr(dut, CSR_FFLAGS) & 0x1FULL) == (0xABULL & 0x1FULL));
- assert((read_csr(dut, CSR_FRM) & 0x7ULL) == ((0xABULL >> 5) & 0x7ULL));
- std::cout << "[PASS] FCSR/FM/FFLAGS read/write" << std::endl;
-
- std::cout << "[INFO] Extending CSR trap coverage" << std::endl;
-
- run_csr_scenario(dut, "interrupt_priority_matrix", [&](Vcsr_unit* target) {
- struct PriorityScenario {
- const char* name;
- bool ext_external;
- bool ext_software;
- bool ext_timer;
- bool sync_illegal;
- uint64_t mepc;
- uint64_t mtval;
- uint64_t expected_cause;
- };
-
- const std::array<PriorityScenario, 5> suite = {{
- {"external_over_others", true, true, true, true, 0xA000ULL, 0x1111ULL, INTERRUPT_BIT | INTERRUPT_M_EXTERNAL},
- {"software_over_timer", false, true, true, true, 0xA040ULL, 0x2222ULL, INTERRUPT_BIT | INTERRUPT_M_SOFTWARE},
- {"timer_over_sync", false, false, true, true, 0xA080ULL, 0x3333ULL, INTERRUPT_BIT | INTERRUPT_M_TIMER},
- {"interrupt_only", true, false, false, false, 0xA0C0ULL, 0x0ULL, INTERRUPT_BIT | INTERRUPT_M_EXTERNAL},
- {"sync_fallback", false, false, false, true, 0xA100ULL, 0x4444ULL, EXC_INST_ILLEGAL}
- }};
-
- for (const auto& scenario : suite) {
- bool chose_interrupt = false;
- const uint64_t chosen_cause = arbitrate_trap_priority(
- scenario.ext_external,
- scenario.ext_software,
- scenario.ext_timer,
- scenario.sync_illegal,
- EXC_INST_ILLEGAL,
- chose_interrupt
- );
- assert(chosen_cause == scenario.expected_cause);
- trigger_trap(
- target,
- scenario.mepc,
- chosen_cause,
- scenario.sync_illegal ? scenario.mtval : 0ULL,
- false
- );
- const uint64_t mcause = read_csr(target, CSR_MCAUSE);
- assert(mcause == scenario.expected_cause);
- assert(read_csr(target, CSR_MEPC) == scenario.mepc);
- const bool mcause_is_interrupt = (mcause & INTERRUPT_BIT) != 0ULL;
- assert(mcause_is_interrupt == chose_interrupt);
- if (scenario.sync_illegal && !mcause_is_interrupt) {
- assert(read_csr(target, CSR_MTVAL) == scenario.mtval);
- }
- assert(target->csr_priv_mode == PRIV_ENCODE_M);
- }
- });
-
- run_csr_scenario(dut, "delegated_multitrap_context", [&](Vcsr_unit* target) {
- csr_op(target, CSR_MEDELEG, CSR_OP_WRITE, 0ULL);
- csr_op(target, CSR_MEDELEG, CSR_OP_SET, (1ULL << EXC_ECALL_FROM_U));
- csr_op(target, CSR_STVEC, CSR_OP_WRITE, 0x4000ULL);
- csr_op(target, CSR_MTVEC, CSR_OP_WRITE, 0x8000ULL);
-
- force_priv_mode_via_mret(target, PRIV_ENCODE_S);
- assert(target->csr_priv_mode == PRIV_ENCODE_S);
-
- const uint64_t delegated_pc = 0x900ULL;
- trigger_trap(target, delegated_pc, EXC_ECALL_FROM_U, 0ULL, true);
- assert(target->csr_trap_to_s_mode);
- assert(target->csr_trap_vector == 0x4000ULL);
- assert(read_csr(target, CSR_SEPC) == delegated_pc);
- assert(read_csr(target, CSR_SCAUSE) == EXC_ECALL_FROM_U);
- assert(target->csr_priv_mode == PRIV_ENCODE_S);
-
- const uint64_t machine_pc = 0xA00ULL;
- trigger_trap(target, machine_pc, EXC_INST_ILLEGAL, 0ULL, false);
- assert(!target->csr_trap_to_s_mode);
- assert(target->csr_trap_vector == 0x8000ULL);
- assert(read_csr(target, CSR_MEPC) == machine_pc);
- assert(read_csr(target, CSR_SEPC) == delegated_pc);
- assert(target->csr_priv_mode == PRIV_ENCODE_M);
- });
-
- run_csr_scenario(dut, "nested_return_sequence", [&](Vcsr_unit* target) {
- force_priv_mode_via_mret(target, PRIV_ENCODE_S);
- csr_op(target, CSR_SSTATUS, CSR_OP_WRITE, (1ULL << SSTATUS_SIE_BIT));
- force_priv_mode_via_mret(target, PRIV_ENCODE_U);
-
- const uint64_t first_trap_pc = 0xB00ULL;
- trigger_trap(target, first_trap_pc, EXC_ECALL_FROM_U, 0ULL, true);
- uint64_t sstatus_snapshot = read_csr(target, CSR_SSTATUS);
- assert(((sstatus_snapshot >> SSTATUS_SIE_BIT) & 1ULL) == 0ULL);
- assert(((sstatus_snapshot >> SSTATUS_SPIE_BIT) & 1ULL) == 1ULL);
- assert(((sstatus_snapshot >> SSTATUS_SPP_BIT) & 1ULL) == 0ULL);
- assert(read_csr(target, CSR_SEPC) == first_trap_pc);
-
- target->trap_ctrl_sret_en = 1;
- tick(target);
- target->trap_ctrl_sret_en = 0;
- uint64_t sstatus_after_sret = read_csr(target, CSR_SSTATUS);
- assert(((sstatus_after_sret >> SSTATUS_SIE_BIT) & 1ULL) == 1ULL);
- assert(((sstatus_after_sret >> SSTATUS_SPIE_BIT) & 1ULL) == 1ULL);
- assert(target->csr_priv_mode == PRIV_ENCODE_U);
-
- // Enable machine interrupts so the subsequent machine trap captures pre-trap MIE=1
- force_priv_mode_via_mret(target, PRIV_ENCODE_M);
- csr_op(target, CSR_MSTATUS, CSR_OP_SET, (1ULL << MSTATUS_MIE_BIT));
- force_priv_mode_via_mret(target, PRIV_ENCODE_U);
- const uint64_t second_trap_pc = 0xB80ULL;
- trigger_trap(target, second_trap_pc, EXC_INST_ILLEGAL, 0ULL, false);
- uint64_t mstatus_snapshot = read_csr(target, CSR_MSTATUS);
- assert(((mstatus_snapshot >> MSTATUS_MIE_BIT) & 1ULL) == 0ULL);
- assert(((mstatus_snapshot >> MSTATUS_MPIE_BIT) & 1ULL) == 1ULL);
- assert(((mstatus_snapshot >> MSTATUS_MPP_LO) & 0x3ULL) == PRIV_ENCODE_U);
- assert(read_csr(target, CSR_MEPC) == second_trap_pc);
-
- target->trap_ctrl_mret_en = 1;
- tick(target);
- target->trap_ctrl_mret_en = 0;
- uint64_t mstatus_after_mret = read_csr(target, CSR_MSTATUS);
- assert(((mstatus_after_mret >> MSTATUS_MIE_BIT) & 1ULL) == 1ULL);
- assert(((mstatus_after_mret >> MSTATUS_MPIE_BIT) & 1ULL) == 1ULL);
- assert(((mstatus_after_mret >> MSTATUS_MPP_LO) & 0x3ULL) == PRIV_ENCODE_U);
- assert(target->csr_priv_mode == PRIV_ENCODE_U);
- });
-
- run_csr_scenario(dut, "illegal_csr_access_guard", [&](Vcsr_unit* target) {
- force_priv_mode_via_mret(target, PRIV_ENCODE_M);
- assert(target->csr_priv_mode == PRIV_ENCODE_M);
- const uint64_t vendor_snapshot = read_csr(target, CSR_MVENDORID);
- target->csr_addr = CSR_MVENDORID;
- target->csr_op = CSR_OP_WRITE;
- target->csr_rs1_data = 0x1234ULL;
- target->csr_req = 1;
- tick(target);
- target->csr_req = 0;
- target->csr_op = 0;
- target->csr_rs1_data = 0;
- target->csr_addr = 0;
- assert(target->csr_illegal_access);
- assert(read_csr(target, CSR_MVENDORID) == vendor_snapshot);
- assert(!target->csr_trap_to_s_mode);
- assert(target->csr_priv_mode == PRIV_ENCODE_M);
- assert(target->csr_trap_vector_next == read_csr(target, CSR_MTVEC));
- tick(target);
- assert(!target->csr_illegal_access);
-
- force_priv_mode_via_mret(target, PRIV_ENCODE_S);
- assert(target->csr_priv_mode == PRIV_ENCODE_S);
- target->csr_addr = CSR_MSTATUS;
- target->csr_op = CSR_OP_WRITE;
- target->csr_rs1_data = 0ULL;
- target->csr_req = 1;
- target->eval();
- assert(target->csr_illegal_access);
- tick(target);
- target->csr_req = 0;
- target->csr_op = 0;
- target->csr_rs1_data = 0;
- target->csr_addr = 0;
- target->eval();
- assert(!target->csr_illegal_access);
-
- force_priv_mode_via_mret(target, PRIV_ENCODE_M);
- csr_op(target, CSR_MCOUNTEREN, CSR_OP_WRITE, 0ULL);
- force_priv_mode_via_mret(target, PRIV_ENCODE_S);
- target->csr_addr = CSR_CYCLE;
- target->csr_op = 0;
- target->csr_rs1_data = 0ULL;
- target->csr_req = 1;
- target->eval();
- assert(target->csr_illegal_access);
- tick(target);
- target->csr_req = 0;
- target->csr_addr = 0;
- target->eval();
-
- force_priv_mode_via_mret(target, PRIV_ENCODE_M);
- csr_op(target, CSR_MCOUNTEREN, CSR_OP_WRITE, 1ULL);
- csr_op(target, CSR_SCOUNTEREN, CSR_OP_WRITE, 0ULL);
- force_priv_mode_via_mret(target, PRIV_ENCODE_U);
- target->csr_addr = CSR_CYCLE;
- target->csr_op = 0;
- target->csr_rs1_data = 0ULL;
- target->csr_req = 1;
- target->eval();
- assert(target->csr_illegal_access);
- tick(target);
- target->csr_req = 0;
- target->csr_addr = 0;
- target->eval();
-
- force_priv_mode_via_mret(target, PRIV_ENCODE_M);
- csr_op(target, CSR_MSTATEEN0, CSR_OP_WRITE, 0ULL);
- force_priv_mode_via_mret(target, PRIV_ENCODE_S);
- target->csr_addr = CSR_SENVCFG;
- target->csr_op = CSR_OP_WRITE;
- target->csr_rs1_data = 1ULL;
- target->csr_req = 1;
- target->eval();
- assert(target->csr_illegal_access);
- tick(target);
- target->csr_req = 0;
- target->csr_op = 0;
- target->csr_rs1_data = 0;
- target->csr_addr = 0;
- target->eval();
-
- force_priv_mode_via_mret(target, PRIV_ENCODE_M);
- csr_op(target, CSR_MSTATEEN0, CSR_OP_WRITE, (1ULL << STATEEN0_ENVCFG_BIT));
- force_priv_mode_via_mret(target, PRIV_ENCODE_S);
- csr_op(target, CSR_SENVCFG, CSR_OP_WRITE, 1ULL);
- assert((read_csr(target, CSR_SENVCFG) & 1ULL) == 1ULL);
- });
-
- run_csr_scenario(dut, "delegation_cross_over", [&](Vcsr_unit* target) {
- csr_op(target, CSR_MTVEC, CSR_OP_WRITE, 0x1000ULL);
- csr_op(target, CSR_STVEC, CSR_OP_WRITE, 0x2000ULL);
- csr_op(target, CSR_MEDELEG, CSR_OP_WRITE, 0ULL);
- csr_op(target, CSR_MIDELEG, CSR_OP_WRITE, 0ULL);
-
- struct DelegationProbe {
- const char* name;
- bool is_interrupt;
- uint64_t cause;
- bool delegate_to_s;
- uint64_t pc;
- };
-
- const std::array<DelegationProbe, 4> probes = {{
- {"medeleg_set", false, EXC_ECALL_FROM_U, true, 0xD000ULL},
- {"medeleg_clear", false, EXC_ECALL_FROM_U, false, 0xD040ULL},
- {"mideleg_set", true, INTERRUPT_S_TIMER, true, 0xD080ULL},
- {"mideleg_clear", true, INTERRUPT_S_TIMER, false, 0xD0C0ULL}
- }};
-
- for (const auto& probe : probes) {
- if (probe.is_interrupt) {
- const uint64_t mask = (1ULL << probe.cause);
- if (probe.delegate_to_s) {
- csr_op(target, CSR_MIDELEG, CSR_OP_SET, mask);
- } else {
- csr_op(target, CSR_MIDELEG, CSR_OP_CLEAR, mask);
- }
- force_priv_mode_via_mret(target, PRIV_ENCODE_S);
- trigger_trap(target, probe.pc, (INTERRUPT_BIT | probe.cause), 0ULL, probe.delegate_to_s);
- if (probe.delegate_to_s) {
- assert(target->csr_trap_to_s_mode);
- assert(read_csr(target, CSR_SEPC) == probe.pc);
- } else {
- assert(!target->csr_trap_to_s_mode);
- assert(read_csr(target, CSR_MEPC) == probe.pc);
- }
- } else {
- const uint64_t mask = (1ULL << probe.cause);
- if (probe.delegate_to_s) {
- csr_op(target, CSR_MEDELEG, CSR_OP_SET, mask);
- } else {
- csr_op(target, CSR_MEDELEG, CSR_OP_CLEAR, mask);
- }
- force_priv_mode_via_mret(target, PRIV_ENCODE_U);
- trigger_trap(target, probe.pc, probe.cause, 0ULL, probe.delegate_to_s);
- if (probe.delegate_to_s) {
- assert(target->csr_trap_to_s_mode);
- assert(read_csr(target, CSR_SEPC) == probe.pc);
- } else {
- assert(!target->csr_trap_to_s_mode);
- assert(read_csr(target, CSR_MEPC) == probe.pc);
- }
- }
-
- const uint64_t expected_vector = probe.delegate_to_s ? read_csr(target, CSR_STVEC)
- : read_csr(target, CSR_MTVEC);
- assert(target->csr_trap_vector == expected_vector);
- }
- });
-
- std::cout << "----------------------------------\n";
- std::cout << "All CSR unit tests passed!\n";
- std::cout << "----------------------------------\n";
-
- delete dut;
- return 0;
-}