aboutsummaryrefslogtreecommitdiff
path: root/rv64cpu/tb/unit
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
parente0948e3fc10029ba4bb4d48d7b5daae94fe575b9 (diff)
downloadrcc-project-2a4c290c55b683352a16747432a7fc4635597cc6.tar.gz
rcc-project-2a4c290c55b683352a16747432a7fc4635597cc6.tar.xz
refactor: reset software and test directories
Diffstat (limited to 'rv64cpu/tb/unit')
-rw-r--r--rv64cpu/tb/unit/tb_alu.cc136
-rw-r--r--rv64cpu/tb/unit/tb_branch.cc100
-rw-r--r--rv64cpu/tb/unit/tb_csr.cc770
-rw-r--r--rv64cpu/tb/unit/tb_decoder.cc353
-rw-r--r--rv64cpu/tb/unit/tb_ex_mem_reg.cc182
-rw-r--r--rv64cpu/tb/unit/tb_ex_stage.cc218
-rw-r--r--rv64cpu/tb/unit/tb_hazard_unit.cc364
-rw-r--r--rv64cpu/tb/unit/tb_id_ex_reg.cc177
-rw-r--r--rv64cpu/tb/unit/tb_id_stage.cc194
-rw-r--r--rv64cpu/tb/unit/tb_if_id_reg.cc127
-rw-r--r--rv64cpu/tb/unit/tb_if_stage.cc132
-rw-r--r--rv64cpu/tb/unit/tb_mem_stage.cc305
-rw-r--r--rv64cpu/tb/unit/tb_mem_wb_reg.cc165
-rw-r--r--rv64cpu/tb/unit/tb_mmu_top.cc236
-rw-r--r--rv64cpu/tb/unit/tb_ptw.cc206
-rw-r--r--rv64cpu/tb/unit/tb_regfile.cc192
-rw-r--r--rv64cpu/tb/unit/tb_tlb.cc172
-rw-r--r--rv64cpu/tb/unit/tb_trap_ctrl.cc284
-rw-r--r--rv64cpu/tb/unit/tb_wb_stage.cc163
19 files changed, 0 insertions, 4476 deletions
diff --git a/rv64cpu/tb/unit/tb_alu.cc b/rv64cpu/tb/unit/tb_alu.cc
deleted file mode 100644
index 0ed5059..0000000
--- a/rv64cpu/tb/unit/tb_alu.cc
+++ /dev/null
@@ -1,136 +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_alu.cc
-// Description: ALU unit test
-#include "Valu.h"
-#include <cassert>
-#include <cstdint>
-#include <iostream>
-#include <verilated.h>
-
-enum AluOp {
- ALU_ADD = 0,
- ALU_SUB = 8,
- ALU_SLL = 1,
- ALU_SLT = 2,
- ALU_SLTU = 3,
- ALU_XOR = 4,
- ALU_SRL = 5,
- ALU_SRA = 13,
- ALU_OR = 6,
- ALU_AND = 7,
- ALU_MUL = 16,
- ALU_MULH = 17,
- ALU_MULHSU = 18,
- ALU_MULHU = 19,
- ALU_DIV = 20,
- ALU_DIVU = 21,
- ALU_REM = 22,
- ALU_REMU = 23
-};
-
-void test_alu(Valu* alu, uint8_t op, uint64_t rs1, uint64_t rs2, uint64_t expected, const char* test_name) {
- alu->alu_op = op;
- alu->operand_a = rs1;
- alu->operand_b = rs2;
-
- alu->eval();
-
- if (alu->alu_result == expected) {
- std::cout << "\033[32m";
- std::cout << "[PASS] " << test_name << " | "
- << rs1 << " op " << rs2 << " = " << expected << "\033[0m" << std::endl;
- } else {
- std::cout << "\033[31m";
- std::cerr << "[FAIL] " << test_name << " | "
- << "Expected: " << expected << ", Got: " << alu->alu_result << "\033[0m" << std::endl;
- assert(false);
- }
-}
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Valu* alu = new Valu;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << " ALU Unit Test" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- test_alu(alu, ALU_ADD, 100, 50, 150, "ADD_Positive");
- test_alu(alu, ALU_SUB, 100, 50, 50, "SUB_Positive");
-
- test_alu(alu, ALU_ADD, (uint64_t)-5, 10, 5, "ADD_Negative");
-
- test_alu(alu, ALU_AND, 0xFF00, 0x0F0F, 0x0F00, "AND_Mask");
- test_alu(alu, ALU_XOR, 0xAAAA, 0xFFFF, 0x5555, "XOR_Invert");
-
- test_alu(alu, ALU_SLL, 1, 10, 1024, "SLL_By_10");
-
- uint64_t minus_one = (uint64_t)-1;
- uint64_t five = 5;
-
- test_alu(alu, ALU_SLTU, minus_one, five, 0, "SLTU (Unsigned: -1 < 5 is False)");
- test_alu(alu, ALU_SLT, minus_one, five, 1, "SLT (Signed: -1 < 5 is True)");
-
-
- // Missing Basic Operations
- test_alu(alu, ALU_OR, 0xAAAA, 0x5555, 0xFFFF, "OR_All_Bits");
- test_alu(alu, ALU_SRL, 0xF000000000000000ULL, 4, 0x0F00000000000000ULL, "SRL_By_4");
-
- // Arithmetic Right Shift (SRA)
- test_alu(alu, ALU_SRA, 0xF000000000000000ULL, 4, 0xFF00000000000000ULL, "SRA_Negative_By_4");
- test_alu(alu, ALU_SRA, 0x7000000000000000ULL, 4, 0x0700000000000000ULL, "SRA_Positive_By_4");
-
- // Shift Edge Cases
- test_alu(alu, ALU_SLL, 1, 0, 1, "SLL_By_0");
- test_alu(alu, ALU_SLL, 1, 63, 0x8000000000000000ULL, "SLL_By_63");
- test_alu(alu, ALU_SRL, 0x8000000000000000ULL, 63, 1, "SRL_By_63");
- test_alu(alu, ALU_SRA, 0x8000000000000000ULL, 63, 0xFFFFFFFFFFFFFFFFULL, "SRA_By_63_SignExt");
-
- // All 0 / All 1 Cases
- test_alu(alu, ALU_ADD, 0, 0, 0, "ADD_Zero");
- test_alu(alu, ALU_ADD, 0xFFFFFFFFFFFFFFFFULL, 1, 0, "ADD_Overflow");
- test_alu(alu, ALU_XOR, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0, "XOR_Same");
-
- // SLT / SLTU Same Value
- test_alu(alu, ALU_SLT, 100, 100, 0, "SLT_Same_Value");
- test_alu(alu, ALU_SLTU, 100, 100, 0, "SLTU_Same_Value");
-
- // SLT Edge Cases
- test_alu(alu, ALU_SLT, 0x8000000000000000ULL, 0x7FFFFFFFFFFFFFFFULL, 1, "SLT_Min_max");
- test_alu(alu, ALU_SLT, 0x7FFFFFFFFFFFFFFFULL, 0x8000000000000000ULL, 0, "SLT_Max_Min");
-
- // M extension
- test_alu(alu, ALU_MUL, 7, 6, 42, "MUL_Positive");
- test_alu(alu, ALU_MUL, 0xFFFFFFFFFFFFFFFFULL, 2, 0xFFFFFFFFFFFFFFFEULL, "MUL_NegativeLow");
- test_alu(alu, ALU_MULH, 0xFFFFFFFFFFFFFFFFULL, 2, 0xFFFFFFFFFFFFFFFFULL, "MULH_Negative");
- test_alu(alu, ALU_MULHU, 0xFFFFFFFFFFFFFFFFULL, 2, 1, "MULHU_UnsignedHigh");
- test_alu(alu, ALU_DIV, (uint64_t)-21, 4, (uint64_t)-5, "DIV_SignedTruncTowardZero");
- test_alu(alu, ALU_DIVU, 21, 4, 5, "DIVU");
- test_alu(alu, ALU_REM, (uint64_t)-21, 4, (uint64_t)-1, "REM_Signed");
- test_alu(alu, ALU_REMU, 21, 4, 1, "REMU");
- test_alu(alu, ALU_DIV, 123, 0, 0xFFFFFFFFFFFFFFFFULL, "DIV_ByZero");
- test_alu(alu, ALU_REM, 123, 0, 123, "REM_ByZero");
- test_alu(alu, ALU_DIV, 0x8000000000000000ULL, 0xFFFFFFFFFFFFFFFFULL,
- 0x8000000000000000ULL, "DIV_Overflow");
- test_alu(alu, ALU_REM, 0x8000000000000000ULL, 0xFFFFFFFFFFFFFFFFULL, 0, "REM_Overflow");
- std::cout << "----------------------------------" << std::endl;
- std::cout << "\033[32mAll tests passed!\033[0m" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- delete alu;
- return 0;
-}
diff --git a/rv64cpu/tb/unit/tb_branch.cc b/rv64cpu/tb/unit/tb_branch.cc
deleted file mode 100644
index e47bf98..0000000
--- a/rv64cpu/tb/unit/tb_branch.cc
+++ /dev/null
@@ -1,100 +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_branch.cc
-// Description: Branch Unit test
-
-#include <iostream>
-#include <iomanip>
-#include <cstdint>
-#include <cassert>
-#include <verilated.h>
-#include "Vbranch_unit.h"
-
-// Enums must match rv64_pkg::branch_op_t
-enum BranchOp {
- BRANCH_EQ = 0b000,
- BRANCH_NE = 0b001,
- BRANCH_LT = 0b100,
- BRANCH_GE = 0b101,
- BRANCH_LTU = 0b110,
- BRANCH_GEU = 0b111
-};
-
-void test_branch(Vbranch_unit* dut, uint8_t is_branch, uint8_t op, uint64_t a, uint64_t b, uint8_t expected, const char* name) {
- dut->is_branch = is_branch;
- dut->branch_op = op;
- dut->operand_a = a;
- dut->operand_b = b;
- dut->eval();
-
- if (dut->branch_taken == expected) {
- std::cout << "\033[32m[PASS] " << name << " | Expected: " << (int)expected << " Got: " << (int)dut->branch_taken << "\033[0m" << std::dec << std::endl;
- } else {
- std::cout << "\033[31m[FAIL] " << name << " | Expected: " << (int)expected << " Got: " << (int)dut->branch_taken << "\033[0m" << std::endl;
- assert(false);
- }
-}
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Vbranch_unit* dut = new Vbranch_unit;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << " Branch Unit Test" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- // --- Not a branch ---
- test_branch(dut, 0, BRANCH_EQ, 10, 10, 0, "Not_A_Branch");
-
- // --- BEQ / BNE ---
- test_branch(dut, 1, BRANCH_EQ, 10, 10, 1, "BEQ_Equal");
- test_branch(dut, 1, BRANCH_EQ, 10, 20, 0, "BEQ_Not_Equal");
- test_branch(dut, 1, BRANCH_NE, 10, 20, 1, "BNE_Not_Equal");
- test_branch(dut, 1, BRANCH_NE, 10, 10, 0, "BNE_Equal");
-
- uint64_t minus_one = (uint64_t)-1; // 0xFFFFFFFFFFFFFFFF
- uint64_t one = 1;
-
- // --- BLT / BGE (Signed) ---
- // -1 is less than 1 (Signed)
- test_branch(dut, 1, BRANCH_LT, minus_one, one, 1, "BLT_Signed_Negative_vs_Positive");
- test_branch(dut, 1, BRANCH_GE, minus_one, one, 0, "BGE_Signed_Negative_vs_Positive");
-
- // 1 is greater than -1 (Signed)
- test_branch(dut, 1, BRANCH_LT, one, minus_one, 0, "BLT_Signed_Positive_vs_Negative");
- test_branch(dut, 1, BRANCH_GE, one, minus_one, 1, "BGE_Signed_Positive_vs_Negative");
-
- // Equal signed
- test_branch(dut, 1, BRANCH_LT, minus_one, minus_one, 0, "BLT_Signed_Equal");
- test_branch(dut, 1, BRANCH_GE, minus_one, minus_one, 1, "BGE_Signed_Equal");
-
- // --- BLTU / BGEU (Unsigned) ---
- // 0xFFFFFFFFFFFFFFFF is NOT less than 1 (Unsigned)
- test_branch(dut, 1, BRANCH_LTU, minus_one, one, 0, "BLTU_Unsigned_Max_vs_1");
- // 0xFFFFFFFFFFFFFFFF is greater than or equal to 1 (Unsigned)
- test_branch(dut, 1, BRANCH_GEU, minus_one, one, 1, "BGEU_Unsigned_Max_vs_1");
-
- // Equal unsigned
- test_branch(dut, 1, BRANCH_LTU, minus_one, minus_one, 0, "BLTU_Unsigned_Equal");
- test_branch(dut, 1, BRANCH_GEU, minus_one, minus_one, 1, "BGEU_Unsigned_Equal");
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << "\033[32mAll tests passed!\033[0m" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- delete dut;
- return 0;
-}
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;
-}
diff --git a/rv64cpu/tb/unit/tb_decoder.cc b/rv64cpu/tb/unit/tb_decoder.cc
deleted file mode 100644
index 1dcfb82..0000000
--- a/rv64cpu/tb/unit/tb_decoder.cc
+++ /dev/null
@@ -1,353 +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_decoder.cc
-// Description: Decoder unit test
-
-#include <iostream>
-#include <iomanip>
-#include <cstdint>
-#include <cstdlib>
-#include <verilated.h>
-#include "Vdecoder.h"
-
-static int test_count = 0;
-static int fail_count = 0;
-
-#define CHECK(dut, signal, expected, test_name) \
- do { \
- uint64_t _got = (uint64_t)(dut->signal); \
- uint64_t _exp = (uint64_t)(expected); \
- if (_got != _exp) { \
- std::cerr << "\033[31m[FAIL] " << test_name \
- << " | " << #signal << " expected: 0x" \
- << std::hex << _exp << " got: 0x" << _got \
- << std::dec << "\033[0m" << std::endl; \
- fail_count++; \
- } \
- } while(0)
-
-void decode(Vdecoder* dut, uint32_t instr, const char* name, uint8_t priv_mode = 3) {
- dut->priv_mode = priv_mode & 0x3;
- dut->instr = instr;
- dut->eval();
- test_count++;
- std::cout << "\033[32m[PASS] [TEST " << std::setw(2) << test_count << "] " << name
- << " (0x" << std::hex << std::setw(8) << std::setfill('0')
- << instr << std::dec << std::setfill(' ') << ")\033[0m" << std::endl;
-}
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Vdecoder* dut = new Vdecoder;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << " Decoder Unit Test" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- // 1. R-type: ADD x1, x2, x3
- // funct7=0000000 rs2=00011 rs1=00010 funct3=000 rd=00001 op=0110011
- decode(dut, 0x003100B3, "ADD x1, x2, x3");
- CHECK(dut, rs1_addr, 2, "ADD");
- CHECK(dut, rs2_addr, 3, "ADD");
- CHECK(dut, rd_addr, 1, "ADD");
- CHECK(dut, reg_write_en, 1, "ADD"); // R-type writes rd
- CHECK(dut, alu_op, 0, "ADD"); // ALU_ADD = 0b0000
- CHECK(dut, alu_src1_sel, 0, "ADD"); // operand1 = rs1
- CHECK(dut, alu_src2_sel, 0, "ADD"); // operand2 = rs2
- CHECK(dut, wb_sel, 0, "ADD"); // writeback from ALU
- CHECK(dut, illegal_instr,0, "ADD");
-
- // 2. R-type: SUB x3, x1, x2
- // funct7=0100000 rs2=00010 rs1=00001 funct3=000 rd=00011 op=0110011
- // Tests: funct7[5] distinguishes ADD vs SUB
- decode(dut, 0x402081B3, "SUB x3, x1, x2");
- CHECK(dut, alu_op, 8, "SUB"); // ALU_SUB = 0b1000
- CHECK(dut, reg_write_en, 1, "SUB");
- CHECK(dut, alu_src2_sel, 0, "SUB"); // operand2 = rs2
-
- // 3. R-type: SRA x1, x2, x3 (vs SRL)
- // funct7=0100000 rs2=00011 rs1=00010 funct3=101 rd=00001 op=0110011
- // Tests: funct7[5] distinguishes SRL vs SRA for funct3=101
- decode(dut, 0x403150B3, "SRA x1, x2, x3");
- CHECK(dut, alu_op, 13, "SRA"); // ALU_SRA = 0b1101
-
- // 4. R-type: SRL x1, x2, x3
- // funct7=0000000 rs2=00011 rs1=00010 funct3=101 rd=00001 op=0110011
- decode(dut, 0x003150B3, "SRL x1, x2, x3");
- CHECK(dut, alu_op, 5, "SRL"); // ALU_SRL = 0b0101
-
- // 5. I-type ALU: ADDI x1, x0, 42
- // imm=000000101010 rs1=00000 funct3=000 rd=00001 op=0010011
- // Tests: I-type sets alu_src2_sel=1 (immediate), reg_write=1
- decode(dut, 0x02A00093, "ADDI x1, x0, 42");
- CHECK(dut, reg_write_en, 1, "ADDI");
- CHECK(dut, alu_op, 0, "ADDI"); // ALU_ADD
- CHECK(dut, alu_src1_sel, 0, "ADDI"); // rs1
- CHECK(dut, alu_src2_sel, 1, "ADDI"); // immediate
- CHECK(dut, imm, 42, "ADDI");
- CHECK(dut, rd_addr, 1, "ADDI");
- CHECK(dut, rs1_addr, 0, "ADDI");
-
- // 6. I-type ALU: ADDI x1, x0, -1 (negative immediate sign extension)
- // imm=111111111111 rs1=00000 funct3=000 rd=00001 op=0010011
- // Tests: 12-bit immediate sign-extended to 64-bit
- decode(dut, 0xFFF00093, "ADDI x1, x0, -1");
- CHECK(dut, imm, (uint64_t)-1, "ADDI -1"); // 0xFFFFFFFFFFFFFFFF
-
- // 7. I-type ALU: SRAI x1, x2, 3
- // imm=010000_000011 rs1=00010 funct3=101 rd=00001 op=0010011
- // Tests: funct7[5]=1 selects SRA for funct3=101 in OP_IMM
- decode(dut, 0x40315093, "SRAI x1, x2, 3");
- CHECK(dut, alu_op, 13, "SRAI"); // ALU_SRA = 0b1101
- CHECK(dut, alu_src2_sel, 1, "SRAI"); // immediate (shamt)
-
- // 8. Load: LD x5, 8(x10)
- // imm=000000001000 rs1=01010 funct3=011 rd=00101 op=0000011
- // Tests: is_load, mem_size, wb_sel=MEM, alu computes address
- decode(dut, 0x00853283, "LD x5, 8(x10)");
- CHECK(dut, is_load, 1, "LD");
- CHECK(dut, reg_write_en, 1, "LD");
- CHECK(dut, alu_src2_sel, 1, "LD"); // imm for address calc
- CHECK(dut, imm, 8, "LD");
- CHECK(dut, mem_size, 3, "LD"); // funct3=011 → doubleword
- CHECK(dut, wb_sel, 1, "LD"); // writeback from MEM
- CHECK(dut, alu_op, 0, "LD"); // ALU_ADD for addr
-
- // 9. Store: SD x5, 16(x10)
- // imm[11:5]=0000000 rs2=00101 rs1=01010 funct3=011 imm[4:0]=10000 op=0100011
- // Tests: is_store, reg_write=0, S-type immediate extraction
- decode(dut, 0x00553823, "SD x5, 16(x10)");
- CHECK(dut, is_store, 1, "SD");
- CHECK(dut, reg_write_en, 0, "SD"); // stores don't write rd
- CHECK(dut, alu_src2_sel, 1, "SD"); // imm for address calc (NOT rs2)
- CHECK(dut, imm, 16, "SD"); // S-type immediate = 16
- CHECK(dut, mem_size, 3, "SD"); // funct3=011 → doubleword
- CHECK(dut, rs1_addr, 10, "SD"); // base register
- CHECK(dut, rs2_addr, 5, "SD"); // data to store
-
- // 10. Branch: BEQ x1, x2, -4
- // Tests: is_branch, branch_op=funct3, negative B-type immediate
- decode(dut, 0xFE208EE3, "BEQ x1, x2, -4");
- CHECK(dut, is_branch, 1, "BEQ");
- CHECK(dut, reg_write_en, 0, "BEQ");
- CHECK(dut, alu_src1_sel, 1, "BEQ"); // PC for target calc
- CHECK(dut, branch_op, 0, "BEQ"); // funct3=000 → BEQ
- CHECK(dut, imm, (uint64_t)(-4), "BEQ"); // sign-extended -4
-
- // 11. Branch: BNE x1, x2, +8
- // imm[12]=0 imm[10:5]=000000 rs2=00010 rs1=00001 funct3=001
- // imm[4:1]=0100 imm[11]=0 op=1100011
- // Tests: branch_op correctly passes funct3 for BNE
- decode(dut, 0x00209463, "BNE x1, x2, +8");
- CHECK(dut, is_branch, 1, "BNE");
- CHECK(dut, branch_op, 1, "BNE"); // funct3=001 → BNE
- CHECK(dut, imm, 8, "BNE");
-
- // 12. LUI x1, 0x12345
- // imm[31:12]=0x12345 rd=00001 op=0110111
- // Tests: U-type immediate, reg_write, alu_src2_sel=imm
- decode(dut, 0x123450B7, "LUI x1, 0x12345");
- CHECK(dut, reg_write_en, 1, "LUI");
- CHECK(dut, alu_src2_sel, 1, "LUI"); // immediate
- CHECK(dut, imm, 0x12345000ULL, "LUI"); // U-type: imm << 12
- CHECK(dut, wb_sel, 0, "LUI"); // ALU result
- CHECK(dut, rd_addr, 1, "LUI");
-
- // 13. AUIPC x2, 0x00001
- // imm[31:12]=0x00001 rd=00010 op=0010111
- // Tests: alu_src1_sel=PC (distinguishes from LUI)
- decode(dut, 0x00001117, "AUIPC x2, 0x1");
- CHECK(dut, reg_write_en, 1, "AUIPC");
- CHECK(dut, alu_src1_sel, 1, "AUIPC"); // PC ← key difference from LUI
- CHECK(dut, alu_src2_sel, 1, "AUIPC"); // immediate
- CHECK(dut, imm, 0x1000ULL, "AUIPC"); // 0x00001 << 12 = 0x1000
-
- // 14. JAL x1, +8
- // J-type imm encoding: imm[20|10:1|11|19:12]
- // offset=8 → imm[20]=0 imm[10:1]=0000000100 imm[11]=0 imm[19:12]=00000000
- // rd=00001 op=1101111
- // Tests: is_jump, wb_sel=PC+4, alu_src1=PC
- decode(dut, 0x008000EF, "JAL x1, +8");
- CHECK(dut, is_jump, 1, "JAL");
- CHECK(dut, reg_write_en, 1, "JAL");
- CHECK(dut, alu_src1_sel, 1, "JAL"); // PC
- CHECK(dut, alu_src2_sel, 1, "JAL"); // immediate
- CHECK(dut, wb_sel, 2, "JAL"); // writeback PC+4
- CHECK(dut, imm, 8, "JAL");
-
- // 15. JALR x1, x5, 4
- // imm=000000000100 rs1=00101 funct3=000 rd=00001 op=1100111
- // Tests: is_jump, alu_src1=rs1 (distinguishes from JAL)
- decode(dut, 0x004280E7, "JALR x1, x5, 4");
- CHECK(dut, is_jump, 1, "JALR");
- CHECK(dut, reg_write_en, 1, "JALR");
- CHECK(dut, alu_src1_sel, 0, "JALR"); // rs1 ← key difference from JAL
- CHECK(dut, alu_src2_sel, 1, "JALR"); // immediate
- CHECK(dut, wb_sel, 2, "JALR"); // writeback PC+4
- CHECK(dut, imm, 4, "JALR");
- CHECK(dut, rs1_addr, 5, "JALR");
-
- // 16. ADDIW x1, x2, 10 (RV64 word-width I-type)
- // imm=000000001010 rs1=00010 funct3=000 rd=00001 op=0011011
- // Tests: is_word_op flag for 32-bit operation
- decode(dut, 0x00A1009B, "ADDIW x1, x2, 10");
- CHECK(dut, reg_write_en, 1, "ADDIW");
- CHECK(dut, is_word_op, 1, "ADDIW"); // 32-bit + sext
- CHECK(dut, alu_op, 0, "ADDIW"); // ALU_ADD
- CHECK(dut, alu_src2_sel, 1, "ADDIW"); // immediate
- CHECK(dut, imm, 10, "ADDIW");
-
- // 17. ADDW x3, x1, x2 (RV64 word-width R-type)
- // funct7=0000000 rs2=00010 rs1=00001 funct3=000 rd=00011 op=0111011
- // Tests: OP_32 path, is_word_op + R-type (alu_src2=rs2)
- decode(dut, 0x002081BB, "ADDW x3, x1, x2");
- CHECK(dut, reg_write_en, 1, "ADDW");
- CHECK(dut, is_word_op, 1, "ADDW");
- CHECK(dut, alu_op, 0, "ADDW"); // ALU_ADD
- CHECK(dut, alu_src2_sel, 0, "ADDW"); // rs2 (R-type!)
-
- // 18. SUBW x3, x1, x2 (RV64 word-width R-type with funct7[5]=1)
- // funct7=0100000 rs2=00010 rs1=00001 funct3=000 rd=00011 op=0111011
- decode(dut, 0x402081BB, "SUBW x3, x1, x2");
- CHECK(dut, alu_op, 8, "SUBW"); // ALU_SUB
- CHECK(dut, is_word_op, 1, "SUBW");
-
- decode(dut, 0x023100B3, "MUL x1, x2, x3");
- CHECK(dut, alu_op, 16, "MUL");
- CHECK(dut, reg_write_en, 1, "MUL");
- CHECK(dut, illegal_instr,0, "MUL");
-
- decode(dut, 0x023140B3, "DIV x1, x2, x3");
- CHECK(dut, alu_op, 20, "DIV");
- CHECK(dut, illegal_instr,0, "DIV");
-
- decode(dut, 0x023160B3, "REM x1, x2, x3");
- CHECK(dut, alu_op, 22, "REM");
- CHECK(dut, illegal_instr,0, "REM");
-
- decode(dut, 0x023100BB, "MULW x1, x2, x3");
- CHECK(dut, alu_op, 16, "MULW");
- CHECK(dut, is_word_op, 1, "MULW");
- CHECK(dut, illegal_instr,0, "MULW");
-
- decode(dut, 0x0021A0AF, "AMOADD.W x1, x2, (x3)");
- CHECK(dut, is_amo, 1, "AMOADD.W");
- CHECK(dut, amo_op, 4, "AMOADD.W");
- CHECK(dut, mem_size, 2, "AMOADD.W");
- CHECK(dut, wb_sel, 1, "AMOADD.W");
- CHECK(dut, reg_write_en, 1, "AMOADD.W");
- CHECK(dut, illegal_instr,0, "AMOADD.W");
-
- decode(dut, 0x100332AF, "LR.D x5, (x6)");
- CHECK(dut, is_amo, 1, "LR.D");
- CHECK(dut, amo_op, 1, "LR.D");
- CHECK(dut, mem_size, 3, "LR.D");
- CHECK(dut, rs2_addr, 0, "LR.D");
- CHECK(dut, illegal_instr,0, "LR.D");
-
- decode(dut, 0x1884B3AF, "SC.D x7, x8, (x9)");
- CHECK(dut, is_amo, 1, "SC.D");
- CHECK(dut, amo_op, 2, "SC.D");
- CHECK(dut, mem_size, 3, "SC.D");
- CHECK(dut, illegal_instr,0, "SC.D");
-
- decode(dut, 0x0EB6252F, "AMOSWAP.W.AQRL x10, x11, (x12)");
- CHECK(dut, is_amo, 1, "AMOSWAP.W.AQRL");
- CHECK(dut, amo_op, 3, "AMOSWAP.W.AQRL");
- CHECK(dut, illegal_instr,0, "AMOSWAP.W.AQRL");
-
- decode(dut, 0x105332AF, "Illegal LR.D with rs2 != x0");
- CHECK(dut, is_amo, 0, "Illegal LR.D");
- CHECK(dut, reg_write_en, 0, "Illegal LR.D");
- CHECK(dut, illegal_instr,1, "Illegal LR.D");
-
- // 19. CSRRW x1, mstatus(0x300), x2
- // imm=001100000000 rs1=00010 funct3=001 rd=00001 op=1110011
- // Tests: is_csr, csr_op=funct3, reg_write=1
- decode(dut, 0x300110F3, "CSRRW x1, mstatus, x2");
- CHECK(dut, is_csr, 1, "CSRRW");
- CHECK(dut, csr_op, 1, "CSRRW"); // funct3=001 → CSRRW
- CHECK(dut, reg_write_en, 1, "CSRRW");
- CHECK(dut, illegal_instr,0, "CSRRW");
-
- decode(dut, 0x300110F3, "CSRRW x1, mstatus, x2 (U-mode)", 0);
- CHECK(dut, illegal_instr,1, "CSRRW U-mode illegal");
- CHECK(dut, is_csr, 0, "CSRRW U-mode is_csr cleared");
-
- // 20. ECALL
- // all-zero fields + opcode=1110011
- decode(dut, 0x00000073, "ECALL");
- CHECK(dut, is_ecall, 1, "ECALL");
- CHECK(dut, reg_write_en, 0, "ECALL");
- CHECK(dut, illegal_instr,0, "ECALL");
-
- // 21. EBREAK
- // funct7=0000000 rs2=00001 rs1=00000 funct3=000 rd=00000 op=1110011
- decode(dut, 0x00100073, "EBREAK");
- CHECK(dut, is_ebreak, 1, "EBREAK");
- CHECK(dut, reg_write_en, 0, "EBREAK");
-
- // 22. MRET
- // funct7=0011000 rs2=00010 rs1=00000 funct3=000 rd=00000 op=1110011
- // Tests: privileged instruction decode from {funct7, rs2}
- decode(dut, 0x30200073, "MRET");
- CHECK(dut, is_mret, 1, "MRET");
- CHECK(dut, reg_write_en, 0, "MRET");
- CHECK(dut, illegal_instr,0, "MRET");
-
- // 23. SRET
- // funct7=0001000 rs2=00010 rs1=00000 funct3=000 rd=00000 op=1110011
- decode(dut, 0x10200073, "SRET", 1);
- CHECK(dut, is_sret, 1, "SRET");
- CHECK(dut, reg_write_en, 0, "SRET");
-
- // 24. WFI
- // funct7=0001000 rs2=00101 rs1=00000 funct3=000 rd=00000 op=1110011
- decode(dut, 0x10500073, "WFI");
- CHECK(dut, is_wfi, 1, "WFI");
-
- // 25. FENCE
- // pred=1111 succ=1111 rs1=00000 funct3=000 rd=00000 op=0001111
- // Tests: MISC_MEM path, is_fence flag
- decode(dut, 0x0FF0000F, "FENCE");
- CHECK(dut, is_fence, 1, "FENCE");
- CHECK(dut, illegal_instr,0, "FENCE");
-
- // 26. FENCE.I (Zifencei)
- // funct3=001 op=0001111
- // Tests: is_fence_i flag (requires I-Cache flush in pipeline)
- decode(dut, 0x0000100F, "FENCE.I");
- CHECK(dut, is_fence_i, 1, "FENCE.I");
-
- // 27. Illegal instruction (opcode=0000000)
- // Tests: default case sets illegal_instr, reg_write stays 0
- decode(dut, 0x00000000, "Illegal (all-zero)");
- CHECK(dut, illegal_instr,1, "Illegal");
- CHECK(dut, reg_write_en, 0, "Illegal");
-
- // Results
- std::cout << "----------------------------------" << std::endl;
- if (fail_count == 0) {
- std::cout << "\033[32mAll " << test_count << " tests passed!\033[0m" << std::endl;
- } else {
- std::cout << "\033[31m" << fail_count << " check(s) FAILED out of "
- << test_count << " tests.\033[0m" << std::endl;
- }
- std::cout << "----------------------------------" << std::endl;
-
- delete dut;
- return (fail_count > 0) ? 1 : 0;
-}
diff --git a/rv64cpu/tb/unit/tb_ex_mem_reg.cc b/rv64cpu/tb/unit/tb_ex_mem_reg.cc
deleted file mode 100644
index 75e5502..0000000
--- a/rv64cpu/tb/unit/tb_ex_mem_reg.cc
+++ /dev/null
@@ -1,182 +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_ex_mem_reg.cc
-// Description: Execution/Memory Stage Pipeline Register unit test
-
-#include <iostream>
-#include <cassert>
-#include <verilated.h>
-#include "Vex_mem_reg.h"
-
-int pass_count = 0;
-
-void tick(Vex_mem_reg* dut) {
- dut->clk = 0;
- dut->eval();
- dut->clk = 1;
- dut->eval();
- dut->clk = 0;
- dut->eval();
-}
-
-void check(bool cond, const char* name) {
- if (cond) {
- std::cout << "\033[32m[PASS] " << name << "\033[0m" << std::endl;
- pass_count++;
- } else {
- std::cout << "\033[31m[FAIL] " << name << "\033[0m" << std::endl;
- assert(false);
- }
-}
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Vex_mem_reg* dut = new Vex_mem_reg;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << " EX/MEM Register Test" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- // 1. Init / Reset
- dut->stall_en = 0;
- dut->flush_en = 0;
-
- // Set some random dangerous EX inputs
- dut->ex_pc = 0x80001000;
- dut->ex_rs1_rdata = 0x11111111;
- dut->ex_rs2_rdata = 0x22222222;
- dut->ex_rs1_addr = 31;
- dut->ex_alu_result = 0x88888888;
- dut->ex_branch_taken = 1;
- dut->ex_branch_target = 0x80001020;
- dut->ex_is_store = 1;
- dut->ex_is_load = 1;
- dut->ex_reg_write_en = 1;
- dut->ex_is_csr = 1;
- dut->ex_illegal_instr = 1;
-
- dut->rst = 1;
- tick(dut);
- dut->rst = 0;
-
- // Verify reset clears out dangerous signals
- check(dut->mem_pc == 0, "Reset_Clears_PC");
- check(dut->mem_branch_taken == 0, "Reset_Clears_BranchTaken");
- check(dut->mem_reg_write_en == 0, "Reset_Clears_RegWrite");
- check(dut->mem_is_store == 0, "Reset_Clears_IsStore");
- check(dut->mem_is_load == 0, "Reset_Clears_IsLoad");
- check(dut->mem_is_csr == 0, "Reset_Clears_IsCSR");
- check(dut->mem_illegal_instr == 0, "Reset_Clears_IllegalInstr");
-
- // 2. Normal Propagation
- dut->ex_pc = 0x80002000;
- dut->ex_rs1_rdata = 0xAAAAAAAA;
- dut->ex_rs2_rdata = 0xBBBBBBBB;
- dut->ex_rs1_addr = 15;
- dut->ex_alu_result = 0xCAFEBABE;
- dut->ex_branch_taken = 1;
- dut->ex_branch_target = 0x80002008;
- dut->ex_is_store = 0;
- dut->ex_is_load = 1;
- dut->ex_reg_write_en = 1;
- dut->ex_rd_addr = 15;
- dut->ex_wb_sel = 1;
-
- tick(dut);
-
- check(dut->mem_pc == 0x80002000, "Propagate_PC");
- check(dut->mem_rs1_rdata == 0xAAAAAAAA, "Propagate_RS1");
- check(dut->mem_rs2_rdata == 0xBBBBBBBB, "Propagate_RS2");
- check(dut->mem_rs1_addr == 15, "Propagate_RS1_Addr");
- check(dut->mem_alu_result == 0xCAFEBABE, "Propagate_ALU_Result");
- check(dut->mem_branch_taken == 1, "Propagate_BranchTaken");
- check(dut->mem_branch_target == 0x80002008, "Propagate_BranchTarget");
- check(dut->mem_is_load == 1, "Propagate_IsLoad");
- check(dut->mem_reg_write_en == 1, "Propagate_RegWriteEn");
- check(dut->mem_rd_addr == 15, "Propagate_RdAddr");
- check(dut->mem_wb_sel == 1, "Propagate_WbSel");
-
- // 3. Stall (Freeze)
- // Change input, but assert stall
- dut->stall_en = 1;
- dut->ex_pc = 0x80003000;
- dut->ex_rs1_rdata = 0xCCCCCCCC;
- dut->ex_rs1_addr = 20;
- dut->ex_branch_taken = 0;
- dut->ex_rd_addr = 20;
- dut->ex_reg_write_en = 0;
- dut->ex_is_load = 0;
-
- tick(dut);
-
- check(dut->mem_pc == 0x80002000, "Stall_Maintains_PC");
- check(dut->mem_rs1_rdata == 0xAAAAAAAA, "Stall_Maintains_RS1");
- check(dut->mem_rs1_addr == 15, "Stall_Maintains_RS1_Addr");
- check(dut->mem_branch_taken == 1, "Stall_Maintains_BranchTaken");
- check(dut->mem_rd_addr == 15, "Stall_Maintains_RdAddr");
- check(dut->mem_reg_write_en == 1, "Stall_Maintains_RegWriteEn");
- check(dut->mem_is_load == 1, "Stall_Maintains_IsLoad");
-
- // Release Stall
- dut->stall_en = 0;
- tick(dut);
- check(dut->mem_pc == 0x80003000, "ReleaseStall_Captures_PC");
- check(dut->mem_rs1_rdata == 0xCCCCCCCC, "ReleaseStall_Captures_RS1");
- check(dut->mem_rs1_addr == 20, "ReleaseStall_Captures_RS1_Addr");
- check(dut->mem_branch_taken == 0, "ReleaseStall_Captures_BranchTaken");
- check(dut->mem_rd_addr == 20, "ReleaseStall_Captures_RdAddr");
- check(dut->mem_reg_write_en == 0, "ReleaseStall_Captures_RegWriteEn");
-
- // 4. Flush (Insert Bubble/NOP)
- // Change input, but assert flush
- dut->flush_en = 1;
- dut->ex_pc = 0x80004000;
- dut->ex_branch_taken = 1;
- dut->ex_reg_write_en = 1;
- dut->ex_is_store = 1;
-
- tick(dut);
-
- check(dut->mem_pc == 0x80003000, "Flush_Maintains_PC");
- check(dut->mem_branch_taken == 0, "Flush_Clears_BranchTaken");
- check(dut->mem_reg_write_en == 0, "Flush_Clears_RegWrite");
- check(dut->mem_is_store == 0, "Flush_Clears_StoreEn");
-
- // 5. Flush Overrides Stall
- dut->flush_en = 0;
- dut->ex_pc = 0x80005000;
- dut->ex_reg_write_en = 1;
- tick(dut);
- check(dut->mem_pc == 0x80005000, "Pre_FlushStall_Setup");
-
- dut->flush_en = 1;
- dut->stall_en = 1;
- dut->ex_pc = 0x80006000;
- dut->ex_branch_taken = 1;
- tick(dut);
-
- check(dut->mem_pc == 0x80005000, "Flush_Overrides_Stall_PC");
- check(dut->mem_branch_taken == 0, "Flush_Overrides_Stall_BranchTaken");
- check(dut->mem_reg_write_en == 0, "Flush_Overrides_Stall_RegWrite");
- check(dut->mem_is_store == 0, "Flush_Overrides_Stall_Store");
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << "\033[32mAll " << pass_count << " tests passed!\033[0m" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- delete dut;
- return 0;
-}
diff --git a/rv64cpu/tb/unit/tb_ex_stage.cc b/rv64cpu/tb/unit/tb_ex_stage.cc
deleted file mode 100644
index 9090a53..0000000
--- a/rv64cpu/tb/unit/tb_ex_stage.cc
+++ /dev/null
@@ -1,218 +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_ex_stage.cc
-// Description: Execution Stage unit test
-
-#include <iostream>
-#include <iomanip>
-#include <cstdint>
-#include <cassert>
-#include <verilated.h>
-#include "Vex_stage.h"
-
-int pass_count = 0;
-
-void eval(Vex_stage* dut) {
- dut->eval();
-}
-
-void check(bool cond, const char* name) {
- if (cond) {
- std::cout << "\033[32m[PASS] " << name << "\033[0m" << std::endl;
- pass_count++;
- } else {
- std::cout << "\033[31m[FAIL] " << name << "\033[0m" << std::endl;
- assert(false);
- }
-}
-
-// Emulate rv64_pkg enums
-enum AluOp { ALU_ADD = 0, ALU_SUB = 8, ALU_SLL = 1, ALU_SLT = 2, ALU_SLTU = 3,
- ALU_XOR = 4, ALU_SRL = 5, ALU_SRA = 13, ALU_OR = 6, ALU_AND = 7 };
-enum BranchOp { BRANCH_EQ = 0, BRANCH_NE = 1, BRANCH_LT = 4, BRANCH_GE = 5, BRANCH_LTU = 6, BRANCH_GEU = 7 };
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Vex_stage* dut = new Vex_stage;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << " Execute Stage Test" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- // Default inputs
- dut->ex_pc = 0;
- dut->ex_rs1_rdata = 0;
- dut->ex_rs2_rdata = 0;
- dut->ex_imm = 0;
- dut->ex_alu_op = ALU_ADD;
- dut->ex_alu_src1_sel = 0;
- dut->ex_alu_src2_sel = 0;
- dut->ex_is_branch = 0;
- dut->ex_branch_op = 0;
- dut->ex_is_jump = 0;
- dut->ex_is_word_op = 0;
-
- // 1. Basic R-Type (ADD)
- // rs1 + rs2
- dut->ex_rs1_rdata = 50;
- dut->ex_rs2_rdata = 25;
- dut->ex_alu_src1_sel = 0; // rs1
- dut->ex_alu_src2_sel = 0; // rs2
- dut->ex_alu_op = ALU_ADD;
-
- eval(dut);
- check(dut->ex_alu_result == 75, "R_Type_ADD");
- check(dut->ex_branch_taken == 0, "R_Type_Not_Branch");
-
- // 2. Basic I-Type (ADDI)
- // rs1 + imm
- dut->ex_rs1_rdata = 100;
- dut->ex_imm = 42;
- dut->ex_alu_src1_sel = 0; // rs1
- dut->ex_alu_src2_sel = 1; // imm
- dut->ex_alu_op = ALU_ADD;
-
- eval(dut);
- check(dut->ex_alu_result == 142, "I_Type_ADDI");
-
- // 3. Branch Taken (BEQ: rs1 == rs2)
- // target = PC + imm_b
- dut->ex_is_branch = 1;
- dut->ex_branch_op = BRANCH_EQ;
- dut->ex_rs1_rdata = 88;
- dut->ex_rs2_rdata = 88; // Equal!
-
- dut->ex_pc = 0x80001000;
- dut->ex_imm = 0x10;
- dut->ex_alu_src1_sel = 1; // PC
- dut->ex_alu_src2_sel = 1; // imm
- dut->ex_alu_op = ALU_ADD;
-
- eval(dut);
- check(dut->ex_branch_taken == 1, "Branch_BEQ_Taken");
- check(dut->ex_branch_target == 0x80001010, "Branch_Target_Calculation");
-
- // 4. Branch Not Taken (BEQ: rs1 != rs2)
- dut->ex_rs1_rdata = 88;
- dut->ex_rs2_rdata = 99; // Not Equal
- eval(dut);
- check(dut->ex_branch_taken == 0, "Branch_BEQ_Not_Taken");
-
- // 5. Jump (JAL)
- // target = PC + imm_j (always taken)
- dut->ex_is_branch = 0;
- dut->ex_is_jump = 1;
- dut->ex_pc = 0x80002000;
- dut->ex_imm = 0x24; // Forward jump 36 bytes
- dut->ex_alu_src1_sel = 1; // PC
- dut->ex_alu_src2_sel = 1; // imm
- dut->ex_alu_op = ALU_ADD;
-
- eval(dut);
- check(dut->ex_branch_taken == 1, "Jump_JAL_Taken");
- check(dut->ex_branch_target == 0x80002024, "Jump_JAL_Target");
-
- // 6. Jump Register (JALR)
- // target = (rs1 + imm_i) & ~1
- dut->ex_is_jump = 1;
- dut->ex_rs1_rdata = 0x80003001; // Intentionally unaligned
- dut->ex_imm = 4;
- dut->ex_alu_src1_sel = 0; // rs1
- dut->ex_alu_src2_sel = 1; // imm
- dut->ex_alu_op = ALU_ADD;
-
- eval(dut);
- // 0x80003001 + 4 = 0x80003005. Target -> 0x80003004.
- check(dut->ex_branch_taken == 1, "Jump_JALR_Taken");
- check(dut->ex_branch_target == 0x80003004, "Jump_JALR_Clears_LSB");
- check(dut->ex_alu_result == 0x80003005, "JALR_Raw_ALU_Result");
-
- // 7. Word Operation (ADDW)
- // Force 32-bit addition with sign extension
- dut->ex_is_jump = 0;
- dut->ex_is_word_op = 1;
- dut->ex_rs1_rdata = 0x000000007FFFFFFF;
- dut->ex_rs2_rdata = 0x0000000000000001;
- dut->ex_alu_src1_sel = 0; // rs1
- dut->ex_alu_src2_sel = 0; // rs2
- dut->ex_alu_op = ALU_ADD;
-
- // 0x7FFFFFFF + 1 = 0x80000000. Under 64-bit ADD this is positive 0x80000000.
- // However, ADDW requires sign-extending the lower 32 bits. Therefore, it becomes 0xFFFFFFFF80000000.
- eval(dut);
- check(dut->ex_alu_result == 0xFFFFFFFF80000000ULL, "Word_Op_ADDW_SignExt");
-
- // 8. Load/Store Address Calculation
- // target = rs1 + imm
- dut->ex_is_word_op = 0;
- dut->ex_rs1_rdata = 0x8000A000;
- dut->ex_imm = 0xFFFFFFFFFFFFFFF0ULL; // -16
- dut->ex_alu_src1_sel = 0; // rs1
- dut->ex_alu_src2_sel = 1; // imm
- dut->ex_alu_op = ALU_ADD;
-
- eval(dut);
- check(dut->ex_alu_result == 0x80009FF0, "LoadStore_Address_Mem");
-
- // 9. Shift Left Logical (SLL)
- dut->ex_is_word_op = 0;
- dut->ex_rs1_rdata = 0x000000000000FFFFULL;
- dut->ex_rs2_rdata = 16; // Shift by 16
- dut->ex_alu_src1_sel = 0;
- dut->ex_alu_src2_sel = 0;
- dut->ex_alu_op = ALU_SLL;
- eval(dut);
- check(dut->ex_alu_result == 0x00000000FFFF0000ULL, "ALU_SLL_64bit");
-
- // 10. Shift Right Arithmetic (SRA)
- dut->ex_rs1_rdata = 0xFFFFFFFF00000000ULL;
- dut->ex_rs2_rdata = 16;
- dut->ex_alu_op = ALU_SRA;
- eval(dut);
- check(dut->ex_alu_result == 0xFFFFFFFFFFFF0000ULL, "ALU_SRA_64bit");
-
- // 11. Word Shift Right Arithmetic (SRAW)
- dut->ex_is_word_op = 1;
- dut->ex_rs1_rdata = 0xFFFFFFFF80000000ULL; // -2147483648
- dut->ex_rs2_rdata = 4;
- dut->ex_alu_op = ALU_SRA;
- eval(dut);
- // 0x80000000 arithmetically shifted right by 4 is 0xF8000000. Sign extended to 64-bit.
- check(dut->ex_alu_result == 0xFFFFFFFFF8000000ULL, "ALU_SRAW_32bit");
-
- // 12. Word Shift Left Logical (SLLW) with large shamt
- dut->ex_rs1_rdata = 0x000000000000FFFFULL;
- dut->ex_rs2_rdata = 0x24; // 36 = 100100 in binary. Masked to 5 bits = 4.
- dut->ex_alu_op = ALU_SLL;
- eval(dut);
- check(dut->ex_alu_result == 0x00000000000FFFF0ULL, "ALU_SLLW_Shamt_Masking");
-
- // 13. Word Shift Right Logical (SRLW) with negative 32-bit
- // Zero extends the 32-bit source, avoiding sign extension during logical shift
- dut->ex_rs1_rdata = 0xFFFFFFFF80000000ULL;
- dut->ex_rs2_rdata = 4;
- dut->ex_alu_op = ALU_SRL;
- eval(dut);
- // 0x80000000 logically shifted right by 4 is 0x08000000. Sign extended to 64-bit -> positive.
- check(dut->ex_alu_result == 0x0000000008000000ULL, "ALU_SRLW_ZeroExtend");
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << "\033[32mAll " << pass_count << " tests passed!\033[0m" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- delete dut;
- return 0;
-}
diff --git a/rv64cpu/tb/unit/tb_hazard_unit.cc b/rv64cpu/tb/unit/tb_hazard_unit.cc
deleted file mode 100644
index 6314ab8..0000000
--- a/rv64cpu/tb/unit/tb_hazard_unit.cc
+++ /dev/null
@@ -1,364 +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_hazard_unit.cc
-// Description: Hazard Unit testbench — exhaustive forwarding, stall, and flush tests
-
-#include <iostream>
-#include <cassert>
-#include <verilated.h>
-#include "Vhazard_unit.h"
-
-int pass_count = 0;
-
-void check(bool cond, const char* name) {
- if (cond) {
- std::cout << "\033[32m[PASS] " << name << "\033[0m" << std::endl;
- pass_count++;
- } else {
- std::cout << "\033[31m[FAIL] " << name << "\033[0m" << std::endl;
- assert(false);
- }
-}
-
-void reset(Vhazard_unit* dut) {
- dut->id_rs1_addr = 0;
- dut->id_rs2_addr = 0;
- dut->ex_rs1_addr = 0;
- dut->ex_rs2_addr = 0;
- dut->ex_rd_addr = 0;
- dut->ex_reg_write_en = 0;
- dut->ex_is_load = 0;
- dut->ex_branch_taken = 0;
- dut->ex_branch_target = 0;
- dut->mem_rd_addr = 0;
- dut->mem_reg_write_en = 0;
- dut->mem_result_late = 0;
- dut->mem_stall_req = 0;
- dut->trap_flush_req = 0;
- dut->trap_pc_sel = 0;
- dut->trap_target_pc = 0;
- dut->wb_rd_addr = 0;
- dut->wb_reg_write_en = 0;
- dut->eval();
-}
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Vhazard_unit* dut = new Vhazard_unit;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << " Hazard Unit Test" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- // -----------------------
- // 1. No hazard: all quiet
- // -----------------------
- reset(dut);
- check(dut->forward_a_sel == 0, "NoHazard_FwdA_None");
- check(dut->forward_b_sel == 0, "NoHazard_FwdB_None");
- check(dut->if_stall_en == 0, "NoHazard_NoStall");
- check(dut->if_id_flush_en == 0, "NoHazard_NoFlush");
- check(dut->id_ex_flush_en == 0, "NoHazard_NoIDEXFlush");
- check(dut->id_ex_stall_en == 0, "NoHazard_NoIDEXStall");
- check(dut->ex_mem_stall_en == 0, "NoHazard_NoEXMEMStall");
- check(dut->pc_redirect_sel == 0, "NoHazard_NoRedirect");
- check(dut->pc_redirect_is_trap == 0, "NoHazard_NoTrapRedirect");
-
- // -----------------------
- // 2. Forward A from MEM stage
- // -----------------------
- reset(dut);
- dut->ex_rs1_addr = 5;
- dut->mem_rd_addr = 5;
- dut->mem_reg_write_en = 1;
- dut->eval();
- check(dut->forward_a_sel == 1, "FwdA_From_MEM");
- check(dut->forward_b_sel == 0, "FwdA_From_MEM_B_None");
-
- // -----------------------
- // 3. Forward A from WB stage
- // -----------------------
- reset(dut);
- dut->ex_rs1_addr = 7;
- dut->wb_rd_addr = 7;
- dut->wb_reg_write_en = 1;
- dut->eval();
- check(dut->forward_a_sel == 2, "FwdA_From_WB");
-
- // -----------------------
- // 4. Forward B from MEM stage
- // -----------------------
- reset(dut);
- dut->ex_rs2_addr = 10;
- dut->mem_rd_addr = 10;
- dut->mem_reg_write_en = 1;
- dut->eval();
- check(dut->forward_b_sel == 1, "FwdB_From_MEM");
- check(dut->forward_a_sel == 0, "FwdB_From_MEM_A_None");
-
- // -----------------------
- // 5. Forward B from WB stage
- // -----------------------
- reset(dut);
- dut->ex_rs2_addr = 12;
- dut->wb_rd_addr = 12;
- dut->wb_reg_write_en = 1;
- dut->eval();
- check(dut->forward_b_sel == 2, "FwdB_From_WB");
-
- // -----------------------
- // 6. MEM > WB priority for same register
- // -----------------------
- reset(dut);
- dut->ex_rs1_addr = 3;
- dut->mem_rd_addr = 3;
- dut->mem_reg_write_en = 1;
- dut->wb_rd_addr = 3;
- dut->wb_reg_write_en = 1;
- dut->eval();
- check(dut->forward_a_sel == 1, "FwdA_MEM_Priority_Over_WB");
-
- reset(dut);
- dut->ex_rs2_addr = 3;
- dut->mem_rd_addr = 3;
- dut->mem_reg_write_en = 1;
- dut->wb_rd_addr = 3;
- dut->wb_reg_write_en = 1;
- dut->eval();
- check(dut->forward_b_sel == 1, "FwdB_MEM_Priority_Over_WB");
-
- reset(dut);
- dut->ex_rs1_addr = 3;
- dut->mem_rd_addr = 3;
- dut->mem_reg_write_en = 1;
- dut->mem_result_late = 1;
- dut->wb_rd_addr = 3;
- dut->wb_reg_write_en = 1;
- dut->eval();
- check(dut->forward_a_sel == 2, "LateResult_ForwardA_From_WB");
-
- reset(dut);
- dut->ex_rs2_addr = 3;
- dut->mem_rd_addr = 3;
- dut->mem_reg_write_en = 1;
- dut->mem_result_late = 1;
- dut->wb_rd_addr = 3;
- dut->wb_reg_write_en = 1;
- dut->eval();
- check(dut->forward_b_sel == 2, "LateResult_ForwardB_From_WB");
-
- // -----------------------
- // 7. No forward to x0 (hardwired zero)
- // -----------------------
- reset(dut);
- dut->ex_rs1_addr = 0;
- dut->mem_rd_addr = 0;
- dut->mem_reg_write_en = 1;
- dut->eval();
- check(dut->forward_a_sel == 0, "NoFwd_x0_A_MEM");
-
- reset(dut);
- dut->ex_rs2_addr = 0;
- dut->wb_rd_addr = 0;
- dut->wb_reg_write_en = 1;
- dut->eval();
- check(dut->forward_b_sel == 0, "NoFwd_x0_B_WB");
-
- // -----------------------
- // 8. No forward if reg_write_en is 0
- // -----------------------
- reset(dut);
- dut->ex_rs1_addr = 5;
- dut->mem_rd_addr = 5;
- dut->mem_reg_write_en = 0;
- dut->eval();
- check(dut->forward_a_sel == 0, "NoFwd_Write_Disabled_MEM");
-
- reset(dut);
- dut->ex_rs2_addr = 5;
- dut->wb_rd_addr = 5;
- dut->wb_reg_write_en = 0;
- dut->eval();
- check(dut->forward_b_sel == 0, "NoFwd_Write_Disabled_WB");
-
- // -----------------------
- // 9. Forward A and B simultaneously
- // -----------------------
- reset(dut);
- dut->ex_rs1_addr = 8;
- dut->ex_rs2_addr = 9;
- dut->mem_rd_addr = 8;
- dut->mem_reg_write_en = 1;
- dut->wb_rd_addr = 9;
- dut->wb_reg_write_en = 1;
- dut->eval();
- check(dut->forward_a_sel == 1, "FwdA_MEM_And_FwdB_WB_A");
- check(dut->forward_b_sel == 2, "FwdA_MEM_And_FwdB_WB_B");
-
- // -----------------------
- // 10. Load-Use stall (rs1 match)
- // -----------------------
- reset(dut);
- dut->ex_is_load = 1;
- dut->ex_rd_addr = 4;
- dut->id_rs1_addr = 4;
- dut->eval();
- check(dut->if_stall_en == 1, "LoadUse_RS1_IF_Stall");
- check(dut->if_id_stall_en == 1, "LoadUse_RS1_IFID_Stall");
- check(dut->id_ex_flush_en == 1, "LoadUse_RS1_IDEX_Flush");
- check(dut->id_ex_stall_en == 0, "LoadUse_RS1_IDEX_NoStall");
-
- // -----------------------
- // 11. Load-Use stall (rs2 match)
- // -----------------------
- reset(dut);
- dut->ex_is_load = 1;
- dut->ex_rd_addr = 6;
- dut->id_rs2_addr = 6;
- dut->eval();
- check(dut->if_stall_en == 1, "LoadUse_RS2_IF_Stall");
- check(dut->if_id_stall_en == 1, "LoadUse_RS2_IFID_Stall");
- check(dut->id_ex_flush_en == 1, "LoadUse_RS2_IDEX_Flush");
-
- // -----------------------
- // 12. No load-use stall to x0
- // -----------------------
- reset(dut);
- dut->ex_is_load = 1;
- dut->ex_rd_addr = 0;
- dut->id_rs1_addr = 0;
- dut->eval();
- check(dut->if_stall_en == 0, "LoadUse_x0_NoStall");
-
- // -----------------------
- // 13. No load-use stall if not a load
- // -----------------------
- reset(dut);
- dut->ex_is_load = 0;
- dut->ex_rd_addr = 4;
- dut->id_rs1_addr = 4;
- dut->eval();
- check(dut->if_stall_en == 0, "NotLoad_NoStall");
-
- // -----------------------
- // 14. Branch taken flush
- // -----------------------
- reset(dut);
- dut->ex_branch_taken = 1;
- dut->ex_branch_target = 0x400;
- dut->eval();
- check(dut->if_id_flush_en == 1, "Branch_IFID_Flush");
- check(dut->id_ex_flush_en == 1, "Branch_IDEX_Flush");
- check(dut->if_stall_en == 0, "Branch_IF_NoStall");
- check(dut->if_id_stall_en == 0, "Branch_IFID_NoStall");
- check(dut->pc_redirect_sel == 1, "Branch_Redirect_Valid");
- check(dut->pc_redirect_is_trap == 0, "Branch_Redirect_NotTrap");
- check(dut->pc_redirect_target == 0x400, "Branch_Redirect_Target");
-
- // -----------------------
- // 15. Branch overrides load-use stall
- // -----------------------
- reset(dut);
- dut->ex_branch_taken = 1;
- dut->ex_is_load = 1;
- dut->ex_rd_addr = 4;
- dut->id_rs1_addr = 4;
- dut->eval();
- check(dut->if_id_flush_en == 1, "BranchOverride_IFID_Flush");
- check(dut->id_ex_flush_en == 1, "BranchOverride_IDEX_Flush");
- check(dut->if_stall_en == 0, "BranchOverride_IF_NoStall");
- check(dut->if_id_stall_en == 0, "BranchOverride_IFID_NoStall");
-
- // -----------------------
- // 16. Memory stall (D-Cache miss)
- // -----------------------
- reset(dut);
- dut->mem_stall_req = 1;
- dut->eval();
- check(dut->if_stall_en == 1, "MemStall_IF_Stall");
- check(dut->if_id_stall_en == 1, "MemStall_IFID_Stall");
- check(dut->id_ex_stall_en == 1, "MemStall_IDEX_Stall");
- check(dut->ex_mem_stall_en == 1, "MemStall_EXMEM_Stall");
- check(dut->if_id_flush_en == 0, "MemStall_NoFlush");
- check(dut->id_ex_flush_en == 0, "MemStall_NoIDEXFlush");
-
- // -----------------------
- // 17. Mem stall suppresses load-use flush
- // -----------------------
- reset(dut);
- dut->mem_stall_req = 1;
- dut->ex_is_load = 1;
- dut->ex_rd_addr = 4;
- dut->id_rs1_addr = 4;
- dut->eval();
- check(dut->if_stall_en == 1, "MemStall_Suppresses_LoadUse_Stall");
- check(dut->id_ex_stall_en == 1, "MemStall_Suppresses_LoadUse_IDEXStall");
- check(dut->id_ex_flush_en == 0, "MemStall_Suppresses_LoadUse_Flush");
-
- // -----------------------
- // 18. Branch overrides mem stall for flush
- // -----------------------
- reset(dut);
- dut->ex_branch_taken = 1;
- dut->ex_branch_target = 0x410;
- dut->mem_stall_req = 1;
- dut->eval();
- check(dut->if_id_flush_en == 1, "Branch_MemStall_IFID_Flush");
- check(dut->id_ex_flush_en == 1, "Branch_MemStall_IDEX_Flush");
- check(dut->if_stall_en == 0, "Branch_MemStall_IF_NoStall");
-
- // -----------------------
- // 19. Trap redirect overrides branch and stalls
- // -----------------------
- reset(dut);
- dut->trap_pc_sel = 1;
- dut->trap_flush_req = 1;
- dut->trap_target_pc = 0x800;
- dut->ex_branch_taken = 1;
- dut->ex_branch_target = 0x900;
- dut->mem_stall_req = 1;
- dut->eval();
- check(dut->pc_redirect_sel == 1, "Trap_Redirect_Valid");
- check(dut->pc_redirect_is_trap == 1, "Trap_Redirect_IsTrap");
- check(dut->pc_redirect_target == 0x800, "Trap_Redirect_Target");
- check(dut->if_id_flush_en == 1, "Trap_Flush_IFID");
- check(dut->id_ex_flush_en == 1, "Trap_Flush_IDEX");
- check(dut->if_stall_en == 0, "Trap_Clears_IF_Stall");
- check(dut->if_id_stall_en == 0, "Trap_Clears_IFID_Stall");
- check(dut->id_ex_stall_en == 0, "Trap_Clears_IDEX_Stall");
- check(dut->ex_mem_stall_en == 0, "Trap_Clears_EXMEM_Stall");
-
- // -----------------------
- // 20. Trap flush overrides load-use stall even without redirect
- // -----------------------
- reset(dut);
- dut->trap_flush_req = 1;
- dut->ex_is_load = 1;
- dut->ex_rd_addr = 4;
- dut->id_rs1_addr = 4;
- dut->eval();
- check(dut->if_id_flush_en == 1, "TrapFlush_IFID");
- check(dut->id_ex_flush_en == 1, "TrapFlush_IDEX");
- check(dut->if_stall_en == 0, "TrapFlush_NoIFStall");
- check(dut->if_id_stall_en == 0, "TrapFlush_NoIFIDStall");
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << "\033[32mAll " << pass_count << " tests passed!\033[0m" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- delete dut;
- return 0;
-}
diff --git a/rv64cpu/tb/unit/tb_id_ex_reg.cc b/rv64cpu/tb/unit/tb_id_ex_reg.cc
deleted file mode 100644
index d9cc329..0000000
--- a/rv64cpu/tb/unit/tb_id_ex_reg.cc
+++ /dev/null
@@ -1,177 +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_id_ex_reg.cc
-// Description: ID/EX Pipeline Register unit test
-
-#include <iostream>
-#include <iomanip>
-#include <cstdint>
-#include <cassert>
-#include <verilated.h>
-#include "Vid_ex_reg.h"
-
-int pass_count = 0;
-
-void tick(Vid_ex_reg* dut) {
- dut->clk = 0;
- dut->eval();
- dut->clk = 1;
- dut->eval();
- dut->clk = 0;
- dut->eval();
-}
-
-void check(bool cond, const char* name) {
- if (cond) {
- std::cout << "\033[32m[PASS] " << name << "\033[0m" << std::endl;
- pass_count++;
- } else {
- std::cout << "\033[31m[FAIL] " << name << "\033[0m" << std::endl;
- assert(false);
- }
-}
-
-// Emulate rv64_pkg::alu_op_t
-enum AluOp { ALU_ADD = 0, ALU_SUB = 8, ALU_SLL = 1, ALU_SLT = 2, ALU_SLTU = 3,
- ALU_XOR = 4, ALU_SRL = 5, ALU_SRA = 13, ALU_OR = 6, ALU_AND = 7 };
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Vid_ex_reg* dut = new Vid_ex_reg;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << " ID/EX Register Test" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- // 1. Init / Reset
- dut->stall_en = 0;
- dut->flush_en = 0;
-
- // Set some random ID stage inputs with dangerous states
- dut->id_pc = 0x80001000;
- dut->id_rs1_rdata = 0x11111111;
- dut->id_rs2_rdata = 0x22222222;
- dut->id_rs1_addr = 31;
- dut->id_rs2_addr = 15;
- dut->id_imm = 0x1A2B3C;
- dut->id_alu_op = ALU_XOR;
- dut->id_reg_write_en = 1;
- dut->id_is_store = 1;
- dut->id_is_load = 1;
- dut->id_is_branch = 1;
- dut->id_is_jump = 1;
- dut->id_is_csr = 1;
- dut->id_illegal_instr = 1;
-
- dut->rst = 1;
- tick(dut);
- dut->rst = 0;
-
- // Verify reset clears out dangerous signals
- check(dut->ex_pc == 0, "Reset_Clears_PC");
- check(dut->ex_reg_write_en == 0, "Reset_Clears_RegWrite");
- check(dut->ex_is_store == 0, "Reset_Clears_IsStore");
- check(dut->ex_is_load == 0, "Reset_Clears_IsLoad");
- check(dut->ex_is_branch == 0, "Reset_Clears_IsBranch");
- check(dut->ex_is_jump == 0, "Reset_Clears_IsJump");
- check(dut->ex_is_csr == 0, "Reset_Clears_IsCSR");
- check(dut->ex_illegal_instr == 0, "Reset_Clears_IllegalInstr");
-
- // 2. Normal Propagation
- dut->id_pc = 0x80002000;
- dut->id_rs1_rdata = 0xAAAAAAAA;
- dut->id_rs2_rdata = 0xBBBBBBBB;
- dut->id_rs1_addr = 10;
- dut->id_rs2_addr = 11;
- dut->id_imm = 42;
- dut->id_alu_op = ALU_ADD;
- dut->id_reg_write_en = 1;
- dut->id_rd_addr = 5;
- dut->id_is_load = 1;
- dut->id_is_branch = 0;
-
- tick(dut);
-
- check(dut->ex_pc == 0x80002000, "Propagate_PC");
- check(dut->ex_rs1_rdata == 0xAAAAAAAA, "Propagate_RS1");
- check(dut->ex_rs2_rdata == 0xBBBBBBBB, "Propagate_RS2");
- check(dut->ex_rs1_addr == 10, "Propagate_RS1_Addr");
- check(dut->ex_rs2_addr == 11, "Propagate_RS2_Addr");
- check(dut->ex_imm == 42, "Propagate_IMM");
- check(dut->ex_alu_op == ALU_ADD, "Propagate_ALU_OP");
- check(dut->ex_reg_write_en == 1, "Propagate_RegWriteEn");
- check(dut->ex_rd_addr == 5, "Propagate_RdAddr");
- check(dut->ex_is_load == 1, "Propagate_IsLoad");
-
- // 3. Stall (Freeze)
- // Change input, but assert stall
- dut->stall_en = 1;
- dut->id_pc = 0x80003000;
- dut->id_rd_addr = 10;
- dut->id_reg_write_en = 0;
- dut->id_is_load = 0;
-
- tick(dut);
-
- check(dut->ex_pc == 0x80002000, "Stall_Maintains_PC");
- check(dut->ex_rd_addr == 5, "Stall_Maintains_RdAddr");
- check(dut->ex_reg_write_en == 1, "Stall_Maintains_RegWriteEn");
- check(dut->ex_is_load == 1, "Stall_Maintains_IsLoad");
-
- // Release Stall
- dut->stall_en = 0;
- tick(dut);
- check(dut->ex_pc == 0x80003000, "ReleaseStall_Captures_PC");
- check(dut->ex_rd_addr == 10, "ReleaseStall_Captures_RdAddr");
- check(dut->ex_reg_write_en == 0, "ReleaseStall_Captures_RegWriteEn");
-
- // 4. Flush (Insert Bubble/NOP)
- // Change input, but assert flush
- dut->flush_en = 1;
- dut->id_pc = 0x80004000;
- dut->id_reg_write_en = 1;
- dut->id_is_store = 1;
- dut->id_is_branch = 1;
-
- tick(dut);
-
- check(dut->ex_pc == 0x80003000, "Flush_Maintains_PC");
- check(dut->ex_reg_write_en == 0, "Flush_Clears_RegWrite");
- check(dut->ex_is_store == 0, "Flush_Clears_StoreEn");
- check(dut->ex_is_branch == 0, "Flush_Clears_BranchEn");
-
- // 5. Flush Overrides Stall
- dut->flush_en = 0;
- dut->id_pc = 0x80005000;
- dut->id_reg_write_en = 1;
- tick(dut);
- check(dut->ex_pc == 0x80005000, "Pre_FlushStall_Setup");
-
- dut->flush_en = 1;
- dut->stall_en = 1;
- dut->id_pc = 0x80006000;
- tick(dut);
-
- check(dut->ex_pc == 0x80005000, "Flush_Overrides_Stall_PC");
- check(dut->ex_reg_write_en == 0, "Flush_Overrides_Stall_RegWrite");
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << "\033[32mAll " << pass_count << " tests passed!\033[0m" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- delete dut;
- return 0;
-}
diff --git a/rv64cpu/tb/unit/tb_id_stage.cc b/rv64cpu/tb/unit/tb_id_stage.cc
deleted file mode 100644
index 26dfd97..0000000
--- a/rv64cpu/tb/unit/tb_id_stage.cc
+++ /dev/null
@@ -1,194 +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_id_stage.cc
-// Description: ID Stage integration test
-// Verifies that id_stage correctly wires decoder + regfile:
-// 1. Decoder control signal pass-through
-// 2. RegFile write-back and read-out
-// 3. PC pass-through
-// 4. Immediate extraction
-
-#include <iostream>
-#include <cstdint>
-#include <cassert>
-#include <verilated.h>
-#include "Vid_stage.h"
-
-static int pass_count = 0;
-
-void tick(Vid_stage* dut) {
- dut->clk = 0;
- dut->eval();
- dut->clk = 1;
- dut->eval();
- dut->clk = 0;
- dut->eval();
-}
-
-void check(bool cond, const char* name) {
- if (cond) {
- std::cout << "\033[32m[PASS] " << name << "\033[0m" << std::endl;
- pass_count++;
- } else {
- std::cout << "\033[31m[FAIL] " << name << "\033[0m" << std::endl;
- assert(false);
- }
-}
-
-// ALU op encoding must match rv64_pkg
-enum AluOp { ALU_ADD = 0, ALU_SUB = 8, ALU_SLL = 1, ALU_SLT = 2, ALU_SLTU = 3,
- ALU_XOR = 4, ALU_SRL = 5, ALU_SRA = 13, ALU_OR = 6, ALU_AND = 7 };
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Vid_stage* dut = new Vid_stage;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << " ID Stage Test" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- // Initialize
- dut->clk = 0;
- dut->rst = 0;
- dut->wb_reg_write_en = 0;
- dut->wb_rd_addr = 0;
- dut->wb_rd_wdata = 0;
- dut->if_pc = 0;
- dut->if_instr = 0;
-
- // Reset the regfile
- dut->rst = 1;
- tick(dut);
- dut->rst = 0;
-
- // -----------------
- // Test 1: PC Pass-through
- dut->if_pc = 0x80001000ULL;
- dut->if_instr = 0x00000013; // NOP (ADDI x0, x0, 0)
- dut->eval();
- check(dut->id_pc == 0x80001000ULL, "PC_Passthrough");
-
- // -----------------
- // Test 2: Decode ADDI x1, x0, 42 (0x02A00093)
- dut->if_instr = 0x02A00093;
- dut->eval();
- check(dut->id_rd_addr == 1, "ADDI_rd_addr");
- check(dut->id_alu_op == ALU_ADD, "ADDI_alu_op");
- check(dut->id_alu_src2_sel == 1, "ADDI_alu_src2_sel");
- check(dut->id_imm == 42, "ADDI_imm");
- check(dut->id_reg_write_en == 1, "ADDI_reg_write_en");
- check(dut->id_is_branch == 0, "ADDI_not_branch");
- check(dut->id_is_jump == 0, "ADDI_not_jump");
- check(dut->id_illegal_instr == 0, "ADDI_not_illegal");
-
- // -----------------
- // Test 3: RegFile write-back then read
- // Write 0xDEADBEEF to x5 via WB port, then decode an instruction that reads x5
- dut->wb_reg_write_en = 1;
- dut->wb_rd_addr = 5;
- dut->wb_rd_wdata = 0xDEADBEEF;
- tick(dut);
- dut->wb_reg_write_en = 0;
-
- // ADD x1, x5, x0 → 0x000280B3
- // rs1=x5, rs2=x0, rd=x1
- dut->if_instr = 0x000280B3;
- dut->eval();
- check(dut->id_rs1_addr == 5, "R_Type_rs1_addr");
- check(dut->id_rs2_addr == 0, "R_Type_rs2_addr");
- check(dut->id_rs1_rdata == 0xDEADBEEF, "RegFile_Read_x5");
- check(dut->id_rs2_rdata == 0, "RegFile_Read_x0");
- check(dut->id_alu_src2_sel == 0, "R_Type_alu_src2_sel");
-
- // -----------------
- // Test 4: Dual-port read via RegFile
- // Write 0x11111111 to x10, then decode ADD x1, x5, x10
- dut->wb_reg_write_en = 1;
- dut->wb_rd_addr = 10;
- dut->wb_rd_wdata = 0x11111111;
- tick(dut);
- dut->wb_reg_write_en = 0;
-
- // ADD x1, x5, x10 → funct7=0000000, rs2=01010, rs1=00101, funct3=000, rd=00001, op=0110011
- // = 0x00A280B3
- dut->if_instr = 0x00A280B3;
- dut->eval();
- check(dut->id_rs1_rdata == 0xDEADBEEF, "DualRead_rs1_x5");
- check(dut->id_rs2_rdata == 0x11111111, "DualRead_rs2_x10");
-
- // -----------------
- // Test 5: Branch BEQ x1, x2, -4
- // Encoding: 0xFE208EE3
- dut->if_instr = 0xFE208EE3;
- dut->eval();
- check(dut->id_is_branch == 1, "BEQ_is_branch");
- check(dut->id_reg_write_en == 0, "BEQ_no_reg_write");
- check(dut->id_branch_op == 0, "BEQ_branch_op");
- check(dut->id_imm == (uint64_t)(-4),"BEQ_imm_neg4");
-
- // -----------------
- // Test 6: JAL x1, +8 (0x008000EF)
- dut->if_instr = 0x008000EF;
- dut->eval();
- check(dut->id_is_jump == 1, "JAL_is_jump");
- check(dut->id_reg_write_en == 1, "JAL_reg_write_en");
- check(dut->id_wb_sel == 2, "JAL_wb_sel_PC4");
- check(dut->id_imm == 8, "JAL_imm_8");
-
- // -----------------
- // Test 7: LD x5, 8(x10) (0x00853283)
- dut->if_instr = 0x00853283;
- dut->eval();
- check(dut->id_is_load == 1, "LD_is_load");
- check(dut->id_reg_write_en == 1, "LD_reg_write_en");
- check(dut->id_wb_sel == 1, "LD_wb_sel_MEM");
- check(dut->id_imm == 8, "LD_imm_8");
- check(dut->id_mem_size == 3, "LD_mem_size_dword");
-
- // -----------------
- // Test 8: SD x5, 16(x10) (0x00553823)
- dut->if_instr = 0x00553823;
- dut->eval();
- check(dut->id_is_store == 1, "SD_is_store");
- check(dut->id_reg_write_en == 0, "SD_no_reg_write");
- check(dut->id_imm == 16, "SD_imm_16");
-
- // -----------------
- // Test 9: Illegal instruction (0x00000000)
- dut->if_instr = 0x00000000;
- dut->eval();
- check(dut->id_illegal_instr == 1, "Illegal_Instr");
-
- // -----------------
- // Test 10: ECALL (0x00000073)
- dut->if_instr = 0x00000073;
- dut->eval();
- check(dut->id_is_ecall == 1, "ECALL");
-
- // -----------------
- // Test 11: LUI x1, 0x12345 (0x123450B7)
- dut->if_instr = 0x123450B7;
- dut->eval();
- check(dut->id_reg_write_en == 1, "LUI_reg_write_en");
- check(dut->id_imm == 0x12345000ULL, "LUI_imm");
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << "\033[32mAll " << pass_count << " tests passed!\033[0m" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- delete dut;
- return 0;
-}
diff --git a/rv64cpu/tb/unit/tb_if_id_reg.cc b/rv64cpu/tb/unit/tb_if_id_reg.cc
deleted file mode 100644
index 6428e6f..0000000
--- a/rv64cpu/tb/unit/tb_if_id_reg.cc
+++ /dev/null
@@ -1,127 +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_if_id_reg.cc
-// Description: IF/ID Pipeline Register unit test
-
-#include <iostream>
-#include <iomanip>
-#include <cstdint>
-#include <cassert>
-#include <verilated.h>
-#include "Vif_id_reg.h"
-
-int pass_count = 0;
-
-void tick(Vif_id_reg* dut) {
- dut->clk = 0;
- dut->eval();
- dut->clk = 1;
- dut->eval();
- dut->clk = 0;
- dut->eval();
-}
-
-void check_reg(uint64_t expected_pc, uint32_t expected_instr, uint64_t actual_pc, uint32_t actual_instr, const char* test_name) {
- if (expected_pc == actual_pc && expected_instr == actual_instr) {
- std::cout << "\033[32m[PASS] " << test_name << " | PC = 0x" << std::hex << actual_pc << " Instr = 0x" << actual_instr << "\033[0m" << std::dec << std::endl;
- pass_count++;
- } else {
- std::cout << "\033[31m[FAIL] " << test_name << "\n Expected: PC=0x" << std::hex << expected_pc << " Instr=0x" << expected_instr
- << "\n Got: PC=0x" << actual_pc << " Instr=0x" << actual_instr << "\033[0m" << std::dec << std::endl;
- assert(false);
- }
-}
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Vif_id_reg* dut = new Vif_id_reg;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << " IF/ID Register Test" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- // --- 1. Init / Reset ---
- dut->stall_en = 0;
- dut->flush_en = 0;
- dut->if_pc = 0x80000000;
- dut->if_instr = 0xDEADBEEF;
-
- dut->rst = 1;
- tick(dut);
- dut->rst = 0;
-
- // After reset, PC should be 0, instr should be NOP (0x00000013)
- check_reg(0, 0x00000013, dut->id_pc, dut->id_instr, "Reset_Inserts_NOP");
-
- // --- 2. Normal Propagation ---
- dut->if_pc = 0x80001000;
- dut->if_instr = 0x02A00093; // ADDI x1, x0, 42
- tick(dut);
- check_reg(0x80001000, 0x02A00093, dut->id_pc, dut->id_instr, "Normal_Propagation");
-
- // --- 3. Stall (Freeze) ---
- dut->if_pc = 0x80001004;
- dut->if_instr = 0x000280B3; // New incoming data
- dut->stall_en = 1;
- tick(dut); // Clock ticks but stall is active
-
- // Register should hold previous values
- check_reg(0x80001000, 0x02A00093, dut->id_pc, dut->id_instr, "Stall_Holds_Previous_Data");
-
- dut->if_pc = 0x80001008;
- dut->if_instr = 0x008000EF;
- tick(dut); // Still stalled
- check_reg(0x80001000, 0x02A00093, dut->id_pc, dut->id_instr, "Stall_Multiple_Cycles");
-
- // Release stall
- dut->stall_en = 0;
- tick(dut);
- check_reg(0x80001008, 0x008000EF, dut->id_pc, dut->id_instr, "Release_Stall_Captures_New");
-
- // --- 4. Flush (Insert NOP) ---
- dut->if_pc = 0x8000100C;
- dut->if_instr = 0xDEADBEEF; // Some invalid instruction
- dut->flush_en = 1;
- tick(dut);
-
- // PC should be 0, instr should be NOP (0x00000013)
- check_reg(0, 0x00000013, dut->id_pc, dut->id_instr, "Flush_Inserts_NOP");
-
- // --- 5. Stall overrides Flush ? OR Flush overrides Stall ? ---
- // Usually, in RISC-V pipelines, if we have a branch mispredict (Flush),
- // we want to flush even if stalled, OR flush takes priority.
- // Based on if_id_reg.sv, `flush_en` has higher priority than `stall_en`.
-
- // Pre-load good data
- dut->flush_en = 0;
- dut->if_pc = 0x80002000;
- dut->if_instr = 0x12345678;
- tick(dut);
- check_reg(0x80002000, 0x12345678, dut->id_pc, dut->id_instr, "Pre_Flush_Stall_Test");
-
- // Apply simultaneous flush & stall
- dut->flush_en = 1;
- dut->stall_en = 1;
- tick(dut);
- check_reg(0, 0x00000013, dut->id_pc, dut->id_instr, "Flush_Overrides_Stall");
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << "\033[32mAll " << pass_count << " tests passed!\033[0m" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- delete dut;
- return 0;
-}
diff --git a/rv64cpu/tb/unit/tb_if_stage.cc b/rv64cpu/tb/unit/tb_if_stage.cc
deleted file mode 100644
index f0110c9..0000000
--- a/rv64cpu/tb/unit/tb_if_stage.cc
+++ /dev/null
@@ -1,132 +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_if_stage.cc
-// Description: IF Stage unit test
-
-#include <iostream>
-#include <iomanip>
-#include <cstdint>
-#include <cassert>
-#include <verilated.h>
-#include "Vif_stage.h"
-
-void tick(Vif_stage* dut) {
- dut->clk = 0;
- dut->eval();
- dut->clk = 1;
- dut->eval();
- dut->clk = 0;
- dut->eval();
-}
-
-void check_pc(uint64_t expected_pc, uint64_t actual_pc, const char* test_name) {
- if (expected_pc == actual_pc) {
- std::cout << "\033[32m[PASS] " << test_name << " | PC = 0x" << std::hex << actual_pc << "\033[0m" << std::dec << std::endl;
- } else {
- std::cout << "\033[31m[FAIL] " << test_name << " | Expected PC = 0x" << std::hex << expected_pc << " | Got PC = 0x" << actual_pc << "\033[0m" << std::dec << std::endl;
- assert(false);
- }
-}
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Vif_stage* dut = new Vif_stage;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << " IF Stage Test" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- // --- 1. Init / Reset ---
- dut->stall_en = 0;
- dut->pc_redirect_sel = 0;
- dut->pc_redirect_target = 0;
- dut->pc_redirect_is_trap = 0;
-
- dut->rst = 1;
- tick(dut);
- dut->rst = 0;
- check_pc(0x80000000ULL, dut->if_pc, "Reset_Boot_Address");
-
- // --- 2. Normal Execution (PC + 4) ---
- tick(dut);
- check_pc(0x80000004ULL, dut->if_pc, "Normal_Increment_1");
- tick(dut);
- check_pc(0x80000008ULL, dut->if_pc, "Normal_Increment_2");
-
- // --- 3. Pipeline Stall ---
- dut->stall_en = 1;
- tick(dut);
- check_pc(0x80000008ULL, dut->if_pc, "Stall_Maintains_PC");
- tick(dut);
- check_pc(0x80000008ULL, dut->if_pc, "Stall_Maintains_PC_Again");
-
- // Release Stall
- dut->stall_en = 0;
- tick(dut);
- check_pc(0x8000000CULL, dut->if_pc, "Release_Stall_Normal_Increment");
-
- // --- 4. Branch Taken ---
- dut->pc_redirect_sel = 1;
- dut->pc_redirect_is_trap = 0;
- dut->pc_redirect_target = 0x80000100ULL;
- tick(dut);
- check_pc(0x80000100ULL, dut->if_pc, "Branch_Taken_Updates_PC");
-
- // Return to normal
- dut->pc_redirect_sel = 0;
- tick(dut);
- check_pc(0x80000104ULL, dut->if_pc, "Post_Branch_Normal_Increment");
-
- // --- 5. Stall and Branch simultaneous priority ---
- // If a branch is taken but the pipeline is stalled, the PC should freeze.
- // However, if the stall clears later, the branch target should theoretically be taken.
- // In our RV64 simple pipeline design, stall_en freezes the PC reg completely.
- dut->pc_redirect_sel = 1;
- dut->pc_redirect_is_trap = 0;
- dut->pc_redirect_target = 0x80000200ULL;
- dut->stall_en = 1;
- tick(dut);
- check_pc(0x80000104ULL, dut->if_pc, "Stall_Overrides_Branch_Target_Load");
-
- dut->stall_en = 0;
- tick(dut);
- check_pc(0x80000200ULL, dut->if_pc, "Branch_Target_Loaded_After_Stall_Clears");
-
- // --- 6. Trap redirect overrides stall ---
- dut->pc_redirect_sel = 0;
- dut->pc_redirect_is_trap = 0;
- dut->pc_redirect_target = 0;
-
- dut->stall_en = 1;
- dut->pc_redirect_sel = 1;
- dut->pc_redirect_is_trap = 1;
- dut->pc_redirect_target = 0x80000300ULL;
- tick(dut);
- check_pc(0x80000300ULL, dut->if_pc, "Trap_Overrides_Stall");
-
- dut->stall_en = 0;
- dut->pc_redirect_sel = 0;
- dut->pc_redirect_is_trap = 0;
- tick(dut);
- check_pc(0x80000304ULL, dut->if_pc, "Resume_After_Trap");
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << "\033[32mAll tests passed!\033[0m" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- delete dut;
- return 0;
-} \ No newline at end of file
diff --git a/rv64cpu/tb/unit/tb_mem_stage.cc b/rv64cpu/tb/unit/tb_mem_stage.cc
deleted file mode 100644
index 417cb95..0000000
--- a/rv64cpu/tb/unit/tb_mem_stage.cc
+++ /dev/null
@@ -1,305 +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_mem_stage.cc
-// Description: Memory Stage unit test
-
-#include <iostream>
-#include <cstdint>
-#include <cassert>
-#include <verilated.h>
-#include "Vmem_stage.h"
-
-int pass_count = 0;
-
-void tick(Vmem_stage* dut) {
- dut->eval();
-}
-
-void clock_tick(Vmem_stage* dut) {
- dut->clk = 0;
- dut->eval();
- dut->clk = 1;
- dut->eval();
- dut->clk = 0;
- dut->eval();
-}
-
-void check(bool cond, const char* name) {
- if (cond) {
- std::cout << "\033[32m[PASS] " << name << "\033[0m" << std::endl;
- pass_count++;
- } else {
- std::cout << "\033[31m[FAIL] " << name << "\033[0m" << std::endl;
- assert(false);
- }
-}
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Vmem_stage* dut = new Vmem_stage;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << " MEM Stage Test" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- // 1. Initial / Default states
- dut->clk = 0;
- dut->rst = 1;
- dut->mem_alu_result = 0;
- dut->mem_rs2_rdata = 0;
- dut->mem_is_load = 0;
- dut->mem_is_store = 0;
- dut->mem_is_amo = 0;
- dut->mem_amo_op = 0;
- dut->mem_mem_size = 0;
- dut->dmem_req_ready = 1;
- dut->dmem_rsp_valid = 1;
- dut->dmem_rdata = 0;
-
- clock_tick(dut);
- dut->rst = 0;
- tick(dut);
-
- check(dut->dmem_read_req == 0, "No_Read_Req");
- check(dut->dmem_write_req == 0, "No_Write_Req");
- check(dut->mem_stall_req == 0, "No_Stall");
-
- // 2. Passthrough signals
- dut->mem_reg_write_en = 1;
- dut->mem_rd_addr = 15;
- dut->mem_wb_sel = 2; // PC+4
- dut->mem_pc = 0x80001000;
- dut->mem_is_csr = 1;
- dut->mem_csr_op = 2; // CSRRS
- dut->mem_rs1_rdata = 0x1234;
- dut->mem_rs1_addr = 5;
- dut->mem_imm = 0x777;
-
- tick(dut);
-
- check(dut->mem_wb_reg_write_en == 1, "Passthrough_RegWriteEn");
- check(dut->mem_wb_rd_addr == 15, "Passthrough_RdAddr");
- check(dut->mem_wb_wb_sel == 2, "Passthrough_WbSel");
- check(dut->mem_wb_pc == 0x80001000, "Passthrough_PC");
- check(dut->mem_wb_is_csr == 1, "Passthrough_IsCSR");
- check(dut->mem_wb_csr_op == 2, "Passthrough_CSROp");
- check(dut->mem_wb_rs1_rdata == 0x1234, "Passthrough_RS1Rdata");
- check(dut->mem_wb_rs1_addr == 5, "Passthrough_RS1Addr");
- check(dut->mem_wb_imm == 0x777, "Passthrough_Imm");
-
- // 3. Store Alignment (SB, SH, SW, SD)
- dut->mem_is_store = 1;
- dut->dmem_req_ready = 1;
- dut->mem_rs2_rdata = 0x1122334455667788ULL;
-
- // SB at offset 0
- dut->mem_mem_size = 0; // SB
- dut->mem_alu_result = 0x1000;
- tick(dut);
- check(dut->dmem_write_req == 1, "Store_Req");
- check(dut->dmem_addr == 0x1000, "Store_Addr");
- check(dut->dmem_wdata == 0x88, "SB_Data_0");
- check(dut->dmem_wstrb == 0x01, "SB_Strobe_0");
-
- // SB at offset 3
- dut->mem_alu_result = 0x1003;
- tick(dut);
- check(dut->dmem_wdata == 0x88000000ULL, "SB_Data_3");
- check(dut->dmem_wstrb == 0x08, "SB_Strobe_3");
-
- // SH at offset 2
- dut->mem_mem_size = 1; // SH
- dut->mem_alu_result = 0x1002;
- tick(dut);
- check(dut->dmem_wdata == 0x77880000ULL, "SH_Data_2");
- check(dut->dmem_wstrb == 0x0C, "SH_Strobe_2");
-
- // SW at offset 4
- dut->mem_mem_size = 2; // SW
- dut->mem_alu_result = 0x1004;
- tick(dut);
- check(dut->dmem_wdata == 0x5566778800000000ULL, "SW_Data_4");
- check(dut->dmem_wstrb == 0xF0, "SW_Strobe_4");
-
- // SD
- dut->mem_mem_size = 3; // SD
- dut->mem_alu_result = 0x1000;
- tick(dut);
- check(dut->dmem_wdata == 0x1122334455667788ULL, "SD_Data");
- check(dut->dmem_wstrb == 0xFF, "SD_Strobe");
-
- dut->mem_is_store = 0;
-
- // 4. Load Alignment and Extension (LB, LH, LW, LD, LBU, LHU, LWU)
- dut->mem_is_load = 1;
- dut->dmem_rdata = 0xAABBCCDDEEFF0011ULL;
- dut->dmem_rsp_valid = 1;
-
- // LB at offset 0 (0x11) -> sign extended
- dut->mem_mem_size = 0; // LB
- dut->mem_alu_result = 0x2000;
- tick(dut);
- check(dut->dmem_read_req == 1, "Load_Req");
- check(dut->mem_wb_mem_rdata == 0x11, "LB_Data_0_Pos");
-
- // LB at offset 1 (0x00)
- dut->mem_alu_result = 0x2001;
- tick(dut);
- check(dut->mem_wb_mem_rdata == 0x0, "LB_Data_1_Zero");
-
- // LB at offset 7 (0xAA) -> sign extended
- dut->mem_alu_result = 0x2007;
- tick(dut);
- check(dut->mem_wb_mem_rdata == 0xFFFFFFFFFFFFFFAAULL, "LB_Data_7_Neg");
-
- // LBU at offset 7 (0xAA) -> zero extended
- dut->mem_mem_size = 4; // LBU
- dut->mem_alu_result = 0x2007;
- tick(dut);
- check(dut->mem_wb_mem_rdata == 0xAA, "LBU_Data_7");
-
- // LH at offset 2 (0xEEFF) -> sign extended
- dut->mem_mem_size = 1; // LH
- dut->mem_alu_result = 0x2002;
- tick(dut);
- check(dut->mem_wb_mem_rdata == 0xFFFFFFFFFFFFEEFFULL, "LH_Data_2_Neg");
-
- // LHU at offset 2 (0xEEFF) -> zero extended
- dut->mem_mem_size = 5; // LHU
- dut->mem_alu_result = 0x2002;
- tick(dut);
- check(dut->mem_wb_mem_rdata == 0xEEFF, "LHU_Data_2");
-
- // LW at offset 4 (0xAABBCCDD) -> sign extended
- dut->mem_mem_size = 2; // LW
- dut->mem_alu_result = 0x2004;
- tick(dut);
- check(dut->mem_wb_mem_rdata == 0xFFFFFFFFAABBCCDDULL, "LW_Data_4_Neg");
-
- // LWU at offset 4 (0xAABBCCDD) -> zero extended
- dut->mem_mem_size = 6; // LWU
- dut->mem_alu_result = 0x2004;
- tick(dut);
- check(dut->mem_wb_mem_rdata == 0xAABBCCDD, "LWU_Data_4");
-
- // LD
- dut->mem_mem_size = 3; // LD
- dut->mem_alu_result = 0x2000;
- tick(dut);
- check(dut->mem_wb_mem_rdata == 0xAABBCCDDEEFF0011ULL, "LD_Data");
-
- dut->mem_is_load = 0;
-
- // 5. Atomic memory operations
- dut->mem_is_amo = 1;
- dut->mem_mem_size = 2; // AMO.W
- dut->mem_amo_op = 4; // AMO_ADD
- dut->mem_alu_result = 0x3004;
- dut->mem_rs2_rdata = 3;
- dut->dmem_rdata = 0x0000000500000000ULL;
- dut->dmem_req_ready = 1;
- dut->dmem_rsp_valid = 1;
- tick(dut);
- check(dut->dmem_read_req == 1, "AMOADDW_ReadReq");
- check(dut->dmem_write_req == 0, "AMOADDW_NoWriteDuringRead");
- check(dut->mem_stall_req == 1, "AMOADDW_StallDuringRead");
- clock_tick(dut);
-
- tick(dut);
- check(dut->dmem_read_req == 0, "AMOADDW_NoReadDuringWrite");
- check(dut->dmem_write_req == 1, "AMOADDW_WriteReq");
- check(dut->dmem_wdata == 0x0000000800000000ULL, "AMOADDW_WriteData");
- check(dut->dmem_wstrb == 0xF0, "AMOADDW_Strobe");
- check(dut->mem_wb_mem_rdata == 5, "AMOADDW_ReturnOldValue");
- check(dut->mem_stall_req == 0, "AMOADDW_NoStallDuringWrite");
- clock_tick(dut);
-
- dut->mem_mem_size = 3; // AMO.D
- dut->mem_amo_op = 1; // AMO_LR
- dut->mem_alu_result = 0x4000;
- dut->dmem_rdata = 0x123456789ABCDEF0ULL;
- tick(dut);
- check(dut->dmem_read_req == 1, "LRD_ReadReq");
- check(dut->dmem_write_req == 0, "LRD_NoWrite");
- check(dut->mem_wb_mem_rdata == 0x123456789ABCDEF0ULL, "LRD_ReturnData");
- clock_tick(dut);
-
- dut->mem_amo_op = 2; // AMO_SC
- dut->mem_rs2_rdata = 0xCAFEBABE11223344ULL;
- tick(dut);
- check(dut->dmem_read_req == 0, "SCD_NoRead");
- check(dut->dmem_write_req == 1, "SCD_SuccessWrite");
- check(dut->dmem_wdata == 0xCAFEBABE11223344ULL, "SCD_WriteData");
- check(dut->mem_wb_mem_rdata == 0, "SCD_SuccessStatus");
- clock_tick(dut);
-
- tick(dut);
- check(dut->dmem_write_req == 0, "SCD_FailNoWrite");
- check(dut->mem_wb_mem_rdata == 1, "SCD_FailStatus");
-
- dut->mem_amo_op = 1; // AMO_LR
- dut->mem_alu_result = 0x5000;
- dut->dmem_rdata = 0x1122334455667788ULL;
- tick(dut);
- check(dut->dmem_read_req == 1, "LRD_BeforeStore_ReadReq");
- clock_tick(dut);
-
- dut->mem_is_amo = 0;
- dut->mem_is_store = 1;
- dut->mem_mem_size = 0; // SB
- dut->mem_alu_result = 0x5003;
- dut->mem_rs2_rdata = 0xAA;
- tick(dut);
- check(dut->dmem_write_req == 1, "StoreAfterLR_WriteReq");
- clock_tick(dut);
-
- dut->mem_is_store = 0;
- dut->mem_is_amo = 1;
- dut->mem_mem_size = 3; // AMO.D
- dut->mem_amo_op = 2; // AMO_SC
- dut->mem_alu_result = 0x5000;
- dut->mem_rs2_rdata = 0x0102030405060708ULL;
- tick(dut);
- check(dut->dmem_write_req == 1, "SCD_AfterStore_WriteReq");
- check(dut->mem_wb_mem_rdata == 0, "SCD_AfterStore_SuccessStatus");
-
- dut->mem_is_amo = 0;
- dut->mem_is_store = 0;
-
- // 6. Stall Logic
- dut->mem_is_load = 1;
- dut->mem_mem_size = 3;
- dut->dmem_req_ready = 0; // Cache miss / AXI busy
- dut->dmem_rsp_valid = 0;
- tick(dut);
- check(dut->mem_stall_req == 1, "Stall_On_Req_NotReady");
-
- dut->dmem_req_ready = 1;
- dut->dmem_rsp_valid = 0; // Read request accepted, but data not valid yet
- tick(dut);
- check(dut->mem_stall_req == 1, "Stall_On_Rsp_NotValid");
-
- dut->dmem_rsp_valid = 1; // Read response arrived
- tick(dut);
- check(dut->mem_stall_req == 0, "No_Stall_On_RspValid");
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << "\033[32mAll " << pass_count << " tests passed!\033[0m" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- delete dut;
- return 0;
-}
diff --git a/rv64cpu/tb/unit/tb_mem_wb_reg.cc b/rv64cpu/tb/unit/tb_mem_wb_reg.cc
deleted file mode 100644
index e91a354..0000000
--- a/rv64cpu/tb/unit/tb_mem_wb_reg.cc
+++ /dev/null
@@ -1,165 +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_mem_wb_reg.cc
-// Description: MEM/WB Stage Register unit test
-
-#include <iostream>
-#include <cassert>
-#include <verilated.h>
-#include "Vmem_wb_reg.h"
-
-int pass_count = 0;
-
-void tick(Vmem_wb_reg* dut) {
- dut->clk = 0;
- dut->eval();
- dut->clk = 1;
- dut->eval();
- dut->clk = 0;
- dut->eval();
-}
-
-void check(bool cond, const char* name) {
- if (cond) {
- std::cout << "\033[32m[PASS] " << name << "\033[0m" << std::endl;
- pass_count++;
- } else {
- std::cout << "\033[31m[FAIL] " << name << "\033[0m" << std::endl;
- assert(false);
- }
-}
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Vmem_wb_reg* dut = new Vmem_wb_reg;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << " MEM/WB Register Test" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- // 1. Init / Reset
- dut->stall_en = 0;
- dut->flush_en = 0;
-
- // Set some dangerous inputs
- dut->mem_pc = 0x80001000;
- dut->mem_alu_result = 0x11111111;
- dut->mem_mem_rdata = 0x22222222;
- dut->mem_reg_write_en = 1;
- dut->mem_rd_addr = 31;
- dut->mem_wb_sel = 1;
- dut->mem_is_csr = 1;
- dut->mem_csr_op = 2;
- dut->mem_rs1_rdata = 0x999;
- dut->mem_rs1_addr = 5;
- dut->mem_imm = 0x333;
- dut->mem_is_ecall = 1;
- dut->mem_illegal_instr = 1;
- dut->mem_is_sret = 1;
-
- dut->rst = 1;
- tick(dut);
- dut->rst = 0;
-
- check(dut->wb_pc == 0, "Reset_Clears_PC");
- check(dut->wb_alu_result == 0, "Reset_Clears_ALU");
- check(dut->wb_reg_write_en == 0, "Reset_Clears_RegWrite");
- check(dut->wb_is_csr == 0, "Reset_Clears_IsCSR");
- check(dut->wb_illegal_instr == 0, "Reset_Clears_IllegalInstr");
-
- // 2. Normal Propagation
- dut->mem_pc = 0x80002000;
- dut->mem_alu_result = 0xAAAAAAAA;
- dut->mem_mem_rdata = 0xBBBBBBBB;
- dut->mem_rd_addr = 15;
- dut->mem_reg_write_en = 1;
- dut->mem_wb_sel = 1;
- dut->mem_is_csr = 1;
- dut->mem_csr_op = 3;
- dut->mem_rs1_rdata = 0x777;
- dut->mem_rs1_addr = 6;
- dut->mem_imm = 0x888;
- dut->mem_is_ecall = 0;
-
- tick(dut);
-
- check(dut->wb_pc == 0x80002000, "Propagate_PC");
- check(dut->wb_alu_result == 0xAAAAAAAA, "Propagate_ALU_Result");
- check(dut->wb_mem_rdata == 0xBBBBBBBB, "Propagate_MemRData");
- check(dut->wb_rd_addr == 15, "Propagate_RdAddr");
- check(dut->wb_reg_write_en == 1, "Propagate_RegWriteEn");
- check(dut->wb_wb_sel == 1, "Propagate_WbSel");
- check(dut->wb_is_csr == 1, "Propagate_IsCSR");
- check(dut->wb_csr_op == 3, "Propagate_CSROp");
- check(dut->wb_rs1_rdata == 0x777, "Propagate_RS1_RData");
- check(dut->wb_rs1_addr == 6, "Propagate_RS1_Addr");
- check(dut->wb_imm == 0x888, "Propagate_Imm");
-
- // 3. Stall (Freeze)
- dut->stall_en = 1;
- dut->mem_pc = 0x80003000;
- dut->mem_rd_addr = 20;
- dut->mem_reg_write_en = 0;
-
- tick(dut);
-
- check(dut->wb_pc == 0x80002000, "Stall_Maintains_PC");
- check(dut->wb_rd_addr == 15, "Stall_Maintains_RdAddr");
- check(dut->wb_reg_write_en == 1, "Stall_Maintains_RegWriteEn");
-
- // Release Stall
- dut->stall_en = 0;
- tick(dut);
- check(dut->wb_pc == 0x80003000, "ReleaseStall_Captures_PC");
- check(dut->wb_rd_addr == 20, "ReleaseStall_Captures_RdAddr");
- check(dut->wb_reg_write_en == 0, "ReleaseStall_Captures_RegWriteEn");
-
- // 4. Flush (Insert Bubble/NOP)
- dut->flush_en = 1;
- dut->mem_pc = 0x80004000;
- dut->mem_alu_result = 0xCCCCCCCC;
- dut->mem_reg_write_en = 1;
- dut->mem_is_csr = 1;
-
- tick(dut);
-
- check(dut->wb_pc == 0x80003000, "Flush_Maintains_PC");
- check(dut->wb_alu_result == 0xAAAAAAAA, "Flush_Maintains_ALU");
- check(dut->wb_reg_write_en == 0, "Flush_Clears_RegWrite");
- check(dut->wb_is_csr == 0, "Flush_Clears_IsCSR");
-
- // 5. Flush Overrides Stall
- dut->flush_en = 0;
- dut->mem_pc = 0x80005000;
- dut->mem_reg_write_en = 1;
- tick(dut);
- check(dut->wb_pc == 0x80005000, "Pre_FlushStall_Setup");
-
- dut->flush_en = 1;
- dut->stall_en = 1;
- dut->mem_pc = 0x80006000;
- tick(dut);
-
- check(dut->wb_pc == 0x80005000, "Flush_Overrides_Stall_PC");
- check(dut->wb_reg_write_en == 0, "Flush_Overrides_Stall_RegWrite");
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << "\033[32mAll " << pass_count << " tests passed!\033[0m" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- delete dut;
- return 0;
-}
diff --git a/rv64cpu/tb/unit/tb_mmu_top.cc b/rv64cpu/tb/unit/tb_mmu_top.cc
deleted file mode 100644
index 5df90ef..0000000
--- a/rv64cpu/tb/unit/tb_mmu_top.cc
+++ /dev/null
@@ -1,236 +0,0 @@
-#include <cassert>
-#include <cstdint>
-#include <iostream>
-#include <unordered_map>
-
-#include <verilated.h>
-
-#include "Vmmu_top.h"
-
-namespace {
-
-constexpr uint8_t MODE_BARE = 0;
-constexpr uint8_t MODE_SV39 = 8;
-constexpr uint8_t MODE_SV48 = 9;
-
-constexpr uint64_t FLAG_V = 1ULL << 0;
-constexpr uint64_t FLAG_R = 1ULL << 1;
-constexpr uint64_t FLAG_W = 1ULL << 2;
-constexpr uint64_t FLAG_X = 1ULL << 3;
-constexpr uint64_t FLAG_A = 1ULL << 6;
-constexpr uint64_t FLAG_D = 1ULL << 7;
-
-int g_pass_count = 0;
-
-void check(bool cond, const char* name) {
- if (cond) {
- std::cout << "\033[32m[PASS] " << name << "\033[0m\n";
- ++g_pass_count;
- } else {
- std::cout << "\033[31m[FAIL] " << name << "\033[0m\n";
- assert(false);
- }
-}
-
-uint64_t make_satp(uint8_t mode, uint16_t asid, uint64_t ppn) {
- return (uint64_t(mode) << 60) | (uint64_t(asid) << 44) | (ppn & ((1ULL << 44) - 1ULL));
-}
-
-uint64_t make_pte(uint64_t ppn, uint64_t flags) {
- return ((ppn & ((1ULL << 44) - 1ULL)) << 10) | (flags & 0x3ffULL);
-}
-
-void tick(Vmmu_top* dut) {
- dut->clk = 0;
- dut->eval();
- dut->clk = 1;
- dut->eval();
-}
-
-void reset(Vmmu_top* dut) {
- dut->rst = 1;
- dut->i_req_valid = 0;
- dut->d_req_valid = 0;
- dut->d_req_is_store = 0;
- dut->flush_all = 0;
- dut->flush_asid_valid = 0;
- dut->flush_asid = 0;
- dut->flush_vpn_valid = 0;
- dut->flush_vpn = 0;
- dut->ptw_mem_req_ready = 1;
- dut->ptw_mem_resp_valid = 0;
- dut->ptw_mem_resp_data = 0;
- tick(dut);
- tick(dut);
- dut->rst = 0;
- tick(dut);
-}
-
-struct StepResult {
- bool issued;
- uint64_t req_addr;
- bool i_resp_valid;
- bool i_resp_hit;
- bool i_resp_page_fault;
- uint64_t i_resp_paddr;
- bool d_resp_valid;
- bool d_resp_hit;
- bool d_resp_page_fault;
- uint64_t d_resp_paddr;
-};
-
-StepResult step(Vmmu_top* dut, bool inject_resp, uint64_t resp_data) {
- dut->ptw_mem_req_ready = 1;
- dut->ptw_mem_resp_valid = inject_resp ? 1 : 0;
- dut->ptw_mem_resp_data = resp_data;
-
- dut->clk = 0;
- dut->eval();
- const bool issued = dut->ptw_mem_req_valid && dut->ptw_mem_req_ready;
- const uint64_t req_addr = dut->ptw_mem_req_addr;
- StepResult result{
- issued,
- req_addr,
- bool(dut->i_resp_valid),
- bool(dut->i_resp_hit),
- bool(dut->i_resp_page_fault),
- dut->i_resp_paddr,
- bool(dut->d_resp_valid),
- bool(dut->d_resp_hit),
- bool(dut->d_resp_page_fault),
- dut->d_resp_paddr,
- };
-
- dut->clk = 1;
- dut->eval();
-
- return result;
-}
-
-void add_sv39_mapping(
- std::unordered_map<uint64_t, uint64_t>& memory,
- uint64_t root_ppn,
- uint64_t vaddr,
- uint64_t l1_ppn,
- uint64_t l0_ppn,
- uint64_t leaf_ppn,
- uint64_t leaf_flags
-) {
- const uint64_t vpn2 = (vaddr >> 30) & 0x1ffULL;
- const uint64_t vpn1 = (vaddr >> 21) & 0x1ffULL;
- const uint64_t vpn0 = (vaddr >> 12) & 0x1ffULL;
- memory[(root_ppn << 12) + (vpn2 << 3)] = make_pte(l1_ppn, FLAG_V);
- memory[(l1_ppn << 12) + (vpn1 << 3)] = make_pte(l0_ppn, FLAG_V);
- memory[(l0_ppn << 12) + (vpn0 << 3)] = make_pte(leaf_ppn, leaf_flags);
-}
-
-StepResult run_until_response(
- Vmmu_top* dut,
- const std::unordered_map<uint64_t, uint64_t>& memory,
- bool want_d
-) {
- bool inject_resp = false;
- uint64_t next_resp = 0;
- StepResult last{};
-
- for (int cycle = 0; cycle < 100; ++cycle) {
- last = step(dut, inject_resp, next_resp);
- if ((!want_d && last.i_resp_valid) || (want_d && last.d_resp_valid)) {
- return last;
- }
-
- inject_resp = false;
- next_resp = 0;
- if (last.issued) {
- const auto it = memory.find(last.req_addr);
- next_resp = (it == memory.end()) ? 0 : it->second;
- inject_resp = true;
- }
- }
-
- check(false, "MMU_ResponseTimeout");
- return last;
-}
-
-} // namespace
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- auto* dut = new Vmmu_top;
-
- std::cout << "----------------------------------\n";
- std::cout << " MMU Top Unit Test\n";
- std::cout << "----------------------------------\n";
-
- dut->satp_value = make_satp(MODE_BARE, 0, 0);
- dut->priv_mode = 1;
- dut->sum = 0;
- dut->mxr = 0;
- reset(dut);
-
- dut->i_req_valid = 1;
- dut->i_req_vaddr = 0x80001234ULL;
- dut->d_req_valid = 1;
- dut->d_req_vaddr = 0x90005678ULL;
- dut->d_req_is_store = 0;
- dut->eval();
- check(dut->i_resp_valid && dut->i_resp_hit && dut->i_resp_paddr == 0x80001234ULL, "Bare_I_Bypass");
- check(dut->d_resp_valid && dut->d_resp_hit && dut->d_resp_paddr == 0x90005678ULL, "Bare_D_Bypass");
- dut->i_req_valid = 0;
- dut->d_req_valid = 0;
- tick(dut);
-
- dut->satp_value = make_satp(MODE_SV48, 1, 0x100);
- dut->i_req_valid = 1;
- dut->i_req_vaddr = 0x0000000040401000ULL;
- dut->eval();
- check(dut->i_resp_valid && dut->i_resp_page_fault, "Sv48_Stub_IFault");
- dut->i_req_valid = 0;
- tick(dut);
-
- const uint64_t root_ppn = 0x100;
- const uint64_t i_va = 0x0000000040401234ULL;
- const uint64_t d_va = 0x0000000080505678ULL;
- const uint64_t i_leaf_ppn = 0x45670;
- const uint64_t d_leaf_ppn = 0x56780;
- std::unordered_map<uint64_t, uint64_t> memory;
- add_sv39_mapping(memory, root_ppn, i_va, 0x210, 0x220, i_leaf_ppn,
- FLAG_V | FLAG_R | FLAG_X | FLAG_A);
- add_sv39_mapping(memory, root_ppn, d_va, 0x230, 0x240, d_leaf_ppn,
- FLAG_V | FLAG_R | FLAG_W | FLAG_A | FLAG_D);
-
- dut->satp_value = make_satp(MODE_SV39, 1, root_ppn);
- dut->i_req_valid = 1;
- dut->i_req_vaddr = i_va;
- StepResult i_resp = run_until_response(dut, memory, false);
- check(i_resp.i_resp_hit && !i_resp.i_resp_page_fault, "Sv39_I_Walk_Hit");
- check(i_resp.i_resp_paddr == ((i_leaf_ppn << 12) | (i_va & 0xfffULL)), "Sv39_I_Walk_Paddr");
- dut->i_req_valid = 0;
- tick(dut);
-
- dut->d_req_valid = 1;
- dut->d_req_vaddr = d_va;
- dut->d_req_is_store = 1;
- StepResult d_resp = run_until_response(dut, memory, true);
- check(d_resp.d_resp_hit && !d_resp.d_resp_page_fault, "Sv39_D_Walk_Hit");
- check(d_resp.d_resp_paddr == ((d_leaf_ppn << 12) | (d_va & 0xfffULL)), "Sv39_D_Walk_Paddr");
-
- dut->d_req_valid = 0;
- tick(dut);
- dut->i_req_valid = 1;
- dut->i_req_vaddr = i_va;
- dut->d_req_valid = 1;
- dut->d_req_vaddr = d_va;
- dut->d_req_is_store = 1;
- dut->eval();
- check(dut->i_resp_valid && dut->i_resp_hit, "Sv39_I_TLB_Hit");
- check(dut->d_resp_valid && dut->d_resp_hit, "Sv39_D_TLB_Hit");
- check(dut->ptw_mem_req_valid == 0, "Sv39_TLB_Hit_NoPTW");
-
- std::cout << "----------------------------------\n";
- std::cout << "All " << g_pass_count << " MMU top tests passed!\n";
- std::cout << "----------------------------------\n";
-
- delete dut;
- return 0;
-}
diff --git a/rv64cpu/tb/unit/tb_ptw.cc b/rv64cpu/tb/unit/tb_ptw.cc
deleted file mode 100644
index f32de41..0000000
--- a/rv64cpu/tb/unit/tb_ptw.cc
+++ /dev/null
@@ -1,206 +0,0 @@
-#include <cassert>
-#include <cstdint>
-#include <iostream>
-#include <unordered_map>
-
-#include <verilated.h>
-
-#include "Vptw.h"
-
-namespace {
-
-constexpr uint8_t MODE_BARE = 0;
-constexpr uint8_t MODE_SV39 = 8;
-constexpr uint8_t MODE_SV48 = 9;
-constexpr uint8_t MODE_SV57 = 10;
-constexpr uint8_t MODE_SV64 = 11;
-
-constexpr uint64_t FLAG_V = 1ULL << 0;
-constexpr uint64_t FLAG_R = 1ULL << 1;
-constexpr uint64_t FLAG_W = 1ULL << 2;
-constexpr uint64_t FLAG_X = 1ULL << 3;
-constexpr uint64_t FLAG_A = 1ULL << 6;
-constexpr uint64_t FLAG_D = 1ULL << 7;
-
-int g_pass_count = 0;
-
-void check(bool cond, const char* name) {
- if (cond) {
- std::cout << "\033[32m[PASS] " << name << "\033[0m\n";
- ++g_pass_count;
- } else {
- std::cout << "\033[31m[FAIL] " << name << "\033[0m\n";
- assert(false);
- }
-}
-
-uint64_t make_satp(uint8_t mode, uint16_t asid, uint64_t ppn) {
- return (uint64_t(mode) << 60) | (uint64_t(asid) << 44) | (ppn & ((1ULL << 44) - 1ULL));
-}
-
-uint64_t make_pte(uint64_t ppn, uint64_t flags) {
- return ((ppn & ((1ULL << 44) - 1ULL)) << 10) | (flags & 0x3ffULL);
-}
-
-void tick(Vptw* dut) {
- dut->clk = 0;
- dut->eval();
- dut->clk = 1;
- dut->eval();
-}
-
-void reset(Vptw* dut) {
- dut->rst = 1;
- dut->req_valid = 0;
- dut->mem_req_ready = 1;
- dut->mem_resp_valid = 0;
- dut->mem_resp_data = 0;
- tick(dut);
- tick(dut);
- dut->rst = 0;
- tick(dut);
-}
-
-void launch(
- Vptw* dut,
- uint64_t vaddr,
- uint8_t mode,
- bool fetch,
- bool store,
- uint64_t satp
-) {
- for (int i = 0; i < 8 && !dut->req_ready; ++i) {
- dut->req_valid = 0;
- dut->mem_resp_valid = 0;
- tick(dut);
- }
-
- dut->req_valid = 1;
- dut->req_vaddr = vaddr;
- dut->req_asid = 1;
- dut->req_mode = mode;
- dut->req_priv = 1;
- dut->req_is_fetch = fetch;
- dut->req_is_store = store;
- dut->req_sum = 0;
- dut->req_mxr = 0;
- dut->satp_value = satp;
- tick(dut);
- dut->req_valid = 0;
-}
-
-void run_walk(Vptw* dut, const std::unordered_map<uint64_t, uint64_t>& memory) {
- bool inject_resp = false;
- uint64_t next_resp_data = 0;
-
- for (int cycle = 0; cycle < 80; ++cycle) {
- dut->mem_req_ready = 1;
- dut->mem_resp_valid = inject_resp ? 1 : 0;
- dut->mem_resp_data = next_resp_data;
-
- dut->clk = 0;
- dut->eval();
-
- if (dut->resp_valid) {
- dut->mem_resp_valid = 0;
- return;
- }
-
- const bool issued = dut->mem_req_valid && dut->mem_req_ready;
- const uint64_t req_addr = dut->mem_req_addr;
-
- dut->clk = 1;
- dut->eval();
-
- inject_resp = false;
- next_resp_data = 0;
- if (issued) {
- const auto it = memory.find(req_addr);
- next_resp_data = (it == memory.end()) ? 0 : it->second;
- inject_resp = true;
- }
- }
-
- check(false, "PTW_Timeout");
-}
-
-void add_sv39_mapping(
- std::unordered_map<uint64_t, uint64_t>& memory,
- uint64_t root_ppn,
- uint64_t vaddr,
- uint64_t l1_ppn,
- uint64_t l0_ppn,
- uint64_t leaf_ppn,
- uint64_t leaf_flags
-) {
- const uint64_t vpn2 = (vaddr >> 30) & 0x1ffULL;
- const uint64_t vpn1 = (vaddr >> 21) & 0x1ffULL;
- const uint64_t vpn0 = (vaddr >> 12) & 0x1ffULL;
- memory[(root_ppn << 12) + (vpn2 << 3)] = make_pte(l1_ppn, FLAG_V);
- memory[(l1_ppn << 12) + (vpn1 << 3)] = make_pte(l0_ppn, FLAG_V);
- memory[(l0_ppn << 12) + (vpn0 << 3)] = make_pte(leaf_ppn, leaf_flags);
-}
-
-} // namespace
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- auto* dut = new Vptw;
-
- std::cout << "----------------------------------\n";
- std::cout << " PTW Unit Test\n";
- std::cout << "----------------------------------\n";
-
- reset(dut);
-
- launch(dut, 0x0000000040401000ULL, MODE_SV48, false, false, make_satp(MODE_BARE, 1, 0x100));
- run_walk(dut, {});
- check(dut->resp_fault == 1, "Sv48_Stub_Fault");
-
- launch(dut, 0x0000000040401000ULL, MODE_SV57, false, false, make_satp(MODE_BARE, 1, 0x100));
- run_walk(dut, {});
- check(dut->resp_fault == 1, "Sv57_Stub_Fault");
-
- launch(dut, 0x0000000040401000ULL, MODE_SV64, false, false, make_satp(MODE_BARE, 1, 0x100));
- run_walk(dut, {});
- check(dut->resp_fault == 1, "Sv64_Stub_Fault");
-
- const uint64_t root_ppn = 0x100;
- const uint64_t va = 0x0000000040401234ULL;
- const uint64_t leaf_ppn = 0x45670;
- std::unordered_map<uint64_t, uint64_t> mem_ok;
- add_sv39_mapping(mem_ok, root_ppn, va, 0x210, 0x220, leaf_ppn,
- FLAG_V | FLAG_R | FLAG_W | FLAG_A | FLAG_D);
-
- launch(dut, va, MODE_SV39, false, false, make_satp(MODE_SV39, 1, root_ppn));
- run_walk(dut, mem_ok);
- check(dut->resp_fault == 0, "Sv39_Success_NoFault");
- check(dut->resp_paddr == ((leaf_ppn << 12) | (va & 0xfffULL)), "Sv39_Success_Paddr");
- check(dut->refill_valid == 1, "Sv39_Success_Refill");
- check(dut->refill_level == 0, "Sv39_Success_Level0");
-
- std::unordered_map<uint64_t, uint64_t> mem_no_x;
- add_sv39_mapping(mem_no_x, root_ppn, va, 0x230, 0x240, 0x50000, FLAG_V | FLAG_R | FLAG_A);
- launch(dut, va, MODE_SV39, true, false, make_satp(MODE_SV39, 1, root_ppn));
- run_walk(dut, mem_no_x);
- check(dut->resp_fault == 1, "Sv39_Fetch_NoX_Fault");
- check(dut->refill_valid == 0, "Sv39_Fetch_NoX_NoRefill");
-
- std::unordered_map<uint64_t, uint64_t> mem_store_d0;
- add_sv39_mapping(mem_store_d0, root_ppn, va, 0x250, 0x260, 0x60000,
- FLAG_V | FLAG_R | FLAG_W | FLAG_A);
- launch(dut, va, MODE_SV39, false, true, make_satp(MODE_SV39, 1, root_ppn));
- run_walk(dut, mem_store_d0);
- check(dut->resp_fault == 1, "Sv39_Store_D0_Fault");
-
- launch(dut, 0x0000008000000000ULL, MODE_SV39, false, false, make_satp(MODE_SV39, 1, root_ppn));
- run_walk(dut, {});
- check(dut->resp_fault == 1, "Sv39_NonCanonical_Fault");
-
- std::cout << "----------------------------------\n";
- std::cout << "All " << g_pass_count << " PTW tests passed!\n";
- std::cout << "----------------------------------\n";
-
- delete dut;
- return 0;
-}
diff --git a/rv64cpu/tb/unit/tb_regfile.cc b/rv64cpu/tb/unit/tb_regfile.cc
deleted file mode 100644
index 8e210ec..0000000
--- a/rv64cpu/tb/unit/tb_regfile.cc
+++ /dev/null
@@ -1,192 +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_regfile.cc
-// Description: Register file unit test
-
-#include <iostream>
-#include <cassert>
-#include <verilated.h>
-#include "Vregfile.h"
-
-void tick(Vregfile* dut) {
- dut->clk = 0;
- dut->eval();
- dut->clk = 1;
- dut->eval();
- dut->clk = 0;
- dut->eval();
-}
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Vregfile* dut = new Vregfile;
-
- dut->clk = 0;
- dut->rst = 0;
- dut->reg_write_en = 0;
- dut->rd_addr = 0;
- dut->rd_wdata = 0;
- dut->rs1_addr = 0;
- dut->rs2_addr = 0;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << " Register File Unit Test" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- // Test 1: Reset
- dut->rst = 1;
- tick(dut);
- dut->rst = 0;
- std::cout << "\033[32m[PASS] Reset complete\033[0m" << std::endl;
-
- // Test 2: Write and read
- dut->reg_write_en = 1;
- dut->rd_addr = 5;
- dut->rd_wdata = 0xDEADBEEF;
- tick(dut);
-
- dut->reg_write_en = 0;
- dut->rs1_addr = 5;
- dut->eval();
- assert(dut->rs1_rdata == 0xDEADBEEF);
- std::cout << "\033[32m[PASS] Write and read (x5) complete\033[0m" << std::endl;
-
- // Test 3: x0
- dut->reg_write_en = 1;
- dut->rd_addr = 0;
- dut->rd_wdata = 0x12345678;
- tick(dut);
-
- dut->reg_write_en = 0;
- dut->rs2_addr = 0;
- dut->eval();
- assert(dut->rs2_rdata == 0);
- std::cout << "\033[32m[PASS] Write and read (x0) complete\033[0m" << std::endl;
-
- // Test 4: Dual-port simultaneous read
- dut->reg_write_en = 1;
- dut->rd_addr = 6;
- dut->rd_wdata = 0x87654321;
- tick(dut);
-
- dut->reg_write_en = 1;
- dut->rd_addr = 7;
- dut->rd_wdata = 0x11223344;
- tick(dut);
-
- dut->rs1_addr = 6;
- dut->rs2_addr = 7;
- dut->eval();
-
- assert(dut->rs1_rdata == 0x87654321 && dut->rs2_rdata == 0x11223344);
- std::cout << "\033[32m[PASS] Dual-port simultaneous read complete\033[0m" << std::endl;
-
-
- // Test 5: Write enable = 0 (Data should not change)
- dut->reg_write_en = 1;
- dut->rd_addr = 10;
- dut->rd_wdata = 0xCAFEBABE;
- tick(dut);
-
- dut->reg_write_en = 0; // Disable write
- dut->rd_addr = 10;
- dut->rd_wdata = 0x11111111; // Attempt to overwrite
- tick(dut);
-
- dut->rs1_addr = 10;
- dut->eval();
- assert(dut->rs1_rdata == 0xCAFEBABE);
- std::cout << "\033[32m[PASS] Write enable = 0 correctly ignores write\033[0m" << std::endl;
-
- // Test 6: Verify full reset behavior
- // First, dirty up some registers
- for (int i = 1; i < 32; i += 5) {
- dut->reg_write_en = 1;
- dut->rd_addr = i;
- dut->rd_wdata = 0x1000 + i;
- tick(dut);
- }
-
- // Apply reset
- dut->rst = 1;
- dut->reg_write_en = 0; // Clear write enable from previous test
- tick(dut);
- dut->rst = 0;
-
- // Check all are zero
- bool reset_ok = true;
- for (int i = 1; i < 32; i++) {
- dut->rs1_addr = i;
- dut->eval();
- if (dut->rs1_rdata != 0) {
- reset_ok = false;
- std::cerr << "\033[31m[FAIL] Reset failed on register x" << i << std::endl;
- }
- }
- assert(reset_ok);
- std::cout << "\033[32m[PASS] Full memory reset verified\033[0m" << std::endl;
-
- // Test 7: Read-After-Write (Same cycle collision check)
- // With internal bypass added, combinational read should return the NEW bypassed value immediately.
- dut->reg_write_en = 0;
- dut->rd_addr = 15;
- dut->rd_wdata = 0x55555555;
- tick(dut); // Write 0x55555555 to x15 to set old value
-
- // Setup for RAW
- dut->reg_write_en = 1;
- dut->rd_addr = 15;
- dut->rd_wdata = 0x99999999;
-
- // Before tick, read should be the new bypassed value (0x99999999)
- dut->rs1_addr = 15;
- dut->eval();
- assert(dut->rs1_rdata == 0x99999999);
-
- tick(dut); // Clock edge happens
-
- dut->reg_write_en = 0; // Turn off write
- dut->eval(); // Combinational read update
- assert(dut->rs1_rdata == 0x99999999);
- std::cout << "\033[32m[PASS] Read-After-Write (RAW) behavior verified\033[0m" << std::endl;
-
- // Test 8: Sweep all registers (Write and Read distinct values)
- for (int i = 1; i < 32; i++) {
- dut->reg_write_en = 1;
- dut->rd_addr = i;
- dut->rd_wdata = 0xABCDE000 + i;
- tick(dut);
- }
-
- dut->reg_write_en = 0;
- bool sweep_ok = true;
- for (int i = 1; i < 32; i++) {
- dut->rs1_addr = i;
- dut->eval();
- if (dut->rs1_rdata != (0xABCDE000 + i)) {
- sweep_ok = false;
- }
- }
- assert(sweep_ok);
- std::cout << "\033[32m[PASS] All 31 mutable registers write/read successfully without collision\033[0m" << std::endl;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << "\033[32mAll 8 tests passed!\033[0m" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- delete dut;
- exit(0);
-} \ No newline at end of file
diff --git a/rv64cpu/tb/unit/tb_tlb.cc b/rv64cpu/tb/unit/tb_tlb.cc
deleted file mode 100644
index 0a57738..0000000
--- a/rv64cpu/tb/unit/tb_tlb.cc
+++ /dev/null
@@ -1,172 +0,0 @@
-#include <cassert>
-#include <cstdint>
-#include <iostream>
-
-#include <verilated.h>
-
-#include "Vtlb.h"
-
-namespace {
-
-constexpr uint8_t MODE_BARE = 0;
-constexpr uint8_t MODE_SV39 = 8;
-constexpr uint8_t MODE_SV48 = 9;
-constexpr uint8_t MODE_SV57 = 10;
-constexpr uint8_t MODE_SV64 = 11;
-
-int g_pass_count = 0;
-
-void check(bool cond, const char* name) {
- if (cond) {
- std::cout << "\033[32m[PASS] " << name << "\033[0m\n";
- ++g_pass_count;
- } else {
- std::cout << "\033[31m[FAIL] " << name << "\033[0m\n";
- assert(false);
- }
-}
-
-void tick(Vtlb* dut) {
- dut->clk = 0;
- dut->eval();
- dut->clk = 1;
- dut->eval();
-}
-
-void clear_req(Vtlb* dut) {
- dut->req_valid = 0;
- dut->req_vaddr = 0;
- dut->req_asid = 0;
- dut->req_mode = MODE_SV39;
- dut->req_priv = 1;
- dut->req_is_fetch = 0;
- dut->req_is_store = 0;
- dut->req_sum = 0;
- dut->req_mxr = 0;
-}
-
-void clear_refill(Vtlb* dut) {
- dut->refill_valid = 0;
- dut->refill_vpn = 0;
- dut->refill_ppn = 0;
- dut->refill_level = 0;
- dut->refill_mode = MODE_SV39;
- dut->refill_asid = 0;
- dut->refill_g = 0;
- dut->refill_u = 0;
- dut->refill_r = 0;
- dut->refill_w = 0;
- dut->refill_x = 0;
- dut->refill_a = 0;
- dut->refill_d = 0;
-}
-
-void clear_flush(Vtlb* dut) {
- dut->flush_all = 0;
- dut->flush_asid_valid = 0;
- dut->flush_asid = 0;
- dut->flush_vpn_valid = 0;
- dut->flush_vpn = 0;
- dut->flush_mode = MODE_SV39;
-}
-
-void reset(Vtlb* dut) {
- dut->rst = 1;
- clear_req(dut);
- clear_refill(dut);
- clear_flush(dut);
- tick(dut);
- tick(dut);
- dut->rst = 0;
- dut->eval();
-}
-
-uint64_t sv39_vpn(uint64_t vaddr) {
- return (vaddr >> 12) & ((1ULL << 27) - 1ULL);
-}
-
-void refill(Vtlb* dut, uint64_t vpn, uint64_t ppn, bool r, bool w, bool x, bool a, bool d) {
- clear_refill(dut);
- dut->refill_valid = 1;
- dut->refill_vpn = vpn;
- dut->refill_ppn = ppn;
- dut->refill_level = 0;
- dut->refill_mode = MODE_SV39;
- dut->refill_asid = 1;
- dut->refill_r = r;
- dut->refill_w = w;
- dut->refill_x = x;
- dut->refill_a = a;
- dut->refill_d = d;
- tick(dut);
- clear_refill(dut);
- dut->eval();
-}
-
-void request(Vtlb* dut, uint64_t vaddr, uint8_t mode, bool fetch, bool store, bool mxr = false) {
- clear_req(dut);
- dut->req_valid = 1;
- dut->req_vaddr = vaddr;
- dut->req_asid = 1;
- dut->req_mode = mode;
- dut->req_priv = 1;
- dut->req_is_fetch = fetch;
- dut->req_is_store = store;
- dut->req_mxr = mxr;
- dut->eval();
-}
-
-} // namespace
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- auto* dut = new Vtlb;
-
- std::cout << "----------------------------------\n";
- std::cout << " L1 TLB Unit Test\n";
- std::cout << "----------------------------------\n";
-
- reset(dut);
-
- request(dut, 0x123456789abcdef0ULL, MODE_BARE, false, false);
- check(dut->resp_hit == 1, "Bare_Bypass_Hit");
- check(dut->resp_page_fault == 0, "Bare_Bypass_NoFault");
- check(dut->resp_paddr == 0x123456789abcdef0ULL, "Bare_Bypass_Paddr");
-
- request(dut, 0x0000000040401000ULL, MODE_SV48, false, false);
- check(dut->resp_page_fault == 1, "Sv48_Stub_Fault");
- request(dut, 0x0000000040401000ULL, MODE_SV57, false, false);
- check(dut->resp_page_fault == 1, "Sv57_Stub_Fault");
- request(dut, 0x0000000040401000ULL, MODE_SV64, false, false);
- check(dut->resp_page_fault == 1, "Sv64_Stub_Fault");
-
- const uint64_t va = 0x0000000040401234ULL;
- const uint64_t ppn = 0x45670ULL;
- request(dut, va, MODE_SV39, false, false);
- check(dut->resp_hit == 0, "Sv39_Miss_NoHit");
- check(dut->resp_need_ptw == 1, "Sv39_Miss_NeedPTW");
- check(dut->resp_page_fault == 0, "Sv39_Miss_NoFault");
-
- refill(dut, sv39_vpn(va), ppn, true, true, false, true, true);
- request(dut, va, MODE_SV39, false, false);
- check(dut->resp_hit == 1, "Sv39_Refill_Hit");
- check(dut->resp_paddr == ((ppn << 12) | (va & 0xfffULL)), "Sv39_Refill_Paddr");
-
- refill(dut, sv39_vpn(0x0000000040500000ULL), 0x50000ULL, true, true, false, true, false);
- request(dut, 0x0000000040500000ULL, MODE_SV39, false, true);
- check(dut->resp_page_fault == 1, "Sv39_Store_D0_Fault");
-
- refill(dut, sv39_vpn(0x0000000040600000ULL), 0x60000ULL, false, false, true, true, false);
- request(dut, 0x0000000040600000ULL, MODE_SV39, false, false, true);
- check(dut->resp_hit == 1, "Sv39_MXR_LoadFromXOnly");
-
- request(dut, 0x0000008000000000ULL, MODE_SV39, false, false);
- check(dut->resp_page_fault == 1, "Sv39_NonCanonical_Fault");
-
- std::cout << "----------------------------------\n";
- std::cout << "All " << g_pass_count << " TLB tests passed!\n";
- std::cout << "----------------------------------\n";
-
- delete dut;
- return 0;
-}
diff --git a/rv64cpu/tb/unit/tb_trap_ctrl.cc b/rv64cpu/tb/unit/tb_trap_ctrl.cc
deleted file mode 100644
index 62867df..0000000
--- a/rv64cpu/tb/unit/tb_trap_ctrl.cc
+++ /dev/null
@@ -1,284 +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_trap_ctrl.cc
-// Description: Standalone unit test for trap_ctrl exception/interrupt control logic
-
-#include <cassert>
-#include <cstdint>
-#include <iostream>
-#include <verilated.h>
-
-#include "Vtrap_ctrl.h"
-
-namespace {
-constexpr uint64_t EXC_INST_ILLEGAL = 2ULL;
-constexpr uint64_t EXC_ECALL_FROM_S = 9ULL;
-constexpr uint64_t EXC_LOAD_PAGE_FAULT= 13ULL;
-constexpr uint64_t EXC_STORE_PAGE_FAULT= 15ULL;
-constexpr uint64_t EXC_LOAD_FAULT = 5ULL;
-constexpr uint64_t EXC_STORE_FAULT = 7ULL;
-
-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 uint64_t INTERRUPT_BIT = 1ULL << 63;
-constexpr uint64_t PRIV_MODE_U = 0ULL;
-constexpr uint64_t PRIV_MODE_S = 1ULL;
-constexpr uint64_t PRIV_MODE_M = 3ULL;
-
-uint64_t mie_bit(uint64_t interrupt_cause) {
- return 1ULL << interrupt_cause;
-}
-
-void drive_defaults(Vtrap_ctrl* dut) {
- dut->wb_pc = 0;
- dut->trap_illegal_instr = 0;
- dut->trap_is_ecall = 0;
- dut->trap_is_ebreak = 0;
- dut->trap_is_mret = 0;
- dut->trap_is_sret = 0;
- dut->trap_load_fault = 0;
- dut->trap_store_fault = 0;
- dut->trap_instr_page_fault = 0;
- dut->trap_load_page_fault = 0;
- dut->trap_store_page_fault = 0;
- dut->trap_bad_addr = 0;
- dut->trap_bad_instr = 0;
-
- dut->csr_mstatus_mie = 0;
- dut->csr_mie = 0;
- dut->csr_sie = 0;
- dut->csr_mip = 0;
- dut->csr_sip = 0;
- dut->csr_mtvec = 0x800ULL;
- dut->csr_stvec = 0x400ULL;
- dut->csr_mepc = 0x1000ULL;
- dut->csr_sepc = 0x2000ULL;
- dut->csr_medeleg = 0ULL;
- dut->csr_mideleg = 0ULL;
- dut->csr_priv_mode = PRIV_MODE_M;
-
- dut->eval();
-}
-
-int pass_count = 0;
-
-void check(bool cond, const char* name) {
- if (cond) {
- std::cout << "\033[32m[PASS] " << name << "\033[0m" << std::endl;
- pass_count++;
- } else {
- std::cout << "\033[31m[FAIL] " << name << "\033[0m" << std::endl;
- assert(false);
- }
-}
-} // namespace
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Vtrap_ctrl* dut = new Vtrap_ctrl;
-
- std::cout << "----------------------------------\n";
- std::cout << " trap_ctrl Testbench\n";
- std::cout << "----------------------------------\n";
-
- // ---------------------------------------------------------------------
- // 1) Synchronous exceptions (illegal instruction, ecall, page fault)
- // ---------------------------------------------------------------------
- drive_defaults(dut);
- dut->csr_priv_mode = PRIV_MODE_M;
- dut->csr_mtvec = 0x880ULL;
- dut->wb_pc = 0x1234ULL;
- dut->trap_bad_instr = 0xDEADBEEFu;
- dut->trap_illegal_instr = 1;
- dut->eval();
- check(dut->trap_trigger, "IllegalInst_TriggersTrap");
- check(dut->trap_mcause == EXC_INST_ILLEGAL, "IllegalInst_Mcause");
- check(dut->trap_mtval == 0xDEADBEEFULL, "IllegalInst_Mtval");
- check(dut->trap_mepc == 0x1234ULL, "IllegalInst_Mepc");
- check(!dut->trap_to_s_mode, "IllegalInst_NoDelegation");
- check(dut->trap_next_pc == 0x880ULL, "IllegalInst_JumpsToMtvec");
-
- drive_defaults(dut);
- dut->csr_priv_mode = PRIV_MODE_S;
- dut->csr_mtvec = 0x440ULL;
- dut->wb_pc = 0x5678ULL;
- dut->trap_is_ecall = 1;
- dut->eval();
- check(dut->trap_trigger, "EcallS_TriggersTrap");
- check(dut->trap_mcause == EXC_ECALL_FROM_S, "EcallS_Mcause");
- check(dut->trap_mtval == 0ULL, "EcallS_MtvalZero");
- check(dut->trap_next_pc == 0x440ULL, "EcallS_JumpsToMtvec");
-
- drive_defaults(dut);
- dut->trap_load_page_fault = 1;
- dut->trap_bad_addr = 0xBADC0FFEE0ULL;
- dut->eval();
- check(dut->trap_mcause == EXC_LOAD_PAGE_FAULT, "LoadPageFault_Mcause");
- check(dut->trap_mtval == 0xBADC0FFEE0ULL, "LoadPageFault_Mtval");
-
- drive_defaults(dut);
- dut->trap_store_page_fault = 1;
- dut->trap_bad_addr = 0xDEADBEEF00ULL;
- dut->eval();
- check(dut->trap_mcause == EXC_STORE_PAGE_FAULT, "StorePageFault_Mcause");
- check(dut->trap_mtval == 0xDEADBEEF00ULL, "StorePageFault_Mtval");
-
- drive_defaults(dut);
- dut->trap_load_fault = 1;
- dut->trap_bad_addr = 0xABCDEF0010ULL;
- dut->eval();
- check(dut->trap_mcause == EXC_LOAD_FAULT, "LoadFault_Mcause");
- check(dut->trap_mtval == 0xABCDEF0010ULL, "LoadFault_Mtval");
-
- drive_defaults(dut);
- dut->trap_store_fault = 1;
- dut->trap_bad_addr = 0xABCDEF0020ULL;
- dut->eval();
- check(dut->trap_mcause == EXC_STORE_FAULT, "StoreFault_Mcause");
- check(dut->trap_mtval == 0xABCDEF0020ULL, "StoreFault_Mtval");
-
- // ---------------------------------------------------------------------
- // 2) Interrupt priority + enable masking
- // ---------------------------------------------------------------------
- drive_defaults(dut);
- dut->csr_mstatus_mie = 1;
- dut->csr_mie = mie_bit(INTERRUPT_M_EXTERNAL) |
- mie_bit(INTERRUPT_M_SOFTWARE) |
- mie_bit(INTERRUPT_M_TIMER);
- dut->csr_priv_mode = PRIV_MODE_M;
- dut->csr_mtvec = 0x300ULL | 0x1ULL; // vectored mode
- dut->wb_pc = 0x4000ULL;
- dut->csr_mip = mie_bit(INTERRUPT_M_EXTERNAL) |
- mie_bit(INTERRUPT_M_SOFTWARE) |
- mie_bit(INTERRUPT_M_TIMER);
- dut->eval();
- const uint64_t expected_vectored_pc = 0x300ULL + (INTERRUPT_M_EXTERNAL << 2);
- check(dut->trap_trigger, "InterruptPriority_Triggers");
- check(dut->trap_mcause == (INTERRUPT_BIT | INTERRUPT_M_EXTERNAL), "InterruptPriority_Mcause");
- check(dut->trap_mtval == 0ULL, "InterruptPriority_MtvalZero");
- check(dut->trap_next_pc == expected_vectored_pc, "InterruptPriority_VectoredJump");
-
- drive_defaults(dut);
- dut->csr_mstatus_mie = 0;
- dut->csr_mie = mie_bit(INTERRUPT_M_EXTERNAL);
- dut->csr_mip = mie_bit(INTERRUPT_M_EXTERNAL);
- dut->eval();
- check(!dut->trap_trigger, "InterruptMaskedByGlobalMie_NoTrigger");
-
- drive_defaults(dut);
- dut->csr_priv_mode = PRIV_MODE_M;
- dut->csr_mstatus_mie = 1;
- dut->csr_mie = 0;
- dut->csr_mip = mie_bit(INTERRUPT_M_EXTERNAL);
- dut->eval();
- check(!dut->trap_trigger, "InterruptMaskedByMie_NoTrigger");
-
- drive_defaults(dut);
- dut->csr_priv_mode = PRIV_MODE_S;
- dut->csr_mstatus_mie = 0;
- dut->csr_mie = mie_bit(INTERRUPT_M_EXTERNAL);
- dut->csr_mtvec = 0x440ULL;
- dut->wb_pc = 0x6780ULL;
- dut->csr_mip = mie_bit(INTERRUPT_M_EXTERNAL);
- dut->eval();
- check(dut->trap_trigger, "MachineInterruptBelowM_IgnoresMstatusMie");
- check(!dut->trap_to_s_mode, "MachineInterruptBelowM_NoDelegationWithoutMideleg");
- check(dut->trap_mcause == (INTERRUPT_BIT | INTERRUPT_M_EXTERNAL), "MachineInterruptBelowM_Mcause");
- check(dut->trap_next_pc == 0x440ULL, "MachineInterruptBelowM_JumpsToMtvec");
-
- drive_defaults(dut);
- dut->csr_priv_mode = PRIV_MODE_S;
- dut->csr_mstatus_mie = 1;
- dut->csr_mie = mie_bit(INTERRUPT_M_TIMER);
- dut->csr_mtvec = 0x880ULL | 0x1ULL; // vectored machine mode
- dut->csr_stvec = 0x400ULL | 0x1ULL;
- dut->csr_mideleg = (1ULL << INTERRUPT_M_TIMER);
- dut->wb_pc = 0xABCDULL;
- dut->csr_mip = mie_bit(INTERRUPT_M_TIMER);
- dut->eval();
- const uint64_t expected_sv_pc = 0x880ULL + (INTERRUPT_M_TIMER << 2);
- check(!dut->trap_to_s_mode, "MachineInterrupt_NoDelegation");
- check(dut->trap_next_pc == expected_sv_pc, "MachineInterrupt_UsesMtvec");
- check(dut->trap_mcause == (INTERRUPT_BIT | INTERRUPT_M_TIMER), "MachineInterrupt_Mcause");
-
- drive_defaults(dut);
- dut->csr_priv_mode = PRIV_MODE_S;
- dut->csr_mstatus = (1ULL << 1);
- dut->csr_sie = mie_bit(INTERRUPT_S_TIMER);
- dut->csr_sip = mie_bit(INTERRUPT_S_TIMER);
- dut->csr_mideleg = mie_bit(INTERRUPT_S_TIMER);
- dut->csr_stvec = 0x900ULL | 0x1ULL;
- dut->wb_pc = 0xABCEULL;
- dut->eval();
- const uint64_t expected_s_timer_pc = 0x900ULL + (INTERRUPT_S_TIMER << 2);
- check(dut->trap_to_s_mode, "SupervisorInterruptDelegated_ToSupervisor");
- check(dut->trap_next_pc == expected_s_timer_pc, "SupervisorInterruptDelegated_UsesStvec");
- check(dut->trap_mcause == (INTERRUPT_BIT | INTERRUPT_S_TIMER), "SupervisorInterruptDelegated_Mcause");
-
- drive_defaults(dut);
- dut->csr_priv_mode = PRIV_MODE_S;
- dut->csr_mstatus = 0;
- dut->csr_sie = mie_bit(INTERRUPT_S_TIMER);
- dut->csr_sip = mie_bit(INTERRUPT_S_TIMER);
- dut->csr_mideleg = mie_bit(INTERRUPT_S_TIMER);
- dut->eval();
- check(!dut->trap_trigger, "SupervisorInterruptMaskedBySstatusSie_NoTrigger");
-
- drive_defaults(dut);
- dut->csr_priv_mode = PRIV_MODE_U;
- dut->csr_mstatus = 0;
- dut->csr_sie = mie_bit(INTERRUPT_S_EXTERNAL);
- dut->csr_sip = mie_bit(INTERRUPT_S_EXTERNAL);
- dut->csr_mideleg = mie_bit(INTERRUPT_S_EXTERNAL);
- dut->csr_stvec = 0x940ULL;
- dut->eval();
- check(dut->trap_to_s_mode, "SupervisorInterruptBelowS_IgnoresSstatusSie");
- check(dut->trap_next_pc == 0x940ULL, "SupervisorInterruptBelowS_UsesStvec");
-
- // ---------------------------------------------------------------------
- // 3) MRET/SRET redirect without new traps
- // ---------------------------------------------------------------------
- drive_defaults(dut);
- dut->csr_mepc = 0xCAFEBABEULL;
- dut->trap_is_mret = 1;
- dut->eval();
- check(dut->mret_en, "Mret_PathEnabled");
- check(dut->trap_next_pc == 0xCAFEBABEULL, "Mret_RestoresMepc");
- check(dut->trap_flush_req && dut->trap_pc_sel, "Mret_ForcesFlush");
- check(!dut->trap_trigger, "Mret_NoTrapOverlap");
-
- drive_defaults(dut);
- dut->csr_priv_mode = PRIV_MODE_S;
- dut->csr_sepc = 0x13579BDFULL;
- dut->trap_is_sret = 1;
- dut->eval();
- check(dut->sret_en, "Sret_PathEnabled");
- check(dut->trap_next_pc == 0x13579BDFULL, "Sret_RestoresSepc");
- check(dut->trap_flush_req && dut->trap_pc_sel, "Sret_ForcesFlush");
-
- std::cout << "----------------------------------\n";
- std::cout << "All " << pass_count << " trap_ctrl tests passed!\n";
- std::cout << "----------------------------------\n";
-
- delete dut;
- return 0;
-}
diff --git a/rv64cpu/tb/unit/tb_wb_stage.cc b/rv64cpu/tb/unit/tb_wb_stage.cc
deleted file mode 100644
index 3f6bedb..0000000
--- a/rv64cpu/tb/unit/tb_wb_stage.cc
+++ /dev/null
@@ -1,163 +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_wb_stage.cc
-// Description: WB Stage unit test
-
-#include <iostream>
-#include <cassert>
-#include <verilated.h>
-#include "Vwb_stage.h"
-
-int pass_count = 0;
-
-void tick(Vwb_stage* dut) {
- dut->eval();
-}
-
-void check(bool cond, const char* name) {
- if (cond) {
- std::cout << "\033[32m[PASS] " << name << "\033[0m" << std::endl;
- pass_count++;
- } else {
- std::cout << "\033[31m[FAIL] " << name << "\033[0m" << std::endl;
- assert(false);
- }
-}
-
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
- Vwb_stage* dut = new Vwb_stage;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << " WB Stage Test" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- // 1. Basic ALU writeback
- dut->mem_wb_alu_result = 0xAA55AA55;
- dut->mem_wb_mem_rdata = 0x11223344;
- dut->mem_wb_pc = 0x80000000;
- dut->mem_wb_pc_plus_4 = 0x80000004;
- dut->mem_wb_reg_write_en = 1;
- dut->mem_wb_rd_addr = 5;
- dut->mem_wb_wb_sel = 0; // ALU
- dut->mem_wb_is_csr = 0;
- dut->mem_wb_csr_op = 0;
- dut->mem_wb_rs1_rdata = 0x99;
- dut->mem_wb_rs1_addr = 1;
- dut->mem_wb_imm = 0x123;
- dut->mem_wb_instr = 0xDEADBEEFu;
- dut->mem_wb_illegal_instr = 0;
- dut->mem_wb_is_ecall = 0;
- dut->mem_wb_is_ebreak = 0;
- dut->mem_wb_is_mret = 0;
- dut->mem_wb_is_sret = 0;
- dut->mem_wb_load_fault = 0;
- dut->mem_wb_store_fault = 0;
- dut->mem_wb_fault_addr = 0;
- dut->csr_rdata = 0x0;
- dut->csr_illegal_access = 0;
-
- tick(dut);
-
- check(dut->wb_rd_wdata == 0xAA55AA55, "WB_Sel_ALU");
- check(dut->wb_rd_addr == 5, "WB_RdAddr");
- check(dut->wb_reg_write_en == 1, "WB_RegWriteEn");
- check(dut->csr_req == 0, "CSR_Req_0");
-
- // 2. Memory read writeback
- dut->mem_wb_wb_sel = 1; // MEM
- tick(dut);
- check(dut->wb_rd_wdata == 0x11223344, "WB_Sel_MEM");
-
- // 3. PC+4 writeback (JAL/JALR)
- dut->mem_wb_wb_sel = 2; // PC+4
- tick(dut);
- check(dut->wb_rd_wdata == 0x80000004, "WB_Sel_PC_Plus_4");
-
- // 4. CSR Read/Write (e.g. CSRRS)
- dut->mem_wb_is_csr = 1;
- dut->mem_wb_wb_sel = 0; // Doesn't matter because is_csr forces CSR path
- dut->mem_wb_csr_op = 2; // CSRRS
- dut->mem_wb_imm = 0x300; // mstatus
- dut->mem_wb_rs1_rdata = 0x00000008; // Set MIE
- dut->mem_wb_rs1_addr = 10;
- dut->csr_rdata = 0x00001800; // Old mstatus
-
- tick(dut);
-
- check(dut->wb_rd_wdata == 0x00001800, "CSR_Read_To_Reg");
- check(dut->csr_req == 1, "CSR_Req_1");
- check(dut->csr_op == 2, "CSR_Op_Propagated");
- check(dut->csr_addr == 0x300, "CSR_Addr_Extracted");
- check(dut->csr_rs1_data == 0x00000008, "CSR_RS1_Data_Rdata");
-
- // 5. CSR Immediate (e.g. CSRRSI)
- dut->mem_wb_csr_op = 6; // CSRRSI (funct3 = 3'b110)
- tick(dut);
- check(dut->csr_rs1_data == 10, "CSR_RS1_Data_Zimm");
-
- // 6. Exception Signals Propagation
- dut->mem_wb_illegal_instr = 1;
- dut->mem_wb_is_ecall = 1;
- dut->mem_wb_is_ebreak = 1;
- dut->mem_wb_is_mret = 1;
- dut->mem_wb_is_sret = 1;
- tick(dut);
-
- check(dut->trap_illegal_instr == 1, "Trap_Illegal_Instr");
- check(dut->trap_is_ecall == 1, "Trap_ECALL");
- check(dut->trap_is_ebreak == 1, "Trap_EBREAK");
- check(dut->trap_is_mret == 1, "Trap_MRET");
- check(dut->trap_is_sret == 1, "Trap_SRET");
- check(dut->trap_epc == 0x80000000, "Trap_EPC");
- check(dut->trap_bad_instr == 0xDEADBEEFu, "Trap_BadInstr_Payload");
- check(dut->wb_reg_write_en == 0, "Trap_Gates_RegWriteEn");
- check(dut->csr_req == 0, "Trap_Gates_CSR_Req");
-
- // 7. CSR illegal access should raise an illegal-instr trap
- dut->mem_wb_illegal_instr = 0;
- dut->mem_wb_is_ecall = 0;
- dut->mem_wb_is_ebreak = 0;
- dut->csr_illegal_access = 1;
- tick(dut);
- check(dut->trap_illegal_instr == 1, "CSR_Illegal_Raises_Trap");
- check(dut->wb_reg_write_en == 0, "CSR_Illegal_Kills_Write");
-
- // 8. Load/store faults propagate to trap outputs and block writeback
- dut->csr_illegal_access = 0;
- dut->mem_wb_load_fault = 1;
- dut->mem_wb_fault_addr = 0x80000010ULL;
- tick(dut);
- check(dut->trap_load_fault == 1, "LoadFault_AssertsTrap");
- check(dut->trap_bad_addr == 0x80000010ULL, "LoadFault_PassesAddress");
- check(dut->wb_reg_write_en == 0, "LoadFault_KillsWrite");
-
- dut->mem_wb_load_fault = 0;
- dut->mem_wb_store_fault = 1;
- dut->mem_wb_fault_addr = 0x80000020ULL;
- tick(dut);
- check(dut->trap_store_fault == 1, "StoreFault_AssertsTrap");
- check(dut->trap_bad_addr == 0x80000020ULL, "StoreFault_PassesAddress");
-
- dut->mem_wb_store_fault = 0;
-
- std::cout << "----------------------------------" << std::endl;
- std::cout << "\033[32mAll " << pass_count << " tests passed!\033[0m" << std::endl;
- std::cout << "----------------------------------" << std::endl;
-
- delete dut;
- return 0;
-}