├── docs ├── images │ ├── circuit.png │ ├── debug.png │ ├── design.pdf │ └── register.png ├── tables │ └── branch_prediction.csv └── main.tex ├── tests ├── out-of-order-1.hex ├── out-of-order-1.asm ├── control-hazard-3.hex ├── data-hazard-1.asm ├── data-hazard-1.hex ├── rename-register-1.hex ├── control-hazard-3.asm ├── data-hazard-2.asm ├── data-hazard-2.hex ├── out-of-order-2.asm ├── control-hazard-1.hex ├── rename-register-1.asm ├── control-hazard-1.asm ├── control-hazard-2.hex ├── out-of-order-2.hex ├── control-hazard-2.asm ├── out-of-order-3.asm └── out-of-order-3.hex ├── src ├── Common │ ├── Session_test.cpp │ ├── Common.h │ ├── utils.h │ ├── utils.cpp │ ├── RegisterFile_test.cpp │ ├── Register.hpp │ ├── Register_test.cpp │ ├── ReorderBuffer.cpp │ ├── Memory_test.cpp │ ├── ReservationStation.cpp │ ├── Session.h │ ├── Parser_test.cpp │ ├── Parser.hpp │ ├── ReorderBuffer.h │ ├── ReservationStation.h │ ├── Session.cpp │ ├── Memory.hpp │ ├── RegisterFile.hpp │ ├── Instruction_test.cpp │ └── Instruction.hpp ├── Pipeline │ ├── OoOCommon.h │ ├── Issue.h │ ├── OoOExecute.h │ ├── OoOExecute.cpp │ └── Issue.cpp ├── Module │ ├── BranchPrediction.h │ ├── BranchPrediction.cpp │ ├── CommitUnit.h │ ├── ALUUnit.h │ ├── LoadStoreUnit.h │ ├── ALUUnit.cpp │ ├── CommitUnit.cpp │ └── LoadStoreUnit.cpp ├── main.cpp └── main_presentation.cpp ├── data ├── naive.data ├── lvalue2.data ├── manyarguments.data ├── gcd.data ├── array_test1.data ├── array_test2.data ├── tak.data ├── expr.data ├── hanoi.data ├── basicopt1.data ├── superloop.data ├── multiarray.data ├── pi.data ├── qsort.data ├── statement_test.data ├── queens.data ├── magic.data └── bulgarian.data ├── .vscode └── settings.json ├── .gitignore ├── .travis.yml ├── LICENSE ├── CMakeLists.txt ├── Branch.md └── README.md /docs/images/circuit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyzh/RISCV-Simulator/HEAD/docs/images/circuit.png -------------------------------------------------------------------------------- /docs/images/debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyzh/RISCV-Simulator/HEAD/docs/images/debug.png -------------------------------------------------------------------------------- /docs/images/design.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyzh/RISCV-Simulator/HEAD/docs/images/design.pdf -------------------------------------------------------------------------------- /docs/images/register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyzh/RISCV-Simulator/HEAD/docs/images/register.png -------------------------------------------------------------------------------- /tests/out-of-order-1.hex: -------------------------------------------------------------------------------- 1 | 00100513 2 | 00100593 3 | 00200613 4 | 00a58633 5 | 00b00533 6 | 00c005b3 7 | ff5ff0ef 8 | -------------------------------------------------------------------------------- /tests/out-of-order-1.asm: -------------------------------------------------------------------------------- 1 | li a0, 1 2 | li a1, 1 3 | li a2, 2 4 | loop: 5 | add a2, a1, a0 6 | mv a0, a1 7 | mv a1, a2 8 | jal loop 9 | -------------------------------------------------------------------------------- /src/Common/Session_test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-02. 3 | // 4 | 5 | #include "gtest/gtest.h" 6 | #include "Session.h" 7 | 8 | 9 | TEST(Session, Construct) { 10 | Session *session = new Session; 11 | delete session; 12 | } 13 | -------------------------------------------------------------------------------- /src/Common/Common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-02. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_COMMON_H 6 | #define RISCV_SIMULATOR_COMMON_H 7 | 8 | using Immediate = unsigned int; 9 | using SImmediate = int; 10 | 11 | #endif //RISCV_SIMULATOR_COMMON_H 12 | -------------------------------------------------------------------------------- /tests/control-hazard-3.hex: -------------------------------------------------------------------------------- 1 | 01000593 2 | 00000013 3 | 00000013 4 | 00000013 5 | 00858567 6 | 00850513 7 | 00850513 8 | 0ff00613 9 | 000306b7 10 | 00c68223 11 | 00000013 12 | 00000013 13 | 00000013 14 | 00000013 15 | 00000013 16 | 00000013 17 | 00000013 18 | 00000013 19 | -------------------------------------------------------------------------------- /tests/data-hazard-1.asm: -------------------------------------------------------------------------------- 1 | .globl main 2 | .text 3 | main: 4 | addi a1, a1, 15 5 | addi a2, a2, 16 6 | add a0, a1, a2 7 | li a2,255 8 | lui a3,0x30 9 | sb a2,4(a3) 10 | nop 11 | nop 12 | nop 13 | nop 14 | nop 15 | nop 16 | nop 17 | nop 18 | nop 19 | nop 20 | nop 21 | nop 22 | nop 23 | -------------------------------------------------------------------------------- /tests/data-hazard-1.hex: -------------------------------------------------------------------------------- 1 | 00f58593 2 | 01060613 3 | 00c58533 4 | 0ff00613 5 | 000306b7 6 | 00c68223 7 | 00000013 8 | 00000013 9 | 00000013 10 | 00000013 11 | 00000013 12 | 00000013 13 | 00000013 14 | 00000013 15 | 00000013 16 | 00000013 17 | 00000013 18 | 00000013 19 | 00000013 20 | -------------------------------------------------------------------------------- /tests/rename-register-1.hex: -------------------------------------------------------------------------------- 1 | 06400593 2 | 06458593 3 | 06458593 4 | 06458593 5 | 06458593 6 | 06458593 7 | 06458593 8 | 06458593 9 | 00b00533 10 | 00000013 11 | 00000013 12 | 00000013 13 | 00000013 14 | 0ff00613 15 | 000306b7 16 | 00c68223 17 | 00000013 18 | 00000013 19 | 00000013 20 | -------------------------------------------------------------------------------- /tests/control-hazard-3.asm: -------------------------------------------------------------------------------- 1 | .globl main 2 | .text 3 | main: 4 | li a1, 0x10 5 | nop 6 | nop 7 | nop 8 | jalr a0, a1, 8 9 | addi a0, a0, 8 10 | addi a0, a0, 8 11 | test: 12 | li a2,255 13 | lui a3,0x30 14 | sb a2,4(a3) 15 | nop 16 | nop 17 | nop 18 | nop 19 | nop 20 | nop 21 | nop 22 | nop 23 | -------------------------------------------------------------------------------- /tests/data-hazard-2.asm: -------------------------------------------------------------------------------- 1 | .globl main 2 | .text 3 | main: 4 | addi a1, a1, 15 5 | nop 6 | addi a2, a2, 16 7 | add a0, a1, a2 8 | li a2,255 9 | lui a3,0x30 10 | sb a2,4(a3) 11 | nop 12 | nop 13 | nop 14 | nop 15 | nop 16 | nop 17 | nop 18 | nop 19 | nop 20 | nop 21 | nop 22 | nop 23 | nop 24 | -------------------------------------------------------------------------------- /src/Common/utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-10. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_UTILS_H 6 | #define RISCV_SIMULATOR_UTILS_H 7 | 8 | #include "Instruction.hpp" 9 | 10 | void debug_immediate(Immediate v, unsigned width = 0); 11 | 12 | #endif //RISCV_SIMULATOR_UTILS_H 13 | -------------------------------------------------------------------------------- /tests/data-hazard-2.hex: -------------------------------------------------------------------------------- 1 | 00f58593 2 | 00000013 3 | 01060613 4 | 00c58533 5 | 0ff00613 6 | 000306b7 7 | 00c68223 8 | 00000013 9 | 00000013 10 | 00000013 11 | 00000013 12 | 00000013 13 | 00000013 14 | 00000013 15 | 00000013 16 | 00000013 17 | 00000013 18 | 00000013 19 | 00000013 20 | 00000013 21 | -------------------------------------------------------------------------------- /tests/out-of-order-2.asm: -------------------------------------------------------------------------------- 1 | li a0, 1 2 | li a1, 1 3 | li a2, 2 4 | li a3, 55 5 | loop: 6 | add a2, a1, a0 7 | mv a0, a1 8 | mv a1, a2 9 | bne a3, a0, loop 10 | nop 11 | nop 12 | nop 13 | nop 14 | nop 15 | nop 16 | nop 17 | nop 18 | nop 19 | nop 20 | nop 21 | nop 22 | nop 23 | nop 24 | nop 25 | nop 26 | -------------------------------------------------------------------------------- /tests/control-hazard-1.hex: -------------------------------------------------------------------------------- 1 | 014000ef 2 | 00850513 3 | 00850513 4 | 00850513 5 | 00850513 6 | 00850513 7 | 0ff00613 8 | 000306b7 9 | 00c68223 10 | 00000013 11 | 00000013 12 | 00000013 13 | 00000013 14 | 00000013 15 | 00000013 16 | 00000013 17 | 00000013 18 | 00000013 19 | 00000013 20 | 00000013 21 | 00000013 22 | 00000013 23 | -------------------------------------------------------------------------------- /tests/rename-register-1.asm: -------------------------------------------------------------------------------- 1 | .text 2 | li a1, 100 3 | addi a1, a1, 100 4 | addi a1, a1, 100 5 | addi a1, a1, 100 6 | addi a1, a1, 100 7 | addi a1, a1, 100 8 | addi a1, a1, 100 9 | addi a1, a1, 100 10 | mv a0, a1 11 | nop 12 | nop 13 | nop 14 | nop 15 | li a2,255 16 | lui a3,0x30 17 | sb a2,4(a3) 18 | nop 19 | nop 20 | nop 21 | -------------------------------------------------------------------------------- /src/Common/utils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-10. 3 | // 4 | 5 | #include "utils.h" 6 | #include 7 | #include 8 | 9 | void debug_immediate(Immediate v, unsigned width) { 10 | std::cout << std::dec << std::setw(width) << (SImmediate) v << "(" << std::hex << std::setw(width) << v << ")"; 11 | } 12 | -------------------------------------------------------------------------------- /tests/control-hazard-1.asm: -------------------------------------------------------------------------------- 1 | .globl main 2 | .text 3 | main: 4 | jal test 5 | addi a0, a0, 8 6 | addi a0, a0, 8 7 | addi a0, a0, 8 8 | addi a0, a0, 8 9 | test: 10 | addi a0, a0, 8 11 | li a2,255 12 | lui a3,0x30 13 | sb a2,4(a3) 14 | nop 15 | nop 16 | nop 17 | nop 18 | nop 19 | nop 20 | nop 21 | nop 22 | nop 23 | nop 24 | nop 25 | nop 26 | nop 27 | -------------------------------------------------------------------------------- /tests/control-hazard-2.hex: -------------------------------------------------------------------------------- 1 | 00a00813 2 | 00a00893 3 | 01180a63 4 | 00850513 5 | 00850513 6 | 00850513 7 | 00850513 8 | 00850513 9 | 0ff00613 10 | 000306b7 11 | 00c68223 12 | 00000013 13 | 00000013 14 | 00000013 15 | 00000013 16 | 00000013 17 | 00000013 18 | 00000013 19 | 00000013 20 | 00000013 21 | 00000013 22 | 00000013 23 | 00000013 24 | 00000013 25 | -------------------------------------------------------------------------------- /tests/out-of-order-2.hex: -------------------------------------------------------------------------------- 1 | 00100513 2 | 00100593 3 | 00200613 4 | 03700693 5 | 00a58633 6 | 00b00533 7 | 00c005b3 8 | fea69ae3 9 | 00000013 10 | 00000013 11 | 00000013 12 | 00000013 13 | 00000013 14 | 00000013 15 | 00000013 16 | 00000013 17 | 00000013 18 | 00000013 19 | 00000013 20 | 00000013 21 | 00000013 22 | 00000013 23 | 00000013 24 | 00000013 25 | -------------------------------------------------------------------------------- /tests/control-hazard-2.asm: -------------------------------------------------------------------------------- 1 | .globl main 2 | .text 3 | main: 4 | li a6, 10 5 | li a7, 10 6 | beq a6, a7, test 7 | addi a0, a0, 8 8 | addi a0, a0, 8 9 | addi a0, a0, 8 10 | addi a0, a0, 8 11 | test: 12 | addi a0, a0, 8 13 | li a2,255 14 | lui a3,0x30 15 | sb a2,4(a3) 16 | nop 17 | nop 18 | nop 19 | nop 20 | nop 21 | nop 22 | nop 23 | nop 24 | nop 25 | nop 26 | nop 27 | nop 28 | nop 29 | -------------------------------------------------------------------------------- /src/Pipeline/OoOCommon.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-10. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_OOOCOMMON_H 6 | #define RISCV_SIMULATOR_OOOCOMMON_H 7 | 8 | enum RSID { 9 | NONE = 0, 10 | RS_BEGIN = 255, 11 | ADD1, ADD2, ADD3, ADD4, 12 | STORE1, STORE2, STORE3, 13 | LOAD1, LOAD2, LOAD3, 14 | RS_END 15 | }; 16 | 17 | #endif //RISCV_SIMULATOR_OOOCOMMON_H 18 | -------------------------------------------------------------------------------- /tests/out-of-order-3.asm: -------------------------------------------------------------------------------- 1 | lui sp,0x20 2 | jal main 3 | li a2,255 4 | lui a3,0x30 5 | sb a2,4(a3) 6 | nop 7 | nop 8 | nop 9 | nop 10 | nop 11 | nop 12 | nop 13 | nop 14 | nop 15 | nop 16 | nop 17 | nop 18 | nop 19 | nop 20 | nop 21 | main: 22 | li a0, 1 23 | li a1, 1 24 | li a2, 2 25 | li a3, 55 26 | loop: 27 | add a2, a1, a0 28 | mv a0, a1 29 | mv a1, a2 30 | bne a3, a0, loop 31 | ret 32 | nop 33 | -------------------------------------------------------------------------------- /src/Common/RegisterFile_test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-02. 3 | // 4 | 5 | #include "gtest/gtest.h" 6 | #include "RegisterFile.hpp" 7 | 8 | 9 | TEST(RegisterFile, ReadWrite) { 10 | RegisterFile r; 11 | r.write(0, 2333); 12 | EXPECT_EQ(r.read(0), 0); 13 | r.write(1, 1); 14 | EXPECT_EQ(r.read(1), 0); 15 | r.tick(); 16 | EXPECT_EQ(r.read(1), 1); 17 | EXPECT_EQ(r.read(0), 0); 18 | } 19 | -------------------------------------------------------------------------------- /tests/out-of-order-3.hex: -------------------------------------------------------------------------------- 1 | 00020137 2 | 04c000ef 3 | 0ff00613 4 | 000306b7 5 | 00c68223 6 | 00000013 7 | 00000013 8 | 00000013 9 | 00000013 10 | 00000013 11 | 00000013 12 | 00000013 13 | 00000013 14 | 00000013 15 | 00000013 16 | 00000013 17 | 00000013 18 | 00000013 19 | 00000013 20 | 00000013 21 | 00100513 22 | 00100593 23 | 00200613 24 | 03700693 25 | 00a58633 26 | 00b00533 27 | 00c005b3 28 | fea69ae3 29 | 00008067 30 | 00000013 31 | -------------------------------------------------------------------------------- /data/naive.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 00 04 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 17 00 00 83 27 C7 06 33 45 F5 00 13 05 D5 0A 6 | 23 26 A7 06 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 17 00 00 83 26 C7 06 B3 C7 D7 00 93 87 97 20 8 | 23 26 F7 06 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 FF 23 26 11 00 13 05 10 0B 10 | EF F0 1F FB B7 17 00 00 03 A5 C7 06 83 20 C1 00 11 | 13 01 01 01 67 80 00 00 12 | @00001068 13 | FD 00 00 00 14 | -------------------------------------------------------------------------------- /src/Module/BranchPrediction.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-04. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_BRANCHPREDICTION_H 6 | #define RISCV_SIMULATOR_BRANCHPREDICTION_H 7 | 8 | #include "../Common/Memory.hpp" 9 | 10 | class BranchPrediction { 11 | public: 12 | unsigned char two_bits[MEMORY_SIZE]; 13 | unsigned char mux[MEMORY_SIZE]; 14 | 15 | BranchPrediction(); 16 | 17 | bool take(unsigned addr); 18 | 19 | void report(unsigned addr, bool taken); 20 | }; 21 | 22 | 23 | #endif //RISCV_SIMULATOR_BRANCHPREDICTION_H 24 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "latex-workshop.latex.tools": [ 3 | { 4 | "name": "xelatex", 5 | "command": "xelatex", 6 | "args": [ 7 | "-synctex=1", 8 | "-interaction=nonstopmode", 9 | "-file-line-error", 10 | "-shell-escape", 11 | "%DOC%" 12 | ] 13 | } 14 | ], 15 | "latex-workshop.latex.recipes": [ 16 | { 17 | "name": "xelatex", 18 | "tools": [ 19 | "xelatex" 20 | ] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles 3 | CMakeScripts 4 | Testing 5 | Makefile 6 | cmake_install.cmake 7 | install_manifest.txt 8 | compile_commands.json 9 | CTestTestfile.cmake 10 | 11 | cmake-build-*/ 12 | Debug/ 13 | Release/ 14 | Bin/ 15 | 16 | .idea/ 17 | data/ 18 | dumps/ 19 | 20 | ### TeX ### 21 | ## Core latex/pdflatex auxiliary files: 22 | *.aux 23 | *.lof 24 | *.log 25 | *.lot 26 | *.fls 27 | *.out 28 | *.toc 29 | 30 | ## Intermediate documents: 31 | *.dvi 32 | *-converted-to.* 33 | <<<<<<< HEAD 34 | 35 | ### Editor ### 36 | ## Vim & Emacs temp files: 37 | .*sw[op] 38 | *~ 39 | 40 | ### Mac OS X ### 41 | ## Mac OS X metadata files: 42 | *.DS_Store 43 | -------------------------------------------------------------------------------- /src/Module/BranchPrediction.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-04. 3 | // 4 | 5 | #include "BranchPrediction.h" 6 | #include 7 | 8 | BranchPrediction::BranchPrediction() { 9 | memset(two_bits, 0, sizeof(two_bits)); 10 | memset(mux, 0, sizeof(mux)); 11 | } 12 | 13 | bool BranchPrediction::take(unsigned addr) { 14 | return two_bits[addr + (mux[addr] & 0x3)] >= 2; 15 | } 16 | 17 | void BranchPrediction::report(unsigned addr, bool taken) { 18 | unsigned branch_addr = addr + (mux[addr] & 0x3); 19 | if (taken) { 20 | if (two_bits[branch_addr] < 3) two_bits[branch_addr]++; 21 | } else { 22 | if (two_bits[branch_addr] > 0) two_bits[branch_addr]--; 23 | } 24 | mux[addr] = mux[addr] << 1 | (taken ? 1 : 0); 25 | } 26 | -------------------------------------------------------------------------------- /src/Common/Register.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-01. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_REGISTER_HPP 6 | #define RISCV_SIMULATOR_REGISTER_HPP 7 | 8 | template 9 | class Register { // : public Tickable { 10 | public: 11 | T prev, next; 12 | 13 | bool _stall; 14 | 15 | Register() : prev((T) 0), next((T) 0), _stall(false) {} 16 | 17 | Register(T d) : prev(d), next(d), _stall(false) {} 18 | 19 | T read() { return prev; } 20 | 21 | T current() { return next; } 22 | 23 | void write(const T &t) { next = t; } 24 | 25 | void tick() { if (!_stall) prev = next; } 26 | 27 | void stall(bool stall) { _stall = stall; } 28 | 29 | operator T() { return read(); } 30 | 31 | void operator=(T next) { write(next); } 32 | }; 33 | 34 | #endif //RISCV_SIMULATOR_REGISTER_HPP 35 | -------------------------------------------------------------------------------- /src/Module/CommitUnit.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-12. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_COMMITUNIT_H 6 | #define RISCV_SIMULATOR_COMMITUNIT_H 7 | 8 | #include "../Common/ReorderBuffer.h" 9 | 10 | class OoOExecute; 11 | 12 | class CommitUnit { 13 | public: 14 | OoOExecute *e; 15 | 16 | CommitUnit(OoOExecute *e); 17 | 18 | void update(); 19 | 20 | void tick(); 21 | 22 | void resolve_branch(ROB &rob_entry); 23 | 24 | Immediate get_next_pc(InstructionBase &inst, ROB &rob_entry, Immediate pc); 25 | 26 | void resolve_op(ROB &rob_entry, unsigned rob_id); 27 | 28 | void resolve_store(ROB &rob_entry); 29 | 30 | void resolve_jalr(ROB& rob_entry); 31 | 32 | void flush_rob_entry(ROB& rob_entry); 33 | 34 | void reset(); 35 | }; 36 | 37 | 38 | #endif //RISCV_SIMULATOR_COMMITUNIT_H 39 | -------------------------------------------------------------------------------- /src/Common/Register_test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-01. 3 | // 4 | 5 | #include "gtest/gtest.h" 6 | #include "Register.hpp" 7 | 8 | 9 | TEST(Register, ReadWrite) { 10 | Register r; 11 | r.write(1); 12 | EXPECT_EQ(r.read(), 0); 13 | r.tick(); 14 | EXPECT_EQ(r.read(), 1); 15 | r.write(2); 16 | r.stall(true); 17 | r.tick(); 18 | EXPECT_EQ(r.read(), 1); 19 | r.stall(false); 20 | r.tick(); 21 | EXPECT_EQ(r.read(), 2); 22 | } 23 | 24 | TEST(Register, ReadWriteOperator) { 25 | Register r; 26 | r = 1; 27 | EXPECT_EQ((unsigned) r, 0); 28 | r.tick(); 29 | EXPECT_EQ((unsigned) r, 1); 30 | r = 2; 31 | r.stall(true); 32 | r.tick(); 33 | EXPECT_EQ((unsigned) r, 1); 34 | r.stall(false); 35 | r.tick(); 36 | EXPECT_EQ((unsigned) r, 2); 37 | } 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | dist: xenial 3 | compiler: 4 | - gcc 5 | cache: 6 | directories: 7 | - /home/travis/vcpkg/ 8 | before_script: 9 | - export CC=gcc-9 10 | - export CXX=g++-9 11 | - pushd . && cd ~ 12 | - if [ ! -f "vcpkg/bootstrap-vcpkg.sh" ] ; then git clone https://github.com/Microsoft/vcpkg.git && cd vcpkg && ./bootstrap-vcpkg.sh && cd .. ; fi 13 | - cd vcpkg && ./vcpkg install gtest 14 | script: 15 | - popd 16 | - cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=~/vcpkg/scripts/buildsystems/vcpkg.cmake 17 | - cd build && cmake --build . 18 | - ./RISCV_Simulator_Test 19 | - ./RISCV_Simulator_Main 20 | env: 21 | - BUILD_TYPE=DEBUG 22 | - BUILD_TYPE=RELEASE 23 | addons: 24 | apt: 25 | sources: 26 | - ubuntu-toolchain-r-test 27 | packages: 28 | - g++-9 29 | - ninja-build 30 | -------------------------------------------------------------------------------- /src/Module/ALUUnit.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-10. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_ALUUNIT_H 6 | #define RISCV_SIMULATOR_ALUUNIT_H 7 | 8 | #include "../Common/ReservationStation.h" 9 | #include "../Pipeline/OoOCommon.h" 10 | 11 | #include 12 | #include 13 | 14 | using std::vector; 15 | using std::string; 16 | 17 | class OoOExecute; 18 | 19 | class ALUUnit { 20 | public: 21 | OoOExecute *e; 22 | vector rs; 23 | enum OP { 24 | NONE_OP = 0, ADD = 233, SUB, SLT, SLTU, XOR, OR, AND, SLL, SRL, SRA 25 | }; 26 | 27 | ALUUnit(OoOExecute *e); 28 | 29 | void update(); 30 | 31 | Immediate get_result(Immediate op, Immediate op1, Immediate op2); 32 | 33 | static string resolve(OP op); 34 | 35 | void tick(); 36 | 37 | void reset(); 38 | }; 39 | 40 | 41 | #endif //RISCV_SIMULATOR_ALUUNIT_H 42 | -------------------------------------------------------------------------------- /src/Common/ReorderBuffer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-11. 3 | // 4 | 5 | #include "ReorderBuffer.h" 6 | #include "utils.h" 7 | #include 8 | #include 9 | 10 | void ROB::debug() { 11 | using std::cout; 12 | using std::endl; 13 | using std::setw; 14 | std::cout << setw(4) << __debug_identifier 15 | << setw(8) << resolve(Inst.current().opcode); 16 | debug_immediate(Dest.current(), 7); 17 | debug_immediate(Value.current(), 11); 18 | debug_immediate(Tag.current(), 7); 19 | std::cout << (Inst.current().is_nop() ? " (⚪idle)" : ""); 20 | std::cout << (Ready.current() ? " (⚪ready)" : ""); 21 | std::cout << std::endl; 22 | } 23 | 24 | void ROB::debug_header() { 25 | char buffer[100]; 26 | sprintf(buffer, "%4s%8s%16s%24s%16s\n", 27 | "ID", "Type", "Dest", "Val", "Tag"); 28 | std::cout << buffer; 29 | } 30 | -------------------------------------------------------------------------------- /src/Common/Memory_test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-02. 3 | // 4 | 5 | #include "gtest/gtest.h" 6 | #include "Memory.hpp" 7 | 8 | TEST(Memory, StoreWord) { 9 | Memory mem; 10 | mem.write_word(0, 233); 11 | mem.write_word(4, -234); 12 | mem.write_word(8, 235); 13 | EXPECT_EQ(mem.read_word(0), 233); 14 | EXPECT_EQ(mem.read_word(4), -234); 15 | EXPECT_EQ(mem.read_word(8), 235); 16 | } 17 | 18 | TEST(Memory, StoreShort) { 19 | Memory mem; 20 | mem.write_ushort(0, 233); 21 | mem.write_ushort(4, -234); 22 | mem.write_ushort(8, 235); 23 | EXPECT_EQ(mem.read_ushort(0), 233); 24 | EXPECT_EQ((short) mem.read_ushort(4), -234); 25 | EXPECT_EQ(mem.read_ushort(8), 235); 26 | } 27 | 28 | TEST(Memory, StoreData) { 29 | Memory mem; 30 | mem[0] = 233; 31 | mem[1] = 234; 32 | mem[2] = 235; 33 | EXPECT_EQ(mem[0], (unsigned char) 233); 34 | EXPECT_EQ(mem[1], (unsigned char) 234); 35 | EXPECT_EQ(mem[2], (unsigned char) 235); 36 | } 37 | 38 | TEST(Memory, Utility) { 39 | unsigned char x = -1; 40 | int y = (char) x; 41 | EXPECT_EQ(y, -1); 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Alex Chi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Common/ReservationStation.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-10. 3 | // 4 | 5 | #include "ReservationStation.h" 6 | #include "../Module/ALUUnit.h" 7 | #include 8 | 9 | void RS::debug() { 10 | using std::cout; 11 | using std::setw; 12 | using std::endl; 13 | cout << resolve(__debug_identifier) << (Busy.current() ? " (⚪busy)" : " ") << "\t"; 14 | cout << setw(8) << ALUUnit::resolve((ALUUnit::OP) Op.current()); 15 | char buffer[100]; 16 | sprintf(buffer, "#%d", Qj.current()); 17 | cout << setw(8) << buffer; 18 | sprintf(buffer, "#%d", Qk.current()); 19 | cout << setw(8) << buffer; 20 | debug_immediate(Vj.current(), 11); 21 | debug_immediate(Vk.current(), 11); 22 | debug_immediate(A.current(), 7); 23 | debug_immediate(Dest.current(), 7); 24 | debug_immediate(Tag.current(), 7); 25 | cout << endl; 26 | } 27 | 28 | void RS::debug_header() { 29 | char buffer[1000]; 30 | sprintf(buffer, "%16s%8s%8s%8s%24s%24s%16s%16s%16s\n", 31 | "", "Op", "Qj", "Qk", "Vj", "Vk", "A", "Dest", "Tag"); 32 | std::cout << buffer << std::endl; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /data/lvalue2.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 00 04 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 17 00 00 83 27 87 13 33 45 F5 00 13 05 D5 0A 6 | 23 2C A7 12 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 17 00 00 83 26 87 13 B3 C7 D7 00 93 87 97 20 8 | 23 2C F7 12 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 FF 23 26 11 00 13 05 20 00 10 | EF F0 1F FB 93 05 D0 0F B7 17 00 00 03 A5 87 13 11 | EF 00 40 09 83 20 C1 00 13 01 01 01 67 80 00 00 12 | 63 40 05 06 63 C6 05 06 13 86 05 00 93 05 05 00 13 | 13 05 F0 FF 63 0C 06 02 93 06 10 00 63 7A B6 00 14 | 63 58 C0 00 13 16 16 00 93 96 16 00 E3 6A B6 FE 15 | 13 05 00 00 63 E6 C5 00 B3 85 C5 40 33 65 D5 00 16 | 93 D6 16 00 13 56 16 00 E3 96 06 FE 67 80 00 00 17 | 93 82 00 00 EF F0 5F FB 13 85 05 00 67 80 02 00 18 | 33 05 A0 40 63 D8 05 00 B3 05 B0 40 6F F0 DF F9 19 | B3 05 B0 40 93 82 00 00 EF F0 1F F9 33 05 A0 40 20 | 67 80 02 00 93 82 00 00 63 CA 05 00 63 4C 05 00 21 | EF F0 9F F7 13 85 05 00 67 80 02 00 B3 05 B0 40 22 | E3 58 05 FE 33 05 A0 40 EF F0 1F F6 33 05 B0 40 23 | 67 80 02 00 24 | @00001124 25 | FD 00 00 00 26 | -------------------------------------------------------------------------------- /src/Common/Session.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-02. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_SESSION_H 6 | #define RISCV_SIMULATOR_SESSION_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "Common.h" 13 | #include "Register.hpp" 14 | #include "Memory.hpp" 15 | #include "RegisterFile.hpp" 16 | 17 | using std::shared_ptr; 18 | 19 | class OoOExecute; 20 | 21 | class Issue; 22 | 23 | class BranchPrediction; 24 | 25 | class Session { // : public Tickable { 26 | bool _debug; 27 | public: 28 | RegisterFile rf; 29 | Memory memory; 30 | OoOExecute *e; 31 | Issue *i; 32 | BranchPrediction* branch; 33 | 34 | 35 | struct Stat { 36 | unsigned long long cycle; 37 | Stat() : cycle(0) {} 38 | } stat; 39 | 40 | Session(bool debug = false); 41 | 42 | void tick(); 43 | 44 | void load_memory(const char *path); 45 | 46 | void load_memory(std::istream &in); 47 | 48 | void load_hex(const char *path); 49 | 50 | void report(std::ostream& out); 51 | 52 | virtual ~Session(); 53 | 54 | void debug(); 55 | }; 56 | 57 | 58 | #endif //RISCV_SIMULATOR_SESSION_H 59 | -------------------------------------------------------------------------------- /src/Module/LoadStoreUnit.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-10. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_LOADSTOREUNIT_H 6 | #define RISCV_SIMULATOR_LOADSTOREUNIT_H 7 | 8 | #include "../Pipeline/OoOCommon.h" 9 | #include "../Common/Register.hpp" 10 | #include "../Common/Instruction.hpp" 11 | 12 | #include 13 | 14 | using std::vector; 15 | 16 | class RS; 17 | 18 | class OoOExecute; 19 | 20 | class LoadStoreUnit { 21 | static const unsigned LOAD_BUFFER_SIZE = 3; 22 | static const unsigned STORE_BUFFER_SIZE = 3; 23 | public: 24 | OoOExecute *e; 25 | vector rs_load, rs_store; 26 | 27 | Register load_cnt[LOAD_BUFFER_SIZE]; 28 | Register load_buffer[LOAD_BUFFER_SIZE]; 29 | Register store_cnt[STORE_BUFFER_SIZE]; 30 | 31 | LoadStoreUnit(OoOExecute *e); 32 | 33 | void load_value(RS *rs, Register &load_buffer); 34 | 35 | void commit_value(RSID r_id, Immediate val); 36 | 37 | void update(); 38 | 39 | void tick(); 40 | 41 | bool no_store_in_rob(unsigned addr, unsigned current_rob); 42 | 43 | void reset(); 44 | 45 | Register& get_store_cnt_register(RSID r_id); 46 | Register& get_load_cnt_register(RSID r_id); 47 | Register& get_load_buffer(RSID r_id); 48 | 49 | }; 50 | 51 | 52 | #endif //RISCV_SIMULATOR_LOADSTOREUNIT_H 53 | -------------------------------------------------------------------------------- /data/manyarguments.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 80 09 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 17 00 00 83 27 07 18 33 45 F5 00 13 05 D5 0A 6 | 23 20 A7 18 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 17 00 00 83 26 07 18 B3 C7 D7 00 93 87 97 20 8 | 23 20 F7 18 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 B3 05 B5 00 B3 85 C5 00 B3 85 D5 00 10 | B3 85 E5 00 B3 85 F5 00 B3 85 05 01 B3 85 15 01 11 | 83 27 01 00 B3 87 F5 00 83 25 41 00 B3 87 B7 00 12 | 83 25 81 00 B3 87 B7 00 03 25 C1 00 B3 87 A7 00 13 | 03 25 01 01 B3 87 A7 00 03 25 41 01 B3 87 A7 00 14 | 03 25 81 01 33 85 A7 00 67 80 00 00 13 01 01 FF 15 | 23 26 11 00 13 05 80 07 EF F0 9F F5 93 05 D0 0F 16 | B7 17 00 00 03 A5 07 18 EF 00 40 09 83 20 C1 00 17 | 13 01 01 01 67 80 00 00 63 40 05 06 63 C6 05 06 18 | 13 86 05 00 93 05 05 00 13 05 F0 FF 63 0C 06 02 19 | 93 06 10 00 63 7A B6 00 63 58 C0 00 13 16 16 00 20 | 93 96 16 00 E3 6A B6 FE 13 05 00 00 63 E6 C5 00 21 | B3 85 C5 40 33 65 D5 00 93 D6 16 00 13 56 16 00 22 | E3 96 06 FE 67 80 00 00 93 82 00 00 EF F0 5F FB 23 | 13 85 05 00 67 80 02 00 33 05 A0 40 63 D8 05 00 24 | B3 05 B0 40 6F F0 DF F9 B3 05 B0 40 93 82 00 00 25 | EF F0 1F F9 33 05 A0 40 67 80 02 00 93 82 00 00 26 | 63 CA 05 00 63 4C 05 00 EF F0 9F F7 13 85 05 00 27 | 67 80 02 00 B3 05 B0 40 E3 58 05 FE 33 05 A0 40 28 | EF F0 1F F6 33 05 B0 40 67 80 02 00 29 | @0000117C 30 | FD 00 00 00 31 | -------------------------------------------------------------------------------- /docs/tables/branch_prediction.csv: -------------------------------------------------------------------------------- 1 | data,beforecycles,beforetime,beforerate,aftercycles,cycleimprovement,aftertime,afterrate,rateimprovement 2 | array\_test1.data,218,0.546ms,54.5455\%,209,4.13\%,0.655ms,54.5455\%,0.00\% 3 | array\_test2.data,247,0.544ms,50\%,237,4.05\%,1.047ms,50\%,0.00\% 4 | basicopt1.data,633149,56.926ms,58.9774\%,559226,11.68\%,52.074ms,98.8887\%,67.67\% 5 | bulgarian.data,419969,40.403ms,50.6399\%,377506,10.11\%,35.758ms,94.4862\%,86.58\% 6 | expr.data,632,0.936ms,37.8378\%,586,7.28\%,3.846ms,75.6757\%,100.00\% 7 | gcd.data,546,0.641ms,62.5\%,534,2.20\%,3.514ms,61.6667\%,-1.33\% 8 | hanoi.data,201773,19.043ms,49.98\%,190794,5.44\%,18.921ms,86.4868\%,73.04\% 9 | lvalue2.data,61,1.49ms,66.6667\%,59,3.28\%,0.95ms,66.6667\%,0.00\% 10 | magic.data,582014,45.829ms,53.1436\%,553419,4.91\%,46.586ms,82.2054\%,54.69\% 11 | manyarguments.data,71,0.646ms,80\%,69,2.82\%,0.912ms,80\%,0.00\% 12 | multiarray.data,1777,0.963ms,18.5185\%,1650,7.15\%,1.03ms,66.0494\%,256.67\% 13 | qsort.data,1590883,107.458ms,30.0067\%,1430582,10.08\%,98.468ms,91.7384\%,205.73\% 14 | queens.data,607530,38.524ms,63.2216\%,582242,4.16\%,41.592ms,81.0156\%,28.15\% 15 | statement\_test.data,1115,0.56ms,59.4059\%,1067,4.30\%,2.527ms,66.8317\%,12.50\% 16 | superloop.data,577259,34.466ms,87.2946\%,532844,7.69\%,42.246ms,95.1932\%,9.05\% 17 | tak.data,1546200,90.022ms,74.9996\%,1530613,1.01\%,101.747ms,75.7038\%,0.94\% 18 | pi.data,127443492,8041.23ms,42.2702\%,109722503,13.90\%,8114.27ms,84.5072\%,99.92\% -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(RISCV_Simulator) 3 | 4 | set(CMAKE_CXX_STANDARD 14) 5 | 6 | add_library(RISCV_Simulator STATIC src/Common/Parser.hpp src/Common/Instruction.hpp src/Common/Register.hpp src/Common/Session.cpp src/Common/Session.h src/Common/Memory.hpp src/Common/RegisterFile.hpp src/Common/Common.h src/Module/BranchPrediction.cpp src/Module/BranchPrediction.h src/Pipeline/Issue.cpp src/Pipeline/Issue.h src/Common/ReservationStation.h src/Pipeline/OoOExecute.cpp src/Pipeline/OoOExecute.h src/Module/ALUUnit.cpp src/Module/ALUUnit.h src/Pipeline/OoOCommon.h src/Module/LoadStoreUnit.cpp src/Module/LoadStoreUnit.h src/Common/utils.h src/Common/utils.cpp src/Common/ReservationStation.cpp src/Common/ReorderBuffer.cpp src/Common/ReorderBuffer.h src/Module/CommitUnit.cpp src/Module/CommitUnit.h) 7 | add_executable(RISCV_Simulator_Test src/Common/Parser_test.cpp src/Common/Instruction_test.cpp src/Common/Register_test.cpp src/Common/Session_test.cpp src/Common/Memory_test.cpp src/Common/RegisterFile_test.cpp) 8 | add_executable(RISCV_Simulator_Main src/main.cpp) 9 | add_executable(RISCV_Simulator_Pre src/main_presentation.cpp) 10 | 11 | find_package(GTest MODULE REQUIRED) 12 | 13 | target_link_libraries(RISCV_Simulator_Test PRIVATE RISCV_Simulator GTest::GTest GTest::Main) 14 | target_link_libraries(RISCV_Simulator_Main RISCV_Simulator) 15 | target_link_libraries(RISCV_Simulator_Pre RISCV_Simulator) 16 | 17 | enable_testing() 18 | add_test(AllTestsInMain RISCV_Simulator_Test) 19 | -------------------------------------------------------------------------------- /src/Common/Parser_test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-01. 3 | // 4 | 5 | #include "gtest/gtest.h" 6 | #include "Parser.hpp" 7 | #include "Memory.hpp" 8 | #include 9 | #include 10 | 11 | TEST(Parser, Hex) { 12 | char str[10] = { 0 }; 13 | for (int i = 0; i < 256; i++) { 14 | if (i % 16 < 10) str[1] = i % 16 + '0'; 15 | else str[1] = i % 16 + 'A' - 10; 16 | if (i / 16 < 10) str[0] = i / 16 + '0'; 17 | else str[0] = i / 16 + 'A' - 10; 18 | EXPECT_EQ(Parser::parse_hex(str), i); 19 | } 20 | } 21 | 22 | TEST(Parser, Parse) { 23 | Memory mem; 24 | std::string test_data = "@100\n00 01 02 03 \n 04 05\n@0\n06 07 08 09 0A"; 25 | std::stringstream ss(test_data); 26 | Parser::parse(ss, mem); 27 | EXPECT_EQ(mem[0x100], 0); 28 | EXPECT_EQ(mem[0x100 + 1], 1); 29 | EXPECT_EQ(mem[0x100 + 2], 2); 30 | EXPECT_EQ(mem[0x100 + 3], 3); 31 | EXPECT_EQ(mem[0x100 + 4], 4); 32 | EXPECT_EQ(mem[0x100 + 5], 5); 33 | EXPECT_EQ(mem[0], 6); 34 | EXPECT_EQ(mem[1], 7); 35 | EXPECT_EQ(mem[2], 8); 36 | EXPECT_EQ(mem[3], 9); 37 | EXPECT_EQ(mem[4], 10); 38 | } 39 | 40 | 41 | TEST(Parser, ParseHex) { 42 | Memory mem; 43 | std::string test_data = "00010203"; 44 | std::stringstream ss(test_data); 45 | Parser::parse_hex(ss, mem); 46 | EXPECT_EQ(mem[0], (char) 0x03); 47 | EXPECT_EQ(mem[1], (char) 0x02); 48 | EXPECT_EQ(mem[2], (char) 0x01); 49 | EXPECT_EQ(mem[3], (char) 0x00); 50 | } 51 | -------------------------------------------------------------------------------- /data/gcd.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 00 08 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 17 00 00 83 27 07 1A 33 45 F5 00 13 05 D5 0A 6 | 23 20 A7 1A 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 17 00 00 83 26 07 1A B3 C7 D7 00 93 87 97 20 8 | 23 20 F7 1A 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 FF 23 26 11 00 23 24 81 00 10 | 13 84 05 00 EF 00 80 11 63 1C 05 00 13 05 04 00 11 | 83 20 C1 00 03 24 81 00 13 01 01 01 67 80 00 00 12 | 93 05 05 00 13 05 04 00 EF F0 DF FC 13 04 05 00 13 | 6F F0 DF FD 13 01 01 FF 23 26 11 00 23 24 81 00 14 | 93 05 10 00 13 05 A0 00 EF F0 DF FA EF F0 5F F6 15 | 37 14 00 00 93 05 F4 C0 37 95 00 00 13 05 A5 8A 16 | EF F0 5F F9 EF F0 DF F4 93 05 30 60 13 05 B4 B5 17 | EF F0 5F F8 EF F0 DF F3 93 05 D0 0F B7 17 00 00 18 | 03 A5 07 1A EF 00 80 09 83 20 C1 00 03 24 81 00 19 | 13 01 01 01 67 80 00 00 63 40 05 06 63 C6 05 06 20 | 13 86 05 00 93 05 05 00 13 05 F0 FF 63 0C 06 02 21 | 93 06 10 00 63 7A B6 00 63 58 C0 00 13 16 16 00 22 | 93 96 16 00 E3 6A B6 FE 13 05 00 00 63 E6 C5 00 23 | B3 85 C5 40 33 65 D5 00 93 D6 16 00 13 56 16 00 24 | E3 96 06 FE 67 80 00 00 93 82 00 00 EF F0 5F FB 25 | 13 85 05 00 67 80 02 00 33 05 A0 40 63 D8 05 00 26 | B3 05 B0 40 6F F0 DF F9 B3 05 B0 40 93 82 00 00 27 | EF F0 1F F9 33 05 A0 40 67 80 02 00 93 82 00 00 28 | 63 CA 05 00 63 4C 05 00 EF F0 9F F7 13 85 05 00 29 | 67 80 02 00 B3 05 B0 40 E3 58 05 FE 33 05 A0 40 30 | EF F0 1F F6 33 05 B0 40 67 80 02 00 31 | @0000119C 32 | FD 00 00 00 33 | -------------------------------------------------------------------------------- /data/array_test1.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 00 04 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 17 00 00 83 27 07 1B 33 45 F5 00 13 05 D5 0A 6 | 23 28 A7 1A 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 17 00 00 83 26 07 1B B3 C7 D7 00 93 87 97 20 8 | 23 28 F7 1A 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 FE 23 2E 11 00 23 2C 81 00 10 | 23 2A 91 00 37 14 00 00 23 20 04 1A 93 07 10 00 11 | 23 20 F1 00 93 07 04 1A 23 A2 07 00 13 07 20 00 12 | 23 22 E1 00 23 A4 07 00 13 07 30 00 23 24 E1 00 13 | 23 A6 07 00 93 07 40 00 23 26 F1 00 13 04 04 1A 14 | 93 04 04 01 03 25 04 00 EF F0 9F F6 13 04 44 00 15 | E3 1A 94 FE 13 05 10 00 EF F0 9F F5 13 05 20 00 16 | EF F0 1F F5 13 05 30 00 EF F0 9F F4 13 05 40 00 17 | EF F0 1F F4 93 05 D0 0F B7 17 00 00 03 A5 07 1B 18 | EF 00 C0 09 83 20 C1 01 03 24 81 01 83 24 41 01 19 | 13 01 01 02 67 80 00 00 63 40 05 06 63 C6 05 06 20 | 13 86 05 00 93 05 05 00 13 05 F0 FF 63 0C 06 02 21 | 93 06 10 00 63 7A B6 00 63 58 C0 00 13 16 16 00 22 | 93 96 16 00 E3 6A B6 FE 13 05 00 00 63 E6 C5 00 23 | B3 85 C5 40 33 65 D5 00 93 D6 16 00 13 56 16 00 24 | E3 96 06 FE 67 80 00 00 93 82 00 00 EF F0 5F FB 25 | 13 85 05 00 67 80 02 00 33 05 A0 40 63 D8 05 00 26 | B3 05 B0 40 6F F0 DF F9 B3 05 B0 40 93 82 00 00 27 | EF F0 1F F9 33 05 A0 40 67 80 02 00 93 82 00 00 28 | 63 CA 05 00 63 4C 05 00 EF F0 9F F7 13 85 05 00 29 | 67 80 02 00 B3 05 B0 40 E3 58 05 FE 33 05 A0 40 30 | EF F0 1F F6 33 05 B0 40 67 80 02 00 31 | @0000119C 32 | FD 00 00 00 33 | -------------------------------------------------------------------------------- /src/Common/Parser.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-01. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_PARSER_HPP 6 | #define RISCV_SIMULATOR_PARSER_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "Memory.hpp" 13 | 14 | class Parser { 15 | public: 16 | static unsigned parse_hex(const std::string &hex) { 17 | return strtol(hex.c_str(), NULL, 16); 18 | } 19 | 20 | static void parse(std::istream &in, Memory &mem) { 21 | if (!in) std::cerr << "parse failed, invalid stream" << std::endl; 22 | unsigned base_addr = 0; 23 | while (in) { 24 | std::string line; 25 | std::getline(in, line); 26 | if (line[0] == '@') { 27 | base_addr = strtol(line.c_str() + 1, NULL, 16); 28 | } else { 29 | std::stringstream ss(line); 30 | std::string hex; 31 | while (ss >> hex) { 32 | if (hex.length() == 0) break; 33 | char data = parse_hex(hex); 34 | mem[base_addr++] = data; 35 | } 36 | } 37 | } 38 | } 39 | 40 | static void parse_hex(std::istream &in, Memory &mem) { 41 | if (!in) std::cerr << "parse failed, invalid stream" << std::endl; 42 | unsigned base_addr = 0; 43 | std::string hex; 44 | while(in >> hex) { 45 | mem.write_word(base_addr, parse_hex(hex)); 46 | base_addr += 4; 47 | } 48 | } 49 | }; 50 | 51 | 52 | #endif //RISCV_SIMULATOR_PARSER_HPP 53 | -------------------------------------------------------------------------------- /data/array_test2.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 00 04 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 17 00 00 83 27 07 1C 33 45 F5 00 13 05 D5 0A 6 | 23 20 A7 1C 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 17 00 00 83 26 07 1C B3 C7 D7 00 93 87 97 20 8 | 23 20 F7 1C 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 FE 23 2E 11 00 23 2C 81 00 10 | 23 2A 91 00 23 28 21 01 23 26 31 01 B7 17 00 00 11 | 03 A9 C7 1A 13 05 40 00 EF F0 9F F9 93 07 10 00 12 | 23 20 F9 00 93 07 20 00 23 22 F9 00 93 07 30 00 13 | 23 24 F9 00 93 07 40 00 23 26 F9 00 13 04 09 00 14 | 93 09 09 01 93 04 09 00 03 A5 04 00 EF F0 5F F6 15 | 93 84 44 00 E3 9A 34 FF 23 20 09 00 23 22 09 00 16 | 23 24 09 00 23 26 09 00 03 25 04 00 EF F0 5F F4 17 | 13 04 44 00 E3 9A 89 FE 93 05 D0 0F B7 17 00 00 18 | 03 A5 07 1C EF 00 40 0A 83 20 C1 01 03 24 81 01 19 | 83 24 41 01 03 29 01 01 83 29 C1 00 13 01 01 02 20 | 67 80 00 00 63 40 05 06 63 C6 05 06 13 86 05 00 21 | 93 05 05 00 13 05 F0 FF 63 0C 06 02 93 06 10 00 22 | 63 7A B6 00 63 58 C0 00 13 16 16 00 93 96 16 00 23 | E3 6A B6 FE 13 05 00 00 63 E6 C5 00 B3 85 C5 40 24 | 33 65 D5 00 93 D6 16 00 13 56 16 00 E3 96 06 FE 25 | 67 80 00 00 93 82 00 00 EF F0 5F FB 13 85 05 00 26 | 67 80 02 00 33 05 A0 40 63 D8 05 00 B3 05 B0 40 27 | 6F F0 DF F9 B3 05 B0 40 93 82 00 00 EF F0 1F F9 28 | 33 05 A0 40 67 80 02 00 93 82 00 00 63 CA 05 00 29 | 63 4C 05 00 EF F0 9F F7 13 85 05 00 67 80 02 00 30 | B3 05 B0 40 E3 58 05 FE 33 05 A0 40 EF F0 1F F6 31 | 33 05 B0 40 67 80 02 00 32 | @000011A8 33 | FD 00 00 00 34 | @000011AC 35 | B0 11 00 00 36 | -------------------------------------------------------------------------------- /data/tak.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 80 0D 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 17 00 00 83 27 C7 1C 33 45 F5 00 13 05 D5 0A 6 | 23 26 A7 1C 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 17 00 00 83 26 C7 1C B3 C7 D7 00 93 87 97 20 8 | 23 26 F7 1C 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 FE 23 2E 11 00 23 2C 81 00 10 | 23 2A 91 00 23 28 21 01 23 26 31 01 23 24 41 01 11 | 13 04 06 00 63 C4 A5 02 13 05 04 00 83 20 C1 01 12 | 03 24 81 01 83 24 41 01 03 29 01 01 83 29 C1 00 13 | 03 2A 81 00 13 01 01 02 67 80 00 00 93 04 05 00 14 | 13 89 05 00 13 05 F5 FF EF F0 DF FA 93 09 05 00 15 | 13 86 04 00 93 05 04 00 13 05 F9 FF EF F0 9F F9 16 | 13 0A 05 00 13 06 09 00 93 85 04 00 13 05 F4 FF 17 | EF F0 5F F8 13 06 05 00 93 05 0A 00 13 85 09 00 18 | EF F0 5F F7 13 04 15 00 6F F0 1F F9 13 01 01 FF 19 | 23 26 11 00 13 06 60 00 93 05 C0 00 13 05 20 01 20 | EF F0 5F F5 EF F0 DF F0 93 05 D0 0F B7 17 00 00 21 | 03 A5 C7 1C EF 00 40 09 83 20 C1 00 13 01 01 01 22 | 67 80 00 00 63 40 05 06 63 C6 05 06 13 86 05 00 23 | 93 05 05 00 13 05 F0 FF 63 0C 06 02 93 06 10 00 24 | 63 7A B6 00 63 58 C0 00 13 16 16 00 93 96 16 00 25 | E3 6A B6 FE 13 05 00 00 63 E6 C5 00 B3 85 C5 40 26 | 33 65 D5 00 93 D6 16 00 13 56 16 00 E3 96 06 FE 27 | 67 80 00 00 93 82 00 00 EF F0 5F FB 13 85 05 00 28 | 67 80 02 00 33 05 A0 40 63 D8 05 00 B3 05 B0 40 29 | 6F F0 DF F9 B3 05 B0 40 93 82 00 00 EF F0 1F F9 30 | 33 05 A0 40 67 80 02 00 93 82 00 00 63 CA 05 00 31 | 63 4C 05 00 EF F0 9F F7 13 85 05 00 67 80 02 00 32 | B3 05 B0 40 E3 58 05 FE 33 05 A0 40 EF F0 1F F6 33 | 33 05 B0 40 67 80 02 00 34 | @000011C8 35 | FD 00 00 00 36 | -------------------------------------------------------------------------------- /data/expr.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 00 04 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 17 00 00 83 27 07 1E 33 45 F5 00 13 05 D5 0A 6 | 23 20 A7 1E 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 17 00 00 83 26 07 1E B3 C7 D7 00 93 87 97 20 8 | 23 20 F7 1E 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 FF 23 26 11 00 B7 17 00 00 10 | 83 A7 47 1D 37 07 00 20 13 07 F7 FF 33 87 E7 00 11 | B7 06 00 40 93 86 E6 FF 63 EA E6 06 37 17 00 00 12 | 03 27 C7 1D B7 16 00 00 83 A6 86 1D 37 05 00 20 13 | 13 05 F5 FF B7 05 00 40 93 85 E5 FF 33 86 E7 40 14 | 33 07 E6 40 33 06 D6 00 13 17 17 00 33 07 E6 40 15 | B3 87 E7 40 33 86 E7 40 13 16 16 00 B3 86 D7 00 16 | B3 86 C6 40 B3 87 D7 00 B3 87 C7 40 33 86 A7 00 17 | E3 F6 C5 FC 37 16 00 00 23 2E E6 1C 37 17 00 00 18 | 23 2C D7 1C 37 17 00 00 23 2A F7 1C B7 17 00 00 19 | 03 A5 C7 1D EF F0 DF F1 B7 17 00 00 03 A5 87 1D 20 | EF F0 1F F1 B7 17 00 00 03 A5 47 1D EF F0 5F F0 21 | 93 05 D0 0F B7 17 00 00 03 A5 07 1E EF 00 40 09 22 | 83 20 C1 00 13 01 01 01 67 80 00 00 63 40 05 06 23 | 63 C6 05 06 13 86 05 00 93 05 05 00 13 05 F0 FF 24 | 63 0C 06 02 93 06 10 00 63 7A B6 00 63 58 C0 00 25 | 13 16 16 00 93 96 16 00 E3 6A B6 FE 13 05 00 00 26 | 63 E6 C5 00 B3 85 C5 40 33 65 D5 00 93 D6 16 00 27 | 13 56 16 00 E3 96 06 FE 67 80 00 00 93 82 00 00 28 | EF F0 5F FB 13 85 05 00 67 80 02 00 33 05 A0 40 29 | 63 D8 05 00 B3 05 B0 40 6F F0 DF F9 B3 05 B0 40 30 | 93 82 00 00 EF F0 1F F9 33 05 A0 40 67 80 02 00 31 | 93 82 00 00 63 CA 05 00 63 4C 05 00 EF F0 9F F7 32 | 13 85 05 00 67 80 02 00 B3 05 B0 40 E3 58 05 FE 33 | 33 05 A0 40 EF F0 1F F6 33 05 B0 40 67 80 02 00 34 | @000011D0 35 | FD 00 00 00 36 | @000011D4 37 | 01 00 00 00 01 00 00 00 01 00 00 00 38 | -------------------------------------------------------------------------------- /src/Common/ReorderBuffer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-11. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_REORDERBUFFER_H 6 | #define RISCV_SIMULATOR_REORDERBUFFER_H 7 | 8 | #include "Register.hpp" 9 | #include 10 | #include "Instruction.hpp" 11 | 12 | class ROB { 13 | public: 14 | Register Inst; 15 | // TODO: Tag is used for branch prediction now and 16 | // will be expanded to eliminate store buffer 17 | Register Dest, Value, Tag; 18 | Register Ready; 19 | 20 | unsigned __debug_identifier; 21 | 22 | ROB() : Inst(0), Dest(0), Value(0), Ready(false) {} 23 | 24 | void tick() { 25 | Inst.tick(); 26 | Dest.tick(); 27 | Value.tick(); 28 | Ready.tick(); 29 | Tag.tick(); 30 | } 31 | 32 | static void debug_header(); 33 | 34 | void debug(); 35 | 36 | std::string resolve(unsigned opcode) { 37 | switch (opcode) { 38 | case 0: 39 | return "none"; 40 | case 0b0110111: 41 | return "lui"; 42 | case 0b0010111: 43 | return "auipc"; 44 | case 0b1101111: 45 | return "jal"; 46 | case 0b1100111: 47 | return "jalr"; 48 | case 0b1100011: 49 | return "branch"; 50 | case 0b0000011: 51 | return "load"; 52 | case 0b0100011: 53 | return "store"; 54 | case 0b0010011: 55 | return "opimm"; 56 | case 0b0110011: 57 | return "op"; 58 | default: 59 | return "other"; 60 | } 61 | } 62 | }; 63 | 64 | 65 | #endif //RISCV_SIMULATOR_REORDERBUFFER_H 66 | -------------------------------------------------------------------------------- /src/Common/ReservationStation.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-10. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_RESERVATIONSTATION_H 6 | #define RISCV_SIMULATOR_RESERVATIONSTATION_H 7 | 8 | #include "Register.hpp" 9 | #include "Instruction.hpp" 10 | #include "../Pipeline/OoOCommon.h" 11 | #include "utils.h" 12 | 13 | #include 14 | 15 | class RS { 16 | public: 17 | Register Op, Qj, Qk, Vj, Vk, A, Tag, Dest; 18 | Register Busy; 19 | unsigned __debug_identifier; 20 | 21 | RS() : Op(0), Qj(0), Qk(0), Vj(0), Vk(0), A(0), Tag(0), Dest(0), Busy(false) {} 22 | 23 | void tick() { 24 | Op.tick(); 25 | Qj.tick(); 26 | Qk.tick(); 27 | Vj.tick(); 28 | Vk.tick(); 29 | Tag.tick(); 30 | A.tick(); 31 | Busy.tick(); 32 | Dest.tick(); 33 | } 34 | 35 | static std::string resolve(Immediate tag) { 36 | switch (tag) { 37 | case ADD1: 38 | return "ADD1"; 39 | case ADD2: 40 | return "ADD2"; 41 | case ADD3: 42 | return "ADD3"; 43 | case ADD4: 44 | return "ADD4"; 45 | case STORE1: 46 | return "STORE1"; 47 | case LOAD1: 48 | return "LOAD1"; 49 | case STORE2: 50 | return "STORE2"; 51 | case LOAD2: 52 | return "LOAD2"; 53 | case STORE3: 54 | return "STORE3"; 55 | case LOAD3: 56 | return "LOAD3"; 57 | case NONE: 58 | return "NONE"; 59 | } 60 | return "UNKNOWN"; 61 | } 62 | 63 | static void debug_header(); 64 | void debug(); 65 | }; 66 | 67 | #endif //RISCV_SIMULATOR_RESERVATIONSTATION_H 68 | -------------------------------------------------------------------------------- /src/Common/Session.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-02. 3 | // 4 | 5 | #include "Session.h" 6 | #include "../Pipeline/Issue.h" 7 | #include "../Pipeline/OoOExecute.h" 8 | #include "../Module/BranchPrediction.h" 9 | #include "Parser.hpp" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using std::make_shared; 17 | 18 | Session::Session(bool debug) : _debug(debug) { 19 | i = new Issue(this); 20 | e = new OoOExecute(this); 21 | branch = new BranchPrediction; 22 | } 23 | 24 | Session::~Session() { 25 | delete i; 26 | delete e; 27 | delete branch; 28 | } 29 | 30 | void Session::tick() { 31 | ++stat.cycle; 32 | 33 | i->update(); 34 | e->update(); 35 | 36 | if (_debug) this->debug(); 37 | 38 | i->tick(); 39 | e->tick(); 40 | 41 | rf.tick(); 42 | } 43 | 44 | void Session::load_memory(const char *path) { 45 | std::fstream in(path); 46 | load_memory(in); 47 | } 48 | 49 | void Session::debug() { 50 | std::cout << " Cycle " << std::dec << stat.cycle << std::endl; 51 | std::cout << "---- Issue Stage ----" << std::endl; 52 | i->debug(); 53 | std::cout << "--- Execute Stage ---" << std::endl; 54 | e->debug(); 55 | std::cout << "----- Registers -----" << std::endl; 56 | rf.debug(); 57 | } 58 | 59 | void Session::load_hex(const char *path) { 60 | std::fstream in(path); 61 | Parser::parse_hex(in, memory); 62 | } 63 | 64 | void Session::load_memory(std::istream &in) { 65 | Parser::parse(in, memory); 66 | } 67 | 68 | void Session::report(std::ostream &out) { 69 | out << "\t--- Session Report ---" << std::endl; 70 | out << "\t" << stat.cycle << " Cycles" << std::endl; 71 | out << std::endl; 72 | i->report(out); 73 | e->report(out); 74 | out << std::endl; 75 | } 76 | -------------------------------------------------------------------------------- /src/Common/Memory.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-02. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_MEMORY_HPP 6 | #define RISCV_SIMULATOR_MEMORY_HPP 7 | 8 | #include "Common.h" 9 | #include 10 | #include 11 | #include 12 | 13 | const int MEMORY_SIZE = 0x400000; 14 | 15 | class Memory { 16 | public: 17 | unsigned char mem[MEMORY_SIZE]; 18 | unsigned char placeholder; 19 | 20 | Memory() { memset(mem, 0, sizeof(mem)); } 21 | 22 | bool check_addr(unsigned int addr) { 23 | if (0 <= addr && addr < MEMORY_SIZE) return true; 24 | // TODO: there may be exception when checking address, 25 | // I'm looking forward to solving this. 26 | // assert(false); 27 | return false; 28 | } 29 | 30 | Immediate read_word(unsigned int addr) { 31 | if (!check_addr(addr)) return 0; 32 | return *(Immediate *) (mem + addr); 33 | } 34 | 35 | void write_word(unsigned int addr, Immediate imm) { 36 | if (!check_addr(addr)) return; 37 | *(Immediate *) (mem + addr) = imm; 38 | } 39 | 40 | unsigned short read_ushort(unsigned int addr) { 41 | if (!check_addr(addr)) return 0; 42 | return *(short *) (mem + addr); 43 | } 44 | 45 | void write_ushort(unsigned int addr, unsigned short imm) { 46 | if (!check_addr(addr)) return; 47 | *(short *) (mem + addr) = imm; 48 | } 49 | 50 | unsigned char &operator[](unsigned int addr) { 51 | if (!check_addr(addr)) return placeholder; 52 | return mem[addr]; 53 | } 54 | 55 | void debug() { 56 | for (int i = 0x20000 - 0x10; i <= 0x20000; i++) { 57 | std::cout << std::hex << (unsigned) mem[i] << " "; 58 | } 59 | std::cout << std::endl; 60 | } 61 | }; 62 | 63 | 64 | #endif //RISCV_SIMULATOR_MEMORY_HPP 65 | -------------------------------------------------------------------------------- /src/Common/RegisterFile.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-02. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_REGISTERFILE_HPP 6 | #define RISCV_SIMULATOR_REGISTERFILE_HPP 7 | 8 | #include "Common.h" 9 | #include "Register.hpp" 10 | 11 | #include 12 | #include 13 | #include 14 | #include "utils.h" 15 | 16 | class RegisterFile { // : public Tickable { 17 | static const int REG_NUM = 32; 18 | public: 19 | Immediate prev[REG_NUM]; 20 | Immediate next[REG_NUM]; 21 | 22 | RegisterFile() { 23 | memset(prev, 0, sizeof(prev)); 24 | memset(next, 0, sizeof(next)); 25 | } 26 | 27 | void tick() { memcpy(prev, next, sizeof(prev)); } 28 | 29 | Immediate read(int id) { return id == 0 ? 0 : prev[id]; } 30 | 31 | void write(int id, Immediate val) { next[id] = val; } 32 | 33 | void debug() { 34 | static std::vector rf_name = {"0", "ra", "sp", "gp", "tp", "t0", "t1", "t2", 35 | "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", 36 | "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", 37 | "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"}; 38 | for (int i = 0; i < 4; i++) { 39 | for (int j = i * 8; j < i * 8 + 8; j++) { 40 | char buffer[20]; 41 | sprintf(buffer, "#%d", j); 42 | std::cout << std::setw(20) << buffer 43 | << std::setw(4) << rf_name[j]; 44 | } 45 | std::cout << std::endl; 46 | for (int j = i * 8; j < i * 8 + 8; j++) { 47 | debug_immediate(next[j], 11); 48 | } 49 | std::cout << std::endl; 50 | } 51 | } 52 | }; 53 | 54 | 55 | #endif //RISCV_SIMULATOR_REGISTERFILE_HPP 56 | -------------------------------------------------------------------------------- /src/Module/ALUUnit.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-10. 3 | // 4 | 5 | #include 6 | #include "ALUUnit.h" 7 | #include "../Common/Instruction.hpp" 8 | #include "../Pipeline/OoOExecute.h" 9 | 10 | void ALUUnit::update() { 11 | for (auto &&r_id : rs) { 12 | RS* r = e->get_rs(r_id); 13 | if (r->Busy) { 14 | if (r->Qj == NONE && r->Qk == NONE) { 15 | Immediate result = get_result(r->Op, r->Vj, r->Vk); 16 | e->put_result(r_id, result); 17 | r->Busy = false; 18 | } 19 | } 20 | } 21 | } 22 | 23 | Immediate ALUUnit::get_result(Immediate op, Immediate op1, Immediate op2) { 24 | if (op == ADD) return op1 + op2; 25 | if (op == SUB) return op1 - op2; 26 | if (op == SLT) { if ((SImmediate) op1 < (SImmediate) op2) return 1; else return 0; } 27 | if (op == SLTU) { if (op1 < op2) return 1; else return 0; } 28 | if (op == XOR) return op1 ^ op2; 29 | if (op == SRL) return op1 >> (op2 & (unsigned) 0x1f); 30 | if (op == SLL) return op1 << (op2 & (unsigned) 0x1f); 31 | if (op == SRA) return (SImmediate) op1 >> (op2 & (unsigned) 0x1f); 32 | if (op == OR) return op1 | op2; 33 | if (op == AND) return op1 & op2; 34 | assert(false); 35 | return 0; 36 | } 37 | 38 | ALUUnit::ALUUnit(OoOExecute *e) 39 | : e(e), rs({ ADD1, ADD2, ADD3, ADD4 }) {} 40 | 41 | std::string ALUUnit::resolve(ALUUnit::OP op) { 42 | switch(op) { 43 | case ADD: return "ADD"; 44 | case SUB: return "SUB"; 45 | case SLT: return "SLT"; 46 | case SLTU: return "SLTU"; 47 | case XOR: return "XOR"; 48 | case SRL: return "SRL"; 49 | case SLL: return "SLL"; 50 | case SRA: return "SRA"; 51 | case OR: return "OR"; 52 | case AND: return "AND"; 53 | case NONE_OP: return "NONE"; 54 | } 55 | return "UNKNOWN"; 56 | } 57 | 58 | void ALUUnit::tick() { 59 | 60 | } 61 | 62 | void ALUUnit::reset() { 63 | 64 | } 65 | -------------------------------------------------------------------------------- /data/hanoi.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 00 12 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 17 00 00 83 27 07 25 33 45 F5 00 13 05 D5 0A 6 | 23 28 A7 24 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 17 00 00 83 26 07 25 B3 C7 D7 00 93 87 97 20 8 | 23 28 F7 24 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 FE 23 2E 11 00 23 2C 81 00 10 | 23 2A 91 00 23 28 21 01 23 26 31 01 23 24 41 01 11 | 13 89 05 00 93 89 06 00 93 04 07 00 93 07 10 00 12 | 63 02 F5 08 13 0A 06 00 13 04 F5 FF 93 06 06 00 13 | 13 86 09 00 13 05 04 00 EF F0 DF FB 93 04 05 00 14 | 37 15 00 00 13 05 C5 23 EF F0 1F F8 13 05 09 00 15 | EF F0 9F F7 37 15 00 00 13 05 45 24 EF F0 DF F6 16 | 13 85 09 00 EF F0 5F F6 13 87 04 00 93 86 09 00 17 | 13 06 09 00 93 05 0A 00 13 05 04 00 EF F0 9F F7 18 | 13 05 15 00 83 20 C1 01 03 24 81 01 83 24 41 01 19 | 03 29 01 01 83 29 C1 00 03 2A 81 00 13 01 01 02 20 | 67 80 00 00 37 15 00 00 13 05 C5 23 EF F0 DF F1 21 | 13 05 09 00 EF F0 5F F1 37 15 00 00 13 05 45 24 22 | EF F0 9F F0 13 85 09 00 EF F0 1F F0 13 85 14 00 23 | 6F F0 5F FB 13 01 01 FD 23 26 11 02 93 07 10 04 24 | 23 2C F1 00 23 0E 01 00 93 07 20 04 23 28 F1 00 25 | 23 0A 01 00 93 07 30 04 23 24 F1 00 23 06 01 00 26 | 13 07 00 00 93 06 81 00 13 06 01 01 93 05 81 01 27 | 13 05 A0 00 EF F0 1F EE EF F0 9F E9 93 05 D0 0F 28 | B7 17 00 00 03 A5 07 25 EF 00 40 09 83 20 C1 02 29 | 13 01 01 03 67 80 00 00 63 40 05 06 63 C6 05 06 30 | 13 86 05 00 93 05 05 00 13 05 F0 FF 63 0C 06 02 31 | 93 06 10 00 63 7A B6 00 63 58 C0 00 13 16 16 00 32 | 93 96 16 00 E3 6A B6 FE 13 05 00 00 63 E6 C5 00 33 | B3 85 C5 40 33 65 D5 00 93 D6 16 00 13 56 16 00 34 | E3 96 06 FE 67 80 00 00 93 82 00 00 EF F0 5F FB 35 | 13 85 05 00 67 80 02 00 33 05 A0 40 63 D8 05 00 36 | B3 05 B0 40 6F F0 DF F9 B3 05 B0 40 93 82 00 00 37 | EF F0 1F F9 33 05 A0 40 67 80 02 00 93 82 00 00 38 | 63 CA 05 00 63 4C 05 00 EF F0 9F F7 13 85 05 00 39 | 67 80 02 00 B3 05 B0 40 E3 58 05 FE 33 05 A0 40 40 | EF F0 1F F6 33 05 B0 40 67 80 02 00 41 | @0000123C 42 | 6D 6F 76 65 20 00 00 00 20 2D 2D 3E 20 00 00 00 43 | @0000124C 44 | FD 00 00 00 45 | -------------------------------------------------------------------------------- /data/basicopt1.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 00 04 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 17 00 00 83 27 C7 27 33 45 F5 00 13 05 D5 0A 6 | 23 2E A7 26 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 17 00 00 83 26 C7 27 B3 C7 D7 00 93 87 97 20 8 | 23 2E F7 26 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 81 23 26 11 7E 23 24 81 7E 10 | 23 22 91 7E 23 20 21 7F 23 2E 31 7D 23 2C 41 7D 11 | 23 2A 51 7D 23 28 61 7D 23 26 71 7D 23 24 81 7D 12 | 37 73 FF FF 13 03 03 B8 33 01 61 00 37 64 FF FF 13 | 37 A7 00 00 93 07 07 C4 B3 87 27 00 33 84 87 00 14 | 13 04 04 55 93 07 07 DD 33 09 F1 00 13 07 04 00 15 | 6F 00 C0 00 13 07 07 19 63 0C 27 01 93 07 07 E7 16 | 23 A0 07 00 93 87 47 00 E3 9C E7 FE 6F F0 9F FE 17 | 93 04 10 00 13 0A A0 03 93 09 30 06 37 6B FF FF 18 | B7 A7 00 00 93 87 07 C4 B3 87 27 00 33 8B 67 01 19 | 6F 00 C0 05 93 0B 00 00 93 05 90 01 13 85 0B 00 20 | EF 00 40 0D 33 0C 55 01 93 05 40 06 13 95 2B 00 21 | EF 00 80 14 93 17 1C 00 B3 87 87 01 93 97 37 00 22 | B3 87 87 01 93 97 27 00 B3 87 A7 00 93 97 27 00 23 | B3 07 FB 00 13 87 2B 03 23 A0 E7 3C 93 8B 1B 00 24 | 93 07 40 06 E3 9A FB FA 93 84 14 00 93 8A F4 FF 25 | 93 87 A4 FE E3 70 FA FA E3 D8 99 FE 13 05 00 00 26 | 6F 00 C0 00 13 04 04 19 63 0E 24 01 93 07 04 E7 27 | 03 A7 07 00 33 05 E5 00 93 87 47 00 E3 9A 87 FE 28 | 6F F0 5F FE EF F0 DF E8 93 05 D0 0F B7 17 00 00 29 | 03 A5 C7 27 EF 00 40 0C 37 93 00 00 13 03 03 48 30 | 33 01 61 00 83 20 C1 7E 03 24 81 7E 83 24 41 7E 31 | 03 29 01 7E 83 29 C1 7D 03 2A 81 7D 83 2A 41 7D 32 | 03 2B 01 7D 83 2B C1 7C 03 2C 81 7C 13 01 01 7F 33 | 67 80 00 00 63 40 05 06 63 C6 05 06 13 86 05 00 34 | 93 05 05 00 13 05 F0 FF 63 0C 06 02 93 06 10 00 35 | 63 7A B6 00 63 58 C0 00 13 16 16 00 93 96 16 00 36 | E3 6A B6 FE 13 05 00 00 63 E6 C5 00 B3 85 C5 40 37 | 33 65 D5 00 93 D6 16 00 13 56 16 00 E3 96 06 FE 38 | 67 80 00 00 93 82 00 00 EF F0 5F FB 13 85 05 00 39 | 67 80 02 00 33 05 A0 40 63 D8 05 00 B3 05 B0 40 40 | 6F F0 DF F9 B3 05 B0 40 93 82 00 00 EF F0 1F F9 41 | 33 05 A0 40 67 80 02 00 93 82 00 00 63 CA 05 00 42 | 63 4C 05 00 EF F0 9F F7 13 85 05 00 67 80 02 00 43 | B3 05 B0 40 E3 58 05 FE 33 05 A0 40 EF F0 1F F6 44 | 33 05 B0 40 67 80 02 00 45 | @00001278 46 | FD 00 00 00 47 | -------------------------------------------------------------------------------- /data/superloop.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 00 04 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 17 00 00 83 27 47 2A 33 45 F5 00 13 05 D5 0A 6 | 23 22 A7 2A 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 17 00 00 83 26 47 2A B3 C7 D7 00 93 87 97 20 8 | 23 22 F7 2A 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 FF 23 26 11 00 B7 17 00 00 10 | 13 07 60 00 23 AE E7 28 B7 17 00 00 83 A8 87 29 11 | B7 17 00 00 03 A3 47 29 B7 17 00 00 03 AE 07 29 12 | B7 17 00 00 83 AE C7 28 B7 17 00 00 03 AF 07 2A 13 | 93 0F 00 00 93 07 10 00 93 06 70 00 93 02 10 00 14 | 6F 00 40 10 13 07 17 00 63 0A D7 0A E3 8C C7 FE 15 | E3 8A A7 FE E3 88 07 FF E3 86 B7 FE E3 84 E7 FE 16 | E3 82 F8 FE E3 00 F3 FE E3 0E FE FC E3 8C FE FC 17 | E3 0A A6 FC E3 08 06 FD E3 06 B6 FC E3 04 E6 FC 18 | E3 82 C8 FC E3 00 C3 FC E3 0E CE FA E3 8C CE FA 19 | E3 0A 05 FB E3 08 B5 FA E3 06 E5 FA E3 84 A8 FA 20 | E3 02 A3 FA E3 00 AE FA E3 8E AE F8 E3 0C B8 F8 21 | E3 0A E8 F8 E3 88 08 F9 E3 06 03 F9 E3 04 0E F9 22 | E3 82 0E F9 E3 80 E5 F8 E3 8E B8 F6 E3 0C B3 F6 23 | E3 0A BE F6 E3 88 BE F6 E3 86 E8 F6 E3 04 E3 F6 24 | E3 02 EE F6 E3 80 EE F6 E3 0E C3 F5 E3 8C D8 F5 25 | 13 0F 1F 00 93 8F 02 00 6F F0 DF F4 93 85 15 00 26 | 63 86 D5 00 13 07 10 00 6F F0 5F F4 13 08 18 00 27 | 63 06 D8 00 93 05 10 00 6F F0 DF FE 13 05 15 00 28 | 63 06 D5 00 13 08 10 00 6F F0 DF FE 13 06 16 00 29 | 63 06 D6 00 13 05 10 00 6F F0 DF FE 93 87 17 00 30 | 63 86 D7 00 13 06 10 00 6F F0 DF FE 63 96 0F 02 31 | B7 17 00 00 03 A5 07 2A EF F0 9F E5 93 05 D0 0F 32 | B7 17 00 00 03 A5 47 2A EF 00 00 0A 83 20 C1 00 33 | 13 01 01 01 67 80 00 00 B7 17 00 00 23 A0 E7 2B 34 | 6F F0 1F FD 63 40 05 06 63 C6 05 06 13 86 05 00 35 | 93 05 05 00 13 05 F0 FF 63 0C 06 02 93 06 10 00 36 | 63 7A B6 00 63 58 C0 00 13 16 16 00 93 96 16 00 37 | E3 6A B6 FE 13 05 00 00 63 E6 C5 00 B3 85 C5 40 38 | 33 65 D5 00 93 D6 16 00 13 56 16 00 E3 96 06 FE 39 | 67 80 00 00 93 82 00 00 EF F0 5F FB 13 85 05 00 40 | 67 80 02 00 33 05 A0 40 63 D8 05 00 B3 05 B0 40 41 | 6F F0 DF F9 B3 05 B0 40 93 82 00 00 EF F0 1F F9 42 | 33 05 A0 40 67 80 02 00 93 82 00 00 63 CA 05 00 43 | 63 4C 05 00 EF F0 9F F7 13 85 05 00 67 80 02 00 44 | B3 05 B0 40 E3 58 05 FE 33 05 A0 40 EF F0 1F F6 45 | 33 05 B0 40 67 80 02 00 46 | @00001288 47 | FD 00 00 00 48 | @0000128C 49 | 66 00 00 00 65 00 00 00 64 00 00 00 63 00 00 00 50 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Common/Session.h" 4 | #include 5 | #include 6 | 7 | void run_session(const char *path, unsigned ret_value, bool use_hex_parser = false) { 8 | std::clock_t c_start = std::clock(); 9 | std::cout << "Running " << path << "... "; 10 | Session *session = new Session(false); 11 | if (use_hex_parser) session->load_hex(path); else session->load_memory(path); 12 | 13 | while (true) { 14 | session->tick(); 15 | 16 | if (session->memory[0x30004]) break; 17 | } 18 | auto ret_val = session->rf.read(10) & 0xff; 19 | std::cout << "\t" << 1000.0 * (std::clock() - c_start) / CLOCKS_PER_SEC << "ms" 20 | << std::endl; 21 | std::cout << "\t" << ret_val << " == " << ret_value << std::endl; 22 | session->report(std::cout); 23 | assert(ret_val == ret_value); 24 | delete session; 25 | } 26 | 27 | int run_all_tests() { 28 | run_session("../data/expr.data", 58); 29 | run_session("../tests/out-of-order-3.hex", 0x37, true); 30 | run_session("../tests/data-hazard-1.hex", 0x1f, true); 31 | run_session("../tests/data-hazard-2.hex", 0x1f, true); 32 | run_session("../tests/control-hazard-1.hex", 0x8, true); 33 | run_session("../tests/control-hazard-2.hex", 0x8, true); 34 | run_session("../tests/control-hazard-3.hex", 0x1c, true); 35 | run_session("../tests/rename-register-1.hex", 0x320 & 0xff, true); 36 | run_session("../data/naive.data", 94); 37 | run_session("../data/gcd.data", 178); 38 | run_session("../data/lvalue2.data", 175); 39 | run_session("../data/manyarguments.data", 40); 40 | run_session("../data/statement_test.data", 50); 41 | run_session("../data/array_test1.data", 123); 42 | run_session("../data/array_test2.data", 43); 43 | run_session("../data/basicopt1.data", 88); 44 | run_session("../data/bulgarian.data", 159); 45 | run_session("../data/hanoi.data", 20); 46 | run_session("../data/magic.data", 106); 47 | run_session("../data/multiarray.data", 115); 48 | run_session("../data/qsort.data", 105); 49 | run_session("../data/queens.data", 171); 50 | run_session("../data/superloop.data", 134); 51 | run_session("../data/tak.data", 186); 52 | run_session("../data/pi.data", 137); 53 | 54 | return 0; 55 | } 56 | 57 | int main() { 58 | run_all_tests(); 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /data/multiarray.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 80 05 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 17 00 00 83 27 C7 39 33 45 F5 00 13 05 D5 0A 6 | 23 2E A7 38 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 17 00 00 83 26 C7 39 B3 C7 D7 00 93 87 97 20 8 | 23 2E F7 38 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 FF 23 26 11 00 EF F0 5F FB 10 | 83 20 C1 00 13 01 01 01 67 80 00 00 13 01 01 FE 11 | 23 2E 11 00 23 2C 81 00 23 2A 91 00 23 28 21 01 12 | 23 26 31 01 23 24 41 01 37 14 00 00 13 04 C4 2E 13 | 13 07 84 02 13 06 84 0D 93 06 80 37 6F 00 C0 00 14 | 13 07 C7 02 63 0C C7 00 93 07 87 FD 23 A0 D7 00 15 | 93 87 47 00 E3 9C E7 FE 6F F0 9F FE B7 17 00 00 16 | 13 07 A0 00 23 A2 E7 2E B7 17 00 00 13 07 F0 FF 17 | 23 AE E7 2A 93 87 C7 2B 23 A4 E7 00 23 A8 E7 00 18 | 23 AC E7 00 23 A0 E7 02 B7 17 00 00 13 07 50 00 19 | 23 A4 E7 2E B7 17 00 00 03 A5 47 39 EF F0 5F F1 20 | 93 06 A0 00 13 06 20 03 93 87 66 FF 13 07 04 00 21 | 23 20 F7 00 93 87 17 00 13 07 47 00 E3 9A F6 FE 22 | 13 04 C4 02 93 86 A6 00 E3 90 C6 FE B7 17 00 00 23 | 23 A4 07 2E 37 14 00 00 93 84 07 00 37 19 00 00 24 | 13 09 C9 2E 93 09 90 00 13 0A 30 00 23 22 04 2E 25 | 13 07 00 00 83 A6 84 2E 93 97 16 00 B3 87 D7 00 26 | 93 97 27 00 B3 87 D7 40 B3 87 E7 00 93 97 27 00 27 | B3 87 27 01 03 A5 07 00 EF F0 9F E9 83 27 44 2E 28 | 13 87 17 00 23 22 E4 2E E3 D6 E9 FC 83 A7 84 2E 29 | 93 87 17 00 23 A4 F4 2E E3 5A FA FA 37 14 00 00 30 | 13 04 C4 2E 23 20 04 08 13 05 00 00 EF F0 5F E6 31 | 37 17 00 00 93 07 E0 FF 23 2E F7 2A 83 27 04 08 32 | 93 97 37 00 13 04 C7 2B B3 87 87 00 93 06 60 FF 33 | 23 A0 D7 00 03 25 C7 2B EF F0 9F E3 03 25 84 00 34 | EF F0 1F E3 93 05 D0 0F B7 17 00 00 03 A5 C7 39 35 | EF 00 80 0A 83 20 C1 01 03 24 81 01 83 24 41 01 36 | 03 29 01 01 83 29 C1 00 03 2A 81 00 13 01 01 02 37 | 67 80 00 00 63 40 05 06 63 C6 05 06 13 86 05 00 38 | 93 05 05 00 13 05 F0 FF 63 0C 06 02 93 06 10 00 39 | 63 7A B6 00 63 58 C0 00 13 16 16 00 93 96 16 00 40 | E3 6A B6 FE 13 05 00 00 63 E6 C5 00 B3 85 C5 40 41 | 33 65 D5 00 93 D6 16 00 13 56 16 00 E3 96 06 FE 42 | 67 80 00 00 93 82 00 00 EF F0 5F FB 13 85 05 00 43 | 67 80 02 00 33 05 A0 40 63 D8 05 00 B3 05 B0 40 44 | 6F F0 DF F9 B3 05 B0 40 93 82 00 00 EF F0 1F F9 45 | 33 05 A0 40 67 80 02 00 93 82 00 00 63 CA 05 00 46 | 63 4C 05 00 EF F0 9F F7 13 85 05 00 67 80 02 00 47 | B3 05 B0 40 E3 58 05 FE 33 05 A0 40 EF F0 1F F6 48 | 33 05 B0 40 67 80 02 00 49 | @000012B8 50 | FD 00 00 00 51 | -------------------------------------------------------------------------------- /data/pi.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 00 04 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 47 00 00 83 27 C7 E8 33 45 F5 00 13 05 D5 0A 6 | 23 26 A7 E8 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 47 00 00 83 26 C7 E8 B3 C7 D7 00 93 87 97 20 8 | 23 26 F7 E8 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 FD 23 26 11 02 23 24 81 02 10 | 23 22 91 02 23 20 21 03 23 2E 31 01 23 2C 41 01 11 | 23 2A 51 01 23 28 61 01 23 26 71 01 23 24 81 01 12 | 23 22 91 01 B7 17 00 00 93 87 87 2C 37 37 00 00 13 | 13 07 07 BC 33 87 E7 00 93 06 00 7D 23 A0 D7 00 14 | 93 87 47 00 E3 9C E7 FE B7 4A 00 00 93 8A 4A E8 15 | 37 1A 00 00 13 0B FA AE 13 0A FA 5D 13 0C 00 00 16 | B7 2B 00 00 93 8B 0B 71 93 0C F0 FF 83 A7 4A 00 17 | 13 94 27 00 33 04 F4 00 13 14 34 00 33 04 F4 40 18 | 13 14 44 00 33 04 F4 00 13 14 44 00 93 05 0A 00 19 | 13 05 04 00 EF 00 C0 1A 23 A2 AA 00 93 05 0A 00 20 | 13 05 04 00 EF 00 80 11 13 04 05 00 93 09 0B 00 21 | 13 09 EA FF 93 84 0A 00 93 85 09 00 13 05 04 00 22 | EF 00 80 0D 83 A7 04 00 13 94 27 00 33 04 F4 00 23 | 13 14 34 00 33 04 F4 40 13 14 44 00 33 04 F4 00 24 | 13 14 44 00 33 04 A4 00 93 05 09 00 13 05 04 00 25 | EF 00 00 15 23 A0 A4 00 93 05 09 00 13 05 04 00 26 | EF 00 C0 0B 13 04 05 00 93 89 F9 FF 93 84 C4 FF 27 | 13 09 E9 FF E3 92 09 FA 93 85 0B 00 EF 00 00 0A 28 | 33 05 85 01 EF F0 DF E8 93 85 0B 00 13 05 04 00 29 | EF 00 00 11 13 0C 05 00 13 0A 4A FE 13 0B 2B FF 30 | 93 8A 8A FC E3 14 9A F3 37 15 00 00 13 05 05 2C 31 | EF F0 9F E7 93 05 D0 0F B7 47 00 00 03 A5 C7 E8 32 | EF 00 00 0E 83 20 C1 02 03 24 81 02 83 24 41 02 33 | 03 29 01 02 83 29 C1 01 03 2A 81 01 83 2A 41 01 34 | 03 2B 01 01 83 2B C1 00 03 2C 81 00 83 2C 41 00 35 | 13 01 01 03 67 80 00 00 13 06 05 00 13 05 00 00 36 | 93 F6 15 00 63 84 06 00 33 05 C5 00 93 D5 15 00 37 | 13 16 16 00 E3 96 05 FE 67 80 00 00 63 40 05 06 38 | 63 C6 05 06 13 86 05 00 93 05 05 00 13 05 F0 FF 39 | 63 0C 06 02 93 06 10 00 63 7A B6 00 63 58 C0 00 40 | 13 16 16 00 93 96 16 00 E3 6A B6 FE 13 05 00 00 41 | 63 E6 C5 00 B3 85 C5 40 33 65 D5 00 93 D6 16 00 42 | 13 56 16 00 E3 96 06 FE 67 80 00 00 93 82 00 00 43 | EF F0 5F FB 13 85 05 00 67 80 02 00 33 05 A0 40 44 | 63 D8 05 00 B3 05 B0 40 6F F0 DF F9 B3 05 B0 40 45 | 93 82 00 00 EF F0 1F F9 33 05 A0 40 67 80 02 00 46 | 93 82 00 00 63 CA 05 00 63 4C 05 00 EF F0 9F F7 47 | 13 85 05 00 67 80 02 00 B3 05 B0 40 E3 58 05 FE 48 | 33 05 A0 40 EF F0 1F F6 33 05 B0 40 67 80 02 00 49 | @000012C0 50 | 0A 00 00 00 51 | @000012C4 52 | FD 00 00 00 53 | -------------------------------------------------------------------------------- /data/qsort.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 80 15 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 B7 00 00 83 27 07 0B 33 45 F5 00 13 05 D5 0A 6 | 23 28 A7 0A 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 B7 00 00 83 26 07 0B B3 C7 D7 00 93 87 97 20 8 | 23 28 F7 0A 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 FF 23 26 11 00 23 24 81 00 10 | 23 22 91 00 93 84 05 00 33 07 B5 00 93 57 F7 01 11 | B3 87 E7 00 93 D7 17 40 93 97 27 00 37 17 00 00 12 | 13 07 07 2E B3 87 E7 00 83 A6 07 00 13 04 05 00 13 | 37 18 00 00 13 08 08 2E B7 08 00 40 93 88 F8 FF 14 | 63 C0 85 06 93 17 24 00 B3 87 07 01 03 A7 07 00 15 | 93 07 14 00 93 97 27 00 B3 87 07 01 63 5E D7 06 16 | 13 04 14 00 93 87 47 00 03 A7 C7 FF E3 4A D7 FE 17 | 93 97 25 00 B3 87 07 01 03 A6 07 00 63 D0 C6 02 18 | B3 87 15 01 93 97 27 00 B3 87 07 01 93 85 F5 FF 19 | 93 87 C7 FF 03 A6 47 00 E3 CA C6 FE 63 D6 85 04 20 | 63 40 B5 02 63 42 94 02 13 05 00 00 83 20 C1 00 21 | 03 24 81 00 83 24 41 00 13 01 01 01 67 80 00 00 22 | EF F0 5F F3 6F F0 1F FE 93 85 04 00 13 05 04 00 23 | EF F0 5F F2 6F F0 5F FD 93 97 25 00 B3 87 07 01 24 | 03 A6 07 00 E3 CE C6 F8 93 17 24 00 B3 87 07 01 25 | 23 A0 C7 00 93 97 25 00 B3 87 07 01 23 A0 E7 00 26 | 13 04 14 00 93 85 F5 FF 6F F0 9F F3 13 01 01 FE 27 | 23 2E 11 00 23 2C 81 00 23 2A 91 00 23 28 21 01 28 | 23 26 31 01 B7 17 00 00 83 A5 C7 2D 63 50 B0 02 29 | 93 87 05 00 37 17 00 00 13 07 47 2E 23 20 F7 00 30 | 93 87 F7 FF 13 07 47 00 E3 9A 07 FE 13 05 10 00 31 | EF F0 5F EA B7 17 00 00 83 A7 C7 2D 63 5C F0 02 32 | 37 14 00 00 13 04 44 2E 93 04 10 00 B7 19 00 00 33 | 37 19 00 00 03 25 04 00 EF F0 9F E3 13 85 09 2D 34 | EF F0 9F E4 93 84 14 00 13 04 44 00 83 27 C9 2D 35 | E3 D2 97 FE 37 15 00 00 13 05 45 2D EF F0 DF E2 36 | 93 05 D0 0F B7 B7 00 00 03 A5 07 0B EF 00 40 0A 37 | 83 20 C1 01 03 24 81 01 83 24 41 01 03 29 01 01 38 | 83 29 C1 00 13 01 01 02 67 80 00 00 63 40 05 06 39 | 63 C6 05 06 13 86 05 00 93 05 05 00 13 05 F0 FF 40 | 63 0C 06 02 93 06 10 00 63 7A B6 00 63 58 C0 00 41 | 13 16 16 00 93 96 16 00 E3 6A B6 FE 13 05 00 00 42 | 63 E6 C5 00 B3 85 C5 40 33 65 D5 00 93 D6 16 00 43 | 13 56 16 00 E3 96 06 FE 67 80 00 00 93 82 00 00 44 | EF F0 5F FB 13 85 05 00 67 80 02 00 33 05 A0 40 45 | 63 D8 05 00 B3 05 B0 40 6F F0 DF F9 B3 05 B0 40 46 | 93 82 00 00 EF F0 1F F9 33 05 A0 40 67 80 02 00 47 | 93 82 00 00 63 CA 05 00 63 4C 05 00 EF F0 9F F7 48 | 13 85 05 00 67 80 02 00 B3 05 B0 40 E3 58 05 FE 49 | 33 05 A0 40 EF F0 1F F6 33 05 B0 40 67 80 02 00 50 | @000012D0 51 | 20 00 00 00 0A 00 00 00 52 | @000012D8 53 | FD 00 00 00 54 | @000012DC 55 | 10 27 00 00 56 | -------------------------------------------------------------------------------- /src/main_presentation.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Common/Session.h" 4 | #include 5 | #include 6 | #include 7 | 8 | void run_session(const char *path, unsigned ret_value, bool use_hex_parser = false) { 9 | std::clock_t c_start = std::clock(); 10 | Session *session = new Session(true); 11 | if (use_hex_parser) session->load_hex(path); else session->load_memory(path); 12 | 13 | while (true) { 14 | for (int i = 0; i < 100; i++) std::cout << std::endl; 15 | std::cout << "\033[1;1H"; 16 | std::cout << "Running " << path << "... " << std::endl; 17 | session->tick(); 18 | if (session->memory[0x30004]) break; 19 | using namespace std::chrono_literals; 20 | std::this_thread::sleep_for(0.5s); 21 | } 22 | auto ret_val = session->rf.read(10) & 0xff; 23 | std::cout << "\t" << 1000.0 * (std::clock() - c_start) / CLOCKS_PER_SEC << "ms" 24 | << std::endl; 25 | std::cout << "\t" << ret_val << " == " << ret_value << std::endl; 26 | session->report(std::cout); 27 | assert(ret_val == ret_value); 28 | delete session; 29 | } 30 | 31 | int run_all_tests() { 32 | run_session("../data/expr.data", 58); 33 | run_session("../tests/out-of-order-3.hex", 0x37, true); 34 | run_session("../tests/data-hazard-1.hex", 0x1f, true); 35 | run_session("../tests/data-hazard-2.hex", 0x1f, true); 36 | run_session("../tests/control-hazard-1.hex", 0x8, true); 37 | run_session("../tests/control-hazard-2.hex", 0x8, true); 38 | run_session("../tests/control-hazard-3.hex", 0x1c, true); 39 | run_session("../tests/rename-register-1.hex", 0x320 & 0xff, true); 40 | run_session("../data/naive.data", 94); 41 | run_session("../data/gcd.data", 178); 42 | run_session("../data/lvalue2.data", 175); 43 | run_session("../data/manyarguments.data", 40); 44 | run_session("../data/statement_test.data", 50); 45 | run_session("../data/array_test1.data", 123); 46 | run_session("../data/array_test2.data", 43); 47 | run_session("../data/basicopt1.data", 88); 48 | run_session("../data/bulgarian.data", 159); 49 | run_session("../data/hanoi.data", 20); 50 | run_session("../data/magic.data", 106); 51 | run_session("../data/multiarray.data", 115); 52 | run_session("../data/qsort.data", 105); 53 | run_session("../data/queens.data", 171); 54 | run_session("../data/superloop.data", 134); 55 | run_session("../data/tak.data", 186); 56 | run_session("../data/pi.data", 137); 57 | 58 | return 0; 59 | } 60 | 61 | int main() { 62 | run_all_tests(); 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /src/Pipeline/Issue.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-10. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_ISSUE_H 6 | #define RISCV_SIMULATOR_ISSUE_H 7 | 8 | #include 9 | #include 10 | 11 | #include "../Common/Instruction.hpp" 12 | #include "../Common/Register.hpp" 13 | #include "../Common/ReservationStation.h" 14 | #include "OoOCommon.h" 15 | 16 | class Session; 17 | 18 | class Issue { 19 | public: 20 | struct Stat { 21 | unsigned long long stall_cycle; 22 | 23 | Stat() : stall_cycle(0) {} 24 | } stat; 25 | 26 | Register pc; 27 | // TODO: this is used for handling w-w hazard in memory, 28 | // should be eliminated once speculation is possible 29 | Register issue_cnt; 30 | 31 | bool __jump_flag; 32 | Immediate __jump_dest; 33 | 34 | InstructionBase _debug_dispatched_inst; 35 | 36 | Session *session; 37 | 38 | Issue(Session *session); 39 | 40 | InstructionBase parse_inst(unsigned opcode, Immediate inst); 41 | 42 | Immediate issue(const InstructionBase &inst); 43 | 44 | void update(); 45 | 46 | void tick(); 47 | 48 | void debug(); 49 | 50 | RSID find_available_unit_in(const std::vector &src); 51 | 52 | RSID find_available_op_unit(); 53 | 54 | std::vector find_available_op_units(unsigned size); 55 | 56 | RSID find_available_load_unit(); 57 | 58 | RSID find_available_store_unit(); 59 | 60 | std::vector find_available_units_in(const std::vector &src, unsigned size); 61 | 62 | Immediate issue_branch(const InstructionBase &inst); 63 | 64 | Immediate issue_immediate_op(const InstructionBase &inst); 65 | 66 | Immediate issue_op(const InstructionBase &inst); 67 | 68 | Immediate issue_jalr(const InstructionBase &inst); 69 | 70 | Immediate issue_load(const InstructionBase &inst); 71 | 72 | Immediate issue_store(const InstructionBase &inst); 73 | 74 | Immediate issue_lui(const InstructionBase &inst); 75 | 76 | Immediate issue_auipc(const InstructionBase &inst); 77 | 78 | Immediate issue_jal(const InstructionBase &inst); 79 | 80 | void issue_rs_to_Vj(unsigned reg_id, RS *rs, RSID unit_id); 81 | 82 | void issue_rs_to_Vk(unsigned reg_id, RS *rs, RSID unit_id); 83 | 84 | void issue_imm_to_Vj(Immediate imm, RS *rs, RSID unit_id); 85 | 86 | void issue_imm_to_Vk(Immediate imm, RS *rs, RSID unit_id); 87 | 88 | void issue_imm_to_A(Immediate imm, RS *rs, RSID unit_id); 89 | 90 | void notify_jump(Immediate jump_dest); 91 | 92 | void report(std::ostream &out); 93 | 94 | bool instruction_stalled(); 95 | }; 96 | 97 | 98 | #endif //RISCV_SIMULATOR_ISSUE_H 99 | -------------------------------------------------------------------------------- /src/Pipeline/OoOExecute.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-10. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_OOOEXECUTE_H 6 | #define RISCV_SIMULATOR_OOOEXECUTE_H 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "OoOCommon.h" 16 | #include "../Common/ReservationStation.h" 17 | #include "../Common/Session.h" 18 | #include "../Common/Register.hpp" 19 | #include "../Common/ReorderBuffer.h" 20 | 21 | using std::unique_ptr; 22 | 23 | class Session; 24 | 25 | class ALUUnit; 26 | 27 | class LoadStoreUnit; 28 | 29 | class CommitUnit; 30 | 31 | class OoOExecute { 32 | public: 33 | static const unsigned MAX_REG = 32; 34 | static const unsigned ROB_SIZE = 8; 35 | 36 | struct Stat { 37 | unsigned long long unit_busy[RS_END]; 38 | unsigned long long flush_cycle; 39 | unsigned long long rob_usage; 40 | unsigned rob_usage_max; 41 | unsigned long long total_branch, correct_branch; 42 | unsigned long long hazard_load; 43 | 44 | Stat() : flush_cycle(0), rob_usage(0), rob_usage_max(0), 45 | total_branch(0), correct_branch(0), 46 | hazard_load(0) { 47 | memset(unit_busy, 0, sizeof(unit_busy)); 48 | } 49 | } stat; 50 | 51 | // Reservation Stations 52 | RS Add1, Add2, Add3, Add4; 53 | RS Load1, Load2, Load3, Store1, Store2, Store3; 54 | 55 | // Register Rename 56 | Register Reorder[MAX_REG]; 57 | Register Busy[MAX_REG]; 58 | 59 | // Reorder Buffer (Queue) 60 | ROB rob[ROB_SIZE + 1]; 61 | Register rob_front, rob_rear; 62 | 63 | ALUUnit *aluUnit; 64 | LoadStoreUnit *loadStoreUnit; 65 | CommitUnit *commitUnit; 66 | 67 | Session *session; 68 | 69 | bool __rob_flush_flag; 70 | 71 | OoOExecute(Session *session); 72 | 73 | virtual ~OoOExecute(); 74 | 75 | void update(); 76 | 77 | void tick(); 78 | 79 | void put_result(RSID id, Immediate result); 80 | 81 | RS *get_rs(RSID id); 82 | 83 | void debug(); 84 | 85 | bool available(RSID id); 86 | 87 | RS *occupy_unit(RSID id); 88 | 89 | bool probe_rob(unsigned size); 90 | 91 | unsigned acquire_rob(); 92 | 93 | std::vector acquire_robs(unsigned size); 94 | 95 | void occupy_register(unsigned reg_id, unsigned b); 96 | 97 | void flush_rob(); 98 | 99 | static unsigned next_rob_entry(unsigned id); 100 | 101 | void report(std::ostream &out); 102 | }; 103 | 104 | #endif //RISCV_SIMULATOR_OOOEXECUTE_H 105 | -------------------------------------------------------------------------------- /data/statement_test.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 00 04 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 17 00 00 83 27 C7 35 33 45 F5 00 13 05 D5 0A 6 | 23 2E A7 34 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 17 00 00 83 26 C7 35 B3 C7 D7 00 93 87 97 20 8 | 23 2E F7 34 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 F4 23 2E 11 0A 23 2C 81 0A 10 | 23 2A 91 0A 23 28 21 0B 23 26 31 0B 23 24 41 0B 11 | 23 22 51 0B 23 20 61 0B 23 2E 71 09 23 2C 81 09 12 | 23 2A 91 09 23 28 A1 09 23 26 B1 09 B7 17 00 00 13 | 13 07 A0 00 23 A2 E7 30 B7 17 00 00 93 87 87 30 14 | 93 86 C7 02 13 07 10 00 23 A0 E7 00 93 87 47 00 15 | E3 9C D7 FE 93 07 10 00 23 24 F1 04 B7 1C 00 00 16 | 93 8C 0C 31 93 0A C1 04 13 0A 20 00 13 0B A0 00 17 | 37 1D 00 00 37 1C 00 00 13 0C 8C 30 B7 1D 00 00 18 | 6F 00 C0 03 13 14 24 00 93 07 01 08 33 84 87 00 19 | 93 85 04 00 03 A5 0A 00 EF 00 00 14 23 22 A4 FC 20 | 03 A5 0A 00 EF F0 DF F0 13 0A 1A 00 03 AB 4D 30 21 | 93 8C 4C 00 93 8A 4A 00 63 4A 4B 0D 83 A7 0C 00 22 | 63 84 07 02 83 27 8D 35 93 87 17 00 23 2C FD 34 23 | 93 97 27 00 13 07 01 08 B3 07 F7 00 23 A4 47 F9 24 | 93 07 FA FF 23 A0 FA 00 83 2B 8D 35 E3 5A 70 FB 25 | 83 24 C1 00 93 05 0A 00 13 85 04 00 EF 00 C0 0D 26 | 13 04 05 00 E3 4E AB F8 93 17 25 00 B3 87 87 01 27 | 23 A0 07 00 93 85 04 00 13 05 0A 00 EF 00 40 16 28 | 93 09 01 01 13 09 10 00 E3 0E 05 F4 13 14 24 00 29 | 93 07 01 08 33 84 87 00 93 85 F4 FF 03 A5 0A 00 30 | EF 00 80 09 23 22 A4 FC 13 09 19 00 E3 CA 2B F5 31 | 83 A4 09 00 93 05 0A 00 13 85 04 00 EF 00 C0 07 32 | 13 04 05 00 E3 4E AB F2 93 17 24 00 B3 87 87 01 33 | 23 A0 07 00 93 89 49 00 93 85 04 00 13 05 0A 00 34 | EF 00 00 10 E3 14 05 FA 6F F0 DF EF 93 05 D0 0F 35 | B7 17 00 00 03 A5 C7 35 EF 00 80 0E 83 20 C1 0B 36 | 03 24 81 0B 83 24 41 0B 03 29 01 0B 83 29 C1 0A 37 | 03 2A 81 0A 83 2A 41 0A 03 2B 01 0A 83 2B C1 09 38 | 03 2C 81 09 83 2C 41 09 03 2D 01 09 83 2D C1 08 39 | 13 01 01 0C 67 80 00 00 13 06 05 00 13 05 00 00 40 | 93 F6 15 00 63 84 06 00 33 05 C5 00 93 D5 15 00 41 | 13 16 16 00 E3 96 05 FE 67 80 00 00 63 40 05 06 42 | 63 C6 05 06 13 86 05 00 93 05 05 00 13 05 F0 FF 43 | 63 0C 06 02 93 06 10 00 63 7A B6 00 63 58 C0 00 44 | 13 16 16 00 93 96 16 00 E3 6A B6 FE 13 05 00 00 45 | 63 E6 C5 00 B3 85 C5 40 33 65 D5 00 93 D6 16 00 46 | 13 56 16 00 E3 96 06 FE 67 80 00 00 93 82 00 00 47 | EF F0 5F FB 13 85 05 00 67 80 02 00 33 05 A0 40 48 | 63 D8 05 00 B3 05 B0 40 6F F0 DF F9 B3 05 B0 40 49 | 93 82 00 00 EF F0 1F F9 33 05 A0 40 67 80 02 00 50 | 93 82 00 00 63 CA 05 00 63 4C 05 00 EF F0 9F F7 51 | 13 85 05 00 67 80 02 00 B3 05 B0 40 E3 58 05 FE 52 | 33 05 A0 40 EF F0 1F F6 33 05 B0 40 67 80 02 00 53 | @00001300 54 | FD 00 00 00 55 | -------------------------------------------------------------------------------- /Branch.md: -------------------------------------------------------------------------------- 1 | # Before 2 | 3 | | data | cycles | time(ms) | success rate | 4 | |---|---|---|---| 5 | | ../data/naive.data | 34 | 4.922ms | nan% | 6 | | ../tests/data-hazard-1.hex | 11 | 0.479ms | nan% | 7 | | ../tests/data-hazard-2.hex | 12 | 0.464ms | nan% | 8 | | ../tests/control-hazard-1.hex | 11 | 0.446ms | nan% | 9 | | ../tests/control-hazard-2.hex | 13 | 0.449ms | 0% | 10 | | ../data/array_test1.data | 218 | 0.546ms | 54.5455% | 11 | | ../data/array_test2.data | 247 | 0.544ms | 50% | 12 | | ../data/basicopt1.data | 633149 | 56.926ms | 58.9774% | 13 | | ../data/bulgarian.data | 419969 | 40.403ms | 50.6399% | 14 | | ../data/expr.data | 632 | 0.936ms | 37.8378% | 15 | | ../data/gcd.data | 546 | 0.641ms | 62.5% | 16 | | ../data/hanoi.data | 201773 | 19.043ms | 49.98% | 17 | | ../data/lvalue2.data | 61 | 1.49ms | 66.6667% | 18 | | ../data/magic.data | 582014 | 45.829ms | 53.1436% | 19 | | ../data/manyarguments.data | 71 | 0.646ms | 80% | 20 | | ../data/multiarray.data | 1777 | 0.963ms | 18.5185% | 21 | | ../data/qsort.data | 1590883 | 107.458ms | 30.0067% | 22 | | ../data/queens.data | 607530 | 38.524ms | 63.2216% | 23 | | ../data/statement_test.data | 1115 | 0.56ms | 59.4059% | 24 | | ../data/superloop.data | 577259 | 34.466ms | 87.2946% | 25 | | ../data/tak.data | 1546200 | 90.022ms | 74.9996% | 26 | | ../data/pi.data | 127443492 | 8041.23ms | 42.2702% | 27 | 28 | # After 29 | 30 | | data | cycles | time(ms) | success rate | 31 | |---|---|---|---| 32 | | ../data/naive.data | 33 | 5.337ms | nan% | 33 | | ../tests/data-hazard-1.hex | 11 | 0.729ms | nan% | 34 | | ../tests/data-hazard-2.hex | 12 | 0.571ms | nan% | 35 | | ../tests/control-hazard-1.hex | 10 | 0.582ms | nan% | 36 | | ../tests/control-hazard-2.hex | 13 | 0.582ms | 0% | 37 | | ../data/array_test1.data | 209 | 0.655ms | 54.5455% | 38 | | ../data/array_test2.data | 237 | 1.047ms | 50% | 39 | | ../data/basicopt1.data | 559226 | 52.074ms | 98.8887% | 40 | | ../data/bulgarian.data | 377506 | 35.758ms | 94.4862% | 41 | | ../data/expr.data | 586 | 3.846ms | 75.6757% | 42 | | ../data/gcd.data | 534 | 3.514ms | 61.6667% | 43 | | ../data/hanoi.data | 190794 | 18.921ms | 86.4868% | 44 | | ../data/lvalue2.data | 59 | 0.95ms | 66.6667% | 45 | | ../data/magic.data | 553419 | 46.586ms | 82.2054% | 46 | | ../data/manyarguments.data | 69 | 0.912ms | 80% | 47 | | ../data/multiarray.data | 1650 | 1.03ms | 66.0494% | 48 | | ../data/qsort.data | 1430582 | 98.468ms | 91.7384% | 49 | | ../data/queens.data | 582242 | 41.592ms | 81.0156% | 50 | | ../data/statement_test.data | 1067 | 2.527ms | 66.8317% | 51 | | ../data/superloop.data | 532844 | 42.246ms | 95.1932% | 52 | | ../data/tak.data | 1530613 | 101.747ms | 75.7038% | 53 | | ../data/pi.data | 109722503 | 8114.27ms | 84.5072% | 54 | -------------------------------------------------------------------------------- /src/Common/Instruction_test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-01. 3 | // 4 | 5 | #include "gtest/gtest.h" 6 | #include "Instruction.hpp" 7 | 8 | TEST(Instruction, CorrectSize) { 9 | EXPECT_EQ(sizeof(Instruction), 4); 10 | } 11 | 12 | TEST(Instruction, InstructionR) { 13 | // 10987654321098765432109876543210 14 | InstructionR inst(0b10101010101010101010101010101010); 15 | // 10987654321098765432109876543210 16 | EXPECT_EQ(inst.opcode, 0b0101010); 17 | EXPECT_EQ(inst.rd, 0b10101); 18 | EXPECT_EQ(inst.rs1, 0b10101); 19 | EXPECT_EQ(inst.rs2, 0b01010); 20 | EXPECT_EQ(inst.funct3, 0b010); 21 | } 22 | 23 | TEST(Instruction, InstructionI) { 24 | // 10987654321098765432109876543210 25 | InstructionI inst(0b10101010101010101010101010101010); 26 | // 10987654321098765432109876543210 27 | EXPECT_EQ(inst.imm, 0b11111111111111111111101010101010); 28 | EXPECT_EQ(inst.opcode, 0b0101010); 29 | EXPECT_EQ(inst.rd, 0b10101); 30 | EXPECT_EQ(inst.rs1, 0b10101); 31 | EXPECT_EQ(inst.funct3, 0b010); 32 | } 33 | 34 | TEST(Instruction, InstructionS) { 35 | // 10987654321098765432109876543210 36 | InstructionS inst(0b10101010101010101010101010101010); 37 | // 10987654321098765432109876543210 38 | EXPECT_EQ(inst.imm, 0b11111111111111111111101010110101); 39 | EXPECT_EQ(inst.opcode, 0b0101010); 40 | EXPECT_EQ(inst.rs1, 0b10101); 41 | EXPECT_EQ(inst.rs2, 0b01010); 42 | EXPECT_EQ(inst.funct3, 0b010); 43 | } 44 | 45 | TEST(Instruction, InstructionB) { 46 | // 10987654321098765432109876543210 47 | InstructionB inst(0b10101010101010101010101010101010); 48 | // 10987654321098765432109876543210 49 | EXPECT_EQ(inst.imm, 0b11111111111111111111101010110100); 50 | EXPECT_EQ(inst.opcode, 0b0101010); 51 | EXPECT_EQ(inst.rs1, 0b10101); 52 | EXPECT_EQ(inst.rs2, 0b01010); 53 | EXPECT_EQ(inst.funct3, 0b010); 54 | } 55 | 56 | TEST(Instruction, InstructionU) { 57 | // 10987654321098765432109876543210 58 | InstructionU inst(0b10101010101010101010101010101010); 59 | // 10987654321098765432109876543210 60 | EXPECT_EQ(inst.imm, 0b10101010101010101010000000000000); 61 | EXPECT_EQ(inst.opcode, 0b0101010); 62 | EXPECT_EQ(inst.rd, 0b10101); 63 | } 64 | 65 | TEST(Instruction, InstructionJ) { 66 | // 10987654321098765432109876543210 67 | InstructionJ inst(0b10101010101010101010101010101010); 68 | // 10987654321098765432109876543210 69 | EXPECT_EQ(inst.imm, 0b11111111111110101010001010101010); 70 | EXPECT_EQ(inst.opcode, 0b0101010); 71 | EXPECT_EQ(inst.rd, 0b10101); 72 | } 73 | -------------------------------------------------------------------------------- /data/queens.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 40 26 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 17 00 00 83 27 C7 41 33 45 F5 00 13 05 D5 0A 6 | 23 2E A7 40 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 17 00 00 83 26 C7 41 B3 C7 D7 00 93 87 97 20 8 | 23 2E F7 40 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 FE 23 2E 11 00 23 2C 81 00 10 | 23 2A 91 00 23 28 21 01 23 26 31 01 23 24 41 01 11 | 23 22 51 01 23 20 61 01 B7 17 00 00 83 A7 87 35 12 | 63 5A F0 06 B7 14 00 00 93 84 C4 35 13 0A 00 00 13 | 37 19 00 00 B7 19 00 00 B7 1A 00 00 37 1B 00 00 14 | 6F 00 40 04 13 85 C9 34 EF F0 1F F8 13 04 14 00 15 | 83 27 89 35 63 5C F4 00 83 A7 04 00 E3 94 87 FE 16 | 13 85 8A 34 EF F0 5F F6 6F F0 5F FE 13 05 0B 35 17 | EF F0 9F F5 13 0A 1A 00 93 84 44 00 83 27 89 35 18 | 63 5A FA 00 83 27 89 35 13 04 00 00 E3 46 F0 FC 19 | 6F F0 DF FD 37 15 00 00 13 05 05 35 EF F0 DF F2 20 | 83 20 C1 01 03 24 81 01 83 24 41 01 03 29 01 01 21 | 83 29 C1 00 03 2A 81 00 83 2A 41 00 03 2B 01 00 22 | 13 01 01 02 67 80 00 00 13 01 01 FD 23 26 11 02 23 | 23 24 81 02 23 22 91 02 23 20 21 03 23 2E 31 01 24 | 23 2C 41 01 23 2A 51 01 23 28 61 01 23 26 71 01 25 | 23 24 81 01 23 22 91 01 B7 17 00 00 83 A7 87 35 26 | 63 86 A7 04 93 09 05 00 63 54 F0 04 37 14 00 00 27 | 13 04 C4 3F 93 14 25 00 37 17 00 00 13 07 C7 37 28 | B3 84 E4 00 13 09 00 00 B7 1B 00 00 93 8B CB 37 29 | 93 1A 25 00 37 17 00 00 13 07 C7 35 B3 8A EA 00 30 | 93 0C 15 00 37 1A 00 00 6F 00 00 05 EF F0 9F EA 31 | 83 20 C1 02 03 24 81 02 83 24 41 02 03 29 01 02 32 | 83 29 C1 01 03 2A 81 01 83 2A 41 01 03 2B 01 01 33 | 83 2B C1 00 03 2C 81 00 83 2C 41 00 13 01 01 03 34 | 67 80 00 00 13 09 19 00 83 27 8A 35 13 04 44 00 35 | 93 84 44 00 E3 5E F9 FA 03 27 04 00 E3 14 07 FE 36 | 03 A7 04 00 E3 10 07 FE B3 87 27 01 93 87 F7 FF 37 | B3 87 37 41 13 87 07 01 13 17 27 00 33 07 77 01 38 | 03 27 07 00 E3 10 07 FC 93 87 07 01 93 97 27 00 39 | B3 87 77 01 13 07 10 00 23 A0 E7 00 23 A0 E4 00 40 | 23 20 E4 00 23 A0 2A 01 13 85 0C 00 EF F0 DF ED 41 | 83 27 8A 35 B3 07 F9 00 B3 87 37 41 93 87 F7 00 42 | 93 97 27 00 B3 87 77 01 23 A0 07 00 23 A0 04 00 43 | 23 20 04 00 6F F0 1F F7 13 01 01 FF 23 26 11 00 44 | 13 05 00 00 EF F0 5F EA 93 05 D0 0F B7 17 00 00 45 | 03 A5 C7 41 EF 00 40 09 83 20 C1 00 13 01 01 01 46 | 67 80 00 00 63 40 05 06 63 C6 05 06 13 86 05 00 47 | 93 05 05 00 13 05 F0 FF 63 0C 06 02 93 06 10 00 48 | 63 7A B6 00 63 58 C0 00 13 16 16 00 93 96 16 00 49 | E3 6A B6 FE 13 05 00 00 63 E6 C5 00 B3 85 C5 40 50 | 33 65 D5 00 93 D6 16 00 13 56 16 00 E3 96 06 FE 51 | 67 80 00 00 93 82 00 00 EF F0 5F FB 13 85 05 00 52 | 67 80 02 00 33 05 A0 40 63 D8 05 00 B3 05 B0 40 53 | 6F F0 DF F9 B3 05 B0 40 93 82 00 00 EF F0 1F F9 54 | 33 05 A0 40 67 80 02 00 93 82 00 00 63 CA 05 00 55 | 63 4C 05 00 EF F0 9F F7 13 85 05 00 67 80 02 00 56 | B3 05 B0 40 E3 58 05 FE 33 05 A0 40 EF F0 1F F6 57 | 33 05 B0 40 67 80 02 00 58 | @00001348 59 | 20 4F 00 00 20 2E 00 00 0A 00 00 00 60 | @00001354 61 | FD 00 00 00 62 | @00001358 63 | 08 00 00 00 64 | -------------------------------------------------------------------------------- /data/magic.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 C0 37 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 17 00 00 83 27 07 4E 33 45 F5 00 13 05 D5 0A 6 | 23 20 A7 4E 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 17 00 00 83 26 07 4E B3 C7 D7 00 93 87 97 20 8 | 23 20 F7 4E 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 B7 17 00 00 23 A6 07 48 63 5C A0 04 10 | B7 17 00 00 93 87 C7 4B 93 16 15 00 B3 86 A6 00 11 | 93 96 26 00 B3 86 F6 00 13 06 10 00 93 05 20 00 12 | 6F 00 00 01 23 A4 07 00 93 87 C7 00 63 8C D7 00 13 | 23 A0 07 00 E3 5A A6 FE 23 A2 07 00 E3 C4 A5 FE 14 | 6F F0 9F FE B7 17 00 00 23 A4 A7 48 B7 17 00 00 15 | 23 A6 A7 48 67 80 00 00 13 01 01 FE 23 2E 11 00 16 | 23 2C 81 00 23 2A 91 00 23 28 21 01 23 26 31 01 17 | 23 24 41 01 23 22 51 01 23 20 61 01 93 09 05 00 18 | 13 0A 06 00 63 9A 05 06 63 06 05 12 13 05 F5 FF 19 | 93 17 15 00 33 87 A7 00 13 17 27 00 37 16 00 00 20 | 13 06 C6 4B 33 07 C7 00 83 26 07 00 03 27 47 00 21 | 33 87 E6 00 B3 87 A7 00 93 97 27 00 B3 87 C7 00 22 | 83 A7 87 00 B3 07 F7 00 13 07 F0 00 63 8C E7 24 23 | 83 20 C1 01 03 24 81 01 83 24 41 01 03 29 01 01 24 | 83 29 C1 00 03 2A 81 00 83 2A 41 00 03 2B 01 00 25 | 13 01 01 02 67 80 00 00 93 07 20 00 63 0A F5 0A 26 | 93 07 20 00 63 98 F5 0A 93 97 19 00 B3 87 37 01 27 | 93 97 27 00 37 17 00 00 13 07 C7 4B B3 87 E7 00 28 | 03 A7 07 00 13 06 F0 00 33 06 E6 40 03 A7 47 00 29 | 33 06 E6 40 23 A4 C7 00 13 07 F6 FF 93 07 80 00 30 | E3 E8 E7 F8 93 17 26 00 37 17 00 00 13 07 07 49 31 | B3 87 E7 00 83 A7 07 00 E3 9C 07 F6 93 17 26 00 32 | 37 14 00 00 13 04 04 49 B3 87 87 00 13 07 10 00 33 | 23 A0 E7 00 33 06 46 01 93 05 00 00 13 85 19 00 34 | EF F0 9F ED 93 97 19 00 B3 87 37 01 93 97 27 00 35 | 37 17 00 00 13 07 C7 4B B3 87 E7 00 83 A7 87 00 36 | 93 97 27 00 B3 87 87 00 23 A0 07 00 6F F0 5F F2 37 | 63 8A F5 02 37 14 00 00 13 04 04 49 93 04 10 00 38 | 13 99 19 00 33 09 39 01 33 09 B9 00 13 19 29 00 39 | B7 17 00 00 93 87 C7 4B 33 09 F9 00 93 8A 15 00 40 | 6F 00 80 13 93 07 D0 02 33 8A C7 40 37 17 00 00 41 | 93 07 C7 4B 23 A0 47 03 03 26 C7 4B 03 A8 47 00 42 | B3 08 06 01 03 A5 87 00 33 87 A8 00 83 A6 C7 00 43 | 83 A5 07 01 03 A3 47 01 B3 87 B6 00 B3 87 67 00 44 | E3 98 E7 EA B7 17 00 00 93 87 C7 4B 03 AE 87 01 45 | 83 AE C7 01 B3 07 DE 01 B3 87 47 01 E3 9A E7 E8 46 | B3 07 D6 00 B3 87 C7 01 E3 94 E7 E8 B3 07 B8 00 47 | B3 87 D7 01 E3 9E E7 E6 B3 07 65 00 B3 87 47 01 48 | E3 98 E7 E6 33 06 B6 00 33 0A 46 01 E3 12 EA E6 49 | B3 85 C5 01 E3 9E B8 E4 37 17 00 00 83 27 87 4B 50 | 93 87 17 00 23 2C F7 4A 37 14 00 00 13 04 C4 4B 51 | 13 0B 44 02 B7 1A 00 00 13 0A 30 00 B7 19 00 00 52 | 13 09 04 00 93 04 00 00 03 25 09 00 EF F0 5F D0 53 | 13 85 CA 47 EF F0 5F D1 93 84 14 00 13 09 49 00 54 | E3 94 44 FF 13 85 09 48 EF F0 1F D0 13 04 C4 00 55 | E3 18 64 FD 37 15 00 00 13 05 05 48 EF F0 DF CE 56 | 6F F0 1F DF 93 07 10 00 23 22 F4 00 23 20 99 00 57 | 33 06 9A 00 93 85 0A 00 13 85 09 00 EF F0 DF D5 58 | 23 20 09 00 23 22 04 00 93 84 14 00 13 04 44 00 59 | 93 07 A0 00 E3 8E F4 DA 83 27 44 00 E3 96 07 FE 60 | 6F F0 5F FC 93 07 20 00 E3 9C F9 DC 6F F0 9F E8 61 | 13 01 01 FF 23 26 11 00 13 05 30 00 EF F0 9F CB 62 | 13 06 00 00 93 05 00 00 13 05 00 00 EF F0 DF D0 63 | B7 17 00 00 03 A5 87 4B EF F0 9F C5 93 05 D0 0F 64 | B7 17 00 00 03 A5 07 4E EF 00 40 09 83 20 C1 00 65 | 13 01 01 01 67 80 00 00 63 40 05 06 63 C6 05 06 66 | 13 86 05 00 93 05 05 00 13 05 F0 FF 63 0C 06 02 67 | 93 06 10 00 63 7A B6 00 63 58 C0 00 13 16 16 00 68 | 93 96 16 00 E3 6A B6 FE 13 05 00 00 63 E6 C5 00 69 | B3 85 C5 40 33 65 D5 00 93 D6 16 00 13 56 16 00 70 | E3 96 06 FE 67 80 00 00 93 82 00 00 EF F0 5F FB 71 | 13 85 05 00 67 80 02 00 33 05 A0 40 63 D8 05 00 72 | B3 05 B0 40 6F F0 DF F9 B3 05 B0 40 93 82 00 00 73 | EF F0 1F F9 33 05 A0 40 67 80 02 00 93 82 00 00 74 | 63 CA 05 00 63 4C 05 00 EF F0 9F F7 13 85 05 00 75 | 67 80 02 00 B3 05 B0 40 E3 58 05 FE 33 05 A0 40 76 | EF F0 1F F6 33 05 B0 40 67 80 02 00 77 | @0000147C 78 | 20 00 00 00 0A 00 00 00 79 | @00001484 80 | FD 00 00 00 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RISCV-Simulator 2 | 3 | [![Build Status](https://travis-ci.com/skyzh/RISCV-Simulator.svg?branch=out-of-order)](https://travis-ci.com/skyzh/RISCV-Simulator) 4 | 5 | RISCV-Simulator implemented in C++. Support RV32I ISA. 6 | 7 | Generally this should be done with a **5-stage pipeline**. This implementation may be found at [pipeline](https://github.com/skyzh/RISCV-Simulator/tree/pipeline) branch. 8 | 9 | All CPU and CPU simulators I've made are listed below. 10 | 11 | | | Technique | Implementation | 12 | |---------------------------------------------------------------------|------------------------------------------------|----------------| 13 | | [RISC-V v1](https://github.com/skyzh/RISCV-Simulator/tree/pipeline) | 5-stage pipeline simulator | C++ | 14 | | [RISC-V v2](https://github.com/skyzh/RISCV-Simulator) | dynamic scheduling simulator
Tomasulo + Speculation | C++ | 15 | | [MIPS](https://github.com/skyzh/mips-simulator) | 5-stage pipeline simulator | Haskell | 16 | | [MIPS](https://github.com/skyzh/mips-cpu) | 5-stage pipeline CPU | Verilog | 17 | 18 | The architecture is derived from **Tomasulo** and **Speculation** described in CA:AQA Chapter 3. While the method in the textbook is not detailed enough to carry out a CPU design (It illustrates the algorithm with only one float unit instead of a CPU), I came up with some ideas to complete the design for a fully-functional RISC-V CPU. It can be summarized as follows: 19 | 20 | * Handle memory hazards 21 | * RISC-V `jalr` instruction 22 | * Handle branch mis-prediction in the architecture with just Tomasulo algorithm 23 | 24 | For a full report on how I made this simulator and solutions to the challenges above, refer to [Make You a RISC-V Simulator (PDF, Chinese)](https://github.com/skyzh/RISCV-Simulator/files/3389385/make-you-a-riscv-simulator.final.2.pdf) 25 | 26 | For statistics and reports on sample programs, refer to [Travis-CI build log](https://travis-ci.com/github/skyzh/RISCV-Simulator). 27 | 28 | This branch simulates a RISC-V CPU of 2 stage: issue and execute, 29 | which supports out-of-order execution. 30 | 31 | It implements out-of-order execution with Tomasulo algorithm. 32 | For branch, It applies hardware speculation to speculate the following 33 | instructions. 3 load buffer, 3 store buffer, 4 ALU unit, and a 12-entry 34 | reorder buffer. Use Two-level adaptive predictor for branch prediction. 35 | 36 | Note that since I was unable to design the equivalent circuit, this branch 37 | just shows a programmer's way to illustrate out-of-order execution design. 38 | 39 | You may go into Presentation Mode to view detailed execution information. 40 | 41 | Screen Shot 2019-09-06 at 10 07 22 AM 42 | 43 | 44 | | Branch | Build Status | Note | 45 | | ------------- | ------------- | ------------- | 46 | | [seq](https://github.com/skyzh/RISCV-Simulator/tree/seq) | [![Build Status](https://travis-ci.com/skyzh/RISCV-Simulator.svg?branch=seq)](https://travis-ci.com/skyzh/RISCV-Simulator) | A sequential implementation. First edition. No feed forward. | 47 | | [feedforward](https://github.com/skyzh/RISCV-Simulator/tree/feedforward) | [![Build Status](https://travis-ci.com/skyzh/RISCV-Simulator.svg?branch=feedforward)](https://travis-ci.com/skyzh/RISCV-Simulator) | Second edition. Based on seq. Feeding forward runs faster. (Though I don't like it.) | 48 | | [pipeline](https://github.com/skyzh/RISCV-Simulator/tree/pipeline) | [![Build Status](https://travis-ci.com/skyzh/RISCV-Simulator.svg?branch=pipeline)](https://travis-ci.com/skyzh/RISCV-Simulator) | Pipelined version. Based on seq. Handle hazard by forwarding. Two-level adaptive predictor. | 49 | | [out-of-order](https://github.com/skyzh/RISCV-Simulator/tree/out-of-order) | [![Build Status](https://travis-ci.com/skyzh/RISCV-Simulator.svg?branch=out-of-order)](https://travis-ci.com/skyzh/RISCV-Simulator) | Out-of-order execution with Tomasulo algorithm and Speculation. | 50 | | [master](https://github.com/skyzh/RISCV-Simulator/tree/master) | [![Build Status](https://travis-ci.com/skyzh/RISCV-Simulator.svg?branch=master)](https://travis-ci.com/skyzh/RISCV-Simulator) | For online judge | 51 | -------------------------------------------------------------------------------- /src/Module/CommitUnit.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-12. 3 | // 4 | 5 | #include "CommitUnit.h" 6 | #include "BranchPrediction.h" 7 | #include "../Common/ReorderBuffer.h" 8 | #include "../Pipeline/OoOExecute.h" 9 | #include "../Pipeline/Issue.h" 10 | 11 | CommitUnit::CommitUnit(OoOExecute *e) : e(e) {} 12 | 13 | void CommitUnit::update() { 14 | auto &rob_front = e->rob_front; 15 | auto &rob_entry = e->rob[rob_front]; 16 | if (rob_entry.Ready) { 17 | InstructionBase inst = rob_entry.Inst; 18 | switch (inst.opcode) { 19 | case 0: 20 | break; 21 | case 0b0100011: // STORE 22 | resolve_store(rob_entry); 23 | break; 24 | case 0b1100011: // BRANCH 25 | resolve_branch(rob_entry); 26 | break; 27 | case 0b1100111: // JALR 28 | resolve_jalr(rob_entry); 29 | break; 30 | case 0b0110111: // LUI 31 | case 0b0010111: // AUIPC 32 | case 0b0010011: // OP-IMM 33 | case 0b0110011: // OP 34 | case 0b1101111: // JAL 35 | case 0b0000011: // LOAD 36 | resolve_op(rob_entry, rob_front); 37 | break; 38 | default: 39 | assert(false); 40 | } 41 | rob_front = OoOExecute::next_rob_entry(rob_front); 42 | } 43 | } 44 | 45 | void CommitUnit::tick() { 46 | 47 | } 48 | 49 | void CommitUnit::resolve_branch(ROB &rob_entry) { 50 | InstructionBase inst = rob_entry.Inst; 51 | auto pc = rob_entry.Dest; 52 | auto next_pc = get_next_pc(inst, rob_entry, pc); 53 | 54 | bool taken = next_pc != pc + 4; 55 | 56 | e->session->branch->report(pc, taken); 57 | e->stat.total_branch++; 58 | 59 | if (next_pc != rob_entry.Tag) { 60 | e->flush_rob(); 61 | e->session->i->notify_jump(next_pc); 62 | } else e->stat.correct_branch++; 63 | 64 | flush_rob_entry(rob_entry); 65 | } 66 | 67 | void CommitUnit::resolve_op(ROB &rob_entry, unsigned rob_id) { 68 | e->session->rf.write(rob_entry.Dest, rob_entry.Value); 69 | 70 | if (e->Reorder[rob_entry.Dest].current() == rob_id) { 71 | e->Busy[rob_entry.Dest] = false; 72 | } 73 | 74 | flush_rob_entry(rob_entry); 75 | } 76 | 77 | Immediate CommitUnit::get_next_pc(InstructionBase &inst, ROB &rob_entry, Immediate pc) { 78 | auto branch_target = pc + inst.imm; 79 | auto next_inst = pc + 4; 80 | switch (inst.funct3) { 81 | case 0b000: // BEQ 82 | if (rob_entry.Value == 0) return branch_target; 83 | else return next_inst; 84 | case 0b001: // BNE 85 | if (rob_entry.Value != 0) return branch_target; 86 | else return next_inst; 87 | case 0b100: // BLT 88 | if (rob_entry.Value == 1) return branch_target; 89 | else return next_inst; 90 | case 0b101: // BGE 91 | if (rob_entry.Value == 0) return branch_target; 92 | else return next_inst; 93 | case 0b110: // BLTU 94 | if (rob_entry.Value == 1) return branch_target; 95 | else return next_inst; 96 | case 0b111: // BGEU 97 | if (rob_entry.Value == 0) return branch_target; 98 | else return next_inst; 99 | } 100 | assert(false); 101 | return 0; 102 | } 103 | 104 | void CommitUnit::resolve_store(ROB &rob_entry) { 105 | InstructionBase inst = rob_entry.Inst; 106 | switch (inst.funct3) { 107 | case 0b000: // SB 108 | e->session->memory[rob_entry.Dest] = rob_entry.Value; 109 | break; 110 | case 0b001: // SH 111 | e->session->memory.write_ushort(rob_entry.Dest, rob_entry.Value); 112 | break; 113 | case 0b010: // SW 114 | e->session->memory.write_word(rob_entry.Dest, rob_entry.Value); 115 | break; 116 | default: 117 | assert(false); 118 | } 119 | 120 | flush_rob_entry(rob_entry); 121 | } 122 | 123 | void CommitUnit::resolve_jalr(ROB &rob_entry) { 124 | e->flush_rob(); 125 | e->session->i->notify_jump(rob_entry.Value); 126 | } 127 | 128 | void CommitUnit::flush_rob_entry(ROB &rob_entry) { 129 | rob_entry.Ready = false; 130 | rob_entry.Inst = InstructionBase::nop(); 131 | } 132 | 133 | void CommitUnit::reset() { 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/Module/LoadStoreUnit.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-10. 3 | // 4 | 5 | #include "LoadStoreUnit.h" 6 | #include "../Common/Instruction.hpp" 7 | #include "../Pipeline/OoOExecute.h" 8 | 9 | void LoadStoreUnit::update() { 10 | for (auto &&r_id : rs_load) { 11 | RS *r = e->get_rs(r_id); 12 | if (r->Busy) { 13 | auto &load_cnt = get_load_cnt_register(r_id); 14 | // here load takes 3 cycles 15 | if (load_cnt == 0) { 16 | if (r->Qj == NONE) { 17 | r->A = r->Vj + r->A; 18 | load_cnt = 1; 19 | } 20 | } else if (load_cnt == 1) { 21 | if (no_store_in_rob(r->A, r->Dest)) { 22 | load_value(r, get_load_buffer(r_id)); 23 | load_cnt = 2; 24 | } else { 25 | ++e->stat.hazard_load; 26 | } 27 | } else if (load_cnt == 2) { 28 | commit_value(r_id, get_load_buffer(r_id)); 29 | r->Busy = false; 30 | load_cnt = 0; 31 | } else 32 | assert(false); 33 | } 34 | } 35 | for (auto &&r_id : rs_store) { 36 | RS *r = e->get_rs(r_id); 37 | if (r->Busy) { 38 | auto &store_cnt = get_store_cnt_register(r_id); 39 | if (store_cnt == 0) { 40 | if (r->Qj == NONE) { 41 | e->rob[r->Dest].Dest = r->Vj + r->A; 42 | store_cnt = 1; 43 | } 44 | } else if (store_cnt == 1) { 45 | if (r->Qk == NONE) { 46 | e->put_result(r_id, r->Vk); 47 | r->Busy = false; 48 | store_cnt = 0; 49 | } 50 | } else 51 | assert(false); 52 | } 53 | } 54 | } 55 | 56 | LoadStoreUnit::LoadStoreUnit(OoOExecute *e) 57 | : e(e), rs_load({LOAD1, LOAD2, LOAD3}), rs_store({STORE1, STORE2, STORE3}) { 58 | reset(); 59 | } 60 | 61 | void LoadStoreUnit::load_value(RS *rs, Register &load_buffer) { 62 | switch (rs->Op) { 63 | case 0b000: // LB 64 | load_buffer = (char) e->session->memory[rs->A]; 65 | break; 66 | case 0b001: // LH 67 | load_buffer = (short) e->session->memory.read_ushort(rs->A); 68 | break; 69 | case 0b010: // LW 70 | load_buffer = e->session->memory.read_word(rs->A); 71 | break; 72 | case 0b100: // LBU 73 | load_buffer = e->session->memory[rs->A]; 74 | break; 75 | case 0b101: // LHU 76 | load_buffer = e->session->memory.read_ushort(rs->A); 77 | break; 78 | default: 79 | assert(false); 80 | } 81 | } 82 | 83 | void LoadStoreUnit::commit_value(RSID r_id, Immediate val) { 84 | e->put_result(r_id, val); 85 | } 86 | 87 | void LoadStoreUnit::tick() { 88 | for (int i = 0; i < LOAD_BUFFER_SIZE; i++) load_cnt[i].tick(); 89 | for (int i = 0; i < LOAD_BUFFER_SIZE; i++) load_buffer[i].tick(); 90 | for (int i = 0; i < STORE_BUFFER_SIZE; i++) store_cnt[i].tick(); 91 | } 92 | 93 | bool LoadStoreUnit::no_store_in_rob(unsigned addr, unsigned current_rob) { 94 | if (current_rob == 0) return true; 95 | for (unsigned i = e->rob_front; i != current_rob; i = OoOExecute::next_rob_entry(i)) { 96 | InstructionBase inst = e->rob[i].Inst; 97 | if (inst.opcode == 0b0100011) { // STORE 98 | if (e->rob[i].Dest == addr) return false; 99 | } 100 | } 101 | return true; 102 | } 103 | 104 | void LoadStoreUnit::reset() { 105 | for (int i = 0; i < LOAD_BUFFER_SIZE; i++) load_cnt[i] = 0; 106 | for (int i = 0; i < STORE_BUFFER_SIZE; i++) store_cnt[i] = 0; 107 | } 108 | 109 | Register &LoadStoreUnit::get_store_cnt_register(RSID r_id) { 110 | switch (r_id) { 111 | case STORE1: 112 | return store_cnt[0]; 113 | case STORE2: 114 | return store_cnt[1]; 115 | case STORE3: 116 | return store_cnt[2]; 117 | default: 118 | assert(false); 119 | } 120 | } 121 | 122 | Register &LoadStoreUnit::get_load_cnt_register(RSID r_id) { 123 | switch (r_id) { 124 | case LOAD1: 125 | return load_cnt[0]; 126 | case LOAD2: 127 | return load_cnt[1]; 128 | case LOAD3: 129 | return load_cnt[2]; 130 | default: 131 | assert(false); 132 | } 133 | } 134 | 135 | Register &LoadStoreUnit::get_load_buffer(RSID r_id) { 136 | switch (r_id) { 137 | case LOAD1: 138 | return load_buffer[0]; 139 | case LOAD2: 140 | return load_buffer[1]; 141 | case LOAD3: 142 | return load_buffer[2]; 143 | default: 144 | assert(false); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /data/bulgarian.data: -------------------------------------------------------------------------------- 1 | @00000000 2 | 37 01 02 00 EF 10 80 43 13 06 F0 0F B7 06 03 00 3 | 23 82 C6 00 6F F0 9F FF 4 | @00001000 5 | 37 27 00 00 83 27 47 89 33 45 F5 00 13 05 D5 0A 6 | 23 2A A7 88 67 80 00 00 83 47 05 00 63 82 07 02 7 | 37 27 00 00 83 26 47 89 B3 C7 D7 00 93 87 97 20 8 | 23 2A F7 88 13 05 15 00 83 47 05 00 E3 94 07 FE 9 | 67 80 00 00 13 01 01 FF 23 26 11 00 23 24 81 00 10 | 23 22 91 00 23 20 21 01 B7 17 00 00 83 A4 47 6E 11 | B7 17 00 00 03 A9 87 6F 93 05 09 00 13 85 04 00 12 | EF 00 00 64 B7 17 00 00 83 A5 C7 6E EF 00 C0 58 13 | 13 04 05 00 93 05 09 00 13 85 04 00 EF 00 00 5A 14 | B7 17 00 00 83 A5 C7 6F EF 00 00 57 33 05 A4 40 15 | 63 46 05 02 B7 17 00 00 23 A2 A7 6E B7 17 00 00 16 | 03 A5 47 6E 83 20 C1 00 03 24 81 00 83 24 41 00 17 | 03 29 01 00 13 01 01 01 67 80 00 00 B7 17 00 00 18 | 83 A7 87 6E 33 85 A7 00 B7 17 00 00 23 A2 A7 6E 19 | 6F F0 DF FC B7 17 00 00 23 A2 A7 6E 67 80 00 00 20 | 13 15 25 00 B7 17 00 00 93 87 47 70 33 05 F5 00 21 | 03 27 05 00 93 95 25 00 B3 85 F5 00 83 A7 05 00 22 | 23 20 F5 00 23 A0 E5 00 67 80 00 00 13 01 01 FF 23 | 23 26 11 00 23 24 81 00 23 22 91 00 23 20 21 01 24 | 13 09 05 00 B7 17 00 00 03 A5 07 70 63 40 A9 08 25 | 13 04 15 00 93 05 04 00 EF 00 00 4C 93 57 F5 01 26 | 33 85 A7 00 13 55 15 40 63 06 A9 06 63 4E 89 02 27 | 93 04 14 00 93 05 04 00 13 85 04 00 EF 00 C0 49 28 | 93 57 F5 01 B3 87 A7 00 93 D7 17 40 63 86 27 01 29 | 13 84 04 00 6F F0 9F FD B7 17 00 00 23 A0 87 70 30 | 13 05 10 00 6F 00 00 01 B7 17 00 00 23 A0 87 70 31 | 13 05 00 00 83 20 C1 00 03 24 81 00 83 24 41 00 32 | 03 29 01 00 13 01 01 01 67 80 00 00 13 05 00 00 33 | 6F F0 5F FE 13 05 10 00 6F F0 DF FD B7 17 00 00 34 | 83 A7 07 6F 63 5C F0 04 13 01 01 FF 23 26 11 00 35 | 23 24 81 00 23 22 91 00 23 20 21 01 37 14 00 00 36 | 13 04 44 70 93 04 00 00 37 19 00 00 03 25 04 00 37 | EF F0 1F E0 93 84 14 00 13 04 44 00 83 27 09 6F 38 | E3 C6 F4 FE 83 20 C1 00 03 24 81 00 83 24 41 00 39 | 03 29 01 00 13 01 01 01 67 80 00 00 67 80 00 00 40 | B7 17 00 00 83 A8 07 6F B7 17 00 00 83 A7 07 70 41 | 13 05 00 00 63 96 F8 0C 13 01 01 E7 63 56 10 03 42 | B7 17 00 00 93 87 47 70 13 07 01 00 13 96 28 00 43 | 33 06 F6 00 83 A6 07 00 23 20 D7 00 93 87 47 00 44 | 13 07 47 00 E3 98 C7 FE 13 08 41 00 13 8E 08 00 45 | 93 95 28 00 B3 85 25 00 13 06 08 00 13 05 00 00 46 | 13 83 F8 FF 6F 00 80 02 93 87 47 00 63 8E B7 00 47 | 03 27 C6 FF 83 A6 07 00 E3 D8 E6 FE 23 2E D6 FE 48 | 23 A0 E7 00 6F F0 5F FE 13 06 46 00 63 5A 65 00 49 | 13 05 15 00 E3 5A 15 FF 93 07 06 00 6F F0 5F FD 50 | 63 58 10 03 03 25 01 00 93 07 10 00 63 16 F5 02 51 | 63 8C C7 01 03 27 08 00 93 87 17 00 13 08 48 00 52 | E3 08 F7 FE 13 05 00 00 13 01 01 19 67 80 00 00 53 | 13 05 10 00 6F F0 5F FF 13 05 00 00 6F F0 DF FE 54 | 67 80 00 00 B7 17 00 00 03 A7 07 6F 63 54 E0 0C 55 | 13 01 01 FF 23 26 11 00 23 24 81 00 23 22 91 00 56 | 23 20 21 01 37 14 00 00 13 04 44 70 93 04 00 00 57 | 13 89 07 00 6F 00 C0 01 13 85 04 00 EF F0 5F DA 58 | 93 84 14 00 03 27 09 6F 13 04 44 00 63 DC E4 02 59 | 83 27 04 00 E3 96 07 FE 93 85 14 00 E3 D2 E5 FE 60 | 83 27 44 00 E3 9A 07 FC 93 07 84 00 93 85 15 00 61 | E3 88 E5 FC 93 87 47 00 83 A6 C7 FF E3 88 06 FE 62 | 6F F0 9F FB 63 5C E0 02 B7 17 00 00 83 A7 47 70 63 | 63 82 07 02 B7 16 00 00 93 86 86 70 93 07 00 00 64 | 93 87 17 00 63 0C F7 00 93 86 46 00 03 A6 C6 FF 65 | E3 18 06 FE 37 17 00 00 23 28 F7 6E 83 20 C1 00 66 | 03 24 81 00 83 24 41 00 03 29 01 00 13 01 01 01 67 | 67 80 00 00 67 80 00 00 B7 17 00 00 03 A6 07 6F 68 | 63 54 C0 02 B7 17 00 00 93 87 47 70 93 16 26 00 69 | B3 86 F6 00 03 A7 07 00 13 07 F7 FF 23 A0 E7 00 70 | 93 87 47 00 E3 98 D7 FE 93 17 26 00 37 17 00 00 71 | 13 07 47 70 B3 87 E7 00 23 A0 C7 00 13 06 16 00 72 | B7 17 00 00 23 A8 C7 6E 67 80 00 00 13 01 01 FE 73 | 23 2E 11 00 23 2C 81 00 23 2A 91 00 23 28 21 01 74 | 23 26 31 01 23 24 41 01 23 22 51 01 B7 17 00 00 75 | 13 07 20 0D 23 AA E7 6E B7 17 00 00 23 A0 07 70 76 | B7 17 00 00 03 A4 87 6E B7 17 00 00 83 A4 C7 6E 77 | 93 85 04 00 13 05 04 00 EF 00 40 1A B7 17 00 00 78 | 23 AC A7 6E 93 85 04 00 13 05 04 00 EF 00 40 21 79 | B7 17 00 00 23 AE A7 6E 13 05 20 0D EF F0 1F C7 80 | 93 07 10 00 63 16 05 02 13 85 07 00 83 20 C1 01 81 | 03 24 81 01 83 24 41 01 03 29 01 01 83 29 C1 00 82 | 03 2A 81 00 83 2A 41 00 13 01 01 02 67 80 00 00 83 | 37 17 00 00 B7 C7 37 00 93 87 27 4F 23 22 F7 6E 84 | EF F0 5F B5 93 05 A0 00 EF 00 80 1B 13 05 15 00 85 | 37 14 00 00 23 28 A4 6E EF F0 9F AF 83 27 04 6F 86 | 93 87 F7 FF 63 50 F0 08 B7 19 00 00 93 89 49 70 87 | 13 04 00 00 13 0A 00 00 B7 14 00 00 B7 1A 00 00 88 | 6F 00 C0 01 13 0A 1A 00 83 A7 0A 6F 93 87 F7 FF 89 | 93 89 49 00 13 04 05 00 63 58 FA 04 EF F0 9F AF 90 | 93 05 A0 00 EF 00 C0 15 13 05 15 00 13 89 09 00 91 | 23 A0 A9 00 33 05 85 00 83 A7 44 6F E3 D4 A7 FC 92 | EF F0 5F AD 93 05 A0 00 EF 00 80 13 13 05 15 00 93 | 23 20 A9 00 33 05 85 00 83 A7 44 6F E3 C2 A7 FE 94 | 6F F0 5F FA 13 05 00 00 93 97 27 00 37 17 00 00 95 | 13 07 47 70 B3 87 E7 00 37 17 00 00 03 27 47 6F 96 | 33 05 A7 40 23 A0 A7 00 EF F0 5F C1 EF F0 9F D5 97 | 13 04 00 00 6F 00 C0 01 13 04 14 00 13 05 04 00 98 | EF F0 1F A3 EF F0 5F E1 EF F0 DF D3 EF F0 1F BF 99 | EF F0 1F C5 E3 02 05 FE 13 05 04 00 EF F0 5F A1 100 | 93 05 D0 0F B7 27 00 00 03 A5 47 89 EF 00 40 0B 101 | 93 07 05 00 6F F0 5F EB 13 06 05 00 13 05 00 00 102 | 93 F6 15 00 63 84 06 00 33 05 C5 00 93 D5 15 00 103 | 13 16 16 00 E3 96 05 FE 67 80 00 00 63 40 05 06 104 | 63 C6 05 06 13 86 05 00 93 05 05 00 13 05 F0 FF 105 | 63 0C 06 02 93 06 10 00 63 7A B6 00 63 58 C0 00 106 | 13 16 16 00 93 96 16 00 E3 6A B6 FE 13 05 00 00 107 | 63 E6 C5 00 B3 85 C5 40 33 65 D5 00 93 D6 16 00 108 | 13 56 16 00 E3 96 06 FE 67 80 00 00 93 82 00 00 109 | EF F0 5F FB 13 85 05 00 67 80 02 00 33 05 A0 40 110 | 63 D8 05 00 B3 05 B0 40 6F F0 DF F9 B3 05 B0 40 111 | 93 82 00 00 EF F0 1F F9 33 05 A0 40 67 80 02 00 112 | 93 82 00 00 63 CA 05 00 63 4C 05 00 EF F0 9F F7 113 | 13 85 05 00 67 80 02 00 B3 05 B0 40 E3 58 05 FE 114 | 33 05 A0 40 EF F0 1F F6 33 05 B0 40 67 80 02 00 115 | @000016E0 116 | FD 00 00 00 117 | @000016E4 118 | 01 00 00 00 FF FF FF 7F 8F BC 00 00 119 | -------------------------------------------------------------------------------- /src/Common/Instruction.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-01. 3 | // 4 | 5 | #ifndef RISCV_SIMULATOR_INSTRUCTION_HPP 6 | #define RISCV_SIMULATOR_INSTRUCTION_HPP 7 | 8 | #include 9 | #include "Common.h" 10 | #include 11 | #include 12 | 13 | using Instruction = unsigned int; 14 | 15 | struct InstructionBase { 16 | class InvalidAccess { 17 | }; 18 | 19 | InstructionBase() { 20 | rs1 = rs2 = rd = 0; 21 | opcode = 0; 22 | imm = 0; 23 | funct3 = funct7 = 0; 24 | this->inst = 0; 25 | } 26 | 27 | InstructionBase(unsigned) { 28 | rs1 = rs2 = rd = 0; 29 | opcode = 0b0010011; 30 | t = I; 31 | imm = 0; 32 | funct3 = funct7 = 0; 33 | this->inst = -1; 34 | } 35 | 36 | static InstructionBase nop() { return InstructionBase(0); } 37 | 38 | bool is_nop() { return this->inst == -1; } 39 | 40 | unsigned opcode, rs1, rs2, rd, funct3, funct7; 41 | Immediate imm; 42 | Instruction inst; 43 | enum Type { 44 | R, I, S, B, U, J 45 | } t; 46 | 47 | static constexpr unsigned int bin_mask(int digits) { 48 | return (1 << digits) - 1; 49 | } 50 | 51 | static unsigned int get_digits(unsigned int n, int hi, int lo) { 52 | return (n >> lo) & bin_mask(hi - lo + 1); 53 | } 54 | 55 | static unsigned int expand_digit(unsigned int digit, int lo) { 56 | return digit ? (0xffffffff << lo) : 0; 57 | } 58 | 59 | bool is_valid(const std::string &key) { 60 | if (t == R) { if (key == "imm") return false; } 61 | if (t == I) { if (key == "rs2" || key == "funct7") return false; } 62 | if (t == S) { if (key == "rd" || key == "funct7") return false; } 63 | if (t == B) { if (key == "rd" || key == "funct7") return false; } 64 | if (t == U || t == J) { if (key == "rs1" || key == "rs2" || key == "funct3" || key == "funct7") return false; } 65 | return true; 66 | } 67 | 68 | void verify(const std::string &key) { 69 | if (!is_valid(key)) throw InvalidAccess(); 70 | }; 71 | 72 | void debug() { 73 | std::cout << std::hex << inst << " "; 74 | if (inst == 0x00000013) std::cout << "nop"; 75 | else if (opcode == 0b0110111) std::cout << "lui "; 76 | else if (opcode == 0b0010111) std::cout << "auipc "; 77 | else if (opcode == 0b1101111) std::cout << "jal "; 78 | else if (opcode == 0b1100111) std::cout << "jalr "; 79 | else if (opcode == 0b1100011) std::cout << "branch"; 80 | else if (opcode == 0b0000011) std::cout << "load"; 81 | else if (opcode == 0b0100011) std::cout << "store"; 82 | else if (opcode == 0b0010011) { 83 | static std::string opi[] = { 84 | "addi", "slli", "slti", "sltiu", "xori", "srli / srai", 85 | "ori", "andi" 86 | }; 87 | std::cout << opi[funct3]; 88 | } 89 | else if (opcode == 0b0110011) std::cout << "op"; 90 | else std::cout << "unknown"; 91 | std::cout << std::endl; 92 | } 93 | 94 | bool has_op1() { 95 | return t != InstructionBase::U && t != InstructionBase::J; 96 | } 97 | 98 | bool has_op2() { 99 | return t == InstructionBase::R || t == InstructionBase::S 100 | || t == InstructionBase::B; 101 | } 102 | }; 103 | 104 | struct InstructionR : InstructionBase { 105 | InstructionR(const Instruction &inst) { 106 | opcode = get_digits(inst, 6, 0); 107 | rd = get_digits(inst, 11, 7); 108 | funct3 = get_digits(inst, 14, 12); 109 | rs1 = get_digits(inst, 19, 15); 110 | rs2 = get_digits(inst, 24, 20); 111 | funct7 = get_digits(inst, 31, 25); 112 | t = R; 113 | this->inst = inst; 114 | } 115 | }; 116 | 117 | struct InstructionI : InstructionBase { 118 | InstructionI(const Instruction &inst) { 119 | opcode = get_digits(inst, 6, 0); 120 | rd = get_digits(inst, 11, 7); 121 | funct3 = get_digits(inst, 14, 12); 122 | rs1 = get_digits(inst, 19, 15); 123 | imm = get_digits(inst, 30, 20) | expand_digit(get_digits(inst, 31, 31), 11); 124 | t = I; 125 | this->inst = inst; 126 | } 127 | }; 128 | 129 | struct InstructionS : InstructionBase { 130 | InstructionS(const Instruction &inst) { 131 | opcode = get_digits(inst, 6, 0); 132 | funct3 = get_digits(inst, 14, 12); 133 | rs1 = get_digits(inst, 19, 15); 134 | rs2 = get_digits(inst, 24, 20); 135 | imm = get_digits(inst, 11, 7) | 136 | (get_digits(inst, 30, 25) << 5) | 137 | expand_digit(get_digits(inst, 31, 31), 11); 138 | t = S; 139 | this->inst = inst; 140 | } 141 | }; 142 | 143 | struct InstructionB : InstructionBase { 144 | InstructionB(const Instruction &inst) { 145 | opcode = get_digits(inst, 6, 0); 146 | funct3 = get_digits(inst, 14, 12); 147 | rs1 = get_digits(inst, 19, 15); 148 | rs2 = get_digits(inst, 24, 20); 149 | imm = (get_digits(inst, 11, 8) << 1) | 150 | (get_digits(inst, 30, 25) << 5) | 151 | (get_digits(inst, 7, 7) << 11) | 152 | expand_digit(get_digits(inst, 31, 31), 12); 153 | t = B; 154 | this->inst = inst; 155 | } 156 | }; 157 | 158 | struct InstructionU : InstructionBase { 159 | InstructionU(const Instruction &inst) { 160 | opcode = get_digits(inst, 6, 0); 161 | rd = get_digits(inst, 11, 7); 162 | imm = (get_digits(inst, 19, 12) << 12) | 163 | (get_digits(inst, 30, 20) << 20) | 164 | (get_digits(inst, 31, 31) << 31); 165 | t = U; 166 | this->inst = inst; 167 | } 168 | }; 169 | 170 | struct InstructionJ : InstructionBase { 171 | InstructionJ(const Instruction &inst) { 172 | opcode = get_digits(inst, 6, 0); 173 | rd = get_digits(inst, 11, 7); 174 | imm = (get_digits(inst, 30, 21) << 1) | 175 | (get_digits(inst, 20, 20) << 11) | 176 | (get_digits(inst, 19, 12) << 12) | 177 | expand_digit(get_digits(inst, 31, 31), 20); 178 | t = J; 179 | this->inst = inst; 180 | } 181 | }; 182 | 183 | #endif //RISCV_SIMULATOR_INSTRUCTION_HPP 184 | -------------------------------------------------------------------------------- /src/Pipeline/OoOExecute.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-10. 3 | // 4 | 5 | #include 6 | #include "OoOExecute.h" 7 | #include "../Module/ALUUnit.h" 8 | #include "../Module/LoadStoreUnit.h" 9 | #include "../Module/CommitUnit.h" 10 | 11 | using std::make_unique; 12 | 13 | OoOExecute::OoOExecute(Session *session) : 14 | session(session), 15 | __rob_flush_flag(false), 16 | rob_front(1), 17 | rob_rear(1) { 18 | aluUnit = new ALUUnit(this); 19 | loadStoreUnit = new LoadStoreUnit(this); 20 | commitUnit = new CommitUnit(this); 21 | 22 | for (int i = RS_BEGIN + 1; i < RS_END; i++) { 23 | RS *rs = get_rs((RSID) i); 24 | if (rs == nullptr) continue; 25 | rs->__debug_identifier = i; 26 | } 27 | for (int i = 1; i <= ROB_SIZE; i++) { 28 | rob[i].__debug_identifier = i; 29 | } 30 | } 31 | 32 | void OoOExecute::update() { 33 | loadStoreUnit->update(); 34 | aluUnit->update(); 35 | commitUnit->update(); 36 | } 37 | 38 | void OoOExecute::tick() { 39 | if (__rob_flush_flag) { 40 | stat.flush_cycle++; 41 | 42 | for (int i = 0; i < MAX_REG; i++) Busy[i] = false; 43 | for (int i = 1; i <= ROB_SIZE; i++) { 44 | rob[i].Ready = false; 45 | // TODO: set to nop just for more debug information 46 | rob[i].Inst = InstructionBase::nop(); 47 | } 48 | for (int i = RS_BEGIN + 1; i < RS_END; i++) { 49 | RS *rs = get_rs((RSID) i); 50 | if (rs == nullptr) continue; 51 | rs->Dest = 0; 52 | rs->Busy = false; 53 | } 54 | rob_front = 1; 55 | rob_rear = 1; 56 | __rob_flush_flag = false; 57 | loadStoreUnit->reset(); 58 | commitUnit->reset(); 59 | aluUnit->reset(); 60 | } 61 | for (int i = RS_BEGIN + 1; i < RS_END; i++) { 62 | RS *rs = get_rs((RSID) i); 63 | if (rs == nullptr) continue; 64 | 65 | if (rs->Busy.current()) stat.unit_busy[i]++; 66 | 67 | rs->tick(); 68 | } 69 | for (int i = 0; i < MAX_REG; i++) Reorder[i].tick(); 70 | for (int i = 0; i < MAX_REG; i++) Busy[i].tick(); 71 | for (int i = 1; i <= ROB_SIZE; i++) rob[i].tick(); 72 | 73 | auto rob_usage_current = (rob_rear - rob_front + ROB_SIZE) % ROB_SIZE; 74 | stat.rob_usage += rob_usage_current; 75 | stat.rob_usage_max = std::max(stat.rob_usage_max, rob_usage_current); 76 | rob_front.tick(); 77 | rob_rear.tick(); 78 | aluUnit->tick(); 79 | loadStoreUnit->tick(); 80 | commitUnit->tick(); 81 | } 82 | 83 | RS *OoOExecute::get_rs(RSID id) { 84 | if (id == ADD1) return &Add1; 85 | if (id == ADD2) return &Add2; 86 | if (id == ADD3) return &Add3; 87 | if (id == ADD4) return &Add4; 88 | if (id == LOAD1) return &Load1; 89 | if (id == LOAD2) return &Load2; 90 | if (id == LOAD3) return &Load3; 91 | if (id == STORE1) return &Store1; 92 | if (id == STORE2) return &Store2; 93 | if (id == STORE3) return &Store3; 94 | return nullptr; 95 | } 96 | 97 | void OoOExecute::put_result(RSID id, Immediate result) { 98 | auto from_rs = get_rs(id); 99 | auto rob_dst = from_rs->Dest; 100 | if (rob_dst == 0) return; 101 | rob[rob_dst].Ready = true; 102 | rob[rob_dst].Value = result; 103 | for (int i = RS_BEGIN + 1; i < RS_END; i++) { 104 | RS *rs = get_rs((RSID) i); 105 | if (rs == nullptr) continue; 106 | if (rs->Qj.current() == rob_dst) { 107 | rs->Qj = NONE; 108 | rs->Vj = result; 109 | } 110 | if (rs->Qk.current() == rob_dst) { 111 | rs->Qk = NONE; 112 | rs->Vk = result; 113 | } 114 | } 115 | } 116 | 117 | void OoOExecute::debug() { 118 | static const std::vector rf_name = { 119 | "0", "ra", "sp", "gp", "tp", "t0", "t1", "t2", 120 | "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", 121 | "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", 122 | "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6", 123 | "branch"}; 124 | std::cout << "Reservation Stations" << std::endl; 125 | RS::debug_header(); 126 | for (int i = RS_BEGIN + 1; i < RS_END; i++) { 127 | RS *rs = get_rs((RSID) i); 128 | if (rs == nullptr) continue; 129 | rs->debug(); 130 | } 131 | std::cout << "Reorder Buffer" << std::endl; 132 | std::cout << "front: " << std::dec << rob_front.current() << "\t"; 133 | std::cout << "rear: " << std::dec << rob_rear.current() << std::endl; 134 | ROB::debug_header(); 135 | for (int i = 1; i <= ROB_SIZE; i++) { 136 | rob[i].debug(); 137 | } 138 | std::cout << "Register Rename" << std::endl; 139 | for (int i = 0; i < 4; i++) { 140 | for (int j = i * 8; j < i * 8 + 8; j++) std::cout << rf_name[j] << "\t"; 141 | std::cout << std::endl; 142 | for (int j = i * 8; j < i * 8 + 8; j++) { 143 | if (Busy[j].current()) { 144 | std::cout << "⚪" << Reorder[j].current() << " \t"; 145 | } else { 146 | std::cout << "idle" << "\t"; 147 | } 148 | } 149 | std::cout << std::endl; 150 | } 151 | if (__rob_flush_flag) { 152 | std::cout << "⭕" << "(rob flush in next cycle)" << std::endl; 153 | } 154 | } 155 | 156 | bool OoOExecute::available(RSID id) { 157 | return !get_rs(id)->Busy; 158 | } 159 | 160 | RS *OoOExecute::occupy_unit(RSID id) { 161 | get_rs(id)->Busy = true; 162 | return get_rs(id); 163 | } 164 | 165 | unsigned OoOExecute::acquire_rob() { 166 | unsigned b = rob_rear; 167 | if (next_rob_entry(b) == rob_front) return -1; 168 | rob[b].Ready = false; 169 | rob_rear = next_rob_entry(b); 170 | return b; 171 | } 172 | 173 | std::vector OoOExecute::acquire_robs(unsigned size) { 174 | // you should check rob slot yourself 175 | unsigned b = rob_rear; 176 | std::vector robs; 177 | for (int i = 0; i < size; i++, b = next_rob_entry(b)) { 178 | rob[b].Ready = false; 179 | robs.push_back(b); 180 | } 181 | rob_rear = b; 182 | return robs; 183 | } 184 | 185 | void OoOExecute::occupy_register(unsigned reg_id, unsigned b) { 186 | if (reg_id == 0) return; 187 | Reorder[reg_id] = b; 188 | Busy[reg_id] = true; 189 | } 190 | 191 | void OoOExecute::flush_rob() { 192 | __rob_flush_flag = true; 193 | } 194 | 195 | bool OoOExecute::probe_rob(unsigned size) { 196 | unsigned front = rob_front; 197 | unsigned rear = rob_rear; 198 | while (size > 0) { 199 | auto next_rear = next_rob_entry(rear); 200 | if (next_rear == front) return false; 201 | rear = next_rear; 202 | --size; 203 | } 204 | return true; 205 | } 206 | 207 | unsigned OoOExecute::next_rob_entry(unsigned id) { 208 | id++; 209 | if (id > ROB_SIZE) return id - ROB_SIZE; 210 | return id; 211 | } 212 | 213 | void OoOExecute::report(std::ostream &out) { 214 | auto total_cycle = session->stat.cycle; 215 | out << "\t--- Execute Stage Report ---" << std::endl; 216 | out << "\t" << stat.flush_cycle; 217 | out << " (" << 100.0 * stat.flush_cycle / total_cycle << "%)"; 218 | out << " cycles with rob flush" << std::endl; 219 | out << "\t" << stat.correct_branch << " out of " << stat.total_branch << " (" << 100.0 * stat.correct_branch / stat.total_branch << "%) branches predicted" << std::endl; 220 | out << "\t" << stat.hazard_load << " load hazard detected" << std::endl; 221 | for (int i = RS_BEGIN + 1; i < RS_END; i++) { 222 | RS *rs = get_rs((RSID) i); 223 | if (rs == nullptr) continue; 224 | out << "\t" << RS::resolve(i) << "\t"; 225 | out << stat.unit_busy[i]; 226 | out << " (" << 100.0 * stat.unit_busy[i] / total_cycle << "%) cycles utilized" << std::endl; 227 | } 228 | out << "\tROB usage mean " << 1.0 * stat.rob_usage / total_cycle << " out of " << ROB_SIZE << std::endl; 229 | out << "\tROB usage max " << stat.rob_usage_max << " out of " << ROB_SIZE << std::endl; 230 | out << std::endl; 231 | } 232 | 233 | OoOExecute::~OoOExecute() { 234 | delete aluUnit; 235 | delete loadStoreUnit; 236 | delete commitUnit; 237 | } 238 | -------------------------------------------------------------------------------- /src/Pipeline/Issue.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alex Chi on 2019-07-10. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | #include "Issue.h" 9 | #include "OoOExecute.h" 10 | 11 | #include "../Module/ALUUnit.h" 12 | #include "../Module/BranchPrediction.h" 13 | #include "../Common/Session.h" 14 | 15 | void Issue::update() { 16 | // Note that pc is register. No matter how 17 | // many times update is executed, pc always 18 | // stays the same. 19 | 20 | auto _inst = session->memory.read_word(pc); 21 | auto inst = parse_inst(_inst & 0x7f, _inst); 22 | 23 | pc = issue(inst); 24 | } 25 | 26 | void Issue::tick() { 27 | if (instruction_stalled()) stat.stall_cycle++; 28 | if (__jump_flag) { 29 | __jump_flag = false; 30 | pc = __jump_dest; 31 | } 32 | pc.tick(); 33 | issue_cnt.tick(); 34 | } 35 | 36 | ALUUnit::OP get_op_ri(InstructionBase inst) { 37 | switch (inst.funct3) { 38 | case 0b000: 39 | return ALUUnit::OP::ADD; 40 | case 0b010: 41 | return ALUUnit::OP::SLT; 42 | case 0b011: 43 | return ALUUnit::OP::SLTU; 44 | case 0b100: 45 | return ALUUnit::OP::XOR; 46 | case 0b110: 47 | return ALUUnit::OP::OR; 48 | case 0b111: 49 | return ALUUnit::OP::AND; 50 | case 0b001: 51 | return ALUUnit::OP::SLL; 52 | case 0b101: 53 | if (inst.imm & (1 << 9)) 54 | return ALUUnit::OP::SRA; 55 | else 56 | return ALUUnit::OP::SRL; 57 | } 58 | assert(false); 59 | return ALUUnit::OP::NONE_OP; 60 | } 61 | 62 | ALUUnit::OP get_op_rr(InstructionBase inst) { 63 | switch (inst.funct3) { 64 | case 0b000: 65 | if (inst.funct7 == 0b0000000) return ALUUnit::OP::ADD; 66 | if (inst.funct7 == 0b0100000) return ALUUnit::OP::SUB; 67 | case 0b001: 68 | return ALUUnit::OP::SLL; 69 | case 0b010: 70 | return ALUUnit::OP::SLT; 71 | case 0b011: 72 | return ALUUnit::OP::SLTU; 73 | case 0b100: 74 | return ALUUnit::OP::XOR; 75 | case 0b101: 76 | if (inst.funct7 == 0b0000000) 77 | return ALUUnit::OP::SRL; 78 | if (inst.funct7 == 0b0100000) 79 | return ALUUnit::OP::SRA; 80 | case 0b110: // OR 81 | return ALUUnit::OP::OR; 82 | case 0b111: // AND 83 | return ALUUnit::OP::AND; 84 | } 85 | assert(false); 86 | return ALUUnit::OP::NONE_OP; 87 | } 88 | 89 | ALUUnit::OP get_op_branch(InstructionBase inst) { 90 | switch (inst.funct3) { 91 | case 0b000: // BEQ 92 | case 0b001: // BNE 93 | return ALUUnit::OP::SUB; 94 | case 0b100: // BLT 95 | case 0b101: // BGE 96 | return ALUUnit::OP::SLT; 97 | case 0b110: // BLTU 98 | case 0b111: // BGEU 99 | return ALUUnit::OP::SLTU; 100 | } 101 | assert(false); 102 | return ALUUnit::OP::NONE_OP; 103 | } 104 | 105 | Immediate Issue::issue(const InstructionBase &inst) { 106 | _debug_dispatched_inst = inst; 107 | issue_cnt.prev = issue_cnt.next = pc; 108 | 109 | if (inst.t == InstructionBase::J) { // JAL 110 | return issue_jal(inst); 111 | } else if (inst.t == InstructionBase::B) { // Branch 112 | return issue_branch(inst); 113 | } else if (inst.opcode == 0b1100111) { // JALR 114 | return issue_jalr(inst); 115 | } else if (inst.opcode == 0b0000011) { // Load 116 | return issue_load(inst); 117 | } else if (inst.opcode == 0b0100011) { // Store 118 | return issue_store(inst); 119 | } else if (inst.opcode == 0b0010011) { // Immediate Op 120 | return issue_immediate_op(inst); 121 | } else if (inst.opcode == 0b0110011) { // Op 122 | return issue_op(inst); 123 | } else if (inst.opcode == 0b0110111) { // LUI 124 | return issue_lui(inst); 125 | } else if (inst.opcode == 0b0110111) { // AUIPC 126 | return issue_auipc(inst); 127 | } 128 | return pc; 129 | } 130 | 131 | Issue::Issue(Session *session) : 132 | session(session), 133 | __jump_flag(false) {} 134 | 135 | InstructionBase Issue::parse_inst(unsigned opcode, Immediate inst) { 136 | if (opcode == 0b0110011) { return InstructionR(inst); } // *** 137 | if (opcode == 0b0010011) { return InstructionI(inst); } // ***I 138 | if (opcode == 0b0100011) { return InstructionS(inst); } // S* 139 | if (opcode == 0b0000011) { return InstructionI(inst); } // L* 140 | if (opcode == 0b1100011) { return InstructionB(inst); } // B** 141 | if (opcode == 0b1100111) { return InstructionI(inst); } // JALR 142 | if (opcode == 0b1101111) { return InstructionJ(inst); } // JAL 143 | if (opcode == 0b0010111) { return InstructionU(inst); } // AUIPC 144 | if (opcode == 0b0110111) { return InstructionU(inst); } // LUI 145 | return InstructionBase::nop(); 146 | } 147 | 148 | void Issue::debug() { 149 | std::cout << "PC prev->current" << std::endl; 150 | std::cout << std::hex << "\t" << pc << "\t" << pc.current(); 151 | if (instruction_stalled()) { 152 | std::cout << " ⭕(instruction stalled)"; 153 | } 154 | std::cout << std::endl; 155 | std::cout << "Instruction" << std::endl; 156 | _debug_dispatched_inst.debug(); 157 | if (__jump_flag) { 158 | std::cout << "⭕" << "(jump to " 159 | << std::hex << __jump_dest << ")" << std::endl; 160 | } 161 | } 162 | 163 | RSID Issue::find_available_op_unit() { 164 | static const std::vector op_unit = {ADD1, ADD2, ADD3, ADD4}; 165 | return find_available_unit_in(op_unit); 166 | } 167 | 168 | std::vector Issue::find_available_op_units(unsigned size) { 169 | static const std::vector op_unit = {ADD1, ADD2, ADD3, ADD4}; 170 | return find_available_units_in(op_unit, size); 171 | } 172 | 173 | RSID Issue::find_available_load_unit() { 174 | static const std::vector load_unit = {LOAD1, LOAD2, LOAD3}; 175 | return find_available_unit_in(load_unit); 176 | } 177 | 178 | RSID Issue::find_available_store_unit() { 179 | static const std::vector store_unit = {STORE1, STORE2, STORE3}; 180 | return find_available_unit_in(store_unit); 181 | } 182 | 183 | 184 | Immediate Issue::issue_jalr(const InstructionBase &inst) { 185 | auto e = session->e; 186 | 187 | auto unit_ids = find_available_op_units(2); 188 | if (unit_ids.size() == 0) return pc; 189 | 190 | // we need 2 slots for jalr instruction 191 | if (!e->probe_rob(2)) return pc; 192 | 193 | auto robs = e->acquire_robs(2); 194 | 195 | { // save pc + 4 to rd 196 | auto b = robs[0]; 197 | auto unit_id = unit_ids[0]; 198 | auto rs = e->occupy_unit(unit_id); 199 | 200 | rs->Op = ALUUnit::OP::ADD; 201 | rs->Tag = issue_cnt; 202 | 203 | issue_imm_to_Vj(pc, rs, unit_id); 204 | issue_imm_to_Vk(4, rs, unit_id); 205 | 206 | rs->Dest = b; 207 | 208 | e->rob[b].Dest = inst.rd; 209 | InstructionBase _mock_inst = inst; 210 | _mock_inst.opcode = 0b1101111; // it just works like JAL 211 | e->rob[b].Inst = _mock_inst; 212 | 213 | e->occupy_register(inst.rd, b); 214 | } 215 | 216 | { // process jalr 217 | auto b = robs[1]; 218 | auto unit_id = unit_ids[1]; 219 | auto rs = e->occupy_unit(unit_id); 220 | 221 | rs->Op = ALUUnit::OP::ADD; 222 | rs->Tag = issue_cnt; 223 | 224 | issue_rs_to_Vj(inst.rs1, rs, unit_id); 225 | issue_imm_to_Vk(inst.imm, rs, unit_id); 226 | 227 | rs->Dest = b; 228 | 229 | e->rob[b].Dest = 0; 230 | e->rob[b].Inst = inst; 231 | } 232 | 233 | // TODO: just stall instead of executing non-sense instructions 234 | auto pred_pc = pc + 4; 235 | 236 | return pred_pc; 237 | } 238 | 239 | Immediate Issue::issue_branch(const InstructionBase &inst) { 240 | auto e = session->e; 241 | 242 | auto unit_id = find_available_op_unit(); 243 | if (unit_id == NONE) return pc; 244 | 245 | auto b = e->acquire_rob(); 246 | if (b == -1) return pc; 247 | 248 | auto rs = e->occupy_unit(unit_id); 249 | 250 | rs->Op = get_op_branch(inst); 251 | rs->Tag = issue_cnt; 252 | 253 | issue_rs_to_Vj(inst.rs1, rs, unit_id); 254 | issue_rs_to_Vk(inst.rs2, rs, unit_id); 255 | 256 | rs->Dest = b; 257 | 258 | // TODO: here we should return predicted branch, 259 | // now we just apply always-not-taken strategy 260 | bool take = session->branch->take(pc); 261 | auto pred_pc = take ? pc + inst.imm : pc + 4; 262 | 263 | e->rob[b].Dest = pc; 264 | e->rob[b].Inst = inst; 265 | e->rob[b].Tag = pred_pc; 266 | 267 | e->occupy_register(inst.rd, b); 268 | 269 | return pred_pc; 270 | } 271 | 272 | Immediate Issue::issue_immediate_op(const InstructionBase &inst) { 273 | auto e = session->e; 274 | 275 | auto unit_id = find_available_op_unit(); 276 | if (unit_id == NONE) return pc; 277 | 278 | auto b = e->acquire_rob(); 279 | if (b == -1) return pc; 280 | 281 | auto rs = e->occupy_unit(unit_id); 282 | 283 | rs->Op = get_op_ri(inst); 284 | rs->Tag = issue_cnt; 285 | 286 | issue_rs_to_Vj(inst.rs1, rs, unit_id); 287 | issue_imm_to_Vk(inst.imm, rs, unit_id); 288 | 289 | rs->Dest = b; 290 | 291 | e->rob[b].Dest = inst.rd; 292 | e->rob[b].Inst = inst; 293 | 294 | e->occupy_register(inst.rd, b); 295 | 296 | return pc + 4; 297 | } 298 | 299 | Immediate Issue::issue_op(const InstructionBase &inst) { 300 | auto e = session->e; 301 | 302 | auto unit_id = find_available_op_unit(); 303 | if (unit_id == NONE) return pc; 304 | 305 | auto b = e->acquire_rob(); 306 | if (b == -1) return pc; 307 | 308 | auto rs = e->occupy_unit(unit_id); 309 | 310 | rs->Op = get_op_rr(inst); 311 | rs->Tag = issue_cnt; 312 | 313 | issue_rs_to_Vj(inst.rs1, rs, unit_id); 314 | issue_rs_to_Vk(inst.rs2, rs, unit_id); 315 | 316 | rs->Dest = b; 317 | 318 | e->rob[b].Dest = inst.rd; 319 | e->rob[b].Inst = inst; 320 | 321 | e->occupy_register(inst.rd, b); 322 | 323 | return pc + 4; 324 | } 325 | 326 | void Issue::issue_rs_to_Vj(unsigned reg_id, RS *rs, RSID unit_id) { 327 | auto e = session->e; 328 | if (reg_id != 0 && e->Busy[reg_id]) { 329 | auto h = e->Reorder[reg_id]; 330 | if (e->rob[h].Ready) { 331 | rs->Vj = e->rob[h].Value; 332 | rs->Qj = 0; 333 | } else rs->Qj = h; 334 | } else { 335 | rs->Vj = session->rf.read(reg_id); 336 | rs->Qj = NONE; 337 | } 338 | } 339 | 340 | void Issue::issue_rs_to_Vk(unsigned reg_id, RS *rs, RSID unit_id) { 341 | auto e = session->e; 342 | if (reg_id != 0 && e->Busy[reg_id]) { 343 | auto h = e->Reorder[reg_id]; 344 | if (e->rob[h].Ready) { 345 | rs->Vk = e->rob[h].Value; 346 | rs->Qk = 0; 347 | } else rs->Qk = h; 348 | } else { 349 | rs->Vk = session->rf.read(reg_id); 350 | rs->Qk = NONE; 351 | } 352 | } 353 | 354 | void Issue::issue_imm_to_Vk(Immediate imm, RS *rs, RSID) { 355 | rs->Qk = NONE; 356 | rs->Vk = imm; 357 | } 358 | 359 | void Issue::issue_imm_to_Vj(Immediate imm, RS *rs, RSID) { 360 | rs->Qj = NONE; 361 | rs->Vj = imm; 362 | } 363 | 364 | Immediate Issue::issue_load(const InstructionBase &inst) { 365 | auto e = session->e; 366 | 367 | auto unit_id = find_available_load_unit(); 368 | if (unit_id == NONE) return pc; 369 | 370 | auto b = e->acquire_rob(); 371 | if (b == -1) return pc; 372 | 373 | auto rs = e->occupy_unit(unit_id); 374 | 375 | rs->Op = inst.funct3; 376 | rs->Tag = issue_cnt; 377 | 378 | issue_rs_to_Vj(inst.rs1, rs, unit_id); 379 | issue_imm_to_Vk(0, rs, unit_id); 380 | issue_imm_to_A(inst.imm, rs, unit_id); 381 | 382 | rs->Dest = b; 383 | 384 | e->rob[b].Inst = inst; 385 | e->rob[b].Dest = inst.rd; 386 | 387 | e->occupy_register(inst.rd, b); 388 | 389 | return pc + 4; 390 | } 391 | 392 | Immediate Issue::issue_store(const InstructionBase &inst) { 393 | auto e = session->e; 394 | 395 | auto unit_id = find_available_store_unit(); 396 | if (unit_id == NONE) return pc; 397 | 398 | auto b = e->acquire_rob(); 399 | if (b == -1) return pc; 400 | 401 | auto rs = e->occupy_unit(unit_id); 402 | 403 | rs->Op = inst.funct3; 404 | rs->Tag = issue_cnt; 405 | 406 | issue_rs_to_Vj(inst.rs1, rs, unit_id); 407 | issue_rs_to_Vk(inst.rs2, rs, unit_id); 408 | issue_imm_to_A(inst.imm, rs, unit_id); 409 | 410 | rs->Dest = b; 411 | 412 | e->rob[b].Inst = inst; 413 | 414 | return pc + 4; 415 | } 416 | 417 | Immediate Issue::issue_lui(const InstructionBase &inst) { 418 | auto e = session->e; 419 | 420 | auto unit_id = find_available_op_unit(); 421 | if (unit_id == NONE) return pc; 422 | 423 | auto b = e->acquire_rob(); 424 | if (b == -1) return pc; 425 | 426 | auto rs = e->occupy_unit(unit_id); 427 | 428 | rs->Op = ALUUnit::OP::ADD; 429 | rs->Tag = issue_cnt; 430 | 431 | issue_imm_to_Vj(0, rs, unit_id); 432 | issue_imm_to_Vk(inst.imm, rs, unit_id); 433 | 434 | rs->Dest = b; 435 | 436 | e->rob[b].Dest = inst.rd; 437 | e->rob[b].Inst = inst; 438 | 439 | e->occupy_register(inst.rd, b); 440 | 441 | return pc + 4; 442 | } 443 | 444 | Immediate Issue::issue_auipc(const InstructionBase &inst) { 445 | auto e = session->e; 446 | 447 | auto unit_id = find_available_op_unit(); 448 | if (unit_id == NONE) return pc; 449 | 450 | auto b = e->acquire_rob(); 451 | if (b == -1) return pc; 452 | 453 | auto rs = e->occupy_unit(unit_id); 454 | 455 | rs->Op = ALUUnit::OP::ADD; 456 | rs->Tag = issue_cnt; 457 | 458 | issue_imm_to_Vj(pc, rs, unit_id); 459 | issue_imm_to_Vk(inst.imm, rs, unit_id); 460 | 461 | rs->Dest = b; 462 | 463 | e->rob[b].Dest = inst.rd; 464 | e->rob[b].Inst = inst; 465 | 466 | e->occupy_register(inst.rd, b); 467 | 468 | return pc + 4; 469 | } 470 | 471 | RSID Issue::find_available_unit_in(const std::vector &src) { 472 | for (auto &&unit : src) { 473 | if (session->e->available(unit)) return unit; 474 | } 475 | return NONE; 476 | } 477 | 478 | std::vector Issue::find_available_units_in(const std::vector &src, unsigned size) { 479 | std::vector rs; 480 | for (auto &&unit : src) { 481 | if (session->e->available(unit)) { 482 | rs.push_back(unit); 483 | --size; 484 | } 485 | if (size == 0) return rs; 486 | } 487 | return std::vector(); 488 | } 489 | 490 | Immediate Issue::issue_jal(const InstructionBase &inst) { 491 | auto e = session->e; 492 | 493 | auto unit_id = find_available_op_unit(); 494 | if (unit_id == NONE) return pc; 495 | 496 | auto b = e->acquire_rob(); 497 | if (b == -1) return pc; 498 | 499 | auto rs = e->occupy_unit(unit_id); 500 | 501 | rs->Op = ALUUnit::OP::ADD; 502 | rs->Tag = issue_cnt; 503 | 504 | issue_imm_to_Vk(pc, rs, unit_id); 505 | issue_imm_to_Vj(4, rs, unit_id); 506 | 507 | rs->Dest = b; 508 | 509 | e->rob[b].Dest = inst.rd; 510 | e->rob[b].Inst = inst; 511 | 512 | e->occupy_register(inst.rd, b); 513 | 514 | return pc + inst.imm; 515 | } 516 | 517 | void Issue::issue_imm_to_A(Immediate imm, RS *rs, RSID) { 518 | rs->A = imm; 519 | } 520 | 521 | void Issue::notify_jump(Immediate jump_dest) { 522 | __jump_flag = true; 523 | __jump_dest = jump_dest; 524 | } 525 | 526 | void Issue::report(std::ostream &out) { 527 | out << "\t--- Issue Stage Report ---" << std::endl; 528 | out << "\t" << stat.stall_cycle; 529 | out << " (" << 100.0 * stat.stall_cycle / session->stat.cycle << "%)"; 530 | out << " cycles stall instruction"; 531 | out << std::endl; 532 | out << std::endl; 533 | } 534 | 535 | bool Issue::instruction_stalled() { 536 | return pc.current() == pc; 537 | } 538 | -------------------------------------------------------------------------------- /docs/main.tex: -------------------------------------------------------------------------------- 1 | \documentclass[12pt]{article} 2 | \usepackage{fontspec} 3 | \usepackage{xeCJK} 4 | \usepackage{xcolor} 5 | \usepackage[a4paper, margin=2cm]{geometry} 6 | \usepackage{setspace} 7 | \usepackage{indentfirst} 8 | \usepackage{tcolorbox} 9 | \usepackage{graphicx} 10 | \usepackage[stable]{footmisc} 11 | \usepackage{hyperref} 12 | \usepackage{csvsimple} 13 | \usepackage{float} 14 | \usepackage{subcaption} 15 | 16 | \hypersetup{ 17 | colorlinks=true, 18 | linkcolor=blue, 19 | filecolor=magenta, 20 | urlcolor=cyan, 21 | } 22 | 23 | \urlstyle{same} 24 | 25 | \graphicspath{ {./images/} } 26 | 27 | \onehalfspacing 28 | \definecolor{friendlybg}{HTML}{f0f0f0} 29 | 30 | \setCJKmainfont{Noto Serif CJK SC} 31 | \setCJKmonofont{Noto Sans CJK SC} 32 | \setromanfont{Noto Serif Regular} 33 | \setsansfont{Noto Sans Regular} 34 | \setmonofont{Fira Code}[ 35 | Contextuals=Alternate % Activate the calt feature 36 | ] 37 | \usepackage{listings} 38 | \usepackage{lstfiracode} 39 | 40 | \definecolor{friendlybg}{HTML}{f0f0f0} 41 | \ActivateVerbatimLigatures 42 | 43 | \title{Make You a RISC-V Simulator} 44 | \author{Alex Chi} 45 | \date{July, 2019} 46 | 47 | \newenvironment{aside}[1] 48 | { \begin{tcolorbox}[enlarge top by=0.5cm, enlarge bottom by=0.5cm] Aside\space\space\space\space \textbf{#1} \\ 49 | } { \end{tcolorbox} } 50 | 51 | \renewcommand{\figurename}{图} 52 | \renewcommand{\tablename}{表} 53 | 54 | \begin{document} 55 | 56 | \maketitle 57 | 58 | 在过去的两个星期里,我用 C++ 实现了一个 RISC-V 模拟器。模拟器支持 RV32I 指令集。 59 | 我先后完成了串行 (Sequential)、并行 (Pipelined) 以及乱序执行 (Out-of-order Execution) 60 | 的模拟器。本文将介绍实现的细节和过程。 61 | 62 | \section{电路、FP 与串行模拟器} 63 | 64 | 在设计之初,我希望模拟器的实现可以尽可能贴近硬件电路。其中的每一个操作,都是 65 | 能够通过电路方便实现的。 66 | 67 | \begin{aside}{什么样的实现是贴近电路的?} 68 | 电路不受外界条件影响,在寄存器的值和内存给定的情况下,不论仿真多少次, 69 | 都能得到同一个值 (pure)。电路中也不宜设计一些复杂的数据结构(比如队列)。 70 | 为了达成这个目标,我设计了第 \ref{register_model} 71 | 章所述的寄存器类,并且在编程的时候增加了几个限制,这一部分参见第 72 | \ref{feed_forward_evaluation} 章。 73 | \end{aside} 74 | 75 | 在 CSAPP HCL 的启发下,我从函数式编程的角度考虑电路仿真,从而 76 | 实现一个尽可能贴近硬件实现的模拟器。 77 | 78 | \begin{aside}{一个 CSAPP HCL 的样例} 79 | \begin{verbatim} 80 | int dstE = [ 81 | icode in { IRRMOVL } : rB; 82 | icode in { IIRMOVL, IOPL} : rB; 83 | icode in { IPUSHL, IPOPL, ICALL, IRET } : RESP; 84 | : RNONE; 85 | ]; 86 | \end{verbatim} 87 | \end{aside} 88 | 89 | \subsection{如何求解电路} 90 | 91 | \begin{figure}[h] 92 | \centering 93 | \includegraphics[width=0.5\textwidth]{circuit} 94 | \caption{一个电路} 95 | \label{fig:circuit} 96 | \end{figure} 97 | 98 | 如图 \ref{fig:circuit} 所示,若要求得节点 C 处的电压,则必要先依次求出 A、B 处的电压。 99 | 而当初始条件确定(电压源确定)时,不论求多少次 C 处电压,它永远都是一个值。由此,我联想到 100 | 某个语言中的惰性求值 (Lazy Evaluation)。 101 | 102 | \begin{verbatim} 103 | v_source = 1.0 104 | current = 0.003333333 105 | voltage_at_c :: Double 106 | voltage_at_b :: Double 107 | voltage_at_a :: Double 108 | voltage_at_c = voltage_at_b - 100 * current 109 | voltage_at_b = voltage_at_a - 100 * current 110 | voltage_at_a = v_source 111 | 112 | ghci> voltage_at_c 113 | 0.33333339999999995 114 | \end{verbatim} 115 | 116 | 在 Haskell 中,我们可以描述函数之间的关系。在定义函数的时候,这些值都不会被立刻求出来。只有 117 | 值被用到的时候,ghc 才会开始执行函数内容求值。这一思想可以被应用在电路仿真中。 118 | 119 | \subsection{如何设计电路}\label{feed_forward_evaluation} 120 | 121 | 在 CPU 电路中,我们大体可以把元件分为两大类:时间控制和非时间控制元件。 122 | 123 | 时间控制的元件包括寄存器、内存等。只有时钟发出信号,它们的值才会被更新,输出端的电平 124 | 才会有变化。这些元件决定了电路的初始条件,是电路的信号源。非时间控制的元件包括各种选择 125 | 器、运算器。这些元件端口的输出值可以通过信号源的电平决定。一旦电路达到稳态,它们的电平 126 | 就不会再改变。 127 | 128 | 因而,我们可以在程序中描述一组电路中的关系,就像之前的 Haskell 代码一样。而后,再用 129 | C++ 求得电路电平的关系,更新寄存器,开始下一个周期。 130 | 131 | 设计电路的原则之一是:数据向前传播。在同一个周期内,这些电路元件之间的求值关系不应该出现 132 | 互相依赖的关系。对于图 \ref{fig:circuit} 的电路,我们也可以用下面这段代码描述。 133 | 134 | \begin{verbatim} 135 | voltage_at_c = voltage_at_b - 100 * current 136 | voltage_at_b = voltage_at_c + 100 * current 137 | \end{verbatim} 138 | 139 | 但这种描述就存在数据间的依赖关系。在设计 CPU 电路时,我们应当保证数据是单向传输的,对于 140 | 每一根导线,我们可以人为规定它数据传播的方向(从寄存器输出到另一个寄存器输入)。在这一 141 | 限制下,一个时钟周期内电路总能达到稳态。 142 | 143 | \subsection{寄存器模型}\label{register_model} 144 | 145 | \begin{figure}[h] 146 | \centering 147 | \includegraphics[page=1,width=0.8\textwidth]{design} 148 | \caption{寄存器模型} 149 | \label{fig:register} 150 | \end{figure} 151 | 152 | 如图 \ref{fig:register} 所示,寄存器有一个数据输入端和一个数据输出端。寄存器由时钟 153 | 控制。收到时钟信号 (tick),如果数据不被搁置 (stall),就将输出端设置为输入端的值。这一模型 154 | 可以由下面的 C++ 代码所描述。 155 | 156 | \begin{verbatim} 157 | template 158 | class Register { 159 | public: 160 | T prev, next; 161 | bool _stall; 162 | Register() : prev((T) 0), next((T) 0), _stall(false) {} 163 | T read() { return prev; } 164 | T current() { return next; } 165 | void write(const T &t) { next = t; } 166 | void tick() { if (!_stall) prev = next; } 167 | void stall(bool stall) { _stall = stall; } 168 | }; 169 | \end{verbatim} 170 | \begin{tcolorbox} 171 | 在第 \ref{new_register_design} 章中,我将会介绍一种可读性更高 172 | 的寄存器类设计。 173 | \end{tcolorbox} 174 | 175 | \subsection{惰性求值在 C++ 中的实现} 176 | 177 | 对于 CPU 中的每一个电路端口,我都确定了一个唯一的标签。通过标签即可得到端口的数据。 178 | 比如,如果我们要在 Write-back 阶段完成 load 指令,将内存读取的值写入寄存器: 179 | 180 | \begin{verbatim} 181 | // 获取寄存器写入目标 182 | auto rd = session->d->get(Decode::rd); 183 | // 获取内存阶段读到的值 184 | auto m_val = session->m->get(MemoryAccess::m_val); 185 | // 写入寄存器 186 | session->rf.write(rd, m_val); 187 | \end{verbatim} 188 | 189 | 在 Memory Access 阶段,我们定义求 \texttt{m\_val} 的方式。 190 | 191 | \begin{verbatim} 192 | unsigned int addr = session->e->get(Execute::e_val); 193 | switch (session->d->get(Decode::funct3)) { 194 | case 0b000: // LB 195 | return (char) session->memory[addr]; 196 | case 0b001: // LH 197 | return (short) session->memory.read_ushort(addr); 198 | ... 199 | } 200 | \end{verbatim} 201 | 202 | 求 \texttt{m\_val} 的过程还会自动求 Execute 阶段所计算的内存地址。 203 | 因此,通过惰性求值,程序能自动解决电路元件间的依赖关系。 204 | 205 | 与此同时,由于一个时钟周期中,每个端口的值不论求多少遍都是一样的,我们可以通过 map 206 | 或者其他方式将该周期里求过的端口值保存下来。在这个实现中,每个阶段的端口都由一个枚举类型 207 | 定义。枚举类型的元素是一个从 0 开始编号的整型数。因此,我们可以直接用数组缓存端口的值。 208 | 209 | \begin{aside}{用 CLion 管理工程} 210 | 在很久以前(大概是四年前),我对 CMake 最头大的事情是它没法用一条指令 211 | 直接把整个目录的源文件都一起编译。在更久以前(大概是五年前),我对 C++ 212 | 最头痛的事情就是写它就要把声明与定义分离。把定义和实现拷来拷去,是一件令 213 | 人头大的事情。后来,我发现了 CLion。我沉迷一键生成定义 (Definition), 214 | 自动加载 CMakeLists.txt。自此以后,写 C++ 变成了一件不那么麻烦的事情。 215 | 后来,随着 vcpkg 这类靠谱的 C++ 包管理器的出现,在 CMakeLists 中增加 216 | 依赖也变得简单了许多。在这个项目中我就用到了 vcpkg 安装的 gtest。 217 | \end{aside} 218 | 219 | \subsection{串行模拟器的实现\protect 220 | \footnote{串行模拟器的实现在 seq 分支。 221 | \url{https://github.com/skyzh/RISCV-Simulator/tree/seq}}} 222 | 223 | 在串行的 RISC-V CPU 中,电路中的时间控制元件只有内存、32 个寄存器和 PC。每个时钟周期中, 224 | 各个电路元件从 PC 开始依次读出指令、获取操作数、执行计算、写内存、写寄存器,并将新的 PC 225 | 写入 PC 寄存器中。写入寄存器的数据在这一个时钟周期中无法被读出。在数据更新完成后,对所有 226 | 时间控制元件执行``tick''操作,开始下一个时钟周期后,这些写入的数据才能被电路元件读取到。 227 | 228 | \begin{aside}{一些常见的坑} 229 | 第 0 个寄存器永远是 0。 230 | 231 | \texttt{JAL}、\texttt{JALR} 指令要把 \texttt{PC + 4} 写入 \texttt{rd}。 232 | \end{aside} 233 | 234 | 因而,从程序模拟的角度来说,我们只需要求得电路输出的值就可以完成一个周期的模拟。在串行 CPU 235 | 中,整个电路有三个输出:到 PC 寄存器的输出,向内存写入,以及向 32 个寄存器写入。这就是每次 236 | 更新所需要做的事情。 237 | 238 | \begin{verbatim} 239 | PC.write(w->get(WriteBack::w_pc)); // 更新 PC 寄存器 240 | m->hook(); // 写入内存 241 | w->hook(); // 写入寄存器 242 | \end{verbatim} 243 | 244 | 由于我们在程序中通过函数的调用关系确定了求值顺序,三句语句可以任意调换而不影响最后的结果。 245 | 246 | \begin{aside}{写点单元测试} 247 | 在完成 PPCA 的几个项目的过程中,我用 Google Test 写了一些单元测试。虽然单元测试通过 248 | 并不能保证整个程序可以跑起来,甚至单元测试也没法覆盖所有样例,但如果单元测试挂了,那必然 249 | 是改程序改错了。比如在写 Parser 的过程中,我发现有些时候内存里读到的指令是 250 | \texttt{0xffffffff}。于是,我加上了 hex dump 中行末有空格、有各种换行的 Parser 251 | 单元测试,发现了问题所在。 252 | \end{aside} 253 | 254 | \section{更快的电路仿真:从串行到并行} 255 | 256 | 在上一章中,我介绍了串行模拟器设计的过程和其中的一些问题。由于从项目一开始我就采用电路仿真 257 | 的思路,所以从串行改并行的过程中,我几乎没有进行大的重构。但是后来我发现,惰性求值效率很低。 258 | 因此,在这一章中,我提出了另外一个电路仿真的方法,并且验证了这种方法依然是 pure 的。 259 | 260 | 在这一部分,我完成了一个通过数据转发 (Forwarding) 解决数据冲突 (Data Hazard) 的 CPU 模拟器。 261 | 模拟器还采用了两位自适应分支预测 (Two-level Adaptive Predictor),在部分样例中达到了 98\% 的 262 | 准确率。 263 | 264 | \subsection{手动指定求值顺序} 265 | 266 | 观察串行模拟器使用的电路,我们可以发现,CPU 的电路设计在程序编写时就已经设计好了。因而, 267 | 我们可以手动指定求解电路的顺序。 268 | \footnote{手动指定求值顺序的串行模拟器实现在 feed-forward 分支。 269 | \url{https://github.com/skyzh/RISCV-Simulator/tree/feed-forward}} 270 | 271 | 比如图 \ref{fig:circuit} 所示的电路,我们完全可以依次求出 A, B, C, D 点所对应的 272 | 电平。因为在求解之前,我们就已经知道电路端口与信号源的远近关系(即拓扑序)。这种按 273 | 顺序一次求值,就能求出当前时钟周期里每个端口的值的方法,和之前的惰性求解是等价的。 274 | 275 | 以串行模拟器为例。要求出 Write-back 阶段写入的值,就要求出 Memory 阶段读取的值, 276 | 以及 Execute 阶段运算的值。求 Execute 阶段运算的值,就要求出 Decode 阶段、 277 | Fetch 阶段的指令和寄存器值。因而,我们很快就能发现五级模块间的依赖关系。所以, 278 | 我们可以以 Fetch, Decode, Execute, Memory, Write-back 的顺序传播 279 | 电路信号,在传播的过程中写入寄存器和内存,并获取下一条指令的地址。 280 | 281 | \subsection{五级流水线设计与实现\protect 282 | \footnote{并行模拟器的实现在 pipeline 分支。 283 | \url{https://github.com/skyzh/RISCV-Simulator/tree/pipeline}}} 284 | 285 | 在改为五级流水线后,情况就不一样了。由于每两级之间都有寄存器隔离,所以每个阶段的 286 | 电路信号仅和两级之间的寄存器有关。在这种情况下,不论以什么顺序求值都可以一次得到 287 | 电路每一个端口的值。 288 | 289 | 在通过暂停流水处理数据冲突时,Decode 阶段需要知道后面三个阶段正在执行的指令和 290 | 需要写入的寄存器。因而,必然有一组导线从后面三个阶段接到 Decode 阶段。在这种 291 | 情况下,电路求值就有了依赖关系。Decode 阶段必须要等到 Execute, Memory 292 | 和 Write-back 的电路端口达到稳态后,才能求值。 293 | 294 | 在处理 Control Hazard 的时候,Fetch 阶段需要知道 Execute 阶段所处理的分支 295 | 指令的跳转目标是否和先前预测的一致。因而,Fetch 阶段必然要在 Execute 阶段之后 296 | 求值。 297 | 298 | 所以,我们可以构建出一个求值顺序,保证一次传播电路信号,就能正确求出当前周期 299 | 中每一个端口的值。其中一种正确的顺序如下。 300 | 301 | \begin{verbatim} 302 | w->hook(); // Write-back 303 | m->hook(); // Memory 304 | e->hook(); // Execute 305 | d->hook(); // Decode 306 | f->hook(); // Fetch 307 | \end{verbatim} 308 | 309 | 在电路信号传播完成后,更新时钟,开始下一个时钟周期,就能完成并行 CPU 的模拟。 310 | 311 | \subsection{验证并行实现确实并行} 312 | 313 | 在 Session.cpp 中随机选取五个阶段的求值顺序。这个循环执行十遍后,电路必然可以达到稳态, 314 | 它和手动指定求值顺序的结果一致。因此,模拟器的运行和五个阶段的求值顺序无关。这个模拟器是并行的。 315 | 验证的代码如下。 316 | 317 | \begin{verbatim} 318 | int seq[5] = { 0 }; 319 | for (int j = 0; j < 5; j++) seq[j] = j; 320 | for (int i = 0; i < 10; i++) { 321 | std::random_shuffle(seq, seq + 5); 322 | for (int j = 0; j < 5; j++) 323 | switch(seq[j]) { 324 | case 0: f->hook(); break; 325 | case 1: d->hook(); break; 326 | case 2: e->hook(); break; 327 | case 3: m->hook(); break; 328 | case 4: w->hook(); break; 329 | } 330 | } 331 | \end{verbatim} 332 | 333 | 当然也有其他方法验证。比如开五个线程分别更新五个阶段,每隔 1ms 更新一次时钟。这种验证方法 334 | 似乎就更加贴近电路中电压的传播了。 335 | 336 | \begin{tcolorbox} 337 | 理论上,我们也可以跑五个线程分别刷新五个阶段的电路。但事实是,这么做会导致 338 | 各种异常。这可能是因为对寄存器的操作存在 data race。 339 | \end{tcolorbox} 340 | 341 | \subsection{模拟器使用的 CPU 电路} 342 | 343 | 模拟器所使用的电路如图 \ref{fig:simulator-design-fetch} 344 | \ref{fig:simulator-design-decode} 345 | \ref{fig:simulator-design-execute} 346 | \ref{fig:simulator-design-memory} 347 | \ref{fig:simulator-design-writeback} 所示。 348 | 349 | \begin{figure} 350 | \centering 351 | \includegraphics[page=2,width=0.8\textwidth]{design.pdf} 352 | \caption{Fetch 阶段} 353 | \label{fig:simulator-design-fetch} 354 | \end{figure} 355 | \begin{figure} 356 | \centering 357 | \includegraphics[page=3,width=0.8\textwidth]{design.pdf} 358 | \caption{Decode 阶段} 359 | \label{fig:simulator-design-decode} 360 | \end{figure} 361 | \begin{figure} 362 | \centering 363 | \includegraphics[page=4,width=0.8\textwidth]{design.pdf} 364 | \caption{Execute 阶段} 365 | \label{fig:simulator-design-execute} 366 | \end{figure} 367 | \begin{figure} 368 | \centering 369 | \includegraphics[page=5,width=0.8\textwidth]{design.pdf} 370 | \caption{Memory Access 阶段} 371 | \label{fig:simulator-design-memory} 372 | \end{figure} 373 | \begin{figure} 374 | \centering 375 | \includegraphics[page=6,width=0.8\textwidth]{design.pdf} 376 | \caption{Write-back 阶段} 377 | \label{fig:simulator-design-writeback} 378 | \end{figure} 379 | 380 | \begin{aside}{自己造数据} 381 | 如果直接用现有的样例测试模拟器的正确性,似乎有些难度。因而,我通过 382 | \href{https://github.com/TheThirdOne/rars}{RARS} 编写 RISC-V 汇编, 383 | 造了几组测试数据,用于检查模拟器面对 Data Hazard 和 384 | Control Hazard 时的行为。这些测试样例都可以在 \texttt{tests/} 目录下找到。 385 | \end{aside} 386 | 387 | \subsection{分支预测} 388 | 389 | 在完成五级流水线后,我开始探究分支预测的实现。原本的实现对于分支指令,一律预测紧接的一句 390 | (Always not taken)。对于 \texttt{JAL}、\texttt{JALR} 之类的指令,也在 Execute 391 | 阶段才进行处理。在此基础上,我实现了两位自适应分支预测 (Two-level Adaptive Predictor)。 392 | 结果如表 \ref{table:branch_prediction} 所示。 393 | 394 | 分支预测在循环较多的程序中,取得了比较好的效果。 395 | 396 | \begin{table}[H] 397 | \caption{分支预测结果} 398 | \vspace{0.3cm} 399 | \label{table:branch_prediction} 400 | \centering 401 | \begin{tabular}{|l|c|c|c|c|} 402 | \hline 403 | \bfseries 数据 & \bfseries 预测前周期 & \bfseries 预测后周期 & \bfseries 周期减少率 & \bfseries 预测成功率 404 | \csvreader[head to column names]{tables/branch_prediction.csv}{} 405 | {\\\hline $\data$ & \beforecycles & \aftercycles & \cycleimprovement & \afterrate} 406 | \\\hline 407 | \end{tabular} 408 | \end{table} 409 | 410 | \begin{aside}{使用 git 管理工程} 411 | 说到分支,就不得不提 git 的分支(虽然此分支非彼分支)。在这个项目中,我通过 git 管理工程,开了许多分支。 412 | 它们包括:seq (Sequential, 串行实现)、feedforward (手动指定求值顺序的串行实现)、pipeline (并行实现)、 413 | out-of-order (乱序实现)。我时常在不同分支间切换,以修改某个版本的 bug。偶尔也会在大改项目 414 | 之后,再切换分支参考之前写过的代码。 415 | \end{aside} 416 | 417 | \section{基于 Tomasulo 算法的乱序执行}\label{out_of_order_execution} 418 | 419 | 在乱序执行中,原有的五级流水线最后只剩下两个大部分:发布指令 (Issue) 阶段和乱序执行 420 | (OoOExecute) 阶段。在乱序执行阶段中,原有 Execute 阶段的 ALU 变成了一个小单元, 421 | Memory Access 变成了 Load-Store 单元。 422 | 423 | 由于我并不知道改如何设计基于 Tomasulo 算法的硬件,在这一章节中的程序仅仅是一个 424 | 展现乱序执行的纯算法模拟。 425 | \footnote{乱序执行模拟器的实现在 out-of-order 分支。 426 | \url{https://github.com/skyzh/RISCV-Simulator/tree/out-of-order}} 427 | 428 | 在这一章中,我将依次介绍仅基于 Tomasulo 算法的实现,以及支持推测执行的实现。最后, 429 | 我将展示一个支持 Tomasulo 算法、推测执行、分支预测 (Speculation) 的 CPU 模拟器 430 | 实现。这个模拟器中,重排缓冲 (Reorder Buffer) 有 12 个空位。在 Execute 阶段, 431 | 有 3 个 Load 缓冲、3 个 Store 缓冲和 4 个 ALU 单元。 432 | 433 | \subsection{新的寄存器类设计}\label{new_register_design} 434 | 435 | \begin{verbatim} 436 | template 437 | class Register { 438 | ... // 这一部分和之前的 Register 类一致 439 | operator T() { return read(); } 440 | void operator=(T next) { write(next); } 441 | }; 442 | \end{verbatim} 443 | 444 | 在这一设计下,整个程序的可读性将大大提高。比如在 Tomasulo 算法的放操作数阶段: 445 | 446 | \begin{verbatim} 447 | void Issue::issue_rs_to_Vk(unsigned reg_id, RS *rs, RSID unit_id) { 448 | if (reg_id != 0 && session->e->should_rename_register(reg_id)) { 449 | rs->Vk = 0; 450 | rs->Qk = session->e->get_renamed_register(reg_id); 451 | } else { 452 | rs->Vk = session->rf.read(reg_id); 453 | rs->Qk = NONE; 454 | } 455 | } 456 | \end{verbatim} 457 | 458 | 在这里,\texttt{Vk}, \texttt{Qk} 都是寄存器。对 \texttt{Vk} 和 \texttt{Qk} 的 459 | 操作,只有在下一个周期才会生效。因而,这种寄存器类的设计在不影响理解的同时,增加了 460 | 代码的可读性。 461 | 462 | 当然,在某些情况下,这种写法或许会让人有些误解。比如在发出指令阶段: 463 | 464 | \begin{verbatim} 465 | pc = pc + 4 466 | \end{verbatim} 467 | 468 | 在这里,我们把当前时钟周期 \texttt{PC} 读出,通过某个硬件单元加了 $4$,并把结果接回了 469 | \texttt{PC} 寄存器上。因而,在同一个时钟周期里,不论这句话被执行几次,下一周期的 \texttt{PC} 470 | 永远是这一周期的 \texttt{PC} 加上 $4$。 471 | 472 | \subsection{仅基于 Tomasulo 算法的 CPU 设计} 473 | 474 | 在 \emph{Computer Architecture: A Quantitative Approach} 的第 3.5 节中,作者介绍了 475 | Tomasulo 算法。但是在这一章节中,缺少很多实现的细节,以至于我无法直接参照书本实现一个 CPU。 476 | 因而,我对书上的设计进行了一定的补充和修改,以便于模拟器的设计。 477 | 478 | 模拟器中 Load Buffer 和 Store Buffer 分别只有一个。只有在两者均空闲的时候,Issue 阶段才 479 | 会发出内存相关的指令。内存控制器单元每个时钟周期仅对一个 Buffer 进行操作。由此,可以保证不出现 480 | 在内存上的 Data Hazard。 481 | 482 | 模拟器碰到分支时会停止发出指令,直到下一条指令的地址被求出。\emph{CAAQA} 以某个 CPU 的浮点单元 483 | 设计为例阐述了 Tomasulo 算法,因而,书上并没有提到如何处理分支的情况。所以,我在模拟器设计 484 | 中做了这个修改。为了实现这个功能,我首先增加了 Branch 预留单元 (Reservation Station)。它的处理 485 | 和其他 ALU 预留单元一致。所以,分支指令会被发送到这个预留单元上。与此同时,我在 RISC-V 32 个寄存 486 | 器的基础上又新增了一个寄存器,称为 Branch 寄存器,专门用来保存分支处理的结果。一旦 Branch 单元空闲, 487 | 就说明分支的下一条语句已经被求出。Issue 阶段只需要读出 Branch 寄存器上,\texttt{rs1} 和 488 | \texttt{rs2} 两个寄存器的比较结果,结合当前分支指令和 \texttt{PC},就可以求出下一条语句的 489 | 地址,从而完成整个分支语句的执行过程。 490 | 491 | \subsection{RV32I 中较难处理的指令} 492 | 493 | 在实现 Tomasulo 算法的过程中,我发现 RV32I 的部分指令需要多个硬件单元或者特殊处理才能完成。 494 | 495 | \texttt{JALR} 指令需要将指令中 \texttt{rs1 + imm} 计算出来作为跳转的目标,而后将 496 | \texttt{PC + 4} 存入 \texttt{rd} 之中。在 Tomasulo 算法的实现中,我将这一指令发送 497 | 到两个计算单元。由 \texttt{BRANCH1} 计算 \texttt{rs1 + imm},并且用 \texttt{ADD1} 498 | 计算 \texttt{PC + 4}。当 Issue 阶段发现这个指令时,流水暂停,直到 \texttt{BRANCH1} 计算出 499 | 下一个指令的地址才继续发送指令。 500 | 501 | 在后文第 \ref{hardware-speculation} 章中实现推测执行时,\texttt{JALR} 指令需要更加复杂 502 | 的处理。由于使用了两个运算单元,它需要分配两个重排缓冲中的空位。两个空位中,第一个为 \texttt{ADD1} 503 | 的输出,并且在向第一个空位发送指令的时候,将指令当作 \texttt{JAL} 处理。(\texttt{JAL} 指令 504 | 在发出的时候就能确定跳转目标,在最后提交的时候就只需要把 \texttt{PC + 4} 存入 \texttt{rd}, 505 | 提交的过程和 \texttt{JALR} 拆分的第一个操作一致)。第二个操作和分支指令类似,只不过是无条件 506 | 跳转。当提交 \texttt{JALR} 的第二个操作时,就相当于分支预测失败,要清除重排缓存。 507 | 508 | 分支指令的实现也比较困难。我给分支指令分配了一个运算单元 \texttt{BRANCH1},它的功能和其他的 ALU 509 | 单元一致。当发出分支指令时,流水线停止,将分支按照跳转方法转换为 ALU 可以处理的运算,并将运算结果 510 | 存入新增的第 33 个寄存器中。当这个专用寄存器被写入、\texttt{BRANCH1} 运算单元空闲时,发出 511 | 阶段就能确定下一条指令的位置。这个过程会导致大量的时钟周期浪费在处理分支上,流水线在处理分支的时候 512 | 不能发出新的指令。这一问题也在后文第 \ref{hardware-speculation} 章中通过硬件推测解决了。 513 | 514 | 515 | \begin{figure}[h] 516 | \centering 517 | \includegraphics[width=0.7\textwidth]{debug} 518 | \caption{Debug 实况} 519 | \end{figure} 520 | 521 | \begin{aside}{调整 Debug 输出,或许会有好心情} 522 | 在实现乱序执行的过程中,我时常要观赏一个时钟周期里的所有硬件数据,来判定问题发生的地方。 523 | 这些数据包括:发出的指令、PC、寄存器的值、寄存器重命名情况、Reservation Station 的 524 | 情况。一个 Cycle 的信息可以打一整屏幕。此时,对 Debug 输出排版就显得尤为重要。 525 | 对于 32 个寄存器,我在调试信息中打出了它们的名字,并同时输出十进制值和十六进制值。 526 | 对于正在处理数据 (busy) 的单元,我会用 Emoji 符号(比如红色的圈圈) 突出显示。 527 | \end{aside} 528 | 529 | \subsection{推测执行与重排缓冲}\label{hardware-speculation} 530 | 531 | 在完成基于 Tomasulo 算法的乱序执行后,根据 \emph{Computer Architecture: A Quantitative Approach} 532 | 的介绍,我对设计进行改进,添加了重排缓冲 (Reorder Buffer),实现了推测执行 533 | (Hardware Speculation)。 534 | 535 | 在这里,我完全按照 \emph{CAAQA} 设计了整个模拟器。在其中,我也碰到了书上没有讲明白的地方, 536 | 并自己解决。 537 | 538 | 当分支预测出错的时候,我在清除 ROB 的同时,还将所有的运算单元恢复到初始状态。比如,内存单元 539 | 需要记录自己 Load 或者 Store 到哪一步。这个单元在清除 ROB 的同时需要被重置。 540 | 541 | 与此同时,我在设计中保留了 Store Buffer。我并没有按照书上实现 store 指令直接发送到 ROB 542 | 中的功能,因而保留 Store Buffer 简化了我的模拟器设计。 543 | 544 | 在完成分支预测时,我在 ROB 中新增了一个寄存器,用来存储当前分支指令在 Issue 阶段预测的下一条 545 | 指令的地址。由此,在提交指令的时候,模拟器才能判断分支预测是否正确,并请求清空 ROB。 546 | 547 | 至此,乱序执行的 RISC-V 模拟器基本完成。 548 | 549 | \end{document} 550 | --------------------------------------------------------------------------------