diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-03-17 02:39:24 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-03-17 02:39:24 +0800 |
| commit | cd638dacbe7d0f890206209317cc119246cf700b (patch) | |
| tree | 8e16bd601d0ae163b795cb58a4b1c1b7b063c268 | |
| parent | bb51da5bb8b5c8621661695bdf3c7bca42f2f49d (diff) | |
| download | rcc-project-cd638dacbe7d0f890206209317cc119246cf700b.tar.gz rcc-project-cd638dacbe7d0f890206209317cc119246cf700b.tar.xz | |
Execute Stage unit and tests
| -rw-r--r-- | rv64cpu/rtl/core/ex_stage.sv | 114 | ||||
| -rw-r--r-- | rv64cpu/tb/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_ex_stage.cc | 218 |
3 files changed, 343 insertions, 0 deletions
diff --git a/rv64cpu/rtl/core/ex_stage.sv b/rv64cpu/rtl/core/ex_stage.sv new file mode 100644 index 0000000..0991b50 --- /dev/null +++ b/rv64cpu/rtl/core/ex_stage.sv @@ -0,0 +1,114 @@ +// 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: rtl/core/ex_stage.sv +// Description: Execute Stage +// Performs ALU operations, branch/jump calculations, and generates memory addresses. + +module ex_stage + import rv64_pkg::*; +( + // Input from ID/EX level pipeline register + input logic [XLEN-1:0] ex_pc, + input logic [XLEN-1:0] ex_rs1_rdata, + input logic [XLEN-1:0] ex_rs2_rdata, + input logic [XLEN-1:0] ex_imm, + + // Execution Control Signals + input alu_op_t ex_alu_op, + input logic ex_alu_src1_sel, // 0: rs1_rdata, 1: PC + input logic ex_alu_src2_sel, // 0: rs2_rdata, 1: imm + input logic ex_is_branch, + input branch_op_t ex_branch_op, + input logic ex_is_jump, + input logic ex_is_word_op, + + // Output to EX/MEM pipeline register & hazard unit + output logic [XLEN-1:0] ex_alu_result, + output logic ex_branch_taken, + output logic [XLEN-1:0] ex_branch_target +); + + logic [XLEN-1:0] alumux_a; + logic [XLEN-1:0] alumux_b; + logic [XLEN-1:0] alu_operand_a; + logic [XLEN-1:0] alu_operand_b; + logic [XLEN-1:0] alu_raw_result; + logic bu_branch_taken; + + // ------------- + // ALU Input Multiplexers (MUX) + assign alumux_a = (ex_alu_src1_sel == 1'b1) ? ex_pc : ex_rs1_rdata; + assign alumux_b = (ex_alu_src2_sel == 1'b1) ? ex_imm : ex_rs2_rdata; + + // ------------- + // RV64 Word Operation (32-bit) Pre-conditioning + // RV64 requires word operations (ADDW/SLLW/SRLW/SRAW) to operate on the + // lower 32 bits and sign-extend the result. + always_comb begin + if (ex_is_word_op) begin + // Shift operations require specific 32-bit extension forms + if (ex_alu_op == ALU_SRA) + alu_operand_a = { {32{alumux_a[31]}}, alumux_a[31:0] }; // Sign extend for arithmetic shift + else if (ex_alu_op == ALU_SRL) + alu_operand_a = { 32'b0, alumux_a[31:0] }; // Zero extend for logical shift + else + alu_operand_a = alumux_a; // ADDW/SUBW/SLLW don't care about upper bits + + // RV32 shift amount is strictly 5 bits (0-31), ignoring bit 5 which might be set in 64-bit registers + if (ex_alu_op == ALU_SLL || ex_alu_op == ALU_SRL || ex_alu_op == ALU_SRA) + alu_operand_b = { 59'b0, alumux_b[4:0] }; + else + alu_operand_b = alumux_b; + end else begin + // 64-bit operations pass through unchanged + alu_operand_a = alumux_a; + alu_operand_b = alumux_b; + end + end + + // -------------- + // Instantiate the core computing engine ALU + alu alu_inst ( + .alu_op (ex_alu_op), + .operand_a (alu_operand_a), + .operand_b (alu_operand_b), + .alu_result (alu_raw_result) + ); + + // ------------- + // Branch Unit + branch_unit bu_inst ( + .is_branch (ex_is_branch), + .branch_op (ex_branch_op), + .operand_a (ex_rs1_rdata), + .operand_b (ex_rs2_rdata), + .branch_taken (bu_branch_taken) + ); + + // ------------- + // 32-bit instruction result truncation and sign extension + assign ex_alu_result = ex_is_word_op ? + { {32{alu_raw_result[31]}}, alu_raw_result[31:0] } : + alu_raw_result; + + // ------------- + // Branch Target Calculation + assign ex_branch_target = alu_raw_result & ~64'b1; + + assign ex_branch_taken = ex_is_jump | (ex_is_branch & bu_branch_taken); + +endmodule diff --git a/rv64cpu/tb/CMakeLists.txt b/rv64cpu/tb/CMakeLists.txt index e0a3238..cd419f9 100644 --- a/rv64cpu/tb/CMakeLists.txt +++ b/rv64cpu/tb/CMakeLists.txt @@ -103,6 +103,17 @@ add_verilator_target( INCLUDE_DIRS ${RTL_COMMON_INCLUDES} TB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/unit/tb_id_ex_reg.cc ) + +add_verilator_target( + NAME tb_ex_stage + TOP_MODULE ex_stage + SOURCES ${RTL_CORE_DIR}/include/rv64_pkg.sv + ${RTL_CORE_DIR}/alu.sv + ${RTL_CORE_DIR}/branch_unit.sv + ${RTL_CORE_DIR}/ex_stage.sv + INCLUDE_DIRS ${RTL_COMMON_INCLUDES} + TB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/unit/tb_ex_stage.cc +) # # add_verilator_target( # NAME tb_csr diff --git a/rv64cpu/tb/unit/tb_ex_stage.cc b/rv64cpu/tb/unit/tb_ex_stage.cc new file mode 100644 index 0000000..9090a53 --- /dev/null +++ b/rv64cpu/tb/unit/tb_ex_stage.cc @@ -0,0 +1,218 @@ +// 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; +} |
