aboutsummaryrefslogtreecommitdiff
path: root/rv64cpu/tb/core
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-11-07 03:30:17 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-11-07 03:31:03 +0800
commit2a4c290c55b683352a16747432a7fc4635597cc6 (patch)
treea150caea2ade12af4c5dcd70f09bacd3318f4d43 /rv64cpu/tb/core
parente0948e3fc10029ba4bb4d48d7b5daae94fe575b9 (diff)
downloadrcc-project-2a4c290c55b683352a16747432a7fc4635597cc6.tar.gz
rcc-project-2a4c290c55b683352a16747432a7fc4635597cc6.tar.xz
refactor: reset software and test directories
Diffstat (limited to 'rv64cpu/tb/core')
-rw-r--r--rv64cpu/tb/core/tb_core.cc1136
1 files changed, 0 insertions, 1136 deletions
diff --git a/rv64cpu/tb/core/tb_core.cc b/rv64cpu/tb/core/tb_core.cc
deleted file mode 100644
index da758c1..0000000
--- a/rv64cpu/tb/core/tb_core.cc
+++ /dev/null
@@ -1,1136 +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/core/tb_core.cc
-// Description: Richard CPU core top-level integration test
-//
-// This testbench drives the richard_core module through its external interfaces
-// (instruction memory + data memory) to verify multi-cycle pipeline behavior.
-// It acts as a simple memory model, feeding instructions and returning data.
-
-#include <algorithm>
-#include <iostream>
-#include <iomanip>
-#include <cstdint>
-#include <iterator>
-#include <vector>
-#include <map>
-#include <string>
-#include <verilated.h>
-#include "Vrichard_core.h"
-
-#if VM_TRACE
-#include <verilated_vcd_c.h>
-#endif
-
-// ---------------------------------
-// CSR addresses & cause codes used in tests
-// ---------------------------------
-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_MEPC = 0x341;
-constexpr uint16_t CSR_MSCRATCH = 0x340;
-constexpr uint16_t CSR_MCAUSE = 0x342;
-constexpr uint16_t CSR_MTVAL = 0x343;
-constexpr uint16_t CSR_SSTATUS = 0x100;
-constexpr uint16_t CSR_STVEC = 0x105;
-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 int MSTATUS_MIE_BIT = 3;
-constexpr int MSTATUS_MPIE_BIT = 7;
-constexpr int MSTATUS_MPP_LO = 11;
-constexpr int SSTATUS_SIE_BIT = 1;
-constexpr int SSTATUS_SPIE_BIT = 5;
-constexpr int SSTATUS_SPP_BIT = 8;
-
-constexpr uint64_t EXC_INST_ILLEGAL = 2ULL;
-constexpr uint64_t EXC_BREAKPOINT = 3ULL;
-constexpr uint64_t EXC_LOAD_FAULT = 5ULL;
-constexpr uint64_t EXC_STORE_FAULT = 7ULL;
-constexpr uint64_t EXC_ECALL_FROM_U = 8ULL;
-constexpr uint64_t EXC_ECALL_FROM_M = 11ULL;
-constexpr uint64_t INTERRUPT_BIT = 1ULL << 63;
-constexpr uint64_t INTERRUPT_M_EXTERNAL = 11ULL;
-constexpr uint64_t INTERRUPT_M_TIMER = 7ULL;
-
-constexpr uint64_t LOG_ENTRY_STRIDE = 24ULL;
-constexpr uint64_t MACHINE_TRAP_VECTOR = 0x00000000ULL;
-constexpr uint64_t SUPERVISOR_TRAP_VECTOR = 0x00000100ULL;
-constexpr uint64_t MACHINE_LOG_PTR_ADDR = 0x000000000000FFF0ULL;
-constexpr uint64_t SUPERVISOR_LOG_PTR_ADDR = 0x000000000000FFE0ULL;
-
-constexpr uint64_t PRIV_MODE_U = 0ULL;
-constexpr uint64_t PRIV_MODE_S = 1ULL;
-constexpr uint64_t PRIV_MODE_M = 3ULL;
-
-// -----------------------
-// RISC-V Instruction Encoding Helpers
-// -----------------------
-
-// R-type: funct7[6:0] | rs2[4:0] | rs1[4:0] | funct3[2:0] | rd[4:0] | opcode[6:0]
-static uint32_t rv_r_type(uint32_t funct7, uint32_t rs2, uint32_t rs1, uint32_t funct3, uint32_t rd, uint32_t opcode) {
- return (funct7 << 25) | (rs2 << 20) | (rs1 << 15) | (funct3 << 12) | (rd << 7) | opcode;
-}
-
-// I-type: imm[11:0] | rs1[4:0] | funct3[2:0] | rd[4:0] | opcode[6:0]
-static uint32_t rv_i_type(uint32_t imm12, uint32_t rs1, uint32_t funct3, uint32_t rd, uint32_t opcode) {
- return ((imm12 & 0xFFF) << 20) | (rs1 << 15) | (funct3 << 12) | (rd << 7) | opcode;
-}
-
-// S-type: imm[11:5] | rs2[4:0] | rs1[4:0] | funct3[2:0] | imm[4:0] | opcode[6:0]
-static uint32_t rv_s_type(uint32_t imm12, uint32_t rs2, uint32_t rs1, uint32_t funct3, uint32_t opcode) {
- uint32_t imm_hi = (imm12 >> 5) & 0x7F;
- uint32_t imm_lo = imm12 & 0x1F;
- return (imm_hi << 25) | (rs2 << 20) | (rs1 << 15) | (funct3 << 12) | (imm_lo << 7) | opcode;
-}
-
-// U-type: imm[31:12] | rd[4:0] | opcode[6:0]
-static uint32_t rv_u_type(uint32_t imm20, uint32_t rd, uint32_t opcode) {
- return ((imm20 & 0xFFFFF) << 12) | (rd << 7) | opcode;
-}
-
-// J-type: imm[20] | imm[10:1] | imm[11] | imm[19:12] | rd[4:0] | opcode[6:0]
-static uint32_t rv_j_type(int32_t offset, uint32_t rd, uint32_t opcode) {
- uint32_t imm = static_cast<uint32_t>(offset);
- uint32_t bit20 = (imm >> 20) & 0x1;
- uint32_t bit10_1 = (imm >> 1) & 0x3FF;
- uint32_t bit11 = (imm >> 11) & 0x1;
- uint32_t bit19_12 = (imm >> 12) & 0xFF;
- return (bit20 << 31) | (bit10_1 << 21) | (bit11 << 20) | (bit19_12 << 12) | (rd << 7) | opcode;
-}
-
-// B-type: imm[12|10:5] | rs2[4:0] | rs1[4:0] | funct3[2:0] | imm[4:1|11] | opcode[6:0]
-static uint32_t rv_b_type(int32_t offset, uint32_t rs2, uint32_t rs1, uint32_t funct3, uint32_t opcode) {
- uint32_t imm = static_cast<uint32_t>(offset);
- uint32_t bit12 = (imm >> 12) & 0x1;
- uint32_t bit10_5 = (imm >> 5) & 0x3F;
- uint32_t bit4_1 = (imm >> 1) & 0xF;
- uint32_t bit11 = (imm >> 11) & 0x1;
- return (bit12 << 31) | (bit10_5 << 25) | (rs2 << 20) | (rs1 << 15) |
- (funct3 << 12) | (bit4_1 << 8) | (bit11 << 7) | opcode;
-}
-
-// Common instruction shorthands
-static uint32_t NOP() { return 0x00000013; } // addi x0, x0, 0
-static uint32_t ILLEGAL() { return 0xFFFFFFFF; }
-static uint32_t ADDI(uint32_t rd, uint32_t rs1, int32_t imm) {
- return rv_i_type(static_cast<uint32_t>(imm) & 0xFFF, rs1, 0x0, rd, 0x13);
-}
-static uint32_t ADD(uint32_t rd, uint32_t rs1, uint32_t rs2) {
- return rv_r_type(0x00, rs2, rs1, 0x0, rd, 0x33);
-}
-static uint32_t SUB(uint32_t rd, uint32_t rs1, uint32_t rs2) {
- return rv_r_type(0x20, rs2, rs1, 0x0, rd, 0x33);
-}
-static uint32_t LD(uint32_t rd, uint32_t rs1, int32_t imm) {
- return rv_i_type(static_cast<uint32_t>(imm) & 0xFFF, rs1, 0x3, rd, 0x03);
-}
-static uint32_t SD(uint32_t rs2, uint32_t rs1, int32_t imm) {
- return rv_s_type(static_cast<uint32_t>(imm) & 0xFFF, rs2, rs1, 0x3, 0x23);
-}
-static uint32_t BEQ(uint32_t rs1, uint32_t rs2, int32_t offset) {
- return rv_b_type(offset, rs2, rs1, 0x0, 0x63);
-}
-static uint32_t BNE(uint32_t rs1, uint32_t rs2, int32_t offset) {
- return rv_b_type(offset, rs2, rs1, 0x1, 0x63);
-}
-static uint32_t LUI(uint32_t rd, uint32_t imm) {
- return rv_u_type(imm, rd, 0x37);
-}
-static uint32_t JAL(uint32_t rd, int32_t offset) {
- return rv_j_type(offset, rd, 0x6f);
-}
-static uint32_t JALR(uint32_t rd, uint32_t rs1, int32_t imm) {
- return rv_i_type(static_cast<uint32_t>(imm) & 0xFFF, rs1, 0x0, rd, 0x67);
-}
-static uint32_t SYSTEM(uint32_t funct12, uint32_t rs1, uint32_t funct3, uint32_t rd) {
- return ((funct12 & 0xFFF) << 20) | ((rs1 & 0x1F) << 15) | ((funct3 & 0x7) << 12) | ((rd & 0x1F) << 7) | 0x73;
-}
-static uint32_t ECALL() { return SYSTEM(0x000, 0, 0x0, 0); }
-static uint32_t EBREAK() { return SYSTEM(0x001, 0, 0x0, 0); }
-static uint32_t MRET() { return SYSTEM(0x302, 0, 0x0, 0); }
-static uint32_t SRET() { return SYSTEM(0x102, 0, 0x0, 0); }
-static uint32_t CSRInstrReg(uint32_t csr, uint32_t funct3, uint32_t rd, uint32_t rs1) {
- return ((csr & 0xFFF) << 20) | ((rs1 & 0x1F) << 15) | ((funct3 & 0x7) << 12) | ((rd & 0x1F) << 7) | 0x73;
-}
-static uint32_t CSRRW(uint32_t rd, uint32_t csr, uint32_t rs1) {
- return CSRInstrReg(csr, 0b001, rd, rs1);
-}
-static uint32_t CSRRS(uint32_t rd, uint32_t csr, uint32_t rs1) {
- return CSRInstrReg(csr, 0b010, rd, rs1);
-}
-static uint32_t CSRRWI(uint32_t rd, uint32_t csr, uint32_t zimm) {
- return CSRInstrReg(csr, 0b101, rd, zimm & 0x1F);
-}
-
-static void emit_load_imm64(std::vector<uint32_t>& prog, uint32_t rd, uint64_t imm) {
- const uint64_t upper = (imm + 0x800ULL) >> 12;
- int32_t lower = static_cast<int32_t>(imm & 0xFFFULL);
- if (lower >= 2048) {
- lower -= 4096;
- }
- prog.push_back(LUI(rd, static_cast<uint32_t>(upper)));
- prog.push_back(ADDI(rd, rd, lower));
-}
-
-static void emit_nops(std::vector<uint32_t>& prog, int count) {
- for (int i = 0; i < count; ++i) {
- prog.push_back(NOP());
- }
-}
-
-static void emit_csrrw_imm64(std::vector<uint32_t>& prog, uint32_t tmp_reg, uint32_t csr, uint64_t value) {
- // CSR source operand is read in ID stage, so we need a short drain window after loading tmp_reg.
- emit_load_imm64(prog, tmp_reg, value);
- emit_nops(prog, 6);
- prog.push_back(CSRRW(0, csr, tmp_reg));
-}
-
-// -----------------------
-// Test Infrastructure
-// -----------------------
-static int g_test_count = 0;
-static int g_pass_count = 0;
-
-#define ASSERT_EQ(actual, expected, msg) do { \
- g_test_count++; \
- if ((actual) != (expected)) { \
- std::cerr << " FAIL [" << g_test_count << "] " << msg \
- << ": expected 0x" << std::hex << (expected) \
- << ", got 0x" << std::hex << (actual) << std::dec << std::endl; \
- } else { \
- g_pass_count++; \
- } \
-} while(0)
-
-// -----------------------
-// Simple Memory Model
-// -----------------------
-class MemoryModel {
-public:
- // Instruction memory: map from byte_addr -> 32-bit word
- std::map<uint64_t, uint32_t> imem;
- // Data memory: map from byte_addr -> 64-bit doubleword
- std::map<uint64_t, uint64_t> dmem;
-
- void load_program(uint64_t base_addr, const std::vector<uint32_t>& program) {
- for (size_t i = 0; i < program.size(); i++) {
- imem[base_addr + i * 4] = program[i];
- }
- }
-
- uint32_t read_instr(uint64_t addr) {
- auto it = imem.find(addr);
- if (it != imem.end()) return it->second;
- return NOP(); // Return NOP for unmapped addresses
- }
-
- uint64_t read_data(uint64_t addr) const {
- // Align to doubleword
- uint64_t aligned = addr & ~0x7ULL;
- auto it = dmem.find(aligned);
- if (it != dmem.end()) return it->second;
- return 0;
- }
-
- void write_data(uint64_t addr, uint64_t data) {
- uint64_t aligned = addr & ~0x7ULL;
- dmem[aligned] = data;
- }
-};
-
-static std::vector<uint32_t> make_machine_trap_handler_program() {
- constexpr uint32_t REG_PTR = 5;
- constexpr uint32_t REG_MCAUSE = 6;
- constexpr uint32_t REG_MEPC = 7;
- constexpr uint32_t REG_MTVAL = 28;
- constexpr uint32_t REG_TMP = 29;
- constexpr uint32_t REG_SLOT_ADDR = 30;
-
- std::vector<uint32_t> prog;
- emit_load_imm64(prog, REG_SLOT_ADDR, MACHINE_LOG_PTR_ADDR);
- prog.push_back(LD(REG_PTR, REG_SLOT_ADDR, 0));
- emit_nops(prog, 12);
- prog.push_back(CSRRS(REG_MCAUSE, CSR_MCAUSE, 0));
- emit_nops(prog, 4);
- prog.push_back(SD(REG_MCAUSE, REG_PTR, 0));
- prog.push_back(CSRRS(REG_MEPC, CSR_MEPC, 0));
- emit_nops(prog, 4);
- prog.push_back(SD(REG_MEPC, REG_PTR, 8));
- prog.push_back(CSRRS(REG_MTVAL, CSR_MTVAL, 0));
- emit_nops(prog, 4);
- prog.push_back(SD(REG_MTVAL, REG_PTR, 16));
- prog.push_back(ADDI(REG_PTR, REG_PTR, static_cast<int32_t>(LOG_ENTRY_STRIDE)));
- prog.push_back(SD(REG_PTR, REG_SLOT_ADDR, 0));
- prog.push_back(CSRRS(REG_TMP, CSR_MEPC, 0));
- prog.push_back(ADDI(REG_TMP, REG_TMP, 4));
- emit_nops(prog, 12);
- prog.push_back(CSRRW(0, CSR_MEPC, REG_TMP));
- prog.push_back(MRET());
- prog.push_back(NOP());
- prog.push_back(NOP());
- return prog;
-}
-
-static std::vector<uint32_t> make_supervisor_trap_handler_program() {
- constexpr uint32_t REG_PTR = 5;
- constexpr uint32_t REG_SCAUSE = 6;
- constexpr uint32_t REG_SEPC = 7;
- constexpr uint32_t REG_STVAL = 28;
- constexpr uint32_t REG_TMP = 29;
- constexpr uint32_t REG_SLOT_ADDR = 30;
-
- std::vector<uint32_t> prog;
- emit_load_imm64(prog, REG_SLOT_ADDR, SUPERVISOR_LOG_PTR_ADDR);
- prog.push_back(LD(REG_PTR, REG_SLOT_ADDR, 0));
- emit_nops(prog, 12);
- prog.push_back(CSRRS(REG_SCAUSE, CSR_SCAUSE, 0));
- emit_nops(prog, 4);
- prog.push_back(SD(REG_SCAUSE, REG_PTR, 0));
- prog.push_back(CSRRS(REG_SEPC, CSR_SEPC, 0));
- emit_nops(prog, 4);
- prog.push_back(SD(REG_SEPC, REG_PTR, 8));
- prog.push_back(CSRRS(REG_STVAL, CSR_STVAL, 0));
- emit_nops(prog, 4);
- prog.push_back(SD(REG_STVAL, REG_PTR, 16));
- prog.push_back(ADDI(REG_PTR, REG_PTR, static_cast<int32_t>(LOG_ENTRY_STRIDE)));
- prog.push_back(SD(REG_PTR, REG_SLOT_ADDR, 0));
- prog.push_back(CSRRS(REG_TMP, CSR_SEPC, 0));
- prog.push_back(ADDI(REG_TMP, REG_TMP, 4));
- emit_nops(prog, 12);
- prog.push_back(CSRRW(0, CSR_SEPC, REG_TMP));
- prog.push_back(SRET());
- prog.push_back(NOP());
- prog.push_back(NOP());
- return prog;
-}
-
-static void install_trap_handlers(MemoryModel& mem) {
- mem.load_program(MACHINE_TRAP_VECTOR, make_machine_trap_handler_program());
- mem.load_program(SUPERVISOR_TRAP_VECTOR, make_supervisor_trap_handler_program());
-}
-
-struct TrapRecord {
- uint64_t mcause;
- uint64_t mepc;
- uint64_t mtval;
-};
-
-static TrapRecord fetch_trap_record(const MemoryModel& mem, uint64_t base, int index) {
- const uint64_t addr = base + static_cast<uint64_t>(index) * LOG_ENTRY_STRIDE;
- TrapRecord rec;
- rec.mcause = mem.read_data(addr + 0);
- rec.mepc = mem.read_data(addr + 8);
- rec.mtval = mem.read_data(addr + 16);
- return rec;
-}
-
-// -----------------------
-// Core Simulation Wrapper
-// -----------------------
-class CoreSim {
-public:
- Vrichard_core* top;
- MemoryModel mem;
- uint64_t sim_time;
- bool m_timer_interrupt_level;
- bool m_software_interrupt_level;
- bool m_external_interrupt_level;
- bool s_timer_interrupt_level;
- bool s_software_interrupt_level;
- bool s_external_interrupt_level;
-#if VM_TRACE
- VerilatedVcdC* tfp;
-#endif
-
- CoreSim() : sim_time(0),
- m_timer_interrupt_level(false),
- m_software_interrupt_level(false),
- m_external_interrupt_level(false),
- s_timer_interrupt_level(false),
- s_software_interrupt_level(false),
- s_external_interrupt_level(false) {
- top = new Vrichard_core;
-#if VM_TRACE
- Verilated::traceEverOn(true);
- tfp = new VerilatedVcdC;
- top->trace(tfp, 99);
- tfp->open("waves/tb_core.vcd");
-#endif
- }
-
- ~CoreSim() {
-#if VM_TRACE
- if (tfp) { tfp->close(); delete tfp; }
-#endif
- delete top;
- }
-
- void reset(int cycles = 3) {
- top->rst = 1;
- top->imem_rdata = NOP();
- top->dmem_rdata = 0;
- clear_interrupts();
- for (int i = 0; i < cycles; i++) {
- tick();
- }
- top->rst = 0;
- }
-
- void tick() {
- // Drive instruction memory response from model
- top->imem_rdata = mem.read_instr(top->imem_addr);
-
- // Drive data memory response
- if (top->dmem_read_en) {
- top->dmem_rdata = mem.read_data(top->dmem_addr);
- } else {
- top->dmem_rdata = 0;
- }
-
- top->m_timer_interrupt = m_timer_interrupt_level;
- top->m_software_interrupt = m_software_interrupt_level;
- top->m_external_interrupt = m_external_interrupt_level;
- top->s_timer_interrupt = s_timer_interrupt_level;
- top->s_software_interrupt = s_software_interrupt_level;
- top->s_external_interrupt = s_external_interrupt_level;
-
- // Rising edge
- top->clk = 0;
- top->eval();
-#if VM_TRACE
- tfp->dump(sim_time++);
-#endif
-
- top->clk = 1;
- top->eval();
-#if VM_TRACE
- tfp->dump(sim_time++);
-#endif
-
- // Handle writes on the falling edge conceptually
- if (top->dmem_write_en) {
- mem.write_data(top->dmem_addr, top->dmem_wdata);
- }
- }
-
- // Run N cycles
- void run(int n) {
- for (int i = 0; i < n; i++) {
- tick();
- }
- }
-
- void set_interrupt_lines(bool timer, bool software, bool external) {
- m_timer_interrupt_level = timer;
- m_software_interrupt_level = software;
- m_external_interrupt_level = external;
- }
-
- void clear_interrupts() {
- set_interrupt_lines(false, false, false);
- set_supervisor_interrupt_lines(false, false, false);
- }
-
- void set_supervisor_interrupt_lines(bool timer, bool software, bool external) {
- s_timer_interrupt_level = timer;
- s_software_interrupt_level = software;
- s_external_interrupt_level = external;
- }
-};
-
-static std::vector<uint64_t> collect_pc_trace(CoreSim& sim, int cycles) {
- std::vector<uint64_t> trace;
- trace.reserve(static_cast<size_t>(cycles));
- for (int i = 0; i < cycles; ++i) {
- sim.tick();
- trace.push_back(sim.top->imem_addr);
- }
- return trace;
-}
-
-// ═══════════════════════════════════════════════════════════════
-// Test Cases
-// ═══════════════════════════════════════════════════════════════
-
-// -----------------------
-// Test 1: Reset behavior
-// -----------------------
-void test_reset(CoreSim& sim) {
- std::cout << "Test 1: Reset behavior..." << std::endl;
-
- // After reset, PC should be at RESET_VECTOR (0x80000000)
- // Note: if_stage updates PC on posedge clk. After reset deasserts,
- // the first tick() advances PC to 0x80000004 (next_pc = PC+4).
- // So imem_addr = 0x80000000 is visible DURING reset, not after tick.
- sim.reset(3);
- // After 3 reset cycles, the PC is held at 0x80000000.
- // The next tick will fetch from 0x80000000 and advance PC to 0x80000004.
- sim.tick();
- // Now PC has advanced: imem_addr shows the NEXT fetch address
- ASSERT_EQ(sim.top->imem_addr, 0x0000000080000004ULL, "PC after reset+1 tick = 0x80000004");
-
- // No data memory operations during NOP flow
- ASSERT_EQ(sim.top->dmem_read_en, 0, "No dmem read during NOP flow");
- ASSERT_EQ(sim.top->dmem_write_en, 0, "No dmem write during NOP flow");
-}
-
-// -----------------------
-// Test 2: NOP pipeline flow
-// -----------------------
-void test_nop_flow(CoreSim& sim) {
- std::cout << "Test 2: NOP pipeline flow (PC increment)..." << std::endl;
-
- std::vector<uint32_t> program = {
- NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP()
- };
- sim.mem.load_program(0x80000000ULL, program);
- sim.reset();
-
- // After reset, first tick fetches from 0x80000000 and advances PC.
- // imem_addr shows the CURRENT fetch address when clk=0 (before tick),
- // but after tick() returns, PC has already advanced.
- sim.tick();
- ASSERT_EQ(sim.top->imem_addr, 0x80000004ULL, "Cycle 1: PC = 0x80000004");
-
- sim.tick();
- ASSERT_EQ(sim.top->imem_addr, 0x80000008ULL, "Cycle 2: PC = 0x80000008");
-
- sim.tick();
- ASSERT_EQ(sim.top->imem_addr, 0x8000000CULL, "Cycle 3: PC = 0x8000000C");
-
- sim.tick();
- ASSERT_EQ(sim.top->imem_addr, 0x80000010ULL, "Cycle 4: PC = 0x80000010");
-
- // No memory operations
- ASSERT_EQ(sim.top->dmem_read_en, 0, "NOP: no dmem read");
- ASSERT_EQ(sim.top->dmem_write_en, 0, "NOP: no dmem write");
-}
-
-// -----------------------
-// Test 3: ADDI instruction
-// Verifies: Decoder → ID/EX → EX (ALU) → EX/MEM → MEM → MEM/WB → WB (regfile write)
-// -----------------------
-void test_addi(CoreSim& sim) {
- std::cout << "Test 3: ADDI x1, x0, 42 → verify pipeline propagation..." << std::endl;
-
- // Program: addi x1, x0, 42 followed by NOPs
- std::vector<uint32_t> program = {
- ADDI(1, 0, 42), // x1 = 0 + 42 = 42
- NOP(), NOP(), NOP(), NOP(), NOP()
- };
- sim.mem.load_program(0x80000000ULL, program);
- sim.reset();
-
- // Run through full pipeline drain (5 stages + some margin)
- sim.run(7);
-
- // After 5+ cycles, the ADDI should have fully committed
- // No dmem activity for ADDI
- ASSERT_EQ(sim.top->dmem_read_en, 0, "ADDI: no dmem read");
- ASSERT_EQ(sim.top->dmem_write_en, 0, "ADDI: no dmem write");
-
- // PC should have advanced past the program
- // After reset (PC=0x80000000), 7 ticks → PC ≈ 0x80000000 + 7*4 = 0x8000001C
- ASSERT_EQ(sim.top->imem_addr, 0x8000001CULL, "ADDI: PC after 7 cycles");
-}
-
-// -----------------------
-// Test 4: Two dependent ADDIs (data forwarding from WB)
-// addi x1, x0, 10
-// addi x2, x1, 20 ← depends on x1 (2 cycles later, forwarded from WB)
-// -----------------------
-void test_forwarding_wb(CoreSim& sim) {
- std::cout << "Test 4: Forwarding from WB stage (2-cycle gap)..." << std::endl;
-
- std::vector<uint32_t> program = {
- ADDI(1, 0, 10), // x1 = 10
- NOP(), // bubble
- NOP(), // bubble
- ADDI(2, 1, 20), // x2 = x1 + 20 = 30 (forward from WB)
- NOP(), NOP(), NOP(), NOP(), NOP()
- };
- sim.mem.load_program(0x80000000ULL, program);
- sim.reset();
-
- sim.run(10);
-
- // Verify no crashes and pipeline continues
- ASSERT_EQ(sim.top->dmem_read_en, 0, "FWD_WB: no dmem read");
- ASSERT_EQ(sim.top->dmem_write_en, 0, "FWD_WB: no dmem write");
-}
-
-// -----------------------
-// Test 5: Back-to-back dependent ADDIs (MEM forwarding)
-// addi x1, x0, 10
-// addi x2, x1, 5 ← depends on x1 (1 cycle later, forwarded from MEM)
-// -----------------------
-void test_forwarding_mem(CoreSim& sim) {
- std::cout << "Test 5: Forwarding from MEM stage (1-cycle gap)..." << std::endl;
-
- std::vector<uint32_t> program = {
- ADDI(1, 0, 10), // x1 = 10
- NOP(), // 1 bubble
- ADDI(2, 1, 5), // x2 = x1 + 5 = 15 (forward from MEM)
- NOP(), NOP(), NOP(), NOP(), NOP()
- };
- sim.mem.load_program(0x80000000ULL, program);
- sim.reset();
-
- sim.run(10);
-
- ASSERT_EQ(sim.top->dmem_read_en, 0, "FWD_MEM: no dmem read");
- ASSERT_EQ(sim.top->dmem_write_en, 0, "FWD_MEM: no dmem write");
-}
-
-// -----------------------
-// Test 6: Load-Use hazard stall
-// addi x1, x0, 0x100 ← x1 = 0x100 (base address for load)
-// ld x2, 0(x1) ← load from memory
-// add x3, x2, x0 ← depends on load result → load-use stall
-// -----------------------
-void test_load_use_stall(CoreSim& sim) {
- std::cout << "Test 6: Load-use stall detection..." << std::endl;
-
- // Pre-load data memory
- sim.mem.write_data(0x100, 0xDEADBEEFCAFEBABEULL);
-
- std::vector<uint32_t> program = {
- ADDI(1, 0, 0x100), // x1 = 0x100
- NOP(),
- NOP(),
- LD(2, 1, 0), // x2 = mem[x1+0] (load)
- ADD(3, 2, 0), // x3 = x2 + x0 → load-use stall expected
- NOP(), NOP(), NOP(), NOP(), NOP()
- };
- sim.mem.load_program(0x80000000ULL, program);
- sim.reset();
-
- sim.run(12);
-
- // After enough cycles, the load should have triggered dmem activity
- // We can't directly read registers, but verify no crash and dmem was read
- // The load instruction at cycle ~4-5 should trigger dmem_read_en
- // This is a pipeline timing test - success means no crashes/hangs
- std::cout << " Load-use stall test: pipeline survived without hang" << std::endl;
- g_test_count++;
- g_pass_count++; // If we got here, the pipeline didn't crash
-}
-
-// -----------------------
-// Test 7: Store operation
-// addi x1, x0, 0x200
-// addi x2, x0, 0x55
-// sd x2, 0(x1) ← store x2 to mem[0x200]
-// -----------------------
-void test_store(CoreSim& sim) {
- std::cout << "Test 7: Store (SD) instruction..." << std::endl;
-
- // x1 and x2 need to fully commit to regfile before SD uses them.
- // Pipeline: ADDI takes 5 stages to write back. SD reads rs1/rs2 in ID.
- // We need enough NOPs so that both ADDIs have written back before SD is decoded.
- std::vector<uint32_t> program = {
- ADDI(1, 0, 0x200), // x1 = 0x200
- ADDI(2, 0, 0x55), // x2 = 0x55
- NOP(), // Bubble
- NOP(), // Bubble
- NOP(), // Bubble — ensures both x1/x2 are in regfile
- SD(2, 1, 0), // mem[x1+0] = x2
- NOP(), NOP(), NOP(), NOP(), NOP()
- };
- sim.mem.load_program(0x80000000ULL, program);
- sim.reset();
-
- sim.run(14);
-
- // Verify that the store wrote to data memory
- uint64_t stored = sim.mem.read_data(0x200);
- ASSERT_EQ(stored, 0x55ULL, "Store: mem[0x200] should be 0x55");
-}
-
-// -----------------------
-// Test 8: Branch taken (BEQ with equal registers → jumps)
-// addi x1, x0, 5
-// addi x2, x0, 5
-// (bubbles)
-// beq x1, x2, +8 → branch taken, skip next instr
-// addi x3, x0, 99 ← should be flushed
-// addi x4, x0, 77 ← branch target
-// -----------------------
-void test_branch_taken(CoreSim& sim) {
- std::cout << "Test 8: Branch taken (BEQ x1==x2)..." << std::endl;
-
- std::vector<uint32_t> program = {
- ADDI(1, 0, 5), // x1 = 5
- ADDI(2, 0, 5), // x2 = 5
- NOP(),
- NOP(),
- BEQ(1, 2, 8), // if x1==x2, branch +8 (skip next instr)
- ADDI(3, 0, 99), // should be flushed (unreachable)
- ADDI(4, 0, 77), // branch target
- NOP(), NOP(), NOP(), NOP(), NOP()
- };
- sim.mem.load_program(0x80000000ULL, program);
- sim.reset();
-
- // Run enough cycles for branch to resolve
- sim.run(12);
-
- // PC should have jumped past the flushed instruction
- // Key test: pipeline doesn't crash on branch flush
- std::cout << " Branch taken test: pipeline survived branch flush" << std::endl;
- g_test_count++;
- g_pass_count++;
-}
-
-// -----------------------
-// Test 9: Branch not-taken (BEQ with unequal registers → falls through)
-// -----------------------
-void test_branch_not_taken(CoreSim& sim) {
- std::cout << "Test 9: Branch not-taken (BEQ x1!=x2)..." << std::endl;
-
- std::vector<uint32_t> program = {
- ADDI(1, 0, 5), // x1 = 5
- ADDI(2, 0, 10), // x2 = 10
- NOP(),
- NOP(),
- BEQ(1, 2, 8), // x1 != x2 → fall through
- ADDI(3, 0, 99), // should execute (not flushed)
- ADDI(4, 0, 77),
- NOP(), NOP(), NOP(), NOP(), NOP()
- };
- sim.mem.load_program(0x80000000ULL, program);
- sim.reset();
-
- sim.run(15);
-
- // PC should proceed sequentially since branch not taken
- std::cout << " Branch not-taken test: pipeline continues sequentially" << std::endl;
- g_test_count++;
- g_pass_count++;
-}
-
-// -----------------------
-// Test 10: Pipeline stress — multiple instructions, mixed operations
-// Tests overall pipeline integrity with a realistic instruction mix
-// -----------------------
-void test_pipeline_stress(CoreSim& sim) {
- std::cout << "Test 10: Pipeline stress test..." << std::endl;
-
- sim.mem.write_data(0x300, 0x1234567890ABCDEFULL);
-
- std::vector<uint32_t> program = {
- ADDI(1, 0, 100), // x1 = 100
- ADDI(2, 0, 200), // x2 = 200
- ADDI(3, 0, 0x300), // x3 = 0x300 (data addr)
- NOP(),
- ADD(4, 1, 2), // x4 = x1 + x2 = 300 (forwarding test)
- SUB(5, 2, 1), // x5 = x2 - x1 = 100 (forwarding test)
- NOP(),
- LD(6, 3, 0), // x6 = mem[0x300]
- NOP(), // Load-use bubble
- ADD(7, 6, 1), // x7 = x6 + x1 (uses load result)
- SD(4, 3, 8), // mem[0x308] = x4
- NOP(),
- BNE(1, 2, 8), // x1 != x2 → branch taken
- ADDI(8, 0, 0xFF), // should be flushed
- ADDI(9, 0, 0x42), // branch target
- NOP(), NOP(), NOP(), NOP(), NOP()
- };
- sim.mem.load_program(0x80000000ULL, program);
- sim.reset();
-
- sim.run(25);
-
- // Verify store result
- uint64_t stored = sim.mem.read_data(0x308);
- // x4 = 300, but forwarding may or may not work perfectly depending on pipeline timing
- // The key test is that the pipeline doesn't crash
- std::cout << " Pipeline stress: completed " << 25 << " cycles without crash" << std::endl;
- std::cout << " mem[0x308] = 0x" << std::hex << stored << std::dec << std::endl;
- g_test_count++;
- g_pass_count++;
-}
-
-// -----------------------
-// Test 11: Hazard unit flush signal verification
-// Verify that branch flush properly inserts bubbles
-// -----------------------
-void test_branch_flush_timing(CoreSim& sim) {
- std::cout << "Test 11: Branch flush timing..." << std::endl;
-
- std::vector<uint32_t> program = {
- ADDI(1, 0, 0), // x1 = 0
- NOP(),
- NOP(),
- NOP(),
- BEQ(1, 0, 12), // x1 == x0 (both 0), branch +12
- ADDI(10, 0, 1), // flushed
- ADDI(11, 0, 2), // flushed
- ADDI(12, 0, 3), // branch target (PC+12 from BEQ)
- NOP(), NOP(), NOP(), NOP()
- };
- sim.mem.load_program(0x80000000ULL, program);
- sim.reset();
-
- sim.run(15);
-
- ASSERT_EQ(sim.top->dmem_read_en, 0, "Flush: no dmem read after branch");
- ASSERT_EQ(sim.top->dmem_write_en, 0, "Flush: no dmem write after branch");
-}
-
-// -----------------------
-// Test 12: Verify reset clears pipeline state
-// -----------------------
-void test_reset_mid_execution(CoreSim& sim) {
- std::cout << "Test 12: Reset during execution..." << std::endl;
-
- std::vector<uint32_t> program = {
- ADDI(1, 0, 42),
- ADDI(2, 0, 100),
- ADD(3, 1, 2),
- NOP(), NOP(), NOP(), NOP(), NOP()
- };
- sim.mem.load_program(0x80000000ULL, program);
- sim.reset();
- sim.run(3); // Partially execute
-
- // Reset mid execution
- sim.reset(3);
- sim.tick();
-
- // After reset+tick, PC has advanced to 0x80000004
- ASSERT_EQ(sim.top->imem_addr, 0x80000004ULL, "Mid-reset: PC = 0x80000004 after reset+1 tick");
- ASSERT_EQ(sim.top->dmem_read_en, 0, "Mid-reset: no dmem read");
- ASSERT_EQ(sim.top->dmem_write_en, 0, "Mid-reset: no dmem write");
-}
-
-// Test 13: RV64 Fibonacci Sequence Integration Test
-void test_fibonacci(CoreSim& sim) {
- std::cout << "Test 13: RV64 Fibonacci Sequence..." << std::endl;
-
- std::vector<uint32_t> program = {
- ADDI(5, 0, 10), // addi x5, x0, 10
- LUI(6, 0x10000), // lui x6, 0x10000
- ADDI(7, 0, 0), // addi x7, x0, 0
- ADDI(8, 0, 1), // addi x8, x0, 1
- BEQ(5, 0, 32), // beq x5, x0, 32
- SD(7, 6, 0), // sd x7, 0(x6)
- ADD(9, 7, 8), // add x9, x7, x8
- ADDI(7, 8, 0), // addi x7, x8, 0
- ADDI(8, 9, 0), // addi x8, x9, 0
- ADDI(6, 6, 8), // addi x6, x6, 8
- ADDI(5, 5, -1), // addi x5, x5, -1
- JAL(0, -28), // jal x0, -28
- JAL(0, 0) // jal x0, 0
- };
-
- sim.mem.load_program(0x80000000ULL, program);
- sim.reset();
-
- sim.run(200);
-
- uint64_t expected_fib[10] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34};
-
- for (int i = 0; i < 10; i++) {
- uint64_t addr = 0x10000000ULL + (i * 8);
- uint64_t actual_data = sim.mem.read_data(addr);
-
- ASSERT_EQ(actual_data, expected_fib[i], "Fibonacci value mismatch");
- }
-}
-
-// ═══════════════════════════════════════════════════════════════
-// Targeted Trap/Interrupt Scenarios (ctest-selectable)
-// ═══════════════════════════════════════════════════════════════
-
-static void run_mem_fault_pipeline_case() {
- std::cout << "[CASE] mem_fault_pipeline" << std::endl;
- CoreSim sim;
- install_trap_handlers(sim.mem);
-
- const uint64_t boot_pc = 0x80000000ULL;
- const uint64_t load_addr = 0x0000000000001003ULL;
- const uint64_t store_addr = 0x0000000000002005ULL;
- const uint64_t log_base = 0x0000000000009000ULL;
- sim.mem.write_data(MACHINE_LOG_PTR_ADDR, log_base);
- ASSERT_EQ(sim.mem.read_data(MACHINE_LOG_PTR_ADDR), log_base, "machine log pointer seeded");
-
- std::vector<uint32_t> program;
- emit_nops(program, 8);
- emit_csrrw_imm64(program, 1, CSR_MTVEC, MACHINE_TRAP_VECTOR);
- emit_nops(program, 8);
- emit_load_imm64(program, 3, load_addr);
- program.push_back(LD(5, 3, 0));
- program.push_back(JAL(0, -4));
-
- sim.mem.load_program(boot_pc, program);
- sim.reset();
- sim.run(80);
- ASSERT_EQ(sim.mem.read_data(MACHINE_LOG_PTR_ADDR), log_base + LOG_ENTRY_STRIDE, "machine log pointer advanced");
-
- const TrapRecord load_trap = fetch_trap_record(sim.mem, log_base, 0);
- ASSERT_EQ(load_trap.mcause, EXC_LOAD_FAULT, "load fault cause code");
- ASSERT_EQ(load_trap.mtval, load_addr, "load fault address latched");
- ASSERT_EQ(sim.mem.read_data(store_addr & ~0x7ULL), 0ULL, "misaligned store suppressed");
-}
-
-static void run_double_exception_flush_case() {
- std::cout << "[CASE] double_exception_flush" << std::endl;
- CoreSim sim;
- install_trap_handlers(sim.mem);
-
- const uint64_t boot_pc = 0x80000000ULL;
- const uint64_t log_base = 0x0000000000009100ULL;
- sim.mem.write_data(MACHINE_LOG_PTR_ADDR, log_base);
- std::vector<uint32_t> program;
- emit_nops(program, 8);
- emit_csrrw_imm64(program, 1, CSR_MTVEC, MACHINE_TRAP_VECTOR);
- emit_nops(program, 8);
- program.push_back(ILLEGAL());
- program.push_back(NOP());
- program.push_back(JAL(0, -4));
-
- sim.mem.load_program(boot_pc, program);
- sim.reset();
- sim.run(80);
-
- const TrapRecord illegal_trap = fetch_trap_record(sim.mem, log_base, 0);
- ASSERT_EQ(illegal_trap.mcause, EXC_INST_ILLEGAL, "first cause illegal");
- ASSERT_EQ(sim.mem.read_data(MACHINE_LOG_PTR_ADDR), log_base + LOG_ENTRY_STRIDE, "single trap logged");
-}
-
-static void run_mret_sret_redirect_case() {
- std::cout << "[CASE] mret_sret_redirect" << std::endl;
- CoreSim sim;
- install_trap_handlers(sim.mem);
-
- const uint64_t boot_pc = 0x80000000ULL;
- const uint64_t m_log_base = 0x0000000000009200ULL;
- const uint64_t post_store_addr = 0x0000000000009400ULL;
- const uint64_t post_trap_addr = 0x0000000000009408ULL;
- sim.mem.write_data(MACHINE_LOG_PTR_ADDR, m_log_base);
-
- std::vector<uint32_t> boot;
- emit_nops(boot, 8);
- emit_csrrw_imm64(boot, 1, CSR_MTVEC, MACHINE_TRAP_VECTOR);
- emit_nops(boot, 8);
- emit_load_imm64(boot, 11, post_store_addr);
- emit_load_imm64(boot, 12, 0xCAFECAFEULL);
- boot.push_back(SD(12, 11, 0));
- boot.push_back(ECALL());
- emit_load_imm64(boot, 13, post_trap_addr);
- emit_load_imm64(boot, 14, 0xBEEFBEEFULL);
- boot.push_back(SD(14, 13, 0));
- boot.push_back(JAL(0, 0));
- sim.mem.load_program(boot_pc, boot);
-
- sim.reset();
- sim.run(300);
-
- const TrapRecord m_trap = fetch_trap_record(sim.mem, m_log_base, 0);
- ASSERT_EQ(m_trap.mcause, EXC_ECALL_FROM_M, "ecall from machine traps to machine");
- ASSERT_EQ(sim.mem.read_data(post_store_addr), 0xFFFFFFFFCAFECAFEULL, "post-trap store committed once");
-}
-
-static void run_branch_interrupt_priority_case() {
- std::cout << "[CASE] branch_interrupt_priority" << std::endl;
- CoreSim sim;
- install_trap_handlers(sim.mem);
-
- const uint64_t boot_pc = 0x80000000ULL;
- const uint64_t log_base = 0x0000000000009600ULL;
- sim.mem.write_data(MACHINE_LOG_PTR_ADDR, log_base);
-
- std::vector<uint32_t> boot;
- emit_nops(boot, 8);
- emit_csrrw_imm64(boot, 1, CSR_MTVEC, MACHINE_TRAP_VECTOR);
- emit_csrrw_imm64(boot, 2, CSR_MSTATUS, (1ULL << MSTATUS_MIE_BIT));
- emit_csrrw_imm64(boot, 3, CSR_MIE, (1ULL << INTERRUPT_M_EXTERNAL));
- boot.push_back(JAL(0, 0));
- sim.mem.load_program(boot_pc, boot);
-
- sim.reset();
- sim.run(10);
- sim.set_interrupt_lines(false, false, true);
- sim.run(40);
- sim.clear_interrupts();
- sim.run(20);
-
- const TrapRecord interrupt_trap = fetch_trap_record(sim.mem, log_base, 0);
- ASSERT_EQ(interrupt_trap.mcause, INTERRUPT_BIT | INTERRUPT_M_EXTERNAL, "external interrupt cause");
- ASSERT_EQ(interrupt_trap.mtval, 0ULL, "interrupt mtval should be zero");
-}
-
-static void run_mie_masking_case() {
- std::cout << "[CASE] mie_masking_response" << std::endl;
- CoreSim sim;
- install_trap_handlers(sim.mem);
-
- const uint64_t boot_pc = 0x80000000ULL;
- const uint64_t log_base = 0x0000000000009700ULL;
- sim.mem.write_data(MACHINE_LOG_PTR_ADDR, log_base);
- for (int i = 0; i < 3; ++i) {
- sim.mem.write_data(log_base + static_cast<uint64_t>(i) * 8ULL, 0ULL);
- }
-
- std::vector<uint32_t> program;
- emit_nops(program, 8);
- emit_csrrw_imm64(program, 1, CSR_MTVEC, MACHINE_TRAP_VECTOR);
- emit_csrrw_imm64(program, 2, CSR_MIE, (1ULL << INTERRUPT_M_EXTERNAL));
- program.push_back(CSRRW(0, CSR_MSTATUS, 0));
- emit_nops(program, 8);
- emit_nops(program, 48);
- emit_csrrw_imm64(program, 4, CSR_MSTATUS, (1ULL << MSTATUS_MIE_BIT));
- program.push_back(JAL(0, 0));
-
- sim.mem.load_program(boot_pc, program);
- sim.reset();
-
- sim.set_interrupt_lines(false, false, true);
- sim.run(20);
- ASSERT_EQ(sim.mem.read_data(log_base), 0ULL, "no trap while MIE=0");
- sim.run(100);
- const TrapRecord interrupt_trap = fetch_trap_record(sim.mem, log_base, 0);
- ASSERT_EQ(interrupt_trap.mcause, INTERRUPT_BIT | INTERRUPT_M_EXTERNAL, "external interrupt cause");
-}
-
-// TODO: Add EXPECT_FAIL once trap_load_page_fault/trap_store_page_fault are driven by the MMU.
-
-struct CaseDispatchEntry {
- const char* name;
- void (*fn)();
-};
-
-static const CaseDispatchEntry kCaseDispatchTable[] = {
- {"mem_fault_pipeline", run_mem_fault_pipeline_case},
- {"double_exception_flush", run_double_exception_flush_case},
- {"mret_sret_redirect", run_mret_sret_redirect_case},
- {"branch_interrupt_priority", run_branch_interrupt_priority_case},
- {"mie_masking_response", run_mie_masking_case}
-};
-
-// ═══════════════════════════════════════════════════════════════
-// Main
-// ═══════════════════════════════════════════════════════════════
-int main(int argc, char** argv) {
- Verilated::commandArgs(argc, argv);
-
- std::string requested_case;
- for (int i = 1; i < argc; ++i) {
- if (std::string(argv[i]) == "--case" && (i + 1) < argc) {
- requested_case = argv[++i];
- }
- }
-
- if (!requested_case.empty()) {
- const auto* entry = std::find_if(
- std::begin(kCaseDispatchTable),
- std::end(kCaseDispatchTable),
- [&](const CaseDispatchEntry& item) { return requested_case == item.name; }
- );
- if (entry == std::end(kCaseDispatchTable)) {
- std::cerr << "Unknown --case value: " << requested_case << std::endl;
- return 2;
- }
- entry->fn();
- std::cout << "================================================================" << std::endl;
- std::cout << " Results: " << g_pass_count << "/" << g_test_count << " assertions passed" << std::endl;
- std::cout << "================================================================" << std::endl;
- return (g_pass_count == g_test_count) ? 0 : 1;
- }
-
- std::cout << "================================================================" << std::endl;
- std::cout << " Richard CPU Core Integration Test" << std::endl;
- std::cout << "================================================================" << std::endl;
-
- {
- CoreSim sim;
- test_reset(sim);
- }
- {
- CoreSim sim;
- test_nop_flow(sim);
- }
- {
- CoreSim sim;
- test_addi(sim);
- }
- {
- CoreSim sim;
- test_forwarding_wb(sim);
- }
- {
- CoreSim sim;
- test_forwarding_mem(sim);
- }
- {
- CoreSim sim;
- test_load_use_stall(sim);
- }
- {
- CoreSim sim;
- test_store(sim);
- }
- {
- CoreSim sim;
- test_branch_taken(sim);
- }
- {
- CoreSim sim;
- test_branch_not_taken(sim);
- }
- {
- CoreSim sim;
- test_pipeline_stress(sim);
- }
- {
- CoreSim sim;
- test_branch_flush_timing(sim);
- }
- {
- CoreSim sim;
- test_reset_mid_execution(sim);
- }
- {
- CoreSim sim;
- test_fibonacci(sim);
- }
-
- run_mem_fault_pipeline_case();
- run_double_exception_flush_case();
- run_mret_sret_redirect_case();
- run_branch_interrupt_priority_case();
- run_mie_masking_case();
-
- std::cout << "================================================================" << std::endl;
- std::cout << " Results: " << g_pass_count << "/" << g_test_count << " assertions passed" << std::endl;
- std::cout << "================================================================" << std::endl;
-
- if (g_pass_count == g_test_count) {
- std::cout << " ALL TESTS PASSED" << std::endl;
- return 0;
- }
-
- std::cout << " SOME TESTS FAILED" << std::endl;
- return 1;
-}