diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-11-07 03:30:17 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-11-07 03:31:03 +0800 |
| commit | 2a4c290c55b683352a16747432a7fc4635597cc6 (patch) | |
| tree | a150caea2ade12af4c5dcd70f09bacd3318f4d43 /rv64cpu/tb/unit | |
| parent | e0948e3fc10029ba4bb4d48d7b5daae94fe575b9 (diff) | |
| download | rcc-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.cc | 136 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_branch.cc | 100 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_csr.cc | 770 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_decoder.cc | 353 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_ex_mem_reg.cc | 182 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_ex_stage.cc | 218 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_hazard_unit.cc | 364 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_id_ex_reg.cc | 177 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_id_stage.cc | 194 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_if_id_reg.cc | 127 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_if_stage.cc | 132 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_mem_stage.cc | 305 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_mem_wb_reg.cc | 165 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_mmu_top.cc | 236 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_ptw.cc | 206 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_regfile.cc | 192 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_tlb.cc | 172 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_trap_ctrl.cc | 284 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_wb_stage.cc | 163 |
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; -} |
