├── bit_tools.cc ├── Windows ├── pdcurses.lib ├── pdcurses64.lib ├── m99_riscv_emulator.sln ├── m99_riscv_emulator.vcxproj.filters └── m99_riscv_emulator.vcxproj ├── .gitignore ├── tests ├── load_assembler_test.h ├── load_assembler.h ├── rvuc-tests.sh ├── rv32um-v-tests.sh ├── rv32um-p-tests.sh ├── rv32ua-p-tests.sh ├── rv32ua-v-tests.sh ├── rv64um-p-tests.sh ├── rv64um-v-tests.sh ├── rv64ua-p-tests.sh ├── rv64ua-v-tests.sh ├── rv32ui-p-tests.sh ├── rv32ui-v-tests.sh ├── load_assembler.cc ├── rv64ui-v-tests.sh ├── rv64ui-p-tests.sh ├── pte_test.cpp ├── memory_wrapper_test.cpp ├── assembler.h └── assembler.cc ├── Disassembler.h ├── riscv_cpu_common.h ├── ScreenEmulation.h ├── RISCV_Emulator.h ├── Mmu.h ├── ScreenEmulation.cpp ├── license.txt ├── system_call_emulator.h ├── memory_wrapper.h ├── instruction_encdec.h ├── Makefile ├── CMakeLists.txt ├── pte.h ├── memory_wrapper.cpp ├── bit_tools.h ├── README.md ├── PeripheralEmulator.h ├── pte.cpp ├── instruction_encdec.cc ├── Mmu.cpp ├── system_call_emulator.cpp ├── RISCV_cpu.h ├── PeripheralEmulator.cpp ├── Disassembler.cpp └── RISCV_Emulator.cc /bit_tools.cc: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Windows/pdcurses.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moizumi99/m99_riscv_emulator/HEAD/Windows/pdcurses.lib -------------------------------------------------------------------------------- /Windows/pdcurses64.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moizumi99/m99_riscv_emulator/HEAD/Windows/pdcurses64.lib -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | /*.o 3 | cpu_test 4 | load_assembler 5 | load_assembler_test 6 | RISCV_Emulator 7 | /cmake-build-debug/ 8 | .clang-format 9 | tests/*.o 10 | -------------------------------------------------------------------------------- /tests/load_assembler_test.h: -------------------------------------------------------------------------------- 1 | #ifndef LOAD_ASSEMBLER_TEST_H 2 | #define LOAD_ASSEMBLER_TEST_H 3 | 4 | namespace load_assembler_test { 5 | 6 | bool RunAllTests(); 7 | 8 | } // load_assembler_test 9 | 10 | #endif // LOAD_ASSEMBLER_TEST_H -------------------------------------------------------------------------------- /Disassembler.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 5/16/20. 3 | // 4 | 5 | #ifndef ASSEMBLER_TEST_DISASSEMBLER_H 6 | #define ASSEMBLER_TEST_DISASSEMBLER_H 7 | 8 | #include "RISCV_cpu.h" 9 | #include 10 | 11 | namespace RISCV_EMULATOR { 12 | 13 | std::string Disassemble(uint32_t ir, int mxl = 1); 14 | 15 | } 16 | 17 | #endif //ASSEMBLER_TEST_DISASSEMBLER_H 18 | -------------------------------------------------------------------------------- /riscv_cpu_common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 5/10/20. 3 | // 4 | 5 | #ifndef RISCV_CPU_RISCV_CPU_COMMON_H 6 | #define RISCV_CPU_RISCV_CPU_COMMON_H 7 | 8 | namespace RISCV_EMULATOR { 9 | 10 | enum class PrivilegeMode { 11 | USER_MODE = 0, 12 | SUPERVISOR_MODE = 1, 13 | MACHINE_MODE = 3 14 | }; 15 | } // namespace RISCV_EMULATOR 16 | 17 | #endif //RISCV_CPU_RISCV_CPU_COMMON_H 18 | -------------------------------------------------------------------------------- /tests/load_assembler.h: -------------------------------------------------------------------------------- 1 | #ifndef LOAD_ASSEMBLER_H 2 | #define LOAD_ASSEMBLER_H 3 | 4 | #include 5 | #include "memory_wrapper.h" 6 | #include 7 | 8 | using namespace RISCV_EMULATOR; 9 | 10 | namespace CPU_TEST { 11 | 12 | uint64_t LoadAssemblerSum(MemoryWrapper &mem, uint64_t address); 13 | 14 | uint64_t LoadAssemblerSort(MemoryWrapper &mem, uint64_t address); 15 | 16 | } // namespace CPU_TEST { 17 | 18 | #endif // CPU_TEST -------------------------------------------------------------------------------- /ScreenEmulation.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 8/29/20. 3 | // 4 | 5 | #ifndef ASSEMBLER_TEST_SCREENEMULATION_H 6 | #define ASSEMBLER_TEST_SCREENEMULATION_H 7 | 8 | #include "ncurses.h" 9 | 10 | class ScreenEmulation { 11 | public: 12 | ScreenEmulation(); 13 | ~ScreenEmulation(); 14 | 15 | bool CheckInput(); 16 | int GetKeyValue(); 17 | void putchar(int c); 18 | 19 | int counter_ = 0; 20 | static constexpr int kThreshold = 1000; 21 | 22 | private: 23 | int key_value_; 24 | bool key_valid_ = false; 25 | void ScreenInit(); 26 | void ScreenExit(); 27 | }; 28 | 29 | #endif // ASSEMBLER_TEST_SCREENEMULATION_H 30 | -------------------------------------------------------------------------------- /tests/rvuc-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test_list=( 3 | "target/share/riscv-tests/isa/rv32uc-p-rvc" 4 | "target/share/riscv-tests/isa/rv32uc-v-rvc" 5 | "target/share/riscv-tests/isa/rv64uc-p-rvc -64" 6 | "target/share/riscv-tests/isa/rv64uc-v-rvc -64" 7 | ) 8 | emulater="./RISCV_Emulator" 9 | flag="-h" 10 | for test in "${test_list[@]}"; do 11 | echo -n "Run ${test} test: " 12 | $(eval "${emulater} ${flag} ${test} 2> /dev/null") 13 | exit_status=$? 14 | if [[ ${exit_status} -eq 0 ]]; then 15 | echo "Pass" 16 | else 17 | echo "Fail" 18 | exit ${exit_status} 19 | fi 20 | done 21 | echo "All tests passed." 22 | exit 0 23 | -------------------------------------------------------------------------------- /tests/rv32um-v-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test_list=( 3 | "target/share/riscv-tests/isa/rv32um-v-div" 4 | "target/share/riscv-tests/isa/rv32um-v-divu" 5 | "target/share/riscv-tests/isa/rv32um-v-mul" 6 | "target/share/riscv-tests/isa/rv32um-v-mulh" 7 | "target/share/riscv-tests/isa/rv32um-v-mulhsu" 8 | "target/share/riscv-tests/isa/rv32um-v-mulhu" 9 | "target/share/riscv-tests/isa/rv32um-v-rem" 10 | "target/share/riscv-tests/isa/rv32um-v-remu" 11 | ) 12 | emulater="./RISCV_Emulator" 13 | flag="-h" 14 | for test in "${test_list[@]}"; do 15 | echo -n "Run ${test} test: " 16 | $(eval "${emulater} ${flag} ${test} 2> /dev/null") 17 | exit_status=$? 18 | if [[ ${exit_status} -eq 0 ]]; then 19 | echo "Pass" 20 | else 21 | echo "Fail" 22 | exit ${exit_status} 23 | fi 24 | done 25 | echo "All tests passed." 26 | exit 0 27 | -------------------------------------------------------------------------------- /tests/rv32um-p-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test_list=( 3 | "target/share/riscv-tests/isa/rv32um-p-div" 4 | "target/share/riscv-tests/isa/rv32um-p-divu" 5 | "target/share/riscv-tests/isa/rv32um-p-mul" 6 | "target/share/riscv-tests/isa/rv32um-p-mulh" 7 | "target/share/riscv-tests/isa/rv32um-p-mulhsu" 8 | "target/share/riscv-tests/isa/rv32um-p-mulhu" 9 | "target/share/riscv-tests/isa/rv32um-p-rem" 10 | "target/share/riscv-tests/isa/rv32um-p-remu" 11 | ) 12 | 13 | emulater="./RISCV_Emulator" 14 | flag="-h" 15 | for test in "${test_list[@]}"; do 16 | echo -n "Run ${test} test: " 17 | $(eval "${emulater} ${flag} ${test} 2> /dev/null") 18 | exit_status=$? 19 | if [[ ${exit_status} -eq 0 ]]; then 20 | echo "Pass" 21 | else 22 | echo "Fail" 23 | exit ${exit_status} 24 | fi 25 | done 26 | echo "All tests passed." 27 | exit 0 28 | -------------------------------------------------------------------------------- /tests/rv32ua-p-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test_list=( 3 | "target/share/riscv-tests/isa/rv32ua-p-amoadd_w" 4 | "target/share/riscv-tests/isa/rv32ua-p-amoand_w" 5 | "target/share/riscv-tests/isa/rv32ua-p-amomaxu_w" 6 | "target/share/riscv-tests/isa/rv32ua-p-amomax_w" 7 | "target/share/riscv-tests/isa/rv32ua-p-amominu_w" 8 | "target/share/riscv-tests/isa/rv32ua-p-amomin_w" 9 | "target/share/riscv-tests/isa/rv32ua-p-amoor_w" 10 | "target/share/riscv-tests/isa/rv32ua-p-amoswap_w" 11 | "target/share/riscv-tests/isa/rv32ua-p-amoxor_w" 12 | # "target/share/riscv-tests/isa/rv32ua-p-lrsc" 13 | ) 14 | emulater="./RISCV_Emulator" 15 | flag="-h" 16 | for test in "${test_list[@]}"; do 17 | echo -n "Run ${test} test: " 18 | $(eval "${emulater} ${flag} ${test} 2> /dev/null") 19 | exit_status=$? 20 | if [[ ${exit_status} -eq 0 ]]; then 21 | echo "Pass" 22 | else 23 | echo "Fail" 24 | exit ${exit_status} 25 | fi 26 | done 27 | echo "All tests passed." 28 | exit 0 29 | -------------------------------------------------------------------------------- /tests/rv32ua-v-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test_list=( 3 | "target/share/riscv-tests/isa/rv32ua-v-amoadd_w" 4 | "target/share/riscv-tests/isa/rv32ua-v-amoand_w" 5 | "target/share/riscv-tests/isa/rv32ua-v-amomaxu_w" 6 | "target/share/riscv-tests/isa/rv32ua-v-amomax_w" 7 | "target/share/riscv-tests/isa/rv32ua-v-amominu_w" 8 | "target/share/riscv-tests/isa/rv32ua-v-amomin_w" 9 | "target/share/riscv-tests/isa/rv32ua-v-amoor_w" 10 | "target/share/riscv-tests/isa/rv32ua-v-amoswap_w" 11 | "target/share/riscv-tests/isa/rv32ua-v-amoxor_w" 12 | # "target/share/riscv-tests/isa/rv32ua-v-lrsc" 13 | ) 14 | emulater="./RISCV_Emulator" 15 | flag="-h" 16 | for test in "${test_list[@]}"; do 17 | echo -n "Run ${test} test: " 18 | $(eval "${emulater} ${flag} ${test} 2> /dev/null") 19 | exit_status=$? 20 | if [[ ${exit_status} -eq 0 ]]; then 21 | echo "Pass" 22 | else 23 | echo "Fail" 24 | exit ${exit_status} 25 | fi 26 | done 27 | echo "All tests passed." 28 | exit 0 29 | -------------------------------------------------------------------------------- /tests/rv64um-p-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test_list=( 3 | "target/share/riscv-tests/isa/rv64um-p-div" 4 | "target/share/riscv-tests/isa/rv64um-p-divu" 5 | "target/share/riscv-tests/isa/rv64um-p-divuw" 6 | "target/share/riscv-tests/isa/rv64um-p-divw" 7 | "target/share/riscv-tests/isa/rv64um-p-mul" 8 | "target/share/riscv-tests/isa/rv64um-p-mulh" 9 | "target/share/riscv-tests/isa/rv64um-p-mulhsu" 10 | "target/share/riscv-tests/isa/rv64um-p-mulhu" 11 | "target/share/riscv-tests/isa/rv64um-p-mulw" 12 | "target/share/riscv-tests/isa/rv64um-p-rem" 13 | "target/share/riscv-tests/isa/rv64um-p-remu" 14 | "target/share/riscv-tests/isa/rv64um-p-remuw" 15 | "target/share/riscv-tests/isa/rv64um-p-remw" 16 | ) 17 | emulater="./RISCV_Emulator" 18 | flag="-h -64" 19 | for test in "${test_list[@]}"; do 20 | echo -n "Run ${test} test: " 21 | $(eval "${emulater} ${flag} ${test} 2> /dev/null") 22 | exit_status=$? 23 | if [[ ${exit_status} -eq 0 ]]; then 24 | echo "Pass" 25 | else 26 | echo "Fail" 27 | exit ${exit_status} 28 | fi 29 | done 30 | echo "All tests passed." 31 | exit 0 32 | -------------------------------------------------------------------------------- /tests/rv64um-v-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test_list=( 3 | "target/share/riscv-tests/isa/rv64um-v-div" 4 | "target/share/riscv-tests/isa/rv64um-v-divu" 5 | "target/share/riscv-tests/isa/rv64um-v-divuw" 6 | "target/share/riscv-tests/isa/rv64um-v-divw" 7 | "target/share/riscv-tests/isa/rv64um-v-mul" 8 | "target/share/riscv-tests/isa/rv64um-v-mulh" 9 | "target/share/riscv-tests/isa/rv64um-v-mulhsu" 10 | "target/share/riscv-tests/isa/rv64um-v-mulhu" 11 | "target/share/riscv-tests/isa/rv64um-v-mulw" 12 | "target/share/riscv-tests/isa/rv64um-v-rem" 13 | "target/share/riscv-tests/isa/rv64um-v-remu" 14 | "target/share/riscv-tests/isa/rv64um-v-remuw" 15 | "target/share/riscv-tests/isa/rv64um-v-remw" 16 | ) 17 | emulater="./RISCV_Emulator" 18 | flag="-h -64" 19 | for test in "${test_list[@]}"; do 20 | echo -n "Run ${test} test: " 21 | $(eval "${emulater} ${flag} ${test} 2> /dev/null") 22 | exit_status=$? 23 | if [[ ${exit_status} -eq 0 ]]; then 24 | echo "Pass" 25 | else 26 | echo "Fail" 27 | exit ${exit_status} 28 | fi 29 | done 30 | echo "All tests passed." 31 | exit 0 32 | -------------------------------------------------------------------------------- /RISCV_Emulator.h: -------------------------------------------------------------------------------- 1 | #ifndef RISCV_EMULATOR_H 2 | #define RISCV_EMULATOR_H 3 | 4 | #include 5 | 6 | namespace RISCV_EMULATOR { 7 | /* 8 | * Memory map 9 | * 0x00000000 - 0x00000FFF : Reserved 10 | * 0x00001000 - 0x0FFFFFFF : Text 11 | * 0x10000000 - 0x3FFFFFFF : Static Data 12 | * 0x40000000 - 0x7FFFFFFF : Stack and Heap 13 | * 0x80000000 - 0xBFFFFFFF : Text (optional) 14 | * 0xC0000000 - 0xFFFFFFFF : System Emulation (e.g. MMU) 15 | */ 16 | 17 | constexpr int kUnitSize = 1024 * 1024; // 1 MB 18 | constexpr int kMaxBinarySize = 1024 * 1024 * 1024; // 1 GB 19 | constexpr uint32_t kTop = 0x80000000; 20 | constexpr uint32_t kBottom = 0x40000000; 21 | 22 | /* 23 | * 32 bit MMU table 24 | * 0xC0000000 - 0xC0100000 : Level 0 25 | * 0xC0100000 - 0xC0101000 : Level 1 26 | */ 27 | constexpr uint32_t k32BitMmuLevel1 = 0xC0000000; // Size = 2 ^ 10 x 4B. 28 | constexpr uint32_t k32BitMmuLevel0 = k32BitMmuLevel1 + (1 << 10) * 4; 29 | 30 | constexpr uint64_t k64BitMmuLevel2 = 0xC0000000; // Size = 2 ^ 9 x 8B. Only 4 is valid. 31 | constexpr uint64_t k64BitMmuLevel1 = 32 | k64BitMmuLevel2 + (1 << 9) * 8; // Size = 4 x 2 ^ 9 x 8B. 33 | constexpr uint64_t k64BitMmuLevel0 = k64BitMmuLevel1 + 4 * (1 << 9) * 8; 34 | 35 | } // RISCV_EMULATOR 36 | 37 | #endif // RISCV_EMULATOR_H -------------------------------------------------------------------------------- /Mmu.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 5/10/20. 3 | // 4 | 5 | #ifndef ASSEMBLER_TEST_MMU_H 6 | #define ASSEMBLER_TEST_MMU_H 7 | 8 | #include 9 | #include "memory_wrapper.h" 10 | #include "riscv_cpu_common.h" 11 | 12 | namespace RISCV_EMULATOR { 13 | 14 | class Mmu { 15 | public: 16 | void SetMemory(std::shared_ptr memory); 17 | void SetMxl(const int mxl) { mxl_ = mxl; } 18 | void SetPrivilege(const PrivilegeMode privilege); 19 | uint64_t VirtualToPhysical(uint64_t virtual_address, uint64_t satp, bool write_access = false); 20 | bool GetPageFault() { return page_fault_; } 21 | uint64_t GetFaultingAddress() { return faulting_address_; } 22 | 23 | private: 24 | uint64_t VirtualToPhysical32(uint64_t virtual_address, uint64_t satp, bool write_access = false); 25 | 26 | uint64_t VirtualToPhysical64(uint64_t virtual_address, uint64_t satp, bool write_access = false); 27 | 28 | std::shared_ptr memory_; 29 | int mxl_ = 0; 30 | bool page_fault_ = false; 31 | uint64_t faulting_address_ = 0; 32 | PrivilegeMode privilege_ = PrivilegeMode::MACHINE_MODE; 33 | static constexpr int kPageSize = 1 << 12; // PAGESIZE is 2^12. 34 | static constexpr int kMmuLevels = 2; 35 | }; 36 | 37 | } // namespace RISCV_EMULATOR 38 | 39 | #endif // ASSEMBLER_TEST_MMU_H 40 | -------------------------------------------------------------------------------- /ScreenEmulation.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 8/29/20. 3 | // 4 | 5 | #include 6 | #include "ScreenEmulation.h" 7 | 8 | ScreenEmulation::ScreenEmulation() { 9 | ScreenInit(); 10 | } 11 | 12 | ScreenEmulation::~ScreenEmulation() { 13 | ScreenExit(); 14 | } 15 | 16 | void ScreenEmulation::ScreenInit() { 17 | // ncurse initialization. 18 | initscr(); 19 | // Capture key stroke without storing them to buffer. 20 | // cbreak(); 21 | raw(); // raw() will make Ctrl-C captured. Test enough before using this. 22 | noecho(); 23 | // Capture special keys. 24 | keypad(stdscr, TRUE); 25 | // Allow scrolling. 26 | scrollok(stdscr, TRUE); 27 | // Don't wait for key hit. 28 | nodelay(stdscr, TRUE); 29 | } 30 | 31 | void ScreenEmulation::ScreenExit() { 32 | endwin(); 33 | } 34 | 35 | bool ScreenEmulation::CheckInput() { 36 | if (++counter_ < kThreshold) { 37 | return key_valid_; 38 | } else { 39 | counter_ = 0; 40 | } 41 | if (key_valid_) { 42 | return key_valid_; 43 | } 44 | int c = getch(); 45 | if (c == ERR) { 46 | return key_valid_; 47 | } 48 | key_value_ = c; 49 | key_valid_ = true; 50 | return true; 51 | } 52 | 53 | int ScreenEmulation::GetKeyValue() { 54 | key_valid_ = false; 55 | return key_value_; 56 | } 57 | 58 | void ScreenEmulation::putchar(int c) { 59 | addch(c); 60 | wrefresh(stdscr); 61 | } 62 | -------------------------------------------------------------------------------- /tests/rv64ua-p-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test_list=( 3 | "target/share/riscv-tests/isa/rv64ua-p-amoadd_d" 4 | "target/share/riscv-tests/isa/rv64ua-p-amoadd_w" 5 | "target/share/riscv-tests/isa/rv64ua-p-amoand_d" 6 | "target/share/riscv-tests/isa/rv64ua-p-amoand_w" 7 | "target/share/riscv-tests/isa/rv64ua-p-amomax_d" 8 | "target/share/riscv-tests/isa/rv64ua-p-amomax_w" 9 | "target/share/riscv-tests/isa/rv64ua-p-amomaxu_d" 10 | "target/share/riscv-tests/isa/rv64ua-p-amomaxu_w" 11 | "target/share/riscv-tests/isa/rv64ua-p-amomin_d" 12 | "target/share/riscv-tests/isa/rv64ua-p-amomin_w" 13 | "target/share/riscv-tests/isa/rv64ua-p-amominu_d" 14 | "target/share/riscv-tests/isa/rv64ua-p-amominu_w" 15 | "target/share/riscv-tests/isa/rv64ua-p-amoor_d" 16 | "target/share/riscv-tests/isa/rv64ua-p-amoor_w" 17 | "target/share/riscv-tests/isa/rv64ua-p-amoswap_d" 18 | "target/share/riscv-tests/isa/rv64ua-p-amoswap_w" 19 | "target/share/riscv-tests/isa/rv64ua-p-amoxor_d" 20 | "target/share/riscv-tests/isa/rv64ua-p-amoxor_w" 21 | # "target/share/riscv-tests/isa/rv64ua-p-a_lrsc" 22 | ) 23 | emulater="./RISCV_Emulator" 24 | flag="-h -64" 25 | for test in "${test_list[@]}"; do 26 | echo -n "Run ${test} test: " 27 | $(eval "${emulater} ${flag} ${test} 2> /dev/null") 28 | exit_status=$? 29 | if [[ ${exit_status} -eq 0 ]]; then 30 | echo "Pass" 31 | else 32 | echo "Fail" 33 | exit ${exit_status} 34 | fi 35 | done 36 | echo "All tests passed." 37 | exit 0 38 | -------------------------------------------------------------------------------- /tests/rv64ua-v-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test_list=( 3 | "target/share/riscv-tests/isa/rv64ua-v-amoadd_d" 4 | "target/share/riscv-tests/isa/rv64ua-v-amoadd_w" 5 | "target/share/riscv-tests/isa/rv64ua-v-amoand_d" 6 | "target/share/riscv-tests/isa/rv64ua-v-amoand_w" 7 | "target/share/riscv-tests/isa/rv64ua-v-amomax_d" 8 | "target/share/riscv-tests/isa/rv64ua-v-amomax_w" 9 | "target/share/riscv-tests/isa/rv64ua-v-amomaxu_d" 10 | "target/share/riscv-tests/isa/rv64ua-v-amomaxu_w" 11 | "target/share/riscv-tests/isa/rv64ua-v-amomin_d" 12 | "target/share/riscv-tests/isa/rv64ua-v-amomin_w" 13 | "target/share/riscv-tests/isa/rv64ua-v-amominu_d" 14 | "target/share/riscv-tests/isa/rv64ua-v-amominu_w" 15 | "target/share/riscv-tests/isa/rv64ua-v-amoor_d" 16 | "target/share/riscv-tests/isa/rv64ua-v-amoor_w" 17 | "target/share/riscv-tests/isa/rv64ua-v-amoswap_d" 18 | "target/share/riscv-tests/isa/rv64ua-v-amoswap_w" 19 | "target/share/riscv-tests/isa/rv64ua-v-amoxor_d" 20 | "target/share/riscv-tests/isa/rv64ua-v-amoxor_w" 21 | # "target/share/riscv-tests/isa/rv64ua-v-lrsc" 22 | ) 23 | emulater="./RISCV_Emulator" 24 | flag="-h -64" 25 | for test in "${test_list[@]}"; do 26 | echo -n "Run ${test} test: " 27 | $(eval "${emulater} ${flag} ${test} 2> /dev/null") 28 | exit_status=$? 29 | if [[ ${exit_status} -eq 0 ]]; then 30 | echo "Pass" 31 | else 32 | echo "Fail" 33 | exit ${exit_status} 34 | fi 35 | done 36 | echo "All tests passed." 37 | exit 0 38 | -------------------------------------------------------------------------------- /Windows/m99_riscv_emulator.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.329 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "m99_riscv_emulator", "m99_riscv_emulator.vcxproj", "{A2D52198-117C-4574-882D-EB1B5D221AD4}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {A2D52198-117C-4574-882D-EB1B5D221AD4}.Debug|x64.ActiveCfg = Debug|x64 17 | {A2D52198-117C-4574-882D-EB1B5D221AD4}.Debug|x64.Build.0 = Debug|x64 18 | {A2D52198-117C-4574-882D-EB1B5D221AD4}.Debug|x86.ActiveCfg = Debug|Win32 19 | {A2D52198-117C-4574-882D-EB1B5D221AD4}.Debug|x86.Build.0 = Debug|Win32 20 | {A2D52198-117C-4574-882D-EB1B5D221AD4}.Release|x64.ActiveCfg = Release|x64 21 | {A2D52198-117C-4574-882D-EB1B5D221AD4}.Release|x64.Build.0 = Release|x64 22 | {A2D52198-117C-4574-882D-EB1B5D221AD4}.Release|x86.ActiveCfg = Release|Win32 23 | {A2D52198-117C-4574-882D-EB1B5D221AD4}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {AF4C5D76-4AEF-4AA8-884C-48A271403F8B} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The following license clause applies to all the files in this directory 2 | except to the ones noted specifically. When a different term is applied, 3 | appropriate license term is included in the folder or the file. 4 | 5 | ----------------------------------------------------------------------- 6 | This is free and unencumbered software released into the public domain. 7 | 8 | Anyone is free to copy, modify, publish, use, compile, sell, or 9 | distribute this software, either in source code form or as a compiled 10 | binary, for any purpose, commercial or non-commercial, and by any 11 | means. 12 | 13 | In jurisdictions that recognize copyright laws, the author or authors 14 | of this software dedicate any and all copyright interest in the 15 | software to the public domain. We make this dedication for the benefit 16 | of the public at large and to the detriment of our heirs and 17 | successors. We intend this dedication to be an overt act of 18 | relinquishment in perpetuity of all present and future rights to this 19 | software under copyright law. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 25 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 26 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | OTHER DEALINGS IN THE SOFTWARE. 28 | ----------------------------------------------------------------------- -------------------------------------------------------------------------------- /system_call_emulator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 2/1/20. 3 | // 4 | 5 | #ifndef ASSEMBLER_TEST_SYSTEM_CALL_EMULATOR_H 6 | #define ASSEMBLER_TEST_SYSTEM_CALL_EMULATOR_H 7 | 8 | #include 9 | #include "memory_wrapper.h" 10 | 11 | namespace RISCV_EMULATOR { 12 | 13 | struct Riscv32NewlibStat { 14 | uint16_t st_dev; // 0 15 | uint16_t st_ino; // 2 16 | uint32_t st_mode; // 4 17 | uint16_t st_nlink; // 8 18 | uint16_t st_uid; // 10 19 | uint16_t st_gid; // 12 20 | uint16_t st_rdev; // 14 21 | int32_t st_size; // 16 22 | int32_t st_spare0; // 20 23 | int64_t st_atim; // 24 24 | int32_t st_spare1; // 32 25 | int32_t st_spare1_2; // 36 26 | int64_t st_mtim; // 40 27 | int32_t st_spare2; // 48 28 | int32_t st_spare2_2; // 52 29 | int64_t st_ctim; // 56 30 | int32_t st_spare3; // 60 31 | int32_t st_blksize; // 68 32 | int32_t st_blocks; // 72 33 | int32_t st_spare4[2]; // 76 34 | }; 35 | 36 | void ShowGuestStat(const Riscv32NewlibStat &guest_stat); 37 | 38 | void ShowHostStat(const struct stat &host_stat); 39 | 40 | void ConvGuestStatToHostStat(const Riscv32NewlibStat &guest_stat, struct stat *host_stat); 41 | 42 | void ConvHostStatToGuestStat(const struct stat &host_stat, Riscv32NewlibStat *guest_stat); 43 | 44 | constexpr size_t kMaxBufferSize = UINT16_MAX; 45 | 46 | size_t MemoryWrapperStrlen(const MemoryWrapper &mem, size_t address, size_t max = kMaxBufferSize); 47 | 48 | char *MemoryWrapperCopy(const MemoryWrapper &mem, size_t address, size_t length, char *dst); 49 | 50 | std::pair SystemCallEmulation(std::shared_ptr memory, uint64_t *reg, const uint64_t top, 51 | uint64_t *break_address, bool debug = false); 52 | 53 | } // namespace RISCV_EMULATOR 54 | 55 | #endif //ASSEMBLER_TEST_SYSTEM_CALL_EMULATOR_H 56 | -------------------------------------------------------------------------------- /Windows/m99_riscv_emulator.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | -------------------------------------------------------------------------------- /memory_wrapper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 1/19/20. 3 | // 4 | 5 | #ifndef MEMORY_WRAPPER_H 6 | #define MEMORY_WRAPPER_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace RISCV_EMULATOR { 14 | 15 | class MemoryWrapperIterator; // Forward declaration. 16 | class MemoryWrapper; 17 | 18 | constexpr int GenerateBitMask(const int bits) { 19 | int mask = 0; 20 | for (int i = 0; i < bits; i++) { 21 | mask = (mask << 1) | 0b1; 22 | } 23 | return mask; 24 | } 25 | 26 | class MemoryWrapper { 27 | static constexpr int kTotalBits = 32; 28 | static constexpr int kOffsetBits = 20; 29 | static constexpr int kWordBits = 2; 30 | static constexpr int kOffsetMask = GenerateBitMask(kOffsetBits); 31 | static constexpr int kEntryBits = kTotalBits - kOffsetBits; 32 | static constexpr int kEntryMask = GenerateBitMask(kEntryBits); 33 | static constexpr int kMapEntry = 1 << kEntryBits; 34 | static constexpr size_t kMaxAddress = ((1ull << kTotalBits) - 1); 35 | public: 36 | MemoryWrapper(); 37 | 38 | const uint8_t ReadByte(size_t i) const; 39 | 40 | const uint16_t Read16(size_t i) const; 41 | 42 | const uint32_t Read32(size_t i) const; 43 | 44 | const uint64_t Read64(size_t i) const; 45 | 46 | void WriteByte(size_t i, uint8_t data); 47 | 48 | void Write16(size_t i, uint16_t value); 49 | 50 | void Write32(size_t i, uint32_t value); 51 | 52 | void Write64(size_t i, uint64_t value); 53 | 54 | MemoryWrapperIterator begin(); 55 | 56 | MemoryWrapperIterator end(); 57 | 58 | bool operator==(MemoryWrapper &r); 59 | 60 | bool operator!=(MemoryWrapper &r); 61 | 62 | private: 63 | inline bool CheckRange(int entry) const { 64 | if (entry < 0 || entry >= kMapEntry) { 65 | throw std::out_of_range("Memory wrapper size out of range."); 66 | } 67 | return assigned_[entry]; 68 | } 69 | 70 | std::array, kMapEntry> mapping_; 71 | std::array assigned_; 72 | }; 73 | 74 | 75 | } // namespace RISCV_EMULATOR 76 | 77 | #endif //MEMORY_WRAPPER_H 78 | -------------------------------------------------------------------------------- /instruction_encdec.h: -------------------------------------------------------------------------------- 1 | #ifndef INSTRUCTION_ENCDEC_H 2 | #define INSTRUCTION_ENCDEC_H 3 | 4 | #include 5 | 6 | namespace RISCV_EMULATOR { 7 | 8 | // TODO: In the current Emulator code, these classes are not being used. Shall they be removed from the test code too? 9 | class BitField { 10 | public: 11 | virtual uint32_t GetValue() = 0; 12 | virtual void SetValue(uint32_t value) = 0; 13 | uint8_t opcode: 7; 14 | uint8_t rd: 5; 15 | uint8_t rs2: 5; 16 | uint8_t rs1: 5; 17 | uint8_t funct3: 3; 18 | }; 19 | 20 | class RType : public BitField { 21 | public: 22 | uint8_t funct7: 7; 23 | uint32_t GetValue() override; 24 | void SetValue(uint32_t value) override; 25 | }; 26 | 27 | class IType : public BitField { 28 | public: 29 | int16_t imm12: 12; 30 | uint32_t GetValue() override ; 31 | void SetValue(uint32_t value) override; 32 | }; 33 | 34 | class SType : public BitField { 35 | public: 36 | int16_t imm12: 12; 37 | uint32_t GetValue() override ; 38 | void SetValue(uint32_t value) override; 39 | }; 40 | 41 | class BType : public BitField { 42 | public: 43 | int16_t imm13: 13; 44 | uint32_t GetValue() override ; 45 | void SetValue(uint32_t value) override; 46 | }; 47 | 48 | class JType : public BitField { 49 | public: 50 | int32_t imm21: 21; 51 | uint32_t GetValue() override ; 52 | void SetValue(uint32_t value) override; 53 | }; 54 | 55 | class UType : public BitField { 56 | public: 57 | uint32_t imm20: 24; 58 | uint32_t GetValue() override ; 59 | void SetValue(uint32_t value) override; 60 | }; 61 | 62 | int32_t GetImm(uint32_t ir); 63 | 64 | uint32_t GetOpcode(uint32_t ir); 65 | 66 | uint32_t GetRd(uint32_t ir); 67 | 68 | uint32_t GetRs1(uint32_t ir); 69 | 70 | uint32_t GetRs2(uint32_t ir); 71 | 72 | int32_t GetImm12(uint32_t ir); 73 | 74 | int32_t GetCsr(uint32_t ir); 75 | 76 | int32_t GetImm13(uint32_t ir); 77 | 78 | int32_t GetImm21(uint32_t ir); 79 | 80 | uint32_t GetImm20(uint32_t ir); 81 | 82 | int32_t GetStypeImm12(uint32_t ir); 83 | 84 | uint32_t GetShamt(uint32_t ir); 85 | 86 | } // namespace // RISCV_EMULATOR 87 | 88 | #endif // INSTRUCTION_ENCDEC_H -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | CPPFLAGS = -Wall -O3 -I. 3 | TARGET = RISCV_Emulator 4 | CPU_OBJS = RISCV_cpu.o bit_tools.o \ 5 | instruction_encdec.o memory_wrapper.o system_call_emulator.o pte.o Mmu.o \ 6 | Disassembler.o PeripheralEmulator.o ScreenEmulation.o 7 | OBJS = RISCV_Emulator.o $(CPU_OBJS) 8 | TEST_DIR = tests 9 | TEST_TARGETS = $(TEST_DIR)/cpu_test $(TEST_DIR)/pte_test 10 | WRAPPER_TESTS = $(TEST_DIR)/memory_wrapper_test $(TEST_DIR)/load_assembler_test 11 | 12 | .PHONY: all 13 | all: $(TARGET) $(TEST_TARGETS) 14 | 15 | $(TARGET): $(OBJS) 16 | $(CXX) -o $(TARGET) $(OBJS) -lncurses 17 | 18 | $(TEST_DIR)/load_assembler_test: $(TEST_DIR)/load_assembler.o $(TEST_DIR)/assembler.o \ 19 | $(TEST_DIR)/load_assembler_test.o bit_tools.o instruction_encdec.o memory_wrapper.o 20 | $(CXX) $(CPPFLAG) -o $@ $^ 21 | 22 | $(TEST_DIR)/cpu_test: $(TEST_DIR)/cpu_test.o $(CPU_OBJS) $(TEST_DIR)/load_assembler.o \ 23 | $(TEST_DIR)/assembler.o 24 | $(CXX) $(CPPFLAG) -o $@ $^ -lncurses 25 | 26 | $(TEST_DIR)/memory_wrapper_test: memory_wrapper.o $(TEST_DIR)/memory_wrapper_test.o 27 | $(CXX) $(CPPFLAG) -o $@ $^ 28 | 29 | $(TEST_DIR)/pte_test: pte.o $(TEST_DIR)/pte_test.o bit_tools.o 30 | $(CXX) $(CPPFLAG) -o $@ $^ 31 | 32 | .cpp.o: 33 | $(CXX) -c $< -o $@ $(CPPFLAGS) 34 | 35 | .PHONY: core_test 36 | core_test: $(TEST_TARGETS) $(TARGET) 37 | $(TEST_DIR)/cpu_test 38 | $(TEST_DIR)/pte_test 39 | $(TEST_DIR)/rv32ui-p-tests.sh 40 | $(TEST_DIR)/rv32ui-v-tests.sh 41 | $(TEST_DIR)/rv64ui-p-tests.sh 42 | $(TEST_DIR)/rv64ui-v-tests.sh 43 | $(TEST_DIR)/rv32um-p-tests.sh 44 | $(TEST_DIR)/rv32um-v-tests.sh 45 | $(TEST_DIR)/rv64um-p-tests.sh 46 | $(TEST_DIR)/rv64um-v-tests.sh 47 | $(TEST_DIR)/rv32ua-p-tests.sh 48 | $(TEST_DIR)/rv32ua-v-tests.sh 49 | $(TEST_DIR)/rv64ua-p-tests.sh 50 | $(TEST_DIR)/rv64ua-v-tests.sh 51 | $(TEST_DIR)/rvuc-tests.sh 52 | 53 | .PHONY: wrapper_test 54 | wrapper_test: $(WRAPPER_TESTS) 55 | $(TEST_DIR)/load_assembler_test 56 | $(TEST_DIR)/memory_wrapper_test 57 | 58 | .PHONY: test 59 | test: wrapper_test core_test 60 | 61 | .PHONY: clean 62 | clean: 63 | rm -rf *.o $(TARGET) $(TEST_TARGETS) $(WRAPPER_TESTS) tests/*.o 64 | -------------------------------------------------------------------------------- /tests/rv32ui-p-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test_list=( 3 | "target/share/riscv-tests/isa/rv32ui-p-add" 4 | "target/share/riscv-tests/isa/rv32ui-p-or" 5 | "target/share/riscv-tests/isa/rv32ui-p-addi" 6 | "target/share/riscv-tests/isa/rv32ui-p-ori" 7 | "target/share/riscv-tests/isa/rv32ui-p-and" 8 | "target/share/riscv-tests/isa/rv32ui-p-sb" 9 | "target/share/riscv-tests/isa/rv32ui-p-andi" 10 | "target/share/riscv-tests/isa/rv32ui-p-sh" 11 | "target/share/riscv-tests/isa/rv32ui-p-auipc" 12 | "target/share/riscv-tests/isa/rv32ui-p-simple" 13 | "target/share/riscv-tests/isa/rv32ui-p-beq" 14 | "target/share/riscv-tests/isa/rv32ui-p-sll" 15 | "target/share/riscv-tests/isa/rv32ui-p-bge" 16 | "target/share/riscv-tests/isa/rv32ui-p-slli" 17 | "target/share/riscv-tests/isa/rv32ui-p-bgeu" 18 | "target/share/riscv-tests/isa/rv32ui-p-slt" 19 | "target/share/riscv-tests/isa/rv32ui-p-blt" 20 | "target/share/riscv-tests/isa/rv32ui-p-slti" 21 | "target/share/riscv-tests/isa/rv32ui-p-bltu" 22 | "target/share/riscv-tests/isa/rv32ui-p-sltiu" 23 | "target/share/riscv-tests/isa/rv32ui-p-bne" 24 | "target/share/riscv-tests/isa/rv32ui-p-sltu" 25 | "target/share/riscv-tests/isa/rv32ui-p-fence_i" 26 | "target/share/riscv-tests/isa/rv32ui-p-sra" 27 | "target/share/riscv-tests/isa/rv32ui-p-jal" 28 | "target/share/riscv-tests/isa/rv32ui-p-srai" 29 | "target/share/riscv-tests/isa/rv32ui-p-jalr" 30 | "target/share/riscv-tests/isa/rv32ui-p-srl" 31 | "target/share/riscv-tests/isa/rv32ui-p-lb" 32 | "target/share/riscv-tests/isa/rv32ui-p-srli" 33 | "target/share/riscv-tests/isa/rv32ui-p-lbu" 34 | "target/share/riscv-tests/isa/rv32ui-p-sub" 35 | "target/share/riscv-tests/isa/rv32ui-p-lh" 36 | "target/share/riscv-tests/isa/rv32ui-p-sw" 37 | "target/share/riscv-tests/isa/rv32ui-p-lhu" 38 | "target/share/riscv-tests/isa/rv32ui-p-xor" 39 | "target/share/riscv-tests/isa/rv32ui-p-lui" 40 | "target/share/riscv-tests/isa/rv32ui-p-xori" 41 | "target/share/riscv-tests/isa/rv32ui-p-lw" 42 | ) 43 | emulater="./RISCV_Emulator" 44 | flag="-h" 45 | for test in "${test_list[@]}"; do 46 | echo -n "Run ${test} test: " 47 | $(eval "${emulater} ${flag} ${test} 2> /dev/null") 48 | exit_status=$? 49 | if [[ ${exit_status} -eq 0 ]]; then 50 | echo "Pass" 51 | else 52 | echo "Fail" 53 | exit ${exit_status} 54 | fi 55 | done 56 | echo "All tests passed." 57 | exit 0 58 | -------------------------------------------------------------------------------- /tests/rv32ui-v-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test_list=( 3 | "target/share/riscv-tests/isa/rv32ui-v-lui" 4 | "target/share/riscv-tests/isa/rv32ui-v-simple" 5 | "target/share/riscv-tests/isa/rv32ui-v-or" 6 | "target/share/riscv-tests/isa/rv32ui-v-lbu" 7 | "target/share/riscv-tests/isa/rv32ui-v-xori" 8 | "target/share/riscv-tests/isa/rv32ui-v-sll" 9 | "target/share/riscv-tests/isa/rv32ui-v-sw" 10 | "target/share/riscv-tests/isa/rv32ui-v-bge" 11 | "target/share/riscv-tests/isa/rv32ui-v-lb" 12 | "target/share/riscv-tests/isa/rv32ui-v-slli" 13 | "target/share/riscv-tests/isa/rv32ui-v-srai" 14 | "target/share/riscv-tests/isa/rv32ui-v-sub" 15 | "target/share/riscv-tests/isa/rv32ui-v-lw" 16 | "target/share/riscv-tests/isa/rv32ui-v-sltiu" 17 | "target/share/riscv-tests/isa/rv32ui-v-bltu" 18 | "target/share/riscv-tests/isa/rv32ui-v-slti" 19 | "target/share/riscv-tests/isa/rv32ui-v-ori" 20 | "target/share/riscv-tests/isa/rv32ui-v-srli" 21 | "target/share/riscv-tests/isa/rv32ui-v-xor" 22 | "target/share/riscv-tests/isa/rv32ui-v-add" 23 | "target/share/riscv-tests/isa/rv32ui-v-bne" 24 | "target/share/riscv-tests/isa/rv32ui-v-bgeu" 25 | "target/share/riscv-tests/isa/rv32ui-v-sra" 26 | "target/share/riscv-tests/isa/rv32ui-v-slt" 27 | "target/share/riscv-tests/isa/rv32ui-v-jal" 28 | "target/share/riscv-tests/isa/rv32ui-v-blt" 29 | "target/share/riscv-tests/isa/rv32ui-v-srl" 30 | "target/share/riscv-tests/isa/rv32ui-v-sb" 31 | "target/share/riscv-tests/isa/rv32ui-v-beq" 32 | "target/share/riscv-tests/isa/rv32ui-v-sltu" 33 | "target/share/riscv-tests/isa/rv32ui-v-lh" 34 | "target/share/riscv-tests/isa/rv32ui-v-lhu" 35 | "target/share/riscv-tests/isa/rv32ui-v-auipc" 36 | "target/share/riscv-tests/isa/rv32ui-v-addi" 37 | "target/share/riscv-tests/isa/rv32ui-v-fence_i" 38 | "target/share/riscv-tests/isa/rv32ui-v-sh" 39 | "target/share/riscv-tests/isa/rv32ui-v-andi" 40 | "target/share/riscv-tests/isa/rv32ui-v-jalr" 41 | "target/share/riscv-tests/isa/rv32ui-v-and" 42 | ) 43 | emulater="./RISCV_Emulator" 44 | flag="-h" 45 | for test in "${test_list[@]}"; do 46 | echo -n "Run ${test} test: " 47 | $(eval "${emulater} ${flag} ${test} 2> /dev/null") 48 | exit_status=$? 49 | if [[ ${exit_status} -eq 0 ]]; then 50 | echo "Pass" 51 | else 52 | echo "Fail" 53 | exit ${exit_status} 54 | fi 55 | done 56 | echo "All tests passed." 57 | exit 0 58 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(assembler_test) 3 | 4 | set(CMAKE_CXX_STANDARD 14) 5 | 6 | ########### Needed for profiling. 7 | # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0") 8 | # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0") 9 | ########### 10 | 11 | include_directories(.) 12 | 13 | add_executable(assembler_test 14 | tests/assembler.cc 15 | tests/assembler.h 16 | bit_tools.cc 17 | bit_tools.h 18 | instruction_encdec.cc 19 | instruction_encdec.h 20 | tests/load_assembler.cc 21 | tests/load_assembler.h 22 | tests/load_assembler_test.cc 23 | tests/load_assembler_test.h 24 | memory_wrapper.h 25 | memory_wrapper.cpp 26 | ) 27 | 28 | add_executable(cpu_test 29 | tests/assembler.cc 30 | tests/assembler.h 31 | bit_tools.cc 32 | bit_tools.h 33 | instruction_encdec.cc 34 | instruction_encdec.h 35 | tests/load_assembler.cc 36 | tests/load_assembler.h 37 | tests/cpu_test.cc 38 | RISCV_cpu.cc 39 | RISCV_cpu.h 40 | memory_wrapper.cpp memory_wrapper.h 41 | system_call_emulator.cpp system_call_emulator.h 42 | pte.cpp pte.h 43 | Mmu.cpp Mmu.h 44 | riscv_cpu_common.h 45 | Disassembler.cpp Disassembler.h 46 | PeripheralEmulator.cpp PeripheralEmulator.h) 47 | 48 | add_executable(RISCV_Emulator 49 | bit_tools.cc 50 | bit_tools.h 51 | instruction_encdec.cc 52 | instruction_encdec.h 53 | RISCV_cpu.cc 54 | RISCV_cpu.h 55 | RISCV_Emulator.cc 56 | RISCV_Emulator.h 57 | memory_wrapper.cpp 58 | memory_wrapper.h 59 | system_call_emulator.cpp 60 | system_call_emulator.h 61 | pte.cpp pte.h 62 | Mmu.cpp Mmu.h 63 | riscv_cpu_common.h 64 | Disassembler.cpp Disassembler.h 65 | PeripheralEmulator.cpp PeripheralEmulator.h 66 | ScreenEmulation.cpp ScreenEmulation.h) 67 | 68 | target_link_libraries(RISCV_Emulator ncurses) 69 | 70 | add_executable(memory_wrapper_test 71 | memory_wrapper.cpp 72 | memory_wrapper.h 73 | tests/memory_wrapper_test.cpp 74 | ) 75 | 76 | add_executable(pte_test 77 | pte.cpp 78 | pte.h 79 | tests/pte_test.cpp 80 | bit_tools.h 81 | bit_tools.cc 82 | ) -------------------------------------------------------------------------------- /pte.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 2/15/20. 3 | // 4 | 5 | #ifndef ASSEMBLER_TEST_PTE_H 6 | #define ASSEMBLER_TEST_PTE_H 7 | 8 | #include 9 | 10 | namespace RISCV_EMULATOR { 11 | 12 | class Pte32 { 13 | public: 14 | Pte32(); 15 | 16 | Pte32(uint32_t pte_value); 17 | 18 | Pte32 &operator=(uint32_t pte_value); 19 | 20 | uint32_t GetValue() const; 21 | 22 | uint32_t GetPpn() const; 23 | 24 | uint32_t GetPpn1() const; 25 | 26 | uint32_t GetPpn0() const; 27 | 28 | uint32_t GetRsw() const; 29 | 30 | uint32_t GetD() const; 31 | 32 | uint32_t GetA() const; 33 | 34 | uint32_t GetG() const; 35 | 36 | uint32_t GetU() const; 37 | 38 | uint32_t GetX() const; 39 | 40 | uint32_t GetW() const; 41 | 42 | uint32_t GetR() const; 43 | 44 | uint32_t GetV() const; 45 | 46 | void SetPpn(uint32_t); 47 | 48 | void SetRsw(uint32_t); 49 | 50 | void SetD(int value); 51 | 52 | void SetA(int value); 53 | 54 | void SetG(int value); 55 | 56 | void SetU(int value); 57 | 58 | void SetX(int value); 59 | 60 | void SetW(int value); 61 | 62 | void SetR(int value); 63 | 64 | void SetV(int value); 65 | 66 | bool IsLeaf() const; 67 | 68 | bool IsValid() const; 69 | 70 | private: 71 | uint32_t pte_; 72 | }; 73 | 74 | class Pte64 { 75 | public: 76 | Pte64(); 77 | 78 | Pte64(uint64_t pte_value); 79 | 80 | Pte64 &operator=(uint64_t pte_value); 81 | 82 | uint64_t GetValue() const; 83 | 84 | uint32_t GetPpn() const; 85 | 86 | uint32_t GetPpn2() const; 87 | 88 | uint32_t GetPpn1() const; 89 | 90 | uint32_t GetPpn0() const; 91 | 92 | uint32_t GetRsw() const; 93 | 94 | uint32_t GetD() const; 95 | 96 | uint32_t GetA() const; 97 | 98 | uint32_t GetG() const; 99 | 100 | uint32_t GetU() const; 101 | 102 | uint32_t GetX() const; 103 | 104 | uint32_t GetW() const; 105 | 106 | uint32_t GetR() const; 107 | 108 | uint32_t GetV() const; 109 | 110 | void SetPpn(uint64_t); 111 | 112 | void SetRsw(uint32_t); 113 | 114 | void SetD(int value); 115 | 116 | void SetA(int value); 117 | 118 | void SetG(int value); 119 | 120 | void SetU(int value); 121 | 122 | void SetX(int value); 123 | 124 | void SetW(int value); 125 | 126 | void SetR(int value); 127 | 128 | void SetV(int value); 129 | 130 | bool IsLeaf() const; 131 | 132 | bool IsValid() const; 133 | 134 | private: 135 | uint64_t pte_; 136 | }; 137 | 138 | } // namespace RISCV_EMULATOR 139 | 140 | #endif //ASSEMBLER_TEST_PTE_H 141 | -------------------------------------------------------------------------------- /tests/load_assembler.cc: -------------------------------------------------------------------------------- 1 | #include "RISCV_cpu.h" 2 | #include "assembler.h" 3 | #include "bit_tools.h" 4 | 5 | using namespace RISCV_EMULATOR; 6 | 7 | namespace CPU_TEST { 8 | 9 | uint64_t AddCmd(MemoryWrapper &mem, uint64_t address, uint32_t cmd) { 10 | mem.WriteByte(address++, cmd & 0xFF); 11 | mem.WriteByte(address++, (cmd >> 8) & 0xFF); 12 | mem.WriteByte(address++, (cmd >> 16) & 0xFF); 13 | mem.WriteByte(address++, (cmd >> 24) & 0xFF); 14 | return address; 15 | } 16 | 17 | uint64_t AddCmdCType(MemoryWrapper &mem, uint64_t address, uint16_t cmd) { 18 | mem.WriteByte(address++, cmd & 0xFF); 19 | mem.WriteByte(address++, (cmd >> 8) & 0xFF); 20 | return address; 21 | } 22 | 23 | uint64_t LoadAssemblerSum(MemoryWrapper &mem, uint64_t address) { 24 | address = AddCmd(mem, address, AsmAddi(T0, ZERO, 0)); 25 | address = AddCmd(mem, address, AsmAddi(T1, ZERO, 0)); 26 | address = AddCmd(mem, address, AsmAddi(T2, ZERO, 10)); 27 | address = AddCmd(mem, address, AsmAddi(T0, T0, 1)); 28 | address = AddCmd(mem, address, AsmAdd(T1, T1, T0)); 29 | address = AddCmd(mem, address, AsmBeq(T0, T2, 8)); 30 | address = AddCmd(mem, address, AsmJal(ZERO, -16)); 31 | address = AddCmd(mem, address, AsmAdd(A0, T1, ZERO)); 32 | address = AddCmd(mem, address, AsmXor(RA, RA, RA)); 33 | address = AddCmd(mem, address, AsmJalr(ZERO, RA, 0)); 34 | 35 | return address; 36 | } 37 | 38 | uint64_t LoadAssemblerSort(MemoryWrapper &mem, uint64_t address) { 39 | // A1 is n and A3 points to A[0] 40 | // A4 is i, A5 is j, a6 is x 41 | address = AddCmd(mem, address, AsmAddi(A3, A0, 4)); // 0 42 | address = AddCmd(mem, address, AsmAddi(A4, X0, 1)); // 4 43 | // Outer Loop 44 | address = AddCmd(mem, address, AsmBltu(A4, A1, +8)); // 8 45 | // Exit Outer Loop 46 | address = AddCmd(mem, address, AsmJalr(X0, X1, 0)); // 0c 47 | // Continue Outer Loop 48 | address = AddCmd(mem, address, AsmLw(A6, A3, 0)); // 10 49 | address = AddCmd(mem, address, AsmAddi(A2, A3, 0)); // 14 50 | address = AddCmd(mem, address, AsmAddi(A5, A4, 0)); // 18 51 | // Inner Loop 52 | address = AddCmd(mem, address, AsmLw(A7, A2, -4)); // 1c 53 | address = AddCmd(mem, address, AsmBge(A6, A7, +20)); // 20 54 | // sw rs2, offset(rs1) <= Note the rs1 / rs2 order. 55 | address = AddCmd(mem, address, AsmSw(A2, A7, 0)); // 24 56 | address = AddCmd(mem, address, AsmAddi(A5, A5, -1)); // 28 57 | address = AddCmd(mem, address, AsmAddi(A2, A2, -4)); // 2c 58 | address = AddCmd(mem, address, AsmBne(A5, X0, -20)); // 30 59 | // Exit Inner Loop 60 | address = AddCmd(mem, address, AsmSlli(A5, A5, 2)); // 34 61 | address = AddCmd(mem, address, AsmAdd(A5, A0, A5)); // 38 62 | address = AddCmd(mem, address, AsmSw(A5, A6, 0)); // 3c 63 | address = AddCmd(mem, address, AsmAddi(A4, A4, 1)); // 40 64 | address = AddCmd(mem, address, AsmAddi(A3, A3, 4)); // 44 65 | address = AddCmd(mem, address, AsmJal(X0, -64)); // 48 66 | return address; 67 | } 68 | 69 | } // namespace RISCV_EMULATOR -------------------------------------------------------------------------------- /tests/rv64ui-v-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test_list=( 3 | "target/share/riscv-tests/isa/rv64ui-v-add" 4 | "target/share/riscv-tests/isa/rv64ui-v-addi" 5 | "target/share/riscv-tests/isa/rv64ui-v-addiw" 6 | "target/share/riscv-tests/isa/rv64ui-v-addw" 7 | "target/share/riscv-tests/isa/rv64ui-v-and" 8 | "target/share/riscv-tests/isa/rv64ui-v-sll" 9 | "target/share/riscv-tests/isa/rv64ui-v-andi" 10 | "target/share/riscv-tests/isa/rv64ui-v-slli" 11 | "target/share/riscv-tests/isa/rv64ui-v-auipc" 12 | "target/share/riscv-tests/isa/rv64ui-v-slliw" 13 | "target/share/riscv-tests/isa/rv64ui-v-sllw" 14 | "target/share/riscv-tests/isa/rv64ui-v-slt" 15 | "target/share/riscv-tests/isa/rv64ui-v-slti" 16 | "target/share/riscv-tests/isa/rv64ui-v-sltiu" 17 | "target/share/riscv-tests/isa/rv64ui-v-sltu" 18 | "target/share/riscv-tests/isa/rv64ui-v-sra" 19 | "target/share/riscv-tests/isa/rv64ui-v-srai" 20 | "target/share/riscv-tests/isa/rv64ui-v-sraiw" 21 | "target/share/riscv-tests/isa/rv64ui-v-jalr" 22 | "target/share/riscv-tests/isa/rv64ui-v-sraw" 23 | "target/share/riscv-tests/isa/rv64ui-v-srl" 24 | "target/share/riscv-tests/isa/rv64ui-v-srli" 25 | "target/share/riscv-tests/isa/rv64ui-v-srliw" 26 | "target/share/riscv-tests/isa/rv64ui-v-srlw" 27 | "target/share/riscv-tests/isa/rv64ui-v-sub" 28 | "target/share/riscv-tests/isa/rv64ui-v-lui" 29 | "target/share/riscv-tests/isa/rv64ui-v-subw" 30 | "target/share/riscv-tests/isa/rv64ui-v-xor" 31 | "target/share/riscv-tests/isa/rv64ui-v-or" 32 | "target/share/riscv-tests/isa/rv64ui-v-xori" 33 | "target/share/riscv-tests/isa/rv64ui-v-ori" 34 | "target/share/riscv-tests/isa/rv64ui-v-beq" 35 | "target/share/riscv-tests/isa/rv64ui-v-bge" 36 | "target/share/riscv-tests/isa/rv64ui-v-bgeu" 37 | "target/share/riscv-tests/isa/rv64ui-v-bltu" 38 | "target/share/riscv-tests/isa/rv64ui-v-bne" 39 | "target/share/riscv-tests/isa/rv64ui-v-blt" 40 | "target/share/riscv-tests/isa/rv64ui-v-jal" 41 | "target/share/riscv-tests/isa/rv64ui-v-lb" 42 | "target/share/riscv-tests/isa/rv64ui-v-lbu" 43 | "target/share/riscv-tests/isa/rv64ui-v-lh" 44 | "target/share/riscv-tests/isa/rv64ui-v-lhu" 45 | "target/share/riscv-tests/isa/rv64ui-v-ld" 46 | "target/share/riscv-tests/isa/rv64ui-v-lw" 47 | "target/share/riscv-tests/isa/rv64ui-v-lwu" 48 | "target/share/riscv-tests/isa/rv64ui-v-sb" 49 | "target/share/riscv-tests/isa/rv64ui-v-sd" 50 | "target/share/riscv-tests/isa/rv64ui-v-sh" 51 | "target/share/riscv-tests/isa/rv64ui-v-sw" 52 | "target/share/riscv-tests/isa/rv64ui-v-fence_i" 53 | "target/share/riscv-tests/isa/rv64ui-v-simple" 54 | ) 55 | emulater="./RISCV_Emulator" 56 | flag="-h -64" 57 | for test in "${test_list[@]}"; do 58 | echo -n "Run ${test} test: " 59 | $(eval "${emulater} ${flag} ${test} 2> /dev/null") 60 | exit_status=$? 61 | if [[ ${exit_status} -eq 0 ]]; then 62 | echo "Pass" 63 | else 64 | echo "Fail" 65 | exit ${exit_status} 66 | fi 67 | done 68 | echo "All tests passed." 69 | exit 0 70 | -------------------------------------------------------------------------------- /tests/rv64ui-p-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test_list=( 3 | "target/share/riscv-tests/isa/rv64ui-p-add" 4 | "target/share/riscv-tests/isa/rv64ui-p-sb" 5 | "target/share/riscv-tests/isa/rv64ui-p-addi" 6 | "target/share/riscv-tests/isa/rv64ui-p-sd" 7 | "target/share/riscv-tests/isa/rv64ui-p-addiw" 8 | "target/share/riscv-tests/isa/rv64ui-p-sh" 9 | "target/share/riscv-tests/isa/rv64ui-p-addw" 10 | "target/share/riscv-tests/isa/rv64ui-p-simple" 11 | "target/share/riscv-tests/isa/rv64ui-p-and" 12 | "target/share/riscv-tests/isa/rv64ui-p-sll" 13 | "target/share/riscv-tests/isa/rv64ui-p-andi" 14 | "target/share/riscv-tests/isa/rv64ui-p-slli" 15 | "target/share/riscv-tests/isa/rv64ui-p-auipc" 16 | "target/share/riscv-tests/isa/rv64ui-p-slliw" 17 | "target/share/riscv-tests/isa/rv64ui-p-beq" 18 | "target/share/riscv-tests/isa/rv64ui-p-sllw" 19 | "target/share/riscv-tests/isa/rv64ui-p-bge" 20 | "target/share/riscv-tests/isa/rv64ui-p-slt" 21 | "target/share/riscv-tests/isa/rv64ui-p-bgeu" 22 | "target/share/riscv-tests/isa/rv64ui-p-slti" 23 | "target/share/riscv-tests/isa/rv64ui-p-blt" 24 | "target/share/riscv-tests/isa/rv64ui-p-sltiu" 25 | "target/share/riscv-tests/isa/rv64ui-p-bltu" 26 | "target/share/riscv-tests/isa/rv64ui-p-sltu" 27 | "target/share/riscv-tests/isa/rv64ui-p-bne" 28 | "target/share/riscv-tests/isa/rv64ui-p-sra" 29 | "target/share/riscv-tests/isa/rv64ui-p-fence_i" 30 | "target/share/riscv-tests/isa/rv64ui-p-srai" 31 | "target/share/riscv-tests/isa/rv64ui-p-jal" 32 | "target/share/riscv-tests/isa/rv64ui-p-sraiw" 33 | "target/share/riscv-tests/isa/rv64ui-p-jalr" 34 | "target/share/riscv-tests/isa/rv64ui-p-sraw" 35 | "target/share/riscv-tests/isa/rv64ui-p-lb" 36 | "target/share/riscv-tests/isa/rv64ui-p-srl" 37 | "target/share/riscv-tests/isa/rv64ui-p-lbu" 38 | "target/share/riscv-tests/isa/rv64ui-p-srli" 39 | "target/share/riscv-tests/isa/rv64ui-p-ld" 40 | "target/share/riscv-tests/isa/rv64ui-p-srliw" 41 | "target/share/riscv-tests/isa/rv64ui-p-lh" 42 | "target/share/riscv-tests/isa/rv64ui-p-srlw" 43 | "target/share/riscv-tests/isa/rv64ui-p-lhu" 44 | "target/share/riscv-tests/isa/rv64ui-p-sub" 45 | "target/share/riscv-tests/isa/rv64ui-p-lui" 46 | "target/share/riscv-tests/isa/rv64ui-p-subw" 47 | "target/share/riscv-tests/isa/rv64ui-p-lw" 48 | "target/share/riscv-tests/isa/rv64ui-p-sw" 49 | "target/share/riscv-tests/isa/rv64ui-p-lwu" 50 | "target/share/riscv-tests/isa/rv64ui-p-xor" 51 | "target/share/riscv-tests/isa/rv64ui-p-or" 52 | "target/share/riscv-tests/isa/rv64ui-p-xori" 53 | "target/share/riscv-tests/isa/rv64ui-p-ori" 54 | ) 55 | emulater="./RISCV_Emulator" 56 | flag="-h -64" 57 | for test in "${test_list[@]}"; do 58 | echo -n "Run ${test} test: " 59 | $(eval "${emulater} ${flag} ${test} 2> /dev/null") 60 | exit_status=$? 61 | if [[ ${exit_status} -eq 0 ]]; then 62 | echo "Pass" 63 | else 64 | echo "Fail" 65 | exit ${exit_status} 66 | fi 67 | done 68 | echo "All tests passed." 69 | exit 0 70 | -------------------------------------------------------------------------------- /memory_wrapper.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 1/19/20. 3 | // 4 | #include 5 | #include 6 | #include "memory_wrapper.h" 7 | 8 | namespace RISCV_EMULATOR { 9 | 10 | MemoryWrapper::MemoryWrapper() { 11 | assigned_ = {}; // Default value 12 | for (auto &a: assigned_) { 13 | a = false; 14 | } 15 | } 16 | 17 | const uint8_t MemoryWrapper::ReadByte(size_t i) const { 18 | uint64_t dram_address = (i >> kWordBits) << kWordBits; 19 | uint64_t read_data = Read32(dram_address); 20 | int byte_offset = i & 0b11; 21 | read_data = read_data >> byte_offset * 8; 22 | return static_cast(read_data & 0xFF); 23 | } 24 | 25 | void MemoryWrapper::WriteByte(size_t i, uint8_t data) { 26 | constexpr std::array mask = { 27 | 0xFFFFFF00, 28 | 0xFFFF00FF, 29 | 0xFF00FFFF, 30 | 0x00FFFFFF, 31 | }; 32 | uint64_t dram_address = (i >> kWordBits) << kWordBits; 33 | uint64_t original_data = Read32(dram_address); 34 | uint64_t write_data = data; 35 | int byte_offset = i & 0b11; 36 | write_data = (original_data & mask[byte_offset]) | (write_data << byte_offset * 8); 37 | Write32(dram_address, (uint32_t)write_data); 38 | } 39 | 40 | const uint16_t MemoryWrapper::Read16(size_t i) const { 41 | assert((i & 1) == 0); 42 | uint64_t dram_address = (i >> kWordBits) << kWordBits; 43 | uint64_t read_data = Read32(dram_address); 44 | int short_word_offset = i & 0b11; 45 | read_data = read_data >> short_word_offset * 8; 46 | return static_cast(read_data & 0xFFFF); 47 | } 48 | 49 | void MemoryWrapper::Write16(size_t i, uint16_t data) { 50 | assert((i & 1) == 0); 51 | constexpr std::array mask = { 52 | 0xFFFF0000, 53 | 0x0000FFFF, 54 | }; 55 | uint64_t dram_address = (i >> kWordBits) << kWordBits; 56 | uint32_t original_data = Read32(dram_address); 57 | int short_word_offset = (i >> 1) & 1; 58 | uint32_t write_data = (original_data & (uint32_t)mask[short_word_offset]) | (data << short_word_offset * 16); 59 | Write32(dram_address, write_data); 60 | } 61 | 62 | const uint32_t MemoryWrapper::Read32(size_t i) const { 63 | assert((i & 0b11) == 0); 64 | int entry = (i >> kOffsetBits) & kEntryMask; 65 | uint32_t read_data = 0; 66 | if (CheckRange(entry)) { 67 | int offset = i & kOffsetMask; 68 | int word_offset = offset >> kWordBits; 69 | read_data = mapping_[entry][word_offset]; 70 | } 71 | return read_data; 72 | } 73 | 74 | void MemoryWrapper::Write32(size_t i, uint32_t value) { 75 | assert((i & 0b11) == 0); 76 | int entry = (i >> kOffsetBits) & kEntryMask; 77 | if (!CheckRange(entry)) { 78 | assigned_[entry] = true; 79 | mapping_[entry].resize(1 << (kOffsetBits - kWordBits)); 80 | } 81 | int offset = i & kOffsetMask; 82 | int word_offset = offset >> kWordBits; 83 | mapping_[entry][word_offset] = value; 84 | } 85 | 86 | const uint64_t MemoryWrapper::Read64(size_t i) const { 87 | assert((i & 0b111) == 0); 88 | uint64_t dram_address = (i >> kWordBits) << kWordBits; 89 | uint64_t read_data_lower = Read32(dram_address); 90 | uint64_t read_data_upper = Read32(dram_address + 4); 91 | uint64_t read_data = read_data_lower | (read_data_upper << 32); 92 | return read_data; 93 | } 94 | 95 | void MemoryWrapper::Write64(size_t i, uint64_t value) { 96 | assert((i & 0b111) == 0); 97 | uint64_t dram_address = (i >> kWordBits) << kWordBits; 98 | Write32(dram_address, value & 0xFFFFFFFF); 99 | Write32(dram_address + 4, (value >> 32) & 0xFFFFFFFF); 100 | } 101 | 102 | bool MemoryWrapper::operator==(MemoryWrapper &r) { 103 | return (mapping_ == r.mapping_ && assigned_ == r.assigned_); 104 | } 105 | 106 | bool MemoryWrapper::operator!=(MemoryWrapper &r) { 107 | return (mapping_ != r.mapping_ || assigned_ != r.assigned_); 108 | } 109 | 110 | } // namespace RISCV_EMULATOR 111 | -------------------------------------------------------------------------------- /bit_tools.h: -------------------------------------------------------------------------------- 1 | #ifndef BIT_TOOLS_H 2 | #define BIT_TOOLS_H 3 | 4 | #include 5 | 6 | template 7 | constexpr T GenMask(int size, int shift) { 8 | T mask = 0; 9 | for (int i = 0; i < size; ++i) { 10 | mask = (mask << 1) | 1; 11 | } 12 | return mask << shift; 13 | } 14 | 15 | template 16 | T SetBit(T data, int value, int pos) { 17 | if (value) { 18 | return data | ((T)1 << pos); 19 | } else { 20 | return data & ~((T)1 << pos); 21 | } 22 | } 23 | 24 | namespace { 25 | 26 | uint64_t mask[] = {0x0, 0x01, 0x03, 0x07, 0x0F, 27 | 0x01F, 0x03F, 0x07F, 0x0FF, 0x01FF, 28 | 0x03FF, 0x07FF, 0x0FFF, 0x01FFF, 0x03FFF, 29 | 0x07FFF, 0x0FFFF, 0x01FFFF, 0x03FFFF, 0x07FFFF, 30 | 0x0FFFFF, 0x01FFFFF, 0x03FFFFF, 0x07FFFFF, 0x0FFFFFF, 31 | 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, 32 | 0x01FFFFFFF, 0x03FFFFFFF, 0x07FFFFFFF, 0x0FFFFFFFF, 33 | 0x01FFFFFFFF, 0x03FFFFFFFF, 0x07FFFFFFFF, 0x0FFFFFFFFF, 34 | 0x01FFFFFFFFF, 0x03FFFFFFFFF, 0x07FFFFFFFFF, 0x0FFFFFFFFFF, 35 | 0x01FFFFFFFFFF, 0x03FFFFFFFFFF, 0x07FFFFFFFFFF, 0x0FFFFFFFFFFF, 36 | 0x01FFFFFFFFFFF, 0x03FFFFFFFFFFF, 0x07FFFFFFFFFFF, 0x0FFFFFFFFFFFF, 37 | 0x01FFFFFFFFFFFF, 0x03FFFFFFFFFFFF, 0x07FFFFFFFFFFFF, 0x0FFFFFFFFFFFFF, 38 | 0x01FFFFFFFFFFFFF, 0x03FFFFFFFFFFFFF, 0x07FFFFFFFFFFFFF, 0x0FFFFFFFFFFFFFF, 39 | 0x01FFFFFFFFFFFFFF, 0x03FFFFFFFFFFFFFF, 0x07FFFFFFFFFFFFFF, 0x0FFFFFFFFFFFFFFF, 40 | 0x01FFFFFFFFFFFFFFF, 0x03FFFFFFFFFFFFFFF, 0x07FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 41 | }; 42 | 43 | uint64_t sign_mask[] = { 44 | 0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFC, 0xFFFFFFFFFFFFFFF8, 0xFFFFFFFFFFFFFFF0, 45 | 0xFFFFFFFFFFFFFFE0, 0xFFFFFFFFFFFFFFC0, 0xFFFFFFFFFFFFFF80, 0xFFFFFFFFFFFFFF00, 46 | 0xFFFFFFFFFFFFFE00, 0xFFFFFFFFFFFFFC00, 0xFFFFFFFFFFFFF800, 0xFFFFFFFFFFFFF000, 47 | 0xFFFFFFFFFFFFE000, 0xFFFFFFFFFFFFC000, 0xFFFFFFFFFFFF8000, 0xFFFFFFFFFFFF0000, 48 | 0xFFFFFFFFFFFE0000, 0xFFFFFFFFFFFC0000, 0xFFFFFFFFFFF80000, 0xFFFFFFFFFFF00000, 49 | 0xFFFFFFFFFFE00000, 0xFFFFFFFFFFC00000, 0xFFFFFFFFFF800000, 0xFFFFFFFFFF000000, 50 | 0xFFFFFFFFFE000000, 0xFFFFFFFFFC000000, 0xFFFFFFFFF8000000, 0xFFFFFFFFF0000000, 51 | 0xFFFFFFFFE0000000, 0xFFFFFFFFC0000000, 0xFFFFFFFF80000000, 0xFFFFFFFF00000000, 52 | 0xFFFFFFFE00000000, 0xFFFFFFFC00000000, 0xFFFFFFF800000000, 0xFFFFFFF000000000, 53 | 0xFFFFFFE000000000, 0xFFFFFFC000000000, 0xFFFFFF8000000000, 0xFFFFFF0000000000, 54 | 0xFFFFFE0000000000, 0xFFFFFC0000000000, 0xFFFFF80000000000, 0xFFFFF00000000000, 55 | 0xFFFFE00000000000, 0xFFFFC00000000000, 0xFFFF800000000000, 0xFFFF000000000000, 56 | 0xFFFE000000000000, 0xFFFC000000000000, 0xFFF8000000000000, 0xFFF0000000000000, 57 | 0xFFE0000000000000, 0xFFC0000000000000, 0xFF80000000000000, 0xFF00000000000000, 58 | 0xFE00000000000000, 0xFC00000000000000, 0xF800000000000000, 0xF000000000000000, 59 | 0xE000000000000000, 0xC000000000000000, 0x8000000000000000, 0x0000000000000000, 60 | }; 61 | 62 | } 63 | 64 | template 65 | T BitShift(T val, int width, int offset, int distpos){ 66 | val >>= offset; 67 | val &= mask[width]; 68 | val <<= distpos; 69 | return val; 70 | } 71 | 72 | template 73 | T bitcrop(const T& val, const int width, const int offset) { 74 | T target_bits = val >> offset; 75 | target_bits &= mask[width]; 76 | return target_bits; 77 | } 78 | 79 | template 80 | T SignExtend(T value, int width){ 81 | if ((value >> (width - 1)) & 1) { 82 | value |= sign_mask[width - 1]; 83 | } else { 84 | value &= ~sign_mask[width - 1]; 85 | } 86 | return value; 87 | } 88 | 89 | template 90 | T bitset(const T& reg, const int width, const int offset, const T& val) { 91 | T new_reg = reg & (~mask[width + offset] | mask[offset]); 92 | new_reg |= (val & mask[width]) << offset; 93 | return new_reg; 94 | } 95 | 96 | #endif // BIT_TOOLS_H -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What's this? 2 | 3 | A simple RISCV32I/RISCV64I emulator that can execute an ELF executable file compiled with gnu tool chain and Newlib. 4 | 5 | ## Supported Instructions 6 | 7 | - RV32I (Including compressed instructions) 8 | - RV32M (Including compressed instructions) 9 | - RV64I (Including compressed instructions) 10 | - RV64M (Including compressed instructions) 11 | 12 | Following instructions are partially supported 13 | - RV32A (except LR and SC) 14 | - RV64A (except LR and SC) 15 | 16 | Support of privilege instruction is partial and not complete. 17 | 18 | Some system calls are emulated with **limitation** 19 | 20 | # Usage 21 | 22 | ## Build 23 | 24 | 1. Run the following command 25 | 26 | ``` 27 | $ make 28 | ``` 29 | 30 | ## How to generate executable for the emulator. 31 | 32 | 1. Install [GNU tool chain](https://github.com/riscv/riscv-gnu-toolchain) for RISCV 32bit with support of ilp. 33 | 34 | ``` 35 | $ ./configure --prefix=/opt/riscv --with-arch=rv32i --with-abi=ilp32 36 | $ make -j 37 | ``` 38 | 39 | 2. Compile the target code for rv32i and ilp32 with static option. For example. 40 | 41 | ``` 42 | $ riscv32-unknown-elf-gcc file_name.c -o file_name -Wall -march=rv32i -mabi=ilp32 -static 43 | ``` 44 | 45 | 3. Run the emulator. 46 | 47 | Move to the emulator folder, then run the following command. 48 | 49 | ``` 50 | $ ./RISCV_Emulator file_name 51 | ``` 52 | 53 | ## Test 54 | 55 | ### Compiling the RISCV-Tests 56 | 57 | To run the RISCV tests with this emulator, first set $RISCV to the location of where m99_riscv_emulator is placed. 58 | 59 | ``` 60 | $ RISCV=/folder/where/the/emulator/is/placed 61 | ``` 62 | 63 | Then, follow the instruction on [riscv-tests](https://github.com/riscv/riscv-tests) page as below. 64 | 65 | ``` 66 | $ git clone https://github.com/riscv/riscv-tests 67 | $ cd riscv-tests 68 | $ git submodule update --init --recursive 69 | $ autoconf 70 | $ ./configure --prefix=$RISCV/target 71 | $ make 72 | $ make install 73 | ``` 74 | 75 | The test suite is generated under `$RISCV/target/` 76 | 77 | Now you can run test. 78 | 79 | ## Running Test 80 | 81 | 1. Run the following command 82 | ``` 83 | $ make test 84 | ``` 85 | 86 | # Details 87 | 88 | ## Runtime options 89 | 90 | `-v`: Verbose. Emulator output the current PC, disassembled instruction, register status 91 | `-e`: System call emulation enabled. See the next section. 92 | `-64`: Run in 64 bit mode. 93 | `-m`: Disable machine interrupt delegation to supervisor mode. This is for compatibility with QEMU. 94 | `-h`: Use "tohost" and "fromhost" functions in riscv-test suite. This is only for riscv-test purpose. 95 | `-d:` Device emulation. Enable uart output, virtio disk, and interrupt timer. Press Ctrl-a to exit. (This option is work-in-progress.) 96 | `-s `: Load as disk iamge. You need to enable device emulation with `-d` option. 97 | `-p`: Paging enabled. This is for testing purpose. 98 | 99 | ## System Call emulation 100 | 101 | Following system calls are supported with `-e` option with limitation. 102 | 103 | - open (1024) 104 | - close (57) 105 | - fstat (80) 106 | - write (64) 107 | - read (63) 108 | - lseek (62) 109 | - brk (214) 110 | - exit (93) 111 | 112 | # Running xv6 for riscv 113 | 114 | _Support of xv6 is still under development._ 115 | 116 | This emulator is able to run [xv6 for riscv](https://github.com/mit-pdos/xv6-riscv) to show sh prompt. 117 | 118 | You first need to compile [xv6 for riscv](https://github.com/mit-pdos/xv6-riscv) with CPUS=1 option. 119 | Then, you need to copy `kernel/kernel` in the Emulator directory. 120 | Also, you need `fs.img` (disk image) from xv6 too. `fs.img` is generated when running xv6 with `QEMU` option. 121 | 122 | Run the emulator with `-64 -m -d -s fs.img` options (64 bit mode, disable machine interrupt delegation, device emulation, load `fs.img` as storage image). 123 | 124 | ``` 125 | $ ./RISCV_Emulator -64 -m -d -s fs.img kernel 126 | ``` 127 | 128 | You will eventually see the prompt (`$`) pops up. 129 | You can run the commands, for example by typing `ls` or `cat README`. 130 | Press `Ctrl-a` to exit. -------------------------------------------------------------------------------- /tests/pte_test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 2/16/20. 3 | // 4 | 5 | #include "pte.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace RISCV_EMULATOR; 12 | 13 | namespace { 14 | 15 | constexpr int kTestCaseSize = 1000; 16 | 17 | constexpr uint32_t GenMask(int size) { 18 | uint32_t mask = 0; 19 | for (int i = 0; i < size; ++i) { 20 | mask = (mask << 1) | 1; 21 | } 22 | return mask; 23 | } 24 | 25 | uint32_t GetRandom32() { 26 | static bool init = false; 27 | static std::random_device rd; 28 | static std::mt19937 gen; 29 | static std::uniform_int_distribution dis(0, UINT32_MAX); 30 | if (!init) { 31 | gen = std::mt19937(rd()); 32 | init = true; 33 | } 34 | return dis(gen); 35 | } 36 | 37 | bool CheckPte(uint32_t value) { 38 | Pte32 pte(value); 39 | bool result = true; 40 | uint32_t d = (value >> 7) & 1; 41 | uint32_t a = (value >> 6) & 1; 42 | uint32_t g = (value >> 5) & 1; 43 | uint32_t u = (value >> 4) & 1; 44 | uint32_t x = (value >> 3) & 1; 45 | uint32_t w = (value >> 2) & 1; 46 | uint32_t r = (value >> 1) & 1; 47 | uint32_t v = value & 1; 48 | uint32_t rsw = ((value >> 8) & GenMask(2)); 49 | uint32_t ppn0 = ((value >> 10) & GenMask(10)); 50 | uint32_t ppn1 = ((value >> 20) & GenMask(12)); 51 | uint32_t ppn = ((value >> 10) & GenMask(22)); 52 | result &= pte.GetD() == d; 53 | result &= pte.GetA() == a; 54 | result &= pte.GetG() == g; 55 | result &= pte.GetU() == u; 56 | result &= pte.GetX() == x; 57 | result &= pte.GetW() == w; 58 | result &= pte.GetR() == r; 59 | result &= pte.GetV() == v; 60 | result &= pte.GetRsw() == rsw; 61 | result &= pte.GetPpn0() == ppn0; 62 | result &= pte.GetPpn1() == ppn1; 63 | result &= pte.GetPpn() == ppn; 64 | if (!result) { 65 | std::cout << "pte = " << std::bitset<32>(value) << std::endl; 66 | std::cout << "GetD = " << pte.GetD() << " vs D = " << d << std::endl; 67 | std::cout << "GetA = " << pte.GetA() << " vs A = " << a << std::endl; 68 | std::cout << "GetG = " << pte.GetG() << " vs G = " << g << std::endl; 69 | std::cout << "GetU = " << pte.GetU() << " vs U = " << u << std::endl; 70 | std::cout << "GetX = " << pte.GetX() << " vs X = " << x << std::endl; 71 | std::cout << "GetW = " << pte.GetW() << " vs W = " << w << std::endl; 72 | std::cout << "GetR = " << pte.GetR() << " vs R = " << r << std::endl; 73 | std::cout << "GetV = " << pte.GetV() << " vs V = " << v << std::endl; 74 | std::cout << "GetRsw = " << std::bitset<2>(pte.GetRsw()) << " vs RSW = " << std::bitset<2>(rsw) << std::endl; 75 | std::cout << "GetPpn0 = " << std::bitset<10>(pte.GetPpn0()) << " vs ppn0 = " << std::bitset<10>(ppn0) << std::endl; 76 | std::cout << "GetPpn1 = " << std::bitset<12>(pte.GetPpn1()) << " vs ppn1 = " << std::bitset<12>(ppn1) << std::endl; 77 | std::cout << "GetPpn = " << std::bitset<22>(pte.GetPpn()) << " vs ppn = " << std::bitset<22>(ppn) << std::endl; 78 | } 79 | return result; 80 | } 81 | 82 | bool CheckValidAndLeaf() { 83 | Pte32 pte; 84 | bool result = true; 85 | for (int i = 0; i <= 0b1111; ++i) { 86 | int v = i & 1; 87 | int r = (i >> 1) & 1; 88 | int w = (i >> 2) & 1; 89 | int x = (i >> 3) & 1; 90 | bool invalid = (v == 0 || (r == 0 && w == 1)); 91 | bool leaf = (r != 0 || x != 0 || w != 0); 92 | 93 | uint32_t pte_value = (GetRandom32() & ~0b01111) | i; 94 | pte = pte_value; 95 | result &= pte.IsValid() != invalid; 96 | result &= pte.IsLeaf() == leaf; 97 | if (!result) { 98 | std::cerr << "pte = " << std::bitset<32>(pte_value) << std::endl; 99 | std::cerr << "pte.IsValid() = " << std::boolalpha << pte.IsValid() << std::endl; 100 | std::cerr << "pte.IsLeaf() = " << pte.IsLeaf() << std::endl; 101 | } 102 | } 103 | return result; 104 | } 105 | 106 | bool WriteTest() { 107 | Pte32 pte; 108 | bool result = true; 109 | for (int i = 0; i < 0b1111; ++i) { 110 | uint32_t access_before = i & 1; 111 | uint32_t access_after = (i >> 1) & 1; 112 | uint32_t dirty_before = (i >> 2) & 1; 113 | uint32_t dirty_after = (i >> 2) & 1; 114 | uint32_t pte_value = (GetRandom32() & ~(0b11 << 6)) | (access_before << 6) | (dirty_before << 7); 115 | pte = pte_value; 116 | pte.SetA(access_after); 117 | pte.SetD(dirty_after); 118 | result &= pte.GetA() == access_after; 119 | result &= pte.GetD() == dirty_after; 120 | if (!result) { 121 | std::cerr << "pte = " << std::bitset<32>(pte.GetValue()) << std::endl; 122 | std::cerr << "pte.GetA() = " << pte.GetA() << std::endl; 123 | std::cerr << "pte.GetD() = " << pte.GetD() << std::endl; 124 | } 125 | } 126 | return result; 127 | } 128 | 129 | bool Test() { 130 | bool test_result = true; 131 | for (int i = 0; i < kTestCaseSize && test_result; ++i) { 132 | uint32_t test_case = GetRandom32(); 133 | test_result &= CheckPte(test_case); 134 | } 135 | test_result &= CheckValidAndLeaf(); 136 | test_result &= WriteTest(); 137 | return test_result; 138 | } 139 | 140 | } // namespace anonymous 141 | 142 | int main() { 143 | bool result = Test(); 144 | if (result) { 145 | std::cout << "Pte test passed." << std::endl; 146 | } else { 147 | std::cout << "PTE test failed." << std::endl; 148 | } 149 | return result ? 0 : 1; 150 | } 151 | 152 | -------------------------------------------------------------------------------- /PeripheralEmulator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 7/2/20. 3 | 4 | // 5 | #ifndef ASSEMBLER_TEST_PERIPHERALEMULATOR_H 6 | #define ASSEMBLER_TEST_PERIPHERALEMULATOR_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include "memory_wrapper.h" 12 | #include "ScreenEmulation.h" 13 | 14 | namespace RISCV_EMULATOR { 15 | 16 | struct VRingDesc { 17 | uint64_t addr; 18 | uint32_t len; 19 | uint16_t flags; 20 | uint16_t next; 21 | }; 22 | 23 | 24 | // Interrupt Controller Addresses. 25 | constexpr uint64_t kPlicAddress = 0x0c000000L; 26 | constexpr uint64_t kPlicClaimAddress = (kPlicAddress + 0x201004); 27 | 28 | struct virtio_blk_outhdr { 29 | uint32_t type; 30 | uint32_t reserved; 31 | uint64_t sector; 32 | }; 33 | 34 | class PeripheralEmulator { 35 | public: 36 | // Memory Mapped Register Addresses. 37 | // Host Interface. 38 | static constexpr uint64_t kToHost0 = 0x80001000; 39 | static constexpr uint64_t kToHost1 = 0x80003000; 40 | static constexpr uint64_t kFromHost = 0x80001040; 41 | 42 | // UART. 43 | static constexpr uint64_t kUartBase = 0x10000000; 44 | static constexpr uint64_t kUartSize = 6; 45 | static constexpr uint8_t kUartLsrReady = 1; 46 | static constexpr uint64_t kUartRhr = kUartBase; 47 | static constexpr uint64_t kUartLsr = kUartBase + 5; 48 | 49 | // Timer. 50 | static constexpr uint64_t kTimerBase = 0x2000000; 51 | static constexpr uint64_t kTimerCmp = kTimerBase + 0x4000; 52 | static constexpr uint64_t kTimerMtime = kTimerBase + 0xbff8; 53 | 54 | // Virtio Disk. 55 | static constexpr int kQueueNumMax = 8; 56 | static constexpr uint64_t kVirtioBase = 0x10001000; 57 | static constexpr uint64_t kVirtioEnd = kVirtioBase + 0x100 - 1; 58 | static constexpr uint64_t kVirtioMmioPageSize = kVirtioBase + 0x28; 59 | static constexpr uint64_t kVirtioMmioQueueSel = kVirtioBase + 0x30; 60 | static constexpr uint64_t kVirtioMmioQueueMax = kVirtioBase + 0x34; 61 | static constexpr uint64_t kVirtioMmioQueueNum = kVirtioBase + 0x38; 62 | static constexpr uint64_t kVirtioMmioQueuePfn = kVirtioBase + 0x40; 63 | static constexpr uint64_t kVirtioMmioQueueNotify = kVirtioBase + 0x50; 64 | 65 | PeripheralEmulator(int mxl); 66 | 67 | void SetMemory(std::shared_ptr memory); 68 | void Emulation(); 69 | 70 | void Initialize(); 71 | void CheckDeviceWrite(uint64_t address, int width, uint64_t data); 72 | void CheckDeviceRead(uint64_t address, int width); 73 | void MemoryMappedValueUpdate(); 74 | 75 | // Host Emulation. 76 | void SetHostEmulationEnable(bool enable); 77 | void HostEmulation(); 78 | 79 | bool GetHostEndFlag(); 80 | 81 | uint64_t GetHostValue(); 82 | 83 | bool GetHostErrorFlag(); 84 | 85 | // Clock Tick. 86 | void TimerTick(); 87 | uint64_t GetTimerInterrupt(); 88 | void ClearTimerInterrupt(); 89 | 90 | // Device Emulation. 91 | void SetDeviceEmulationEnable(bool enable) { device_emulation_enable = enable; } 92 | 93 | // UART interface. 94 | void UartInit(); 95 | void UartEmulation(); 96 | bool GetUartInterruptStatus() {return uart_interrupt_; } 97 | void ClearUartInterruptStatus() { uart_interrupt_ = false;} 98 | bool GetUartBreak() { return uart_break_; } 99 | 100 | // Virtio Disk Emulation. 101 | void VirtioInit(); 102 | void VirtioEmulation(); 103 | void SetDiskImage(std::shared_ptr> disk_image); 104 | bool GetInterruptStatus() {return virtio_interrupt_; } 105 | void ClearInterruptStatus() { virtio_interrupt_ = false;} 106 | 107 | private: 108 | std::shared_ptr memory_; 109 | int mxl_; 110 | bool host_emulation_enable_ = false; 111 | int host_write_ = false; 112 | uint64_t host_value_ = 0; 113 | bool end_flag_ = false; 114 | bool error_flag_ = false; 115 | 116 | // UART emulation enable. 117 | bool device_emulation_enable = false; 118 | bool uart_write_ = false; 119 | bool uart_read_ = false; 120 | bool uart_full_ = false; 121 | uint8_t uart_write_value_ = 0; 122 | std::queue uart_queue; 123 | bool uart_interrupt_ = false; 124 | bool uart_break_ = false; 125 | std::unique_ptr scr_emulation; 126 | void SetUartBuffer(int key); 127 | void ClearUartBuffer(); 128 | void UartInterrupt(); 129 | 130 | // Timer. 131 | uint64_t elapsed_cycles_ = 0; 132 | uint64_t next_cycle_ = 0; 133 | bool timercmp_update_ = true; 134 | bool timer_interrupt_ = false; 135 | 136 | // Virtio 137 | static constexpr int kSectorSize = 512; // 1 sector = 512 bytes. 138 | std::shared_ptr> disk_image_; 139 | bool virtio_write_ = false; 140 | uint64_t virtio_address_ = 0; 141 | uint64_t virtio_width_ = 0; 142 | uint64_t virtio_data_ = 0; 143 | int queue_num_ = 8; 144 | bool virtio_interrupt_ = false; 145 | void VirtioDiskAccess(uint64_t queue_address); 146 | uint16_t get_desc_index(uint64_t avail_address) const; 147 | void read_desc(VRingDesc *desc, uint64_t desc_address, uint16_t desc_index) const; 148 | void read_outhdr(virtio_blk_outhdr *outhdr, uint64_t outhdr_address) const; 149 | void process_disc_access(uint64_t desc, int desc_index); 150 | void disc_access(uint64_t sector, uint64_t buffer_address, uint32_t len, bool write); 151 | void process_used_buffer(uint64_t used_buffer_address, uint16_t index); 152 | }; 153 | } // namespace RISCV_EMULATOR 154 | 155 | #endif // ASSEMBLER_TEST_PERIPHERALEMULATOR_H 156 | -------------------------------------------------------------------------------- /pte.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 2/15/20. 3 | // 4 | 5 | #include "pte.h" 6 | #include "bit_tools.h" 7 | 8 | namespace RISCV_EMULATOR { 9 | 10 | // 32 bit version, 11 | Pte32::Pte32() : Pte32(0) {} 12 | 13 | Pte32::Pte32(uint32_t pte_value) : pte_(pte_value) {} 14 | 15 | Pte32 &Pte32::operator=(uint32_t pte_value) { 16 | this->pte_ = pte_value; 17 | return (*this); 18 | } 19 | 20 | uint32_t Pte32::GetValue() const { 21 | return pte_; 22 | }; 23 | 24 | uint32_t Pte32::GetPpn() const { 25 | return bitcrop(pte_, 22, 10); 26 | } 27 | 28 | uint32_t Pte32::GetPpn1() const { 29 | return bitcrop(pte_, 12, 20); 30 | } 31 | 32 | uint32_t Pte32::GetPpn0() const { 33 | return bitcrop(pte_, 10, 10); 34 | } 35 | 36 | uint32_t Pte32::GetRsw() const { 37 | return bitcrop(pte_, 2, 8); 38 | } 39 | 40 | uint32_t Pte32::GetD() const { 41 | return bitcrop(pte_, 1, 7); 42 | } 43 | 44 | uint32_t Pte32::GetA() const { 45 | return bitcrop(pte_, 1, 6); 46 | } 47 | 48 | uint32_t Pte32::GetG() const { 49 | return bitcrop(pte_, 1, 5); 50 | } 51 | 52 | uint32_t Pte32::GetU() const { 53 | return bitcrop(pte_, 1, 4); 54 | } 55 | 56 | uint32_t Pte32::GetX() const { 57 | return bitcrop(pte_, 1, 3); 58 | } 59 | 60 | uint32_t Pte32::GetW() const { 61 | return bitcrop(pte_, 1, 2); 62 | } 63 | 64 | uint32_t Pte32::GetR() const { 65 | return bitcrop(pte_, 1, 1); 66 | } 67 | 68 | uint32_t Pte32::GetV() const { 69 | return bitcrop(pte_, 1, 0); 70 | } 71 | 72 | void Pte32::SetPpn(uint32_t ppn) { 73 | pte_ = (pte_ & ~GenMask(22, 10)) | (ppn << 10); 74 | } 75 | 76 | void Pte32::SetRsw(uint32_t rsw) { 77 | pte_ = (pte_ & ~GenMask(2, 8)) | (rsw << 8); 78 | } 79 | 80 | 81 | void Pte32::SetD(int value) { 82 | pte_ = SetBit(pte_, value, 7); 83 | } 84 | 85 | void Pte32::SetA(int value) { 86 | pte_ = SetBit(pte_, value, 6); 87 | } 88 | 89 | void Pte32::SetG(int value) { 90 | pte_ = SetBit(pte_, value, 5); 91 | } 92 | 93 | void Pte32::SetU(int value) { 94 | pte_ = SetBit(pte_, value, 4); 95 | } 96 | 97 | void Pte32::SetX(int value) { 98 | pte_ = SetBit(pte_, value, 3); 99 | } 100 | 101 | void Pte32::SetW(int value) { 102 | pte_ = SetBit(pte_, value, 2); 103 | } 104 | 105 | void Pte32::SetR(int value) { 106 | pte_ = SetBit(pte_, value, 1); 107 | } 108 | 109 | void Pte32::SetV(int value) { 110 | pte_ = SetBit(pte_, value, 0); 111 | } 112 | 113 | bool Pte32::IsValid() const { 114 | uint32_t v = this->GetV(); 115 | uint32_t r = this->GetR(); 116 | uint16_t w = this->GetW(); 117 | if (v == 0 || (r == 0 && w == 1)) { 118 | return false; 119 | } else { 120 | return true; 121 | } 122 | } 123 | 124 | bool Pte32::IsLeaf() const { 125 | return GetR() != 0 || GetW() != 0 || GetX() != 0; 126 | } 127 | 128 | // 64 bit version, 129 | Pte64::Pte64() : pte_(0) {} 130 | 131 | Pte64::Pte64(uint64_t pte_value) : pte_(pte_value) {} 132 | 133 | Pte64 &Pte64::operator=(uint64_t pte_value) { 134 | this->pte_ = pte_value; 135 | return (*this); 136 | } 137 | 138 | uint64_t Pte64::GetValue() const { 139 | return pte_; 140 | }; 141 | 142 | uint32_t Pte64::GetPpn() const { 143 | return bitcrop(pte_, 44, 10); 144 | } 145 | 146 | uint32_t Pte64::GetPpn2() const { 147 | return bitcrop(pte_, 26, 28); 148 | } 149 | 150 | uint32_t Pte64::GetPpn1() const { 151 | return bitcrop(pte_, 9, 19); 152 | } 153 | 154 | uint32_t Pte64::GetPpn0() const { 155 | return bitcrop(pte_, 9, 10); 156 | } 157 | 158 | uint32_t Pte64::GetRsw() const { 159 | return bitcrop(pte_, 2, 8); 160 | } 161 | 162 | uint32_t Pte64::GetD() const { 163 | return bitcrop(pte_, 1, 7); 164 | } 165 | 166 | uint32_t Pte64::GetA() const { 167 | return bitcrop(pte_, 1, 6); 168 | } 169 | 170 | uint32_t Pte64::GetG() const { 171 | return bitcrop(pte_, 1, 5); 172 | } 173 | 174 | uint32_t Pte64::GetU() const { 175 | return bitcrop(pte_, 1, 4); 176 | } 177 | 178 | uint32_t Pte64::GetX() const { 179 | return bitcrop(pte_, 1, 3); 180 | } 181 | 182 | uint32_t Pte64::GetW() const { 183 | return bitcrop(pte_, 1, 2); 184 | } 185 | 186 | uint32_t Pte64::GetR() const { 187 | return bitcrop(pte_, 1, 1); 188 | } 189 | 190 | uint32_t Pte64::GetV() const { 191 | return bitcrop(pte_, 1, 0); 192 | } 193 | 194 | void Pte64::SetPpn(uint64_t ppn) { 195 | pte_ = (pte_ & ~GenMask(22, 10)) | (ppn << 10); 196 | } 197 | 198 | void Pte64::SetRsw(uint32_t rsw) { 199 | pte_ = (pte_ & ~GenMask(2, 8)) | ((uint64_t)rsw << 8); 200 | } 201 | 202 | void Pte64::SetD(int value) { 203 | pte_ = SetBit(pte_, value, 7); 204 | } 205 | 206 | void Pte64::SetA(int value) { 207 | pte_ = SetBit(pte_, value, 6); 208 | } 209 | 210 | void Pte64::SetG(int value) { 211 | pte_ = SetBit(pte_, value, 5); 212 | } 213 | 214 | void Pte64::SetU(int value) { 215 | pte_ = SetBit(pte_, value, 4); 216 | } 217 | 218 | void Pte64::SetX(int value) { 219 | pte_ = SetBit(pte_, value, 3); 220 | } 221 | 222 | void Pte64::SetW(int value) { 223 | pte_ = SetBit(pte_, value, 2); 224 | } 225 | 226 | void Pte64::SetR(int value) { 227 | pte_ = SetBit(pte_, value, 1); 228 | } 229 | 230 | void Pte64::SetV(int value) { 231 | pte_ = SetBit(pte_, value, 0); 232 | } 233 | 234 | bool Pte64::IsValid() const { 235 | uint32_t v = this->GetV(); 236 | uint32_t r = this->GetR(); 237 | uint16_t w = this->GetW(); 238 | if (v == 0 || (r == 0 && w == 1)) { 239 | return false; 240 | } else { 241 | return true; 242 | } 243 | } 244 | 245 | bool Pte64::IsLeaf() const { 246 | return GetR() != 0 || GetW() != 0 || GetX() != 0; 247 | } 248 | 249 | } // namespace RISCV_EMULATOR -------------------------------------------------------------------------------- /instruction_encdec.cc: -------------------------------------------------------------------------------- 1 | #include "instruction_encdec.h" 2 | #include "bit_tools.h" 3 | #include "RISCV_cpu.h" 4 | #include 5 | 6 | namespace RISCV_EMULATOR { 7 | 8 | uint32_t RType::GetValue() { 9 | uint32_t value = 0; 10 | value |= funct7 << 25 | rs2 << 20 | rs1 << 15; 11 | value |= funct3 << 12 | rd << 7 | opcode; 12 | return value; 13 | }; 14 | 15 | void RType::SetValue(uint32_t value) { 16 | funct7 = bitcrop(value, 7, 25); 17 | rs2 = bitcrop(value, 5, 20); 18 | rs1 = bitcrop(value, 5, 15); 19 | funct3 = bitcrop(value, 3, 12); 20 | rd = bitcrop(value, 5, 7); 21 | opcode = bitcrop(value, 7, 0); 22 | }; 23 | 24 | uint32_t IType::GetValue() { 25 | uint32_t value = 0; 26 | value |= imm12 << 20 | rs1 << 15; 27 | value |= funct3 << 12 | rd << 7 | opcode; 28 | return value; 29 | }; 30 | 31 | void IType::SetValue(uint32_t value) { 32 | opcode = bitcrop(value, 7, 0); 33 | funct3 = bitcrop(value, 3, 12); 34 | imm12 = GetImm12(value); 35 | // For SLLIW, SRLIW, SRAIW, only the lower 6 bits are relevant. 36 | if ((opcode == OPCODE_ARITHLOG_I || opcode == OPCODE_ARITHLOG_I64) && 37 | (funct3 == FUNC3_SR || funct3 == FUNC3_SL)) { 38 | imm12 &= 0b0111111; 39 | } 40 | rs1 = bitcrop(value, 5, 15); 41 | rd = bitcrop(value, 5, 7); 42 | }; 43 | 44 | uint32_t SType::GetValue() { 45 | uint32_t value = 0; 46 | uint32_t imm11_5_7 = bitcrop(imm12, 7, 5); 47 | uint32_t imm4_0_5 = bitcrop(imm12, 5, 0); 48 | value |= imm11_5_7 << 25 | rs2 << 20 | rs1 << 15; 49 | value |= funct3 << 12 | imm4_0_5 << 7 | opcode; 50 | return value; 51 | }; 52 | 53 | void SType::SetValue(uint32_t value) { 54 | rs2 = bitcrop(value, 5, 20); 55 | rs1 = bitcrop(value, 5, 15); 56 | funct3 = bitcrop(value, 3, 12); 57 | opcode = bitcrop(value, 7, 0); 58 | imm12 = GetStypeImm12(value); 59 | }; 60 | 61 | uint32_t BType::GetValue() { 62 | uint32_t value = 0; 63 | uint32_t imm12_1 = bitcrop(imm13, 1, 12); 64 | uint32_t imm10_5_6 = bitcrop(imm13, 6, 5); 65 | uint32_t imm4_1_4 = bitcrop(imm13, 4, 1); 66 | uint32_t imm11_1 = bitcrop(imm13, 1, 11); 67 | value |= imm12_1 << 31 | imm10_5_6 << 25; 68 | value |= rs2 << 20 | rs1 << 15; 69 | value |= funct3 << 12 | imm4_1_4 << 8 | imm11_1 << 7 | opcode; 70 | return value; 71 | }; 72 | 73 | void BType::SetValue(uint32_t value) { 74 | rs2 = bitcrop(value, 5, 20); 75 | rs1 = bitcrop(value, 5, 15); 76 | funct3 = bitcrop(value, 3, 12); 77 | opcode = bitcrop(value, 7, 0); 78 | imm13 = GetImm13(value); 79 | }; 80 | 81 | uint32_t UType::GetValue() { 82 | uint32_t value = 0; 83 | value |= (imm20 & (0xFFFFF)) << 12; 84 | value |= rd << 7 | opcode; 85 | return value; 86 | }; 87 | 88 | void UType::SetValue(uint32_t value) { 89 | imm20 = GetImm20(value); 90 | rd = bitcrop(value, 5, 7); 91 | opcode = bitcrop(value, 7, 0); 92 | }; 93 | 94 | uint32_t JType::GetValue() { 95 | uint32_t value = 0; 96 | uint32_t imm20_1 = bitcrop(imm21, 1, 20); 97 | uint32_t imm10_1_10 = bitcrop(imm21, 10, 1); 98 | uint32_t imm11_1 = bitcrop(imm21, 1, 11); 99 | uint32_t imm19_12 = bitcrop(imm21, 8, 12); 100 | value |= 101 | (imm20_1 << 31) | (imm10_1_10 << 21) | (imm11_1 << 20) | (imm19_12 << 12); 102 | value |= rd << 7 | opcode; 103 | return value; 104 | }; 105 | 106 | void JType::SetValue(uint32_t value) { 107 | imm21 = GetImm21(value); 108 | rd = bitcrop(value, 5, 7); 109 | opcode = bitcrop(value, 7, 0); 110 | }; 111 | 112 | uint32_t GetOpcode(uint32_t ir) { return bitcrop(ir, 7, 0); } 113 | 114 | uint32_t GetRd(uint32_t ir) { return bitcrop(ir, 5, 7); } 115 | 116 | uint32_t GetRs1(uint32_t ir) { return bitcrop(ir, 5, 15); } 117 | 118 | uint32_t GetRs2(uint32_t ir) { return bitcrop(ir, 5, 20); } 119 | 120 | int32_t GetImm12(uint32_t ir) { 121 | int32_t imm12 = SignExtend(bitcrop(ir, 12, 20), 12); 122 | return imm12; 123 | } 124 | 125 | int32_t GetCsr(uint32_t ir) { 126 | int32_t csr = bitcrop(ir, 12, 20); 127 | return csr; 128 | } 129 | 130 | int32_t GetImm(uint32_t ir) { 131 | int32_t imm = 0; 132 | uint16_t opcode = bitcrop(ir, 7, 0); 133 | uint8_t funct3 = bitcrop(ir, 3, 12); 134 | imm = GetImm12(ir); 135 | switch (opcode) { 136 | case OPCODE_ARITHLOG_I: 137 | case OPCODE_ARITHLOG_I64: 138 | if (funct3 == FUNC3_SL || funct3 == FUNC3_SR) { 139 | imm = imm & 0b111111; 140 | } 141 | break; 142 | case OPCODE_B: 143 | imm = GetImm13(ir); 144 | break; 145 | case OPCODE_LUI: 146 | case OPCODE_AUIPC: 147 | imm = GetImm20(ir) << 12; 148 | break; 149 | case OPCODE_J: 150 | imm = GetImm21(ir); 151 | break; 152 | case OPCODE_S: 153 | imm = GetStypeImm12(ir); 154 | break; 155 | } 156 | return imm; 157 | } 158 | 159 | int32_t GetImm13(uint32_t ir) { 160 | int32_t imm13 = 0; 161 | imm13 |= BitShift(ir, 1, 31, 12) | BitShift(ir, 6, 25, 5); 162 | imm13 |= BitShift(ir, 4, 8, 1) | BitShift(ir, 1, 7, 11); 163 | imm13 = SignExtend(imm13, 13); 164 | return imm13; 165 | } 166 | 167 | int32_t GetImm21(uint32_t ir) { 168 | int32_t imm21 = 0; 169 | uint32_t offset19_12 = bitcrop(ir, 8, 12); 170 | uint32_t offset11 = bitcrop(ir, 1, 20); 171 | uint32_t offset10_1 = bitcrop(ir, 10, 21); 172 | uint32_t offset20 = bitcrop(ir, 1, 31); 173 | imm21 = (offset20 << 20) | (offset19_12 << 12) | (offset11 << 11) | 174 | (offset10_1 << 1); 175 | // imm21 = (bitcrop(ir, 1, 31) << 20) | (bitcrop(ir, 8, 12) << 12); 176 | // imm21 |= (bitcrop(ir, 1, 20) << 11) | (bitcrop(ir, 10, 21) << 1); 177 | 178 | imm21 = SignExtend(imm21, 21); 179 | return imm21; 180 | } 181 | 182 | int32_t GetStypeImm12(uint32_t ir) { 183 | int32_t imm12 = (bitcrop(ir, 7, 25) << 5) | bitcrop(ir, 5, 7); 184 | imm12 = SignExtend(imm12, 12); 185 | return imm12; 186 | } 187 | 188 | uint32_t GetShamt(uint32_t ir) { 189 | uint32_t shamt = bitcrop(ir, 6, 20); 190 | return shamt; 191 | } 192 | 193 | uint32_t GetImm20(uint32_t ir) { 194 | uint32_t imm20 = bitcrop(ir, 20, 12); 195 | imm20 = SignExtend(imm20, 20); 196 | return imm20; 197 | } 198 | 199 | } // namespace RISCV_EMULATOR -------------------------------------------------------------------------------- /Mmu.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 5/10/20. 3 | // 4 | 5 | #include 6 | #include "Mmu.h" 7 | #include "bit_tools.h" 8 | #include "pte.h" 9 | 10 | namespace RISCV_EMULATOR { 11 | 12 | void Mmu::SetMemory(std::shared_ptr memory) { 13 | memory_ = memory; 14 | } 15 | 16 | void Mmu::SetPrivilege(const PrivilegeMode privilege) { 17 | privilege_ = privilege; 18 | } 19 | 20 | uint64_t Mmu::VirtualToPhysical(uint64_t virtual_address, uint64_t satp, 21 | bool write_access) { 22 | page_fault_ = false; 23 | faulting_address_ = 0; 24 | // privilege_ here must take MPRV into account. 25 | if (privilege_ == PrivilegeMode::MACHINE_MODE) { 26 | return virtual_address; 27 | } 28 | if (mxl_ == 1) { 29 | return VirtualToPhysical32(virtual_address, satp, write_access); 30 | } else { 31 | // if (xlen == 64) { 32 | return VirtualToPhysical64(virtual_address, satp, write_access); 33 | } 34 | } 35 | 36 | uint64_t Mmu::VirtualToPhysical64(uint64_t virtual_address, uint64_t satp, 37 | bool write_access) { 38 | // TODO: Implement v48 MMU emulation. 39 | constexpr int kPteSize = 8; 40 | uint64_t physical_address = virtual_address; 41 | MemoryWrapper &mem = *memory_; 42 | // uint64_t satp = csrs_[SATP]; 43 | uint8_t mode = bitcrop(satp, 4, 60); 44 | if (mode == 0) { 45 | return physical_address; 46 | } else if (mode != 8) { 47 | std::cerr << "Unsupported virtual address translation mode (" << mode << ")" 48 | << std::endl; 49 | page_fault_ = true; 50 | faulting_address_ = virtual_address; 51 | return physical_address; 52 | } 53 | // uint16_t asid = bitcrop(sptbr, 9, 22); 54 | uint64_t ppn = bitcrop(satp, 44, 0); 55 | uint64_t vpn[3]; 56 | vpn[2] = bitcrop(virtual_address, 9, 30); 57 | vpn[1] = bitcrop(virtual_address, 9, 21); 58 | vpn[0] = bitcrop(virtual_address, 9, 12); 59 | uint16_t offset = bitcrop(virtual_address, 12, 0); 60 | Pte64 pte; 61 | int level; 62 | uint64_t pte_address; 63 | constexpr int k64BitMmuLevels = 3; 64 | for (level = k64BitMmuLevels - 1; level >= 0; --level) { 65 | pte_address = ppn * kPageSize + vpn[level] * kPteSize; 66 | uint64_t pte_value = mem.Read64(pte_address); 67 | pte = pte_value; 68 | if (!pte.IsValid()) { 69 | std::cerr << "PTE not valid." << std::endl; 70 | std::cerr << "PTE = " << std::hex << pte.GetValue() << std::endl; 71 | std::cerr << "virtual_address = " << virtual_address << std::endl; 72 | std::cerr << "PTE entry address = " << pte_address << std::endl; 73 | page_fault_ = true; 74 | faulting_address_ = virtual_address; 75 | return physical_address; 76 | } 77 | if (pte.IsLeaf()) { 78 | break; 79 | } 80 | if (level == 0) { 81 | std::cerr << "Non-leaf block in level 0." << std::endl; 82 | page_fault_ = true; 83 | faulting_address_ = virtual_address; 84 | return physical_address; 85 | } 86 | ppn = pte.GetPpn(); 87 | } 88 | if ((level > 0 && pte.GetPpn0() != 0) || (level > 1 && pte.GetPpn1() != 0)) { 89 | // Misaligned superpage. 90 | std::cerr << "Misaligned super page." << std::endl; 91 | page_fault_ = true; 92 | faulting_address_ = virtual_address; 93 | return physical_address; 94 | } 95 | // Access and Dirty bit process; 96 | pte.SetA(1); 97 | if (write_access) { 98 | pte.SetD(1); 99 | } 100 | mem.Write64(pte_address, pte.GetValue()); 101 | // TODO: Add PMP check. (Page 70 of RISC-V Privileged Architectures Manual Vol. II.) 102 | uint64_t ppn2 = pte.GetPpn2(); 103 | uint64_t ppn1 = (level > 1) ? vpn[1] : pte.GetPpn1(); 104 | uint32_t ppn0 = (level > 0) ? vpn[0] : pte.GetPpn0(); 105 | physical_address = (ppn2 << 30) | (ppn1 << 21) | ((uint64_t)ppn0 << 12) | offset; 106 | 107 | constexpr uint64_t kMask56Bit = 0x00FFFFFFFFFFFFFF; 108 | uint64_t physical_address_64bit = static_cast(physical_address & kMask56Bit); 109 | return physical_address_64bit; 110 | } 111 | 112 | uint64_t Mmu::VirtualToPhysical32(uint64_t virtual_address, uint64_t satp, 113 | bool write_access) { 114 | constexpr int kPteSize = 4; 115 | uint64_t physical_address = virtual_address; 116 | MemoryWrapper &mem = *memory_; 117 | // uint32_t satp = csrs_[SATP]; 118 | uint8_t mode = bitcrop(satp, 1, 31); 119 | if (mode == 0) { 120 | return physical_address; 121 | } 122 | // uint16_t asid = bitcrop(sptbr, 9, 22); 123 | uint32_t ppn = bitcrop(satp, 22, 0); 124 | uint16_t vpn1 = bitcrop(virtual_address, 10, 22); 125 | uint16_t vpn0 = bitcrop(virtual_address, 10, 12); 126 | uint16_t offset = bitcrop(virtual_address, 12, 0); 127 | Pte32 pte; 128 | int level; 129 | uint32_t vpn = vpn1; 130 | uint32_t pte_address; 131 | for (level = kMmuLevels - 1; level >= 0; --level) { 132 | pte_address = ppn * kPageSize + vpn * kPteSize; 133 | uint32_t pte_value = mem.Read32(pte_address); 134 | pte = pte_value; 135 | if (!pte.IsValid()) { 136 | // TODO: Do page-fault exception. 137 | std::cerr << "PTE not valid." << std::endl; 138 | std::cerr << "PTE = " << std::hex << pte.GetValue() << std::endl; 139 | std::cerr << "virtual_address = " << virtual_address << std::endl; 140 | std::cerr << "PTE entry address = " << pte_address << std::endl; 141 | page_fault_ = true; 142 | faulting_address_ = virtual_address; 143 | return physical_address; 144 | } 145 | if (pte.IsLeaf()) { 146 | break; 147 | } 148 | if (level == 0) { 149 | std::cerr << "Non-leaf block in level 0." << std::endl; 150 | page_fault_ = true; 151 | faulting_address_ = virtual_address; 152 | return physical_address; 153 | } 154 | ppn = pte.GetPpn(); 155 | vpn = vpn0; 156 | } 157 | if (level > 0 && pte.GetPpn0() != 0) { 158 | // Misaligned superpage. 159 | // TODO: Do page-fault exception. 160 | std::cerr << "Misaligned super page." << std::endl; 161 | page_fault_ = true; 162 | faulting_address_ = virtual_address; 163 | return physical_address; 164 | } 165 | // TODO(moizumi): Is this the right place to check the dirty bit? 166 | // Access and Dirty bit process; 167 | pte.SetA(1); 168 | if (write_access) { 169 | pte.SetD(1); 170 | } 171 | mem.Write32(pte_address, pte.GetValue()); 172 | // TODO: Add PMP check. (Page 70 of RISC-V Privileged Architectures Manual Vol. II.) 173 | uint64_t ppn1 = pte.GetPpn1(); 174 | uint32_t ppn0 = (level == 1) ? vpn0 : pte.GetPpn0(); 175 | physical_address = (ppn1 << 22) | ((uint64_t)ppn0 << 12) | offset; 176 | 177 | uint64_t physical_address_64bit = static_cast(physical_address & 178 | 0xFFFFFFFF); 179 | return physical_address_64bit; 180 | } 181 | 182 | } //namespace RISCV_EMULATOR 183 | -------------------------------------------------------------------------------- /tests/memory_wrapper_test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 1/19/20. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "memory_wrapper.h" 10 | 11 | using namespace RISCV_EMULATOR; 12 | 13 | namespace { 14 | constexpr size_t kMaxTestSize = 16 * 1024 * 1024; 15 | constexpr size_t kSmallTestSize = 1 * 1024 * 1024; 16 | constexpr int kTestCycle = 8; 17 | constexpr int kSmallTestCycle = 100; 18 | } // namespace anonumous 19 | 20 | namespace { 21 | 22 | int GetHash64(uint32_t val) { 23 | static std::hash h; 24 | return h(val); 25 | } 26 | 27 | int GetHash32(uint32_t val) { 28 | static std::hash h; 29 | return h(val); 30 | } 31 | 32 | int GetHash8(uint32_t val) { 33 | return GetHash32(val) & 0xFF; 34 | } 35 | 36 | int GetHash16(uint32_t val) { 37 | return GetHash32(val) & 0xFFFF; 38 | } 39 | 40 | bool MemoryWrapperTest(size_t start, size_t end, int val) { 41 | bool result = true; 42 | MemoryWrapper mw; 43 | for (size_t j = start; j < end; j++) { 44 | mw.WriteByte(j, GetHash8(j + val)); 45 | } 46 | 47 | for (size_t j = start; j < end && result; j++) { 48 | bool local_result = mw.ReadByte(j) == GetHash8(j + val); 49 | if (!local_result) { 50 | std::cout << "mw.Read(" << j << ") = " << static_cast(mw.ReadByte(j)) 51 | << ", expectation = " << GetHash8(j) 52 | << std::endl; 53 | } 54 | result &= local_result; 55 | } 56 | if (!result) { 57 | std::cout << "Memory Wrapper Test failed. (" << start << ", " << end 58 | << ")\n"; 59 | } 60 | return result; 61 | } 62 | 63 | 64 | std::mt19937 rnd; 65 | constexpr int kSeed = 155719; 66 | 67 | void InitRandom() { 68 | rnd.seed(kSeed); 69 | } 70 | 71 | 72 | bool RunTestSingle(size_t start, size_t end, int val, bool verbose = false) { 73 | bool result = true; 74 | if (verbose) { 75 | std::cout << "Start: " << start << ", end: " << end << std::endl; 76 | } 77 | result = result && MemoryWrapperTest(start, end, val); 78 | return result; 79 | } 80 | 81 | bool RunTests(int test_cycle, size_t test_size, bool verbose = false) { 82 | bool result = true; 83 | for (int i = 0; i < test_cycle && result; i++) { 84 | InitRandom(); 85 | size_t start = 0, end = 0; 86 | int val = 0; 87 | while (end <= start || (end - start) > test_size) { 88 | start = rand(); 89 | end = rand(); 90 | val = rand(); 91 | } 92 | result = result && RunTestSingle(start, end, val, verbose); 93 | if (i % 10 == 0 && i > 0) { 94 | std::cout << i << " tests finished." << std::endl; 95 | } 96 | } 97 | return result; 98 | } 99 | 100 | bool Run32bitTest(const size_t start, const size_t end, const size_t val, 101 | const bool verbose) { 102 | if (verbose) { 103 | std::cout << "Start: " << start << ", end: " << end << std::endl; 104 | } 105 | int result = true; 106 | MemoryWrapper mw; 107 | for (size_t i = start; i < end; i += 4) { 108 | mw.Write32(i, GetHash32(i + val)); 109 | } 110 | for (size_t i = start; i < end && result; i += 4) { 111 | uint32_t expectation = GetHash32(i + val); 112 | uint32_t read_value = mw.Read32(i); 113 | result &= read_value == expectation; 114 | if (!result) { 115 | std::cout << "At i = " << i << ", expectation = " << std::hex 116 | << expectation << ", actual = " << read_value << std::endl; 117 | } 118 | } 119 | return result; 120 | } 121 | 122 | bool Run32bitTest(int test_cycle, size_t test_size, bool verbose = false) { 123 | bool result = true; 124 | for (int i = 0; i < test_cycle && result; i++) { 125 | InitRandom(); 126 | size_t start = 0, end = 0; 127 | int val = 0; 128 | while (end <= start || (end - start) > test_size) { 129 | start = (rand() >> 2) << 2; 130 | end = rand(); 131 | val = rand(); 132 | } 133 | result = result && Run32bitTest(start, end, val, verbose); 134 | if (i % 10 == 0 && i > 0) { 135 | std::cout << i << " tests finished." << std::endl; 136 | } 137 | } 138 | return result; 139 | } 140 | 141 | bool Run64bitTest(const size_t start, const size_t end, const size_t val, 142 | const bool verbose) { 143 | if (verbose) { 144 | std::cout << "Start: " << start << ", end: " << end << std::endl; 145 | } 146 | int result = true; 147 | MemoryWrapper mw; 148 | for (size_t i = start; i < end; i += 8) { 149 | mw.Write64(i, GetHash64(i + val)); 150 | } 151 | for (size_t i = start; i < end && result; i += 8) { 152 | uint64_t expectation = GetHash64(i + val); 153 | uint64_t read_value = mw.Read64(i); 154 | result &= read_value == expectation; 155 | if (!result) { 156 | std::cout << "At i = " << i << ", expectation = " << std::hex 157 | << expectation << ", actual = " << read_value << std::endl; 158 | } 159 | } 160 | return result; 161 | } 162 | 163 | bool Run64bitTest(int test_cycle, size_t test_size, bool verbose = false) { 164 | bool result = true; 165 | for (int i = 0; i < test_cycle && result; i++) { 166 | InitRandom(); 167 | size_t start = 0, end = 0; 168 | int val = 0; 169 | while (end <= start || (end - start) > test_size) { 170 | start = (rand() >> 3) << 3; 171 | end = rand(); 172 | val = rand(); 173 | } 174 | result = result && Run64bitTest(start, end, val, verbose); 175 | if (i % 10 == 0 && i > 0) { 176 | std::cout << i << " tests finished." << std::endl; 177 | } 178 | } 179 | return result; 180 | } 181 | 182 | bool Run16bitTest(const size_t start, const size_t end, const size_t val, 183 | const bool verbose) { 184 | if (verbose) { 185 | std::cout << "Start: " << start << ", end: " << end << std::endl; 186 | } 187 | int result = true; 188 | MemoryWrapper mw; 189 | for (size_t i = start; i < end; i += 2) { 190 | uint16_t hash_value = GetHash16(i + val); 191 | mw.Write16(i, hash_value); 192 | } 193 | for (size_t i = start; i < end && result; i += 2) { 194 | uint16_t expectation = GetHash16(i + val); 195 | uint16_t read_value = mw.Read16(i); 196 | result &= read_value == expectation; 197 | if (!result) { 198 | std::cout << "At i = " << i << ", expectation = " << std::hex 199 | << expectation << ", actual = " << read_value << std::endl; 200 | } 201 | } 202 | return result; 203 | } 204 | 205 | bool Run16bitTest(int test_cycle, size_t test_size, bool verbose = false) { 206 | bool result = true; 207 | for (int i = 0; i < test_cycle && result; i++) { 208 | InitRandom(); 209 | size_t start = 0, end = 0; 210 | int val = 0; 211 | while (end <= start || (end - start) > test_size) { 212 | start = (rand() >> 2) << 2; 213 | end = rand(); 214 | val = rand(); 215 | } 216 | result = result && Run16bitTest(start, end, val, verbose); 217 | if (i % 10 == 0 && i > 0) { 218 | std::cout << i << " tests finished." << std::endl; 219 | } 220 | } 221 | return result; 222 | } 223 | 224 | } // namespace anonymous 225 | 226 | int main() { 227 | bool result = RunTests(kSmallTestCycle, kSmallTestSize, false); 228 | if (result) { 229 | std::cout << kSmallTestCycle << " small tests passed." << std::endl; 230 | } else { 231 | std::cout << "Small test failed." << std::endl; 232 | } 233 | result = result && RunTests(kTestCycle, kMaxTestSize, true); 234 | if (result) { 235 | std::cout << "memory_wrapper test pass." << std::endl; 236 | } else { 237 | std::cout << "memory_wrapper test fail." << std::endl; 238 | } 239 | result = result && Run32bitTest(kSmallTestCycle, kSmallTestSize, false); 240 | if (result) { 241 | std::cout << "32 bit read/write test pass." << std::endl; 242 | } else { 243 | std::cout << "32 bit read/write test fail." << std::endl; 244 | } 245 | result = result && Run64bitTest(kSmallTestCycle, kSmallTestSize, false); 246 | if (result) { 247 | std::cout << "64 bit read/write test pass." << std::endl; 248 | } else { 249 | std::cout << "64 bit read/write test fail." << std::endl; 250 | } 251 | result = result && Run16bitTest(kSmallTestCycle, kSmallTestSize, false); 252 | if (result) { 253 | std::cout << "16 bit read/write test pass." << std::endl; 254 | } else { 255 | std::cout << "16 bit read/write test fail." << std::endl; 256 | } 257 | return result ? 0 : 1; 258 | } -------------------------------------------------------------------------------- /tests/assembler.h: -------------------------------------------------------------------------------- 1 | #ifndef ASSEMBLER_H 2 | #define ASSEMBLER_H 3 | 4 | #include "memory_wrapper.h" 5 | #include 6 | #include 7 | 8 | using namespace RISCV_EMULATOR; 9 | 10 | namespace CPU_TEST { 11 | 12 | uint64_t AddCmd(MemoryWrapper &mem, uint64_t address, uint32_t cmd); 13 | 14 | uint64_t AddCmdCType(MemoryWrapper &mem, uint64_t address, uint16_t cmd); 15 | 16 | uint32_t AsmAdd(uint32_t rd, uint32_t rs1, uint32_t rs2); 17 | 18 | uint32_t AsmAddw(uint32_t rd, uint32_t rs1, uint32_t rs2); 19 | 20 | uint32_t AsmSub(uint32_t rd, uint32_t rs1, uint32_t rs2); 21 | 22 | uint32_t AsmSubw(uint32_t rd, uint32_t rs1, uint32_t rs2); 23 | 24 | uint32_t AsmAnd(uint32_t rd, uint32_t rs1, uint32_t rs2); 25 | 26 | uint32_t AsmOr(uint32_t rd, uint32_t rs1, uint32_t rs2); 27 | 28 | uint32_t AsmXor(uint32_t rd, uint32_t rs1, uint32_t rs2); 29 | 30 | uint32_t AsmSll(uint32_t rd, uint32_t rs1, uint32_t rs2); 31 | 32 | uint32_t AsmSllw(uint32_t rd, uint32_t rs1, uint32_t rs2); 33 | 34 | uint32_t AsmSrl(uint32_t rd, uint32_t rs1, uint32_t rs2); 35 | 36 | uint32_t AsmSrlw(uint32_t rd, uint32_t rs1, uint32_t rs2); 37 | 38 | uint32_t AsmSra(uint32_t rd, uint32_t rs1, uint32_t rs2); 39 | 40 | uint32_t AsmSraw(uint32_t rd, uint32_t rs1, uint32_t rs2); 41 | 42 | uint32_t AsmSlt(uint32_t rd, uint32_t rs1, uint32_t rs2); 43 | 44 | uint32_t AsmSltu(uint32_t rd, uint32_t rs1, uint32_t rs2); 45 | 46 | uint32_t AsmMret(); 47 | 48 | uint32_t AsmAddi(uint32_t rd, uint32_t rs1, int32_t imm12); 49 | 50 | uint32_t AsmAddiw(uint32_t rd, uint32_t rs1, int32_t imm12); 51 | 52 | uint32_t AsmAndi(uint32_t rd, uint32_t rs1, int32_t imm12); 53 | 54 | uint32_t AsmOri(uint32_t rd, uint32_t rs1, int32_t imm12); 55 | 56 | uint32_t AsmXori(uint32_t rd, uint32_t rs1, int32_t imm12); 57 | 58 | uint32_t AsmSlli(uint32_t rd, uint32_t rs1, int32_t imm12); 59 | 60 | uint32_t AsmSlliw(uint32_t rd, uint32_t rs1, int32_t imm12); 61 | 62 | uint32_t AsmSrli(uint32_t rd, uint32_t rs1, int32_t imm12); 63 | 64 | uint32_t AsmSrliw(uint32_t rd, uint32_t rs1, int32_t imm12); 65 | 66 | uint32_t AsmSrai(uint32_t rd, uint32_t rs1, int32_t imm12); 67 | 68 | uint32_t AsmSraiw(uint32_t rd, uint32_t rs1, int32_t imm12); 69 | 70 | uint32_t AsmSlti(uint32_t rd, uint32_t rs1, int32_t imm12); 71 | 72 | uint32_t AsmSltiu(uint32_t rd, uint32_t rs1, int32_t imm12); 73 | 74 | uint32_t AsmBeq(uint32_t rs1, uint32_t rs2, int32_t offset13); 75 | 76 | uint32_t AsmBge(uint32_t rs1, uint32_t rs2, int32_t offset13); 77 | 78 | uint32_t AsmBgeu(uint32_t rs1, uint32_t rs2, int32_t offset13); 79 | 80 | uint32_t AsmBlt(uint32_t rs1, uint32_t rs2, int32_t offset13); 81 | 82 | uint32_t AsmBltu(uint32_t rs1, uint32_t rs2, int32_t offset13); 83 | 84 | uint32_t AsmBne(uint32_t rs1, uint32_t rs2, int32_t offset13); 85 | 86 | uint32_t AsmJal(uint32_t rd, int32_t offset21); 87 | 88 | uint32_t AsmLb(uint32_t rd, uint32_t rs1, int32_t offset12); 89 | 90 | uint32_t AsmLbu(uint32_t rd, uint32_t rs1, int32_t offset12); 91 | 92 | uint32_t AsmLh(uint32_t rd, uint32_t rs1, int32_t offset12); 93 | 94 | uint32_t AsmLhu(uint32_t rd, uint32_t rs1, int32_t offset12); 95 | 96 | uint32_t AsmLw(uint32_t rd, uint32_t rs1, int32_t offset12); 97 | 98 | uint32_t AsmLd(uint32_t rd, uint32_t rs1, int32_t offset12); 99 | 100 | uint32_t AsmLwu(uint32_t rd, uint32_t rs1, int32_t offset12); 101 | 102 | uint32_t AsmSw(uint32_t rs1, uint32_t rs2, int32_t offset12); 103 | 104 | uint32_t AsmSh(uint32_t rs1, uint32_t rs2, int32_t offset12); 105 | 106 | uint32_t AsmSb(uint32_t rs1, uint32_t rs2, int32_t offset12); 107 | 108 | uint32_t AsmSd(uint32_t rs1, uint32_t rs2, int32_t offset12); 109 | 110 | uint32_t AsmJalr(uint32_t rd, uint32_t rs1, int32_t offset12); 111 | 112 | uint32_t AsmEbreak(); 113 | 114 | uint32_t AsmEcall(); 115 | 116 | uint32_t AsmCsrrc(uint32_t rd, uint32_t rs1, int32_t offset12); 117 | 118 | uint32_t AsmCsrrci(uint32_t rd, uint32_t zimm, int32_t offset12); 119 | 120 | uint32_t AsmCsrrs(uint32_t rd, uint32_t rs1, int32_t offset12); 121 | 122 | uint32_t AsmCsrrsi(uint32_t rd, uint32_t zimm, int32_t offset12); 123 | 124 | uint32_t AsmCsrrw(uint32_t rd, uint32_t rs1, int32_t offset12); 125 | 126 | uint32_t AsmCsrrwi(uint32_t rd, uint32_t zimm, int32_t offset12); 127 | 128 | uint32_t AsmFence(uint32_t pred, uint32_t succ); 129 | 130 | uint32_t AsmFencei(); 131 | 132 | uint32_t AsmLui(uint32_t rd, int32_t imm20); 133 | 134 | uint32_t AsmAuipc(uint32_t rd, int32_t imm20); 135 | 136 | uint32_t AsmMul(uint32_t rd, uint32_t rs1, uint32_t rs2); 137 | 138 | uint32_t AsmMulh(uint32_t rd, uint32_t rs1, uint32_t rs2); 139 | 140 | uint32_t AsmMulhsu(uint32_t rd, uint32_t rs1, uint32_t rs2); 141 | 142 | uint32_t AsmMulhu(uint32_t rd, uint32_t rs1, uint32_t rs2); 143 | 144 | uint32_t AsmMulw(uint32_t rd, uint32_t rs1, uint32_t rs2); 145 | 146 | uint32_t AsmDiv(uint32_t rd, uint32_t rs1, uint32_t rs2); 147 | 148 | uint32_t AsmDivu(uint32_t rd, uint32_t rs1, uint32_t rs2); 149 | 150 | uint32_t AsmDivuw(uint32_t rd, uint32_t rs1, uint32_t rs2); 151 | 152 | uint32_t AsmDivw(uint32_t rd, uint32_t rs1, uint32_t rs2); 153 | 154 | uint32_t AsmRem(uint32_t rd, uint32_t rs1, uint32_t rs2); 155 | 156 | uint32_t AsmRemu(uint32_t rd, uint32_t rs1, uint32_t rs2); 157 | 158 | uint32_t AsmRemw(uint32_t rd, uint32_t rs1, uint32_t rs2); 159 | 160 | uint32_t AsmRemuw(uint32_t rd, uint32_t rs1, uint32_t rs2); 161 | 162 | // C Instructions. 163 | 164 | uint16_t AsmCAdd(uint32_t rd, uint32_t rs2); 165 | 166 | uint16_t AsmCEbreak(); 167 | 168 | uint16_t AsmCFldsp(uint32_t rd, uint32_t uimm); 169 | 170 | uint16_t AsmCFlwsp(uint32_t rd, uint32_t uimm); 171 | 172 | uint16_t AsmCFsdsp(uint32_t rs2, uint32_t uimm); 173 | 174 | uint16_t AsmCFswsp(uint32_t rs2, uint32_t uimm); 175 | 176 | uint16_t AsmCJalr(uint32_t rs1); 177 | 178 | uint16_t AsmCJr(uint32_t rs1); 179 | 180 | uint16_t AsmCLdsp(uint32_t rd, uint32_t uimm); 181 | 182 | uint16_t AsmCLwsp(uint32_t rd, uint32_t uimm); 183 | 184 | uint16_t AsmCMv(uint32_t rd, uint32_t rs2); 185 | 186 | uint16_t AsmCSdsp(uint32_t rs2, uint32_t uimm); 187 | 188 | uint16_t AsmCSlli(uint32_t rd, uint32_t uimm); 189 | 190 | uint16_t AsmCSwsp(uint32_t rs2, uint32_t uimm); 191 | 192 | uint16_t AsmCAddi(uint32_t rd, int32_t imm); 193 | 194 | uint16_t AsmCAddi16sp(int32_t imm); 195 | 196 | uint16_t AsmCAddiw(uint32_t rd, int32_t imm); 197 | 198 | uint16_t AsmCAnd(uint32_t rd, uint32_t rs2); 199 | 200 | uint16_t AsmCAnd(uint32_t rd, uint32_t rs2); 201 | 202 | uint16_t AsmCAddw(uint32_t rd, uint32_t rs2); 203 | 204 | uint16_t AsmCOr(uint32_t rd, uint32_t rs2); 205 | 206 | uint16_t AsmCSub(uint32_t rd, uint32_t rs2); 207 | 208 | uint16_t AsmCSubw(uint32_t rd, uint32_t rs2); 209 | 210 | uint16_t AsmCXor(uint32_t rd, uint32_t rs2); 211 | 212 | uint16_t AsmCAndi(uint32_t rd, int32_t imm); 213 | 214 | uint16_t AsmCSrai(uint32_t rd, int32_t imm); 215 | 216 | uint16_t AsmCSrli(uint32_t rd, int32_t imm); 217 | 218 | uint16_t AsmCBeqz(uint32_t rs1, int32_t offset); 219 | 220 | uint16_t AsmCBnez(uint32_t rs1, int32_t offset); 221 | 222 | uint16_t AsmCJ(int32_t imm); 223 | 224 | uint16_t AsmCJal(int32_t imm); 225 | 226 | uint16_t AsmCLi(uint32_t rd, int32_t imm); 227 | 228 | uint16_t AsmCLui(uint32_t rd, int32_t imm); 229 | 230 | uint16_t AsmCAddi4spn(uint32_t rd, uint32_t uimm); 231 | 232 | uint16_t AsmCFld(uint32_t rd, uint32_t rs1, uint32_t uimm); 233 | 234 | uint16_t AsmCFlw(uint32_t rd, uint32_t rs1, uint32_t uimm); 235 | 236 | uint16_t AsmCFsd(uint32_t rs1, uint32_t rs2, uint32_t uimm); 237 | 238 | uint16_t AsmCFsw(uint32_t rs1, uint32_t rs2, uint32_t uimm); 239 | 240 | uint16_t AsmCLd(uint32_t rd, uint32_t rs1, uint32_t uimm); 241 | 242 | uint16_t AsmCLw(uint32_t rd, uint32_t rs1, uint32_t uimm); 243 | 244 | uint16_t AsmCSd(uint32_t rs1, uint32_t rs2, uint32_t uimm); 245 | 246 | uint16_t AsmCsw(uint32_t rs1, uint32_t rs2, uint32_t uimm); 247 | 248 | // Atomic instructions. 249 | 250 | uint32_t AsmAmoAddd(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 251 | 252 | uint32_t AsmAmoAddw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 253 | 254 | uint32_t AsmAmoAndd(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 255 | 256 | uint32_t AsmAmoAndw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 257 | 258 | uint32_t AsmAmoMaxd(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 259 | 260 | uint32_t AsmAmoMaxw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 261 | 262 | uint32_t AsmAmoMaxud(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 263 | 264 | uint32_t AsmAmoMaxuw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 265 | 266 | uint32_t AsmAmoMind(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 267 | 268 | uint32_t AsmAmoMinw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 269 | 270 | uint32_t AsmAmoMinud(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 271 | 272 | uint32_t AsmAmoMinuw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 273 | 274 | uint32_t AsmAmoOrd(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 275 | 276 | uint32_t AsmAmoOrw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 277 | 278 | uint32_t AsmAmoSwapd(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 279 | 280 | uint32_t AsmAmoSwapw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 281 | 282 | uint32_t AsmAmoXord(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 283 | 284 | uint32_t AsmAmoXorw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl); 285 | 286 | 287 | } // namespace RISCV_EMULATOR 288 | 289 | #endif // ASSEMBLER_H -------------------------------------------------------------------------------- /Windows/m99_riscv_emulator.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 15.0 36 | {A2D52198-117C-4574-882D-EB1B5D221AD4} 37 | Win32Proj 38 | m99riscvemulator 39 | 10.0 40 | 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | Application 57 | true 58 | v142 59 | Unicode 60 | 61 | 62 | Application 63 | false 64 | v142 65 | true 66 | Unicode 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | true 88 | 89 | 90 | true 91 | 92 | 93 | false 94 | 95 | 96 | false 97 | 98 | 99 | 100 | NotUsing 101 | Level3 102 | Disabled 103 | true 104 | WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 105 | true 106 | 107 | 108 | . 109 | 110 | 111 | Console 112 | true 113 | pdcurses.lib;%(AdditionalDependencies) 114 | 115 | 116 | 117 | 118 | NotUsing 119 | Level3 120 | Disabled 121 | true 122 | _DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 123 | true 124 | 125 | 126 | . 127 | 128 | 129 | Console 130 | true 131 | pdcurses64.lib;%(AdditionalDependencies) 132 | 133 | 134 | 135 | 136 | NotUsing 137 | Level3 138 | MaxSpeed 139 | true 140 | true 141 | true 142 | WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 143 | true 144 | 145 | 146 | . 147 | 148 | 149 | Console 150 | true 151 | true 152 | true 153 | pdcurses.lib;%(AdditionalDependencies) 154 | 155 | 156 | 157 | 158 | NotUsing 159 | Level3 160 | MaxSpeed 161 | true 162 | true 163 | true 164 | NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 165 | true 166 | 167 | 168 | . 169 | 170 | 171 | Console 172 | true 173 | true 174 | true 175 | pdcurses64.lib;%(AdditionalDependencies) 176 | 177 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /system_call_emulator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 2/1/20. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | #ifndef _WIN32 9 | 10 | #include 11 | 12 | #else 13 | #endif 14 | 15 | #include 16 | 17 | #include "system_call_emulator.h" 18 | #include "RISCV_cpu.h" 19 | 20 | #ifdef _WIN32 21 | #include "io.h" 22 | #define CIO_open _open 23 | #define CIO_close _close 24 | #define CIO_lseek _lseek 25 | #define CIO_read _read 26 | #define CIO_write _write 27 | typedef signed int ssize_t; 28 | #else 29 | #define CIO_open open 30 | #define CIO_close close 31 | #define CIO_lseek lseek 32 | #define CIO_read read 33 | #define CIO_write write 34 | #endif 35 | 36 | namespace RISCV_EMULATOR { 37 | 38 | void ShowGuestStat(const Riscv32NewlibStat &guest_stat) { 39 | std::cerr << "st_dev: " << guest_stat.st_dev << std::endl; 40 | std::cerr << "st_ino: " << guest_stat.st_ino << std::endl; 41 | std::cerr << "st_mode: " << guest_stat.st_mode << std::endl; 42 | std::cerr << "st_nlink: " << guest_stat.st_nlink << std::endl; 43 | std::cerr << "st_uid: " << guest_stat.st_uid << std::endl; 44 | std::cerr << "st_gid: " << guest_stat.st_gid << std::endl; 45 | std::cerr << "st_rdev: " << guest_stat.st_rdev << std::endl; 46 | std::cerr << "st_size: " << guest_stat.st_size << std::endl; 47 | std::cerr << "st_atime: " << guest_stat.st_atim << std::endl; 48 | std::cerr << "st_mtime: " << guest_stat.st_mtim << std::endl; 49 | std::cerr << "st_ctime: " << guest_stat.st_ctim << std::endl; 50 | std::cerr << "st_blksize: " << guest_stat.st_blksize << std::endl; 51 | std::cerr << "st_blocks: " << guest_stat.st_blocks << std::endl; 52 | } 53 | 54 | void ShowHostStat(const struct stat &host_stat) { 55 | std::cerr << "st_dev: " << host_stat.st_dev << std::endl; 56 | std::cerr << "st_ino: " << host_stat.st_ino << std::endl; 57 | std::cerr << "st_mode: " << host_stat.st_mode << std::endl; 58 | std::cerr << "st_nlink: " << host_stat.st_nlink << std::endl; 59 | std::cerr << "st_uid: " << host_stat.st_uid << std::endl; 60 | std::cerr << "st_gid: " << host_stat.st_gid << std::endl; 61 | std::cerr << "st_rdev: " << host_stat.st_rdev << std::endl; 62 | std::cerr << "st_size: " << host_stat.st_size << std::endl; 63 | #ifndef _WIN32 64 | std::cerr << "st_blksize: " << host_stat.st_blksize << std::endl; 65 | std::cerr << "st_blocks: " << host_stat.st_blocks << std::endl; 66 | std::cerr << "st_atim.tv_sec: " << host_stat.st_atim.tv_sec << std::endl; 67 | std::cerr << "st_atim.tv_nsec: " << host_stat.st_atim.tv_nsec << std::endl; 68 | std::cerr << "st_mtim.tv_sec: " << host_stat.st_mtim.tv_sec << std::endl; 69 | std::cerr << "st_mtim.tv_nsec: " << host_stat.st_mtim.tv_nsec << std::endl; 70 | std::cerr << "st_ctim.tv_sec: " << host_stat.st_ctim.tv_sec << std::endl; 71 | std::cerr << "st_ctim.tv_nsec: " << host_stat.st_ctim.tv_nsec << std::endl; 72 | #endif 73 | } 74 | 75 | void ConvGuestStatToHostStat(const Riscv32NewlibStat &guest_stat, 76 | struct stat *host_stat) { 77 | host_stat->st_dev = guest_stat.st_dev; 78 | host_stat->st_ino = guest_stat.st_ino; 79 | host_stat->st_mode = guest_stat.st_mode; 80 | host_stat->st_nlink = guest_stat.st_nlink; 81 | host_stat->st_uid = guest_stat.st_uid; 82 | host_stat->st_gid = guest_stat.st_gid; 83 | host_stat->st_rdev = guest_stat.st_rdev; 84 | host_stat->st_size = guest_stat.st_size; 85 | #ifndef _WIN32 86 | host_stat->st_blksize = guest_stat.st_blksize; 87 | host_stat->st_blocks = guest_stat.st_blocks; 88 | host_stat->st_atim.tv_sec = guest_stat.st_atim; 89 | host_stat->st_atim.tv_nsec = 0; 90 | host_stat->st_mtim.tv_sec = guest_stat.st_mtim; 91 | host_stat->st_mtim.tv_nsec = 0; 92 | host_stat->st_ctim.tv_sec = guest_stat.st_ctim; 93 | host_stat->st_ctim.tv_nsec = 0; 94 | #endif 95 | } 96 | 97 | void ConvHostStatToGuestStat(const struct stat &host_stat, 98 | Riscv32NewlibStat *guest_stat) { 99 | guest_stat->st_dev = host_stat.st_dev; 100 | guest_stat->st_ino = host_stat.st_ino; 101 | guest_stat->st_mode = host_stat.st_mode; 102 | guest_stat->st_nlink = host_stat.st_nlink; 103 | guest_stat->st_uid = host_stat.st_uid; 104 | guest_stat->st_gid = host_stat.st_gid; 105 | guest_stat->st_rdev = host_stat.st_rdev; 106 | guest_stat->st_size = host_stat.st_size; 107 | #ifndef _WIN32 108 | guest_stat->st_blksize = host_stat.st_blksize; 109 | guest_stat->st_blocks = host_stat.st_blocks; 110 | guest_stat->st_atim = (int64_t) host_stat.st_atim.tv_sec; 111 | guest_stat->st_mtim = (int64_t) host_stat.st_mtim.tv_sec; 112 | guest_stat->st_ctim = (int64_t) host_stat.st_ctim.tv_sec; 113 | #endif 114 | } 115 | 116 | size_t 117 | MemoryWrapperStrlen(const MemoryWrapper &mem, size_t address, size_t max) { 118 | size_t counter = 0; 119 | size_t index = address; 120 | while (mem.ReadByte(index++) && counter < max) { 121 | ++counter; 122 | } 123 | return counter; 124 | } 125 | 126 | char *MemoryWrapperCopy(const MemoryWrapper &mem, size_t address, size_t length, 127 | char *dst) { 128 | for (size_t i = 0; i < length; ++i) { 129 | dst[i] = mem.ReadByte(address + i); 130 | } 131 | return dst; 132 | } 133 | 134 | int openHandles = 0; 135 | 136 | std::pair 137 | SystemCallEmulation(std::shared_ptr memory, uint64_t *reg, 138 | const uint64_t top, 139 | uint64_t *break_address, bool debug) { 140 | auto &brk = *break_address; 141 | auto &mem = *memory; 142 | bool end_flag = false; 143 | bool error_flag = false; 144 | if (reg[A7] == 93) { 145 | // Exit system call. 146 | if (debug) { 147 | std::cerr << "Exit System Call" << std::endl; 148 | } 149 | end_flag = true; 150 | } else if (reg[A7] == 64) { 151 | // Write. 152 | if (debug) { 153 | std::cerr << "Write System Call" << std::endl; 154 | std::cerr << "FD = " << reg[A0] << ", length = " << reg[A2] << std::endl; 155 | } 156 | int length = reg[A2]; 157 | unsigned char *buffer = new unsigned char[length]; 158 | for (int i = 0; i < length; i++) { 159 | buffer[i] = mem.ReadByte(reg[A1] + i); 160 | } 161 | std::cerr << std::endl; 162 | ssize_t return_value = CIO_write(reg[A0], buffer, length); 163 | reg[A0] = return_value; 164 | delete[] buffer; 165 | } else if (reg[A7] == 214) { 166 | // BRK. 167 | if (debug) { 168 | std::cerr << "BRK System Call" << std::endl; 169 | } 170 | if (reg[A0] == 0) { 171 | reg[A0] = brk; 172 | } else if (reg[A0] < top) { 173 | brk = reg[A0]; 174 | // reg[A0] = 0; 175 | } else { 176 | reg[A0] = -1; 177 | } 178 | } else if (reg[A7] == 63) { 179 | // READ. 180 | if (debug) { 181 | std::cerr << "Read System Call" << std::endl; 182 | } 183 | int length = reg[A2]; 184 | unsigned char *buffer = new unsigned char[length]; 185 | size_t return_value = CIO_read(reg[A0], buffer, length); 186 | reg[A0] = (uint32_t) return_value; 187 | for (int i = 0; i < length; i++) { 188 | mem.WriteByte(reg[A1] + i, buffer[i]); 189 | } 190 | delete[] buffer; 191 | } else if (reg[A7] == 80) { 192 | // FSTAT. 193 | struct Riscv32NewlibStat guest_stat; 194 | if (debug) { 195 | std::cerr << "Fstat System Call" << std::endl; 196 | std::cerr << "fd: " << reg[A0] << std::endl; 197 | std::cerr << "riscv32_stat size: " << sizeof(struct Riscv32NewlibStat) 198 | << std::endl; 199 | } 200 | unsigned char *statbuf_p = (unsigned char *) &guest_stat; 201 | 202 | struct stat host_stat; 203 | int return_value = fstat(reg[A0], &host_stat); 204 | 205 | ConvHostStatToGuestStat(host_stat, &guest_stat); 206 | if (debug) { 207 | std::cerr << "ret: " << reg[A0] << std::endl; 208 | std::cerr << "Guest: struct stat\n"; 209 | ShowGuestStat(guest_stat); 210 | } 211 | for (unsigned int i = 0; i < sizeof(Riscv32NewlibStat); i++) { 212 | mem.WriteByte(reg[A1] + i, statbuf_p[i]); 213 | } 214 | reg[A0] = return_value; 215 | } else if (reg[A7] == 57) { 216 | // Close. 217 | if (debug) { 218 | std::cerr << "Close System Call" << std::endl; 219 | } 220 | int return_value; 221 | 222 | // Try to prevent closing if open is not used 223 | if (openHandles > 0) { 224 | return_value = CIO_close(reg[A0]); 225 | openHandles--; 226 | } else { 227 | return_value = 0; 228 | } 229 | 230 | reg[A0] = return_value; 231 | } else if (reg[A7] == 1024) { 232 | // Open. 233 | if (debug) { 234 | std::cerr << "Open System Call" << std::endl; 235 | } 236 | // These values are found in newlib/libc/include/sys/_default_fcntl.h 237 | constexpr uint32_t kO_READ = 0x000001; 238 | constexpr uint32_t kO_WRITE = 0x000002; 239 | constexpr uint32_t kO_APPEND = 0x000008; 240 | constexpr uint32_t kO_CLOEXEC = 0x040000; 241 | constexpr uint32_t kO_CREAT = 0x000200; 242 | constexpr uint32_t kO_DIRECT = 0x080000; 243 | constexpr uint32_t kO_DIRECTORY = 0x200000; 244 | constexpr uint32_t kO_EXCL = 0x000800; 245 | constexpr uint32_t kO_NOCTTY = 0x008000; 246 | constexpr uint32_t kO_NONBLOCK = 0x004000; 247 | constexpr uint32_t kO_SYNC = 0x002000; 248 | constexpr uint32_t kO_TRUNC = 0x000400; 249 | #ifndef _WIN32 250 | uint32_t flag = (reg[A1] & kO_WRITE) ? O_RDWR : O_RDONLY; 251 | flag |= (reg[A1] & kO_APPEND) ? O_APPEND : 0; 252 | flag |= (reg[A1] & kO_CLOEXEC) ? O_CLOEXEC : 0; 253 | flag |= (reg[A1] & kO_CREAT) ? O_CREAT : 0; 254 | flag |= (reg[A1] & kO_DIRECT) ? O_DIRECT : 0; 255 | flag |= (reg[A1] & kO_DIRECTORY) ? O_DIRECTORY : 0; 256 | flag |= (reg[A1] & kO_EXCL) ? O_EXCL : 0; 257 | flag |= (reg[A1] & kO_NOCTTY) ? O_NOCTTY : 0; 258 | flag |= (reg[A1] & kO_NONBLOCK) ? O_NONBLOCK : 0; 259 | flag |= (reg[A1] & kO_SYNC) ? O_SYNC : 0; 260 | flag |= (reg[A1] & kO_TRUNC) ? O_TRUNC : 0; 261 | #else 262 | uint32_t flag = (reg[A1] & kO_WRITE) ? O_RDWR : O_RDONLY; 263 | flag |= (reg[A1] & kO_APPEND) ? O_APPEND : 0; 264 | flag |= (reg[A1] & kO_CREAT) ? O_CREAT : 0; 265 | flag |= (reg[A1] & kO_EXCL) ? O_EXCL : 0; 266 | flag |= (reg[A1] & kO_TRUNC) ? O_TRUNC : 0; 267 | #endif 268 | constexpr size_t kMax = 1024; 269 | size_t length = MemoryWrapperStrlen(mem, reg[A0], kMax); 270 | int return_value = -1; 271 | if (length > 0) { 272 | char *buffer = new char[length + 1]; 273 | MemoryWrapperCopy(mem, reg[A0], length, buffer); 274 | buffer[length] = 0; 275 | if (debug) { 276 | std::cerr << "File path: " << buffer << std::endl; 277 | std::cerr << "File parameter: " << std::hex << reg[A1] << ", " 278 | << reg[A2] 279 | << std::dec << std::endl; 280 | std::cerr << "Flag = " << std::hex << flag << std::dec << std::endl; 281 | } 282 | return_value = CIO_open(buffer, flag, reg[A2]); 283 | if (return_value >= 0) openHandles++; 284 | delete[] buffer; 285 | } 286 | reg[A0] = return_value; 287 | } else if (reg[A7] == 62) { 288 | // lseek. 289 | std::cerr << "Lseek System Call" << std::endl; 290 | int return_value = CIO_lseek(reg[A0], reg[A1], reg[A2]); 291 | reg[A0] = return_value; 292 | } else { 293 | std::cerr << "Undefined system call (" << reg[A7] << "). Exit.\n"; 294 | end_flag = true; 295 | } 296 | 297 | return std::make_pair(error_flag, end_flag); 298 | } 299 | 300 | } // namespace RISCV_EMULATOR 301 | -------------------------------------------------------------------------------- /RISCV_cpu.h: -------------------------------------------------------------------------------- 1 | #ifndef RISCV_CPU_H 2 | #define RISCV_CPU_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "Mmu.h" 9 | #include "PeripheralEmulator.h" 10 | #include "bit_tools.h" 11 | #include "memory_wrapper.h" 12 | #include "riscv_cpu_common.h" 13 | 14 | namespace RISCV_EMULATOR { 15 | 16 | class RiscvCpu { 17 | static constexpr int kCsrSize = 4096; 18 | static constexpr int kRegSize = 32; 19 | static constexpr int kRegNum = 32; 20 | int xlen_; 21 | int mxl_ = 1; 22 | 23 | public: 24 | RiscvCpu(bool en64bit); 25 | 26 | RiscvCpu(); 27 | 28 | ~RiscvCpu(){ 29 | 30 | }; 31 | 32 | void SetRegister(uint32_t num, uint64_t value); 33 | 34 | uint64_t ReadRegister(uint32_t num); 35 | 36 | void SetMemory(std::shared_ptr memory); 37 | 38 | void SetCsr(uint32_t index, uint64_t value); 39 | 40 | uint64_t ReadCsr(uint32_t index); 41 | 42 | int RunCpu(uint64_t start_pc, bool verbose = true); 43 | 44 | static void GetCode16(uint32_t ir, int mxl, uint32_t *instruction_out, 45 | uint32_t *rd_out, uint32_t *rs1_out, uint32_t *rs2_out, int32_t *imm_out); 46 | 47 | private: 48 | uint64_t VirtualToPhysical(uint64_t virtual_address, 49 | bool write_access = false); 50 | 51 | uint64_t Sext32bit(uint64_t data32bit); 52 | 53 | uint64_t reg_[kRegSize]; 54 | uint64_t pc_; 55 | bool timer_interrupt_; 56 | 57 | uint32_t ir_; 58 | uint64_t next_pc_; 59 | uint64_t mstatus_; 60 | uint64_t mie_ = 0; 61 | uint64_t mip_ = 0; 62 | PrivilegeMode privilege_; 63 | std::shared_ptr memory_; 64 | std::vector csrs_; 65 | bool ctype_; 66 | 67 | void InitializeCsrs(); 68 | 69 | void ClearTimerInterruptFlag(); 70 | 71 | void Ecall(); 72 | 73 | void CsrsInstruction(uint32_t instruction, uint32_t csr, uint32_t rd, 74 | uint32_t rs1); 75 | 76 | bool CheckPendingInterrupt(); 77 | 78 | uint64_t BranchInstruction(uint32_t instruction, uint32_t rs1, uint32_t rs2, 79 | int32_t imm13); 80 | 81 | void OperationInstruction(uint32_t instruction, uint32_t rd, uint32_t rs1, 82 | uint32_t rs2); 83 | 84 | void ImmediateInstruction(uint32_t instruction, uint32_t rd, uint32_t rs1, 85 | int32_t imm12); 86 | 87 | void ImmediateShiftInstruction(uint32_t instruction, uint32_t rd, 88 | uint32_t rs1, uint32_t shamt); 89 | 90 | void LoadInstruction(uint32_t instruction, uint32_t rd, uint32_t rs1, 91 | int32_t imm12); 92 | 93 | void StoreInstruction(uint32_t instruction, uint32_t rd, uint32_t rs1, 94 | uint32_t rs2, int32_t imm12_stype); 95 | 96 | void SystemInstruction(uint32_t instruction, uint32_t rd, int32_t imm); 97 | 98 | void MultInstruction(uint32_t instruction, uint32_t rd, uint32_t rs1, 99 | uint32_t rs2); 100 | 101 | void AmoInstruction(uint32_t instruction, uint32_t rd, uint32_t rs1, 102 | uint32_t rs2); 103 | 104 | void Mret(); 105 | 106 | void Sret(); 107 | 108 | uint32_t LoadCmd(uint64_t pc); 109 | 110 | uint32_t GetCode32(uint32_t ir); 111 | 112 | int GetLoadWidth(uint32_t instruction); 113 | 114 | int GetStoreWidth(uint32_t instruction); 115 | 116 | int GetAccessWidth(uint32_t width, uint64_t address); 117 | 118 | std::pair SystemCall(); 119 | 120 | uint64_t LoadWd(uint64_t physical_address, int width = 4); 121 | 122 | void StoreWd(uint64_t physical_address, uint64_t data, int width = 4); 123 | 124 | void Trap(int cause, bool interrupt); 125 | static constexpr bool kInterrupt = true; 126 | static constexpr bool kException = false; 127 | 128 | bool page_fault_ = false; 129 | bool prev_page_fault_ = false; 130 | uint64_t prev_faulting_address_ = 0; 131 | bool error_flag_, end_flag_; 132 | uint64_t faulting_address_; 133 | Mmu mmu_; 134 | 135 | inline bool CheckShiftSign(uint8_t shamt, uint8_t instruction, 136 | const std::string &message_str); 137 | 138 | PrivilegeMode IntToPrivilegeMode(int value); 139 | int PriviledgeToInt(PrivilegeMode priv); 140 | 141 | void DumpPrivilegeStatus(); 142 | 143 | void DumpDisassembly(bool verbose); 144 | 145 | void DumpCpuStatus(); 146 | 147 | void DumpRegisters(); 148 | 149 | void UpdateStatus(int16_t csr); 150 | 151 | void UpdateMstatus(int16_t csr); 152 | 153 | void UpdateInterruptPending(int16_t csr); 154 | 155 | static constexpr uint64_t kUipMask = 0b0000100010001; 156 | static constexpr uint64_t kSipMask = 0b0001100110011; 157 | void ApplyInterruptPending(); 158 | 159 | void UpdateInterruptEnable(int16_t csr); 160 | 161 | static constexpr uint64_t kUieMask = 0b0000100010001; 162 | static constexpr uint64_t kSieMask = 0b0001100110011; 163 | void ApplyInterruptEnable(); 164 | 165 | void ApplyMstatusToCsr(); 166 | // Below are for system call and host emulation 167 | public: 168 | void SetWorkMemory(uint64_t top, uint64_t bottom); 169 | 170 | void SetEcallEmulationEnable(bool ecall_emulation) { 171 | ecall_emulation_ = ecall_emulation; 172 | }; 173 | 174 | void DisableMachineInterruptDelegation(bool disable_machine_interrupt_delegation) { 175 | disable_machine_interrupt_delegation_ = disable_machine_interrupt_delegation; 176 | } 177 | 178 | 179 | void SetHostEmulationEnable(bool host_emulation) { 180 | host_emulation_ = host_emulation; 181 | peripheral_->SetHostEmulationEnable(host_emulation); 182 | }; 183 | 184 | void SetDeviceEmulationEnable(bool enable) { 185 | peripheral_emulation_ = enable; 186 | peripheral_->SetDeviceEmulationEnable(enable); 187 | } 188 | 189 | void SetDiskImage(std::shared_ptr> disk_image); 190 | 191 | void DeviceInitialization(); 192 | 193 | private: 194 | bool TimerTick(); 195 | void InterruptCheck(); 196 | void DiskInterruptCheck(); 197 | void UartInterruptCheck(); 198 | void PeripheralEmulations(); 199 | void SetInterruptPending(int cause); 200 | void ClearInterruptPending(int cause); 201 | std::unique_ptr peripheral_; 202 | bool ecall_emulation_ = false; 203 | bool host_emulation_ = false; 204 | bool peripheral_emulation_ = false; 205 | bool virtio_interrupt_ = false; 206 | bool uart_interrupt_ = false; 207 | bool disable_machine_interrupt_delegation_ = false; 208 | uint64_t top_ = 0x80000000; 209 | uint64_t bottom_ = 0x40000000; 210 | uint64_t brk_ = bottom_; 211 | }; 212 | 213 | enum ExceptionCode { 214 | USER_SOFTWARE_INTERRUPT = 0, 215 | SUPERVISOR_SOFTWARRE_INTERRUPT = 1, 216 | MACHINE_SOFTWARE_INTERRUPT = 3, 217 | SUPERVIOR_TIMER_INTERRUPT = 5, 218 | MACHINE_TIMER_INTERRUPT = 7, 219 | SUPERVISOR_EXTERNAL_INTERRUPT = 9, 220 | MACHINE_EXTERNAL_INTERRUPT = 11, 221 | INSTRUCTION_ADDRESS_MISALIGNED = 0, 222 | INSTRUCTION_ACCESS_FAULT = 1, 223 | ILLEGAL_INSTRUCTION = 2, 224 | BREAK_POINT = 3, 225 | LOAD_ADDRESS_MISALIGNED = 4, 226 | LOAD_ACCESS_FAULT = 5, 227 | STORE_ADDRESS_MISALIGNED = 6, 228 | STORE_ACCESS_FAULT = 7, 229 | ECALL_UMODE = 8, 230 | ECALL_SMODE = 9, 231 | ECALL_MMODE = 11, 232 | INSTRUCTION_PAGE_FAULT = 12, 233 | LOAD_PAGE_FAULT = 13, 234 | STORE_PAGE_FAULT = 15 235 | }; 236 | 237 | enum CsrsAddresses { 238 | // User Trap Handling 239 | USTATUS = 0x000, // User status register. 240 | UIE = 0x004, // User interrupt-enable register. 241 | UTVEC = 0x005, // User trap handler base address. 242 | USCRATCH = 0x040, // Scratch register for user trap handlers. 243 | UEPC = 0x041, // User exception program counter. 244 | UCAUSE = 0x042, // User trap cause. 245 | UTVAL = 0x43, // User bad address or instruction. 246 | UIP = 0x44, // User interrupt pending. 247 | // Supervisor Trap Handling. 248 | SSTATUS = 0x100, // Supervisor status register. 249 | SEDELEG = 0x102, // Supervisor exception delegation register. 250 | SIDELEG = 0X103, // Supervisor interrupt delegation register. 251 | SIE = 0x104, // Supervisor interrupt-enable register. 252 | STVEC = 0x105, // Supervisor trap handler base address. 253 | SCOUNTEREN = 0x106, // Supervisor counter enable. 254 | SSCRATCH = 0x140, // Scratch register for supervisor trap handlers. 255 | SEPC = 0x141, // Supervisor exception program counter. 256 | SCAUSE = 0x142, // Supervisor trap cause, 257 | STVAL = 0x143, // Supervisor bad address or instruction. 258 | SIP = 0x144, // Supervisor interrupt pending. 259 | // Super visor Protection and Translation. 260 | SATP = 0x180, // Page-table base register. Former satp register. 261 | // Machine Trap Setup 262 | MSTATUS = 0x300, // Machine status register. 263 | MISA = 0x301, // ISA and extensions. 264 | MEDELEG = 0x302, // Machine exception delegation register. 265 | MIDELEG = 0x303, // Machine interrupt delegation register. 266 | MIE = 0x304, // Machine interrupt-enable register. 267 | MTVEC = 0x305, // Machine trap-handler base address. 268 | MCOUNTEREN = 0x306, // Machine counter enable. 269 | // Machine Trap Handling. 270 | MSCRATCH = 0x340, // Scratch register for machine trap handlers. 271 | MEPC = 0x341, // Machine exception program counter. 272 | MCAUSE = 0x342, // Machine trap cause. 273 | MTVAL = 0x343, // Machine bad address 274 | MIP = 0x344, // Machine interrupt pending 275 | // TDOD: add other CSR addresses. 276 | // https://riscv.org/specifications/privileged-isa/ 277 | }; 278 | 279 | enum Registers { 280 | ZERO = 0, 281 | X0 = 0, 282 | X1 = 1, 283 | X2 = 2, 284 | X3 = 3, 285 | X4 = 4, 286 | X5 = 5, 287 | X6 = 6, 288 | X7 = 7, 289 | X8 = 8, 290 | X9 = 9, 291 | X10 = 10, 292 | X11 = 11, 293 | X12 = 12, 294 | X13 = 13, 295 | X14 = 14, 296 | X15 = 15, 297 | X16 = 16, 298 | RA = 1, 299 | SP = 2, 300 | GP = 3, 301 | TP = 4, 302 | T0 = 5, 303 | T1 = 6, 304 | T2 = 7, 305 | FP = 8, 306 | S0 = 8, 307 | S1 = 9, 308 | A0 = 10, 309 | A1 = 11, 310 | A2 = 12, 311 | A3 = 13, 312 | A4 = 14, 313 | A5 = 15, 314 | A6 = 16, 315 | A7 = 17, 316 | S2 = 18, 317 | S3 = 19, 318 | S4 = 20, 319 | S5 = 21, 320 | S6 = 22, 321 | S7 = 23, 322 | S8 = 24, 323 | S9 = 25, 324 | S10 = 26, 325 | S11 = 27, 326 | T3 = 28, 327 | T4 = 29, 328 | T5 = 30, 329 | T6 = 31 330 | }; 331 | 332 | enum op_label { 333 | OPCODE_ARITHLOG = 0b00110011, 334 | OPCODE_ARITHLOG_64 = 0b00111011, 335 | OPCODE_ARITHLOG_I = 0b00010011, 336 | OPCODE_ARITHLOG_I64 = 0b0011011, 337 | OPCODE_B = 0b01100011, 338 | OPCODE_LD = 0b00000011, 339 | OPCODE_J = 0b01101111, 340 | OPCODE_S = 0b00100011, 341 | OPCODE_JALR = 0b01100111, 342 | OPCODE_LUI = 0b00110111, 343 | OPCODE_AUIPC = 0b00010111, 344 | OPCODE_SYSTEM = 0b01110011, 345 | OPCODE_FENCE = 0b0001111, 346 | OPCODE_AMO = 0b0101111, 347 | }; 348 | 349 | enum op_funct { 350 | FUNC_NORM = 0b0000000, 351 | FUNC_ALT = 0b0100000, 352 | FUNC_MRET = 0b0011000, 353 | FUNC_MULT = 0b0000001, 354 | }; 355 | 356 | enum op_funct3 { 357 | FUNC3_ADDSUB = 0b000, 358 | FUNC3_AND = 0b111, 359 | FUNC3_OR = 0b110, 360 | FUNC3_XOR = 0b100, 361 | FUNC3_SL = 0b001, 362 | FUNC3_SR = 0b0101, 363 | FUNC3_SLT = 0b010, 364 | FUNC3_SLTU = 0b011, 365 | FUNC3_BEQ = 0b000, 366 | FUNC3_BGE = 0b101, 367 | FUNC3_BGEU = 0b111, 368 | FUNC3_BLT = 0b100, 369 | FUNC3_BLTU = 0b110, 370 | FUNC3_BNE = 0b001, 371 | FUNC3_LSB = 0b000, 372 | FUNC3_LSBU = 0b100, 373 | FUNC3_LSH = 0b001, 374 | FUNC3_LSHU = 0b101, 375 | FUNC3_LSW = 0b010, 376 | FUNC3_LSD = 0b011, 377 | FUNC3_LSWU = 0b110, 378 | FUNC3_JALR = 0b000, 379 | FUNC3_SYSTEM = 0b000, 380 | FUNC3_CSRRC = 0b011, 381 | FUNC3_CSRRCI = 0b111, 382 | FUNC3_CSRRS = 0b010, 383 | FUNC3_CSRRSI = 0b110, 384 | FUNC3_CSRRW = 0b001, 385 | FUNC3_CSRRWI = 0b101, 386 | FUNC3_FENCE = 0b000, 387 | FUNC3_FENCEI = 0b001, 388 | FUNC3_MUL = 0b000, 389 | FUNC3_MULH = 0b001, 390 | FUNC3_MULHSU = 0b010, 391 | FUNC3_MULHU = 0b011, 392 | FUNC3_DIV = 0b100, 393 | FUNC3_DIVU = 0b101, 394 | FUNC3_REM = 0b110, 395 | FUNC3_REMU = 0b111, 396 | FUNC3_AMOW = 0b010, 397 | FUNC3_AMOD = 0b011, 398 | }; 399 | 400 | enum op_funct5 { 401 | FUNC5_AMOADD = 0b00000, 402 | FUNC5_AMOAND = 0b01100, 403 | FUNC5_AMOMAX = 0b10100, 404 | FUNC5_AMOMAXU = 0b11100, 405 | FUNC5_AMOMIN = 0b10000, 406 | FUNC5_AMOMINU = 0b11000, 407 | FUNC5_AMOOR = 0b01000, 408 | FUNC5_AMOSWAP = 0b00001, 409 | FUNC5_AMOXOR = 0b00100, 410 | }; 411 | 412 | enum instruction { 413 | INST_ERROR, 414 | INST_ADD, 415 | INST_ADDW, 416 | INST_AND, 417 | INST_SUB, 418 | INST_SUBW, 419 | INST_OR, 420 | INST_XOR, 421 | INST_SLL, 422 | INST_SLLW, 423 | INST_SRL, 424 | INST_SRLW, 425 | INST_SRA, 426 | INST_SRAW, 427 | INST_SLT, 428 | INST_SLTU, 429 | INST_ADDI, 430 | INST_ADDIW, 431 | INST_ANDI, 432 | INST_ORI, 433 | INST_XORI, 434 | INST_SLLI, 435 | INST_SLLIW, 436 | INST_SRLI, 437 | INST_SRLIW, 438 | INST_SRAI, 439 | INST_SRAIW, 440 | INST_SLTI, 441 | INST_SLTIU, 442 | INST_BEQ, 443 | INST_BGE, 444 | INST_BGEU, 445 | INST_BLT, 446 | INST_BLTU, 447 | INST_BNE, 448 | INST_JAL, 449 | INST_JALR, 450 | INST_LB, 451 | INST_LBU, 452 | INST_LH, 453 | INST_LHU, 454 | INST_LW, 455 | INST_LWU, 456 | INST_LD, 457 | INST_SB, 458 | INST_SH, 459 | INST_SW, 460 | INST_SD, 461 | INST_LUI, 462 | INST_AUIPC, 463 | INST_SYSTEM, 464 | INST_CSRRC, 465 | INST_CSRRCI, 466 | INST_CSRRS, 467 | INST_CSRRSI, 468 | INST_CSRRW, 469 | INST_CSRRWI, 470 | INST_FENCE, 471 | INST_FENCEI, 472 | // RV32M/RV64M instructions. 473 | INST_MUL, 474 | INST_MULH, 475 | INST_MULHSU, 476 | INST_MULHU, 477 | INST_MULW, 478 | INST_DIV, 479 | INST_DIVU, 480 | INST_DIVUW, 481 | INST_DIVW, 482 | INST_REM, 483 | INST_REMU, 484 | INST_REMUW, 485 | INST_REMW, 486 | // AMO instructions. 487 | INST_AMOADDD, 488 | INST_AMOADDW, 489 | INST_AMOANDD, 490 | INST_AMOANDW, 491 | INST_AMOMAXD, 492 | INST_AMOMAXW, 493 | INST_AMOMAXUD, 494 | INST_AMOMAXUW, 495 | INST_AMOMIND, 496 | INST_AMOMINW, 497 | INST_AMOMINUD, 498 | INST_AMOMINUW, 499 | INST_AMOORD, 500 | INST_AMOORW, 501 | INST_AMOXORD, 502 | INST_AMOXORW, 503 | INST_AMOSWAPD, 504 | INST_AMOSWAPW, 505 | }; 506 | 507 | } // namespace RISCV_EMULATOR 508 | 509 | #endif // RISCV_CPU_H -------------------------------------------------------------------------------- /PeripheralEmulator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 7/2/20. 3 | // 4 | 5 | #include "PeripheralEmulator.h" 6 | #include 7 | #include 8 | #include "ScreenEmulation.h" 9 | 10 | namespace RISCV_EMULATOR { 11 | 12 | constexpr int CTRL_A = 'a' & 0x1f; 13 | 14 | PeripheralEmulator::PeripheralEmulator(int mxl) : mxl_(mxl) {} 15 | 16 | void PeripheralEmulator::SetMemory(std::shared_ptr memory) { memory_ = memory; } 17 | 18 | void PeripheralEmulator::SetDiskImage(std::shared_ptr> disk_image) { disk_image_ = disk_image; }; 19 | 20 | void PeripheralEmulator::SetHostEmulationEnable(bool enable) { host_emulation_enable_ = enable; } 21 | 22 | uint64_t PeripheralEmulator::GetHostValue() { return host_value_; } 23 | 24 | bool PeripheralEmulator::GetHostErrorFlag() { return error_flag_; } 25 | 26 | bool PeripheralEmulator::GetHostEndFlag() { return end_flag_; } 27 | 28 | void PeripheralEmulator::Initialize() { 29 | UartInit(); 30 | VirtioInit(); 31 | } 32 | 33 | // reference: https://github.com/riscv/riscv-isa-sim/issues/364 34 | void PeripheralEmulator::CheckDeviceWrite(uint64_t address, int width, uint64_t data) { 35 | // Check if the write is to host communication. 36 | if (mxl_ == 1) { 37 | host_write_ |= (address & 0xFFFFFFFF) == kToHost0 ? 1 : 0; 38 | host_write_ |= (address & 0xFFFFFFFF) == kToHost1 ? 2 : 0; 39 | } else { 40 | host_write_ |= address == kToHost0 ? 1 : 0; 41 | host_write_ |= address == kToHost1 ? 2 : 0; 42 | } 43 | // Check if it writes to UART addresses. 44 | if (kUartBase < address + width && address <= kUartBase) { 45 | uint64_t offset = kUartBase - address; 46 | uart_write_value_ = (data >> (offset * 8)) & 0xFF; 47 | uart_write_ = true; 48 | } 49 | if (kVirtioBase < address + width && address <= kVirtioEnd) { 50 | virtio_address_ = address; 51 | virtio_data_ = data; 52 | virtio_width_ = width; 53 | virtio_write_ = true; 54 | } 55 | if (kTimerCmp < address + width && address < kTimerCmp + 8) { 56 | timercmp_update_ = true; 57 | } 58 | } 59 | 60 | void PeripheralEmulator::CheckDeviceRead(uint64_t address, int width) { 61 | // Check if it reads from UART addresses. 62 | if (kUartBase < address + width && address <= kUartBase) { 63 | uart_read_ = true; 64 | } 65 | } 66 | 67 | void PeripheralEmulator::MemoryMappedValueUpdate() { memory_->Write64(kTimerMtime, elapsed_cycles_); } 68 | 69 | void PeripheralEmulator::Emulation() { 70 | if (host_emulation_enable_) { 71 | HostEmulation(); 72 | } 73 | if (device_emulation_enable) { 74 | UartEmulation(); 75 | VirtioEmulation(); 76 | } 77 | } 78 | 79 | // reference: https://github.com/riscv/riscv-isa-sim/issues/364 80 | void PeripheralEmulator::HostEmulation() { 81 | if (host_write_ == 0) { 82 | return; 83 | } 84 | uint64_t payload; 85 | uint8_t device; 86 | uint32_t command; 87 | uint64_t value = 0; 88 | if ((host_write_ & 0b10) != 0) { 89 | payload = (mxl_ == 1) ? memory_->Read32(kToHost1) : memory_->Read64(kToHost1); 90 | host_value_ = payload >> 1; 91 | host_write_ = 0; 92 | end_flag_ = true; 93 | return; 94 | } 95 | 96 | end_flag_ = false; 97 | if (mxl_ == 1) { 98 | // This address should be physical. 99 | payload = memory_->Read32(kToHost0); 100 | device = 0; 101 | command = 0; 102 | } else { 103 | // This address should be physical. 104 | payload = memory_->Read64(kToHost0); 105 | device = (payload >> 56) & 0xFF; 106 | command = (payload >> 48) & 0x3FFFF; 107 | } 108 | if (device == 0) { 109 | if (command == 0) { 110 | value = payload & 0xFFFFFFFFFFFF; 111 | if ((value & 1) == 0) { 112 | // Syscall emulation 113 | std::cerr << "Syscall Emulation Not Implemented Yet." << std::endl; 114 | } else { 115 | value = value >> 1; 116 | host_value_ = value; 117 | end_flag_ = true; 118 | } 119 | } else { 120 | std::cerr << "Unsupported Host command " << command << " for Device 0" << std::endl; 121 | error_flag_ = true; 122 | end_flag_ = true; 123 | } 124 | } else if (device == 1) { 125 | if (command == 1) { 126 | char character = value & 0xFF; 127 | std::cout << character; 128 | } else if (command == 0) { 129 | // TODO: Implement Read. 130 | } else { 131 | std::cerr << "Unsupported host command " << command << " for Device 1" << std::endl; 132 | } 133 | } else { 134 | std::cerr << "Unsupported Host Device " << device << std::endl; 135 | } 136 | host_write_ = 0; 137 | return; 138 | } 139 | 140 | void PeripheralEmulator::UartInit() { 141 | uint8_t isr = memory_->ReadByte(kUartBase + 5); 142 | isr |= (1 << 5); 143 | memory_->WriteByte(kUartBase + 5, isr); 144 | 145 | // Initialize the screen to use ncurse library. 146 | scr_emulation = std::make_unique(); 147 | // No need to call endwin() explicitly afterward because the destructor calls it. 148 | } 149 | void PeripheralEmulator::UartEmulation() { 150 | // UART Rx. 151 | if (uart_write_) { 152 | scr_emulation->putchar(uart_write_value_); 153 | uart_write_ = false; 154 | // TODO: Add interrupt processing. 155 | } 156 | // UART Tx. 157 | if (uart_read_) { 158 | ClearUartBuffer(); 159 | uart_read_ = false; 160 | } 161 | if (uart_full_) { 162 | return; 163 | } 164 | if (!scr_emulation->CheckInput()) { 165 | return; 166 | } 167 | int key_input = scr_emulation->GetKeyValue(); 168 | switch (key_input) { 169 | case KEY_BACKSPACE: 170 | key_input = 8; 171 | break; 172 | case KEY_DC: 173 | key_input = 127; 174 | break; 175 | case 'a' & 0x1f: 176 | case 'c' & 0x1f: 177 | uart_break_ = true; 178 | break; 179 | } 180 | SetUartBuffer(key_input); 181 | UartInterrupt(); 182 | } 183 | 184 | void PeripheralEmulator::SetUartBuffer(int key) { 185 | uint8_t uart_lsr_status = memory_->ReadByte(kUartLsr); 186 | uart_lsr_status |= kUartLsrReady; 187 | memory_->WriteByte(kUartRhr, static_cast(key)); 188 | memory_->WriteByte(kUartLsr, uart_lsr_status); 189 | uart_full_ = true; 190 | } 191 | 192 | void PeripheralEmulator::ClearUartBuffer() { 193 | uint8_t uart_lsr_status = memory_->ReadByte(kUartLsr); 194 | uart_lsr_status &= ~kUartLsrReady; 195 | memory_->WriteByte(kUartLsr, uart_lsr_status); 196 | uart_full_ = false; 197 | } 198 | 199 | void PeripheralEmulator::UartInterrupt() { 200 | constexpr int kUartIrq = 10; 201 | memory_->Write32(kPlicClaimAddress, kUartIrq); 202 | uart_interrupt_ = true; 203 | } 204 | 205 | void PeripheralEmulator::TimerTick() { 206 | ++elapsed_cycles_; 207 | if (timercmp_update_) { 208 | next_cycle_ = memory_->Read64(kTimerCmp); 209 | timercmp_update_ = false; 210 | } 211 | if (elapsed_cycles_ == next_cycle_) { 212 | timer_interrupt_ = true; 213 | } 214 | } 215 | 216 | uint64_t PeripheralEmulator::GetTimerInterrupt() { return timer_interrupt_; } 217 | 218 | void PeripheralEmulator::ClearTimerInterrupt() { timer_interrupt_ = false; } 219 | 220 | void PeripheralEmulator::VirtioInit() { 221 | assert(memory_); 222 | memory_->Write32(kVirtioBase + 0x00, 0x74726976); 223 | memory_->Write32(kVirtioBase + 0x4, 1); 224 | memory_->Write32(kVirtioBase + 0x8, 2); 225 | memory_->Write32(kVirtioBase + 0xc, 0x554d4551); 226 | memory_->Write32(kVirtioBase + 0x34, 8); 227 | } 228 | 229 | void PeripheralEmulator::VirtioEmulation() { 230 | if (!virtio_write_) { 231 | return; 232 | } 233 | virtio_write_ = false; 234 | constexpr int kWordWidth = 4; 235 | if (kVirtioMmioQueueSel < virtio_address_ + virtio_width_ && virtio_address_ < kVirtioMmioQueueSel + kWordWidth) { 236 | const uint32_t queue_sel = memory_->Read32(kVirtioMmioQueueSel); 237 | const uint32_t queue_num_max = (queue_sel == 0) ? kQueueNumMax : 0; 238 | memory_->Write32(kVirtioMmioQueueMax, queue_num_max); 239 | } 240 | if (virtio_address_ + virtio_width_ <= kVirtioMmioQueueNotify || 241 | kVirtioMmioQueueNotify + kWordWidth <= virtio_address_) { 242 | // If QUEUE_NOTIFY is not touched. End of the process. 243 | return; 244 | } 245 | // The rest processes the read/write request. 246 | uint32_t queue_number = memory_->Read32(kVirtioMmioQueueNotify); 247 | // For now, only 0th queue is available. 248 | assert(queue_number == 0); 249 | queue_num_ = memory_->Read32(kVirtioMmioQueueNum); 250 | assert(queue_num_ <= kQueueNumMax); 251 | const int kPageSize = memory_->Read32(kVirtioMmioPageSize); 252 | assert(kPageSize == 4096); 253 | const uint64_t kQueueAddress = memory_->Read32(kVirtioMmioQueuePfn) * kPageSize; 254 | VirtioDiskAccess(kQueueAddress); 255 | // Fire an interrupt. 256 | // New standard has a way to suspend interrupt until index reaches a certain value, but not supported in xv6. 257 | constexpr int kVirtioIrq = 1; 258 | memory_->Write32(kPlicClaimAddress, kVirtioIrq); 259 | virtio_interrupt_ = true; 260 | } 261 | 262 | void PeripheralEmulator::read_desc(VRingDesc *desc, uint64_t desc_address, uint16_t desc_index) const { 263 | constexpr int kVRingSize = 16; 264 | // std::cerr << "desc base address = " << std::hex << desc_address << std::endl; 265 | // std::cerr << "desc index = " << std::dec << desc_index << std::endl; 266 | uint64_t address = desc_address + desc_index * kVRingSize; 267 | // std::cerr << "desc address = " << std::hex << address << std::endl; 268 | desc->addr = memory_->Read64(address); 269 | desc->len = memory_->Read32(address + 8); 270 | desc->flags = memory_->Read16(address + 12); 271 | desc->next = memory_->Read16(address + 14); 272 | // std::cerr << "desc->addr = " << std::hex << desc->addr << std::endl; 273 | // std::cerr << "desc->len = " << std::dec << desc->len << std::endl; 274 | // std::cerr << "desc->flags = " << desc->flags << std::endl; 275 | // std::cerr << "desc->next = " << desc->next << std::endl; 276 | } 277 | 278 | void PeripheralEmulator::VirtioDiskAccess(uint64_t queue_address) { 279 | uint64_t desc_address = queue_address; 280 | constexpr uint32_t kDescSizeBytes = 16; 281 | uint64_t avail_address = queue_address + queue_num_ * kDescSizeBytes; 282 | constexpr int kPageSize = 4096; 283 | // used_address is at the page boundary. 284 | uint64_t used_address = ((avail_address + 2 * (2 + queue_num_) + kPageSize - 1) / kPageSize) * kPageSize; 285 | // std::cerr << "queue_address = " << std::hex << queue_address << std::endl; 286 | // std::cerr << "avail_address = " << std::hex << avail_address << std::endl; 287 | // std::cerr << "used_address = " << std::hex << used_address << std::endl; 288 | uint16_t desc_index = get_desc_index(avail_address); 289 | process_disc_access(desc_address, desc_index); 290 | process_used_buffer(used_address, desc_index); 291 | } 292 | 293 | uint16_t PeripheralEmulator::get_desc_index( 294 | uint64_t avail_address) const { // Second word (16 bit) in Available Ring shows the next index. 295 | uint16_t index = memory_->Read16(avail_address + 2) % queue_num_; 296 | // std::cerr << "avail_index = " << index << std::endl; 297 | assert(index < queue_num_); 298 | uint16_t desc_index = memory_->Read16(avail_address + 4 + index * 2); 299 | return desc_index; 300 | } 301 | 302 | void PeripheralEmulator::process_disc_access(uint64_t desc_address, int desc_index) { 303 | constexpr uint8_t kOk = 0; 304 | 305 | VRingDesc desc; 306 | virtio_blk_outhdr outhdr; 307 | uint64_t buffer_address; 308 | uint32_t len; 309 | read_desc(&desc, desc_address, desc_index); 310 | read_outhdr(&outhdr, desc.addr); 311 | bool write_access = outhdr.type == 1; 312 | uint64_t sector = outhdr.sector; 313 | if ((desc.flags & 0b01) != 1) { 314 | // The first desc always need the next desc. 315 | std::cerr << "No next desc in the first entry" << std::endl; 316 | goto ERROR; 317 | } 318 | desc_index = desc.next; 319 | read_desc(&desc, desc_address, desc_index); 320 | buffer_address = desc.addr; 321 | if (((desc.flags & 0b10) == 0) != write_access) { 322 | // The read/write descriptions in outhdr and descriptor should match. 323 | // Note that "device write" is "disk read." 324 | std::cerr << "desc.flags = " << desc.flags << std::endl; 325 | std::cerr << "write_access = " << write_access << std::endl; 326 | goto ERROR; 327 | } 328 | len = desc.len; 329 | disc_access(sector, buffer_address, len, write_access); 330 | // Write to status. OK = 0. 331 | desc_index = desc.next; 332 | read_desc(&desc, desc_address, desc_index); 333 | if (desc.len != 1 || (desc.flags & 0b11) != 0b10) { 334 | // write access, and there's no next descriptor. 335 | std::cerr << "desc.len = " << desc.len << std::endl; 336 | std::cerr << "desc.flags = " << desc.flags << std::endl; 337 | goto ERROR; 338 | } 339 | buffer_address = desc.addr; 340 | memory_->WriteByte(buffer_address, kOk); 341 | return; 342 | ERROR: 343 | // TODO: Add error handling. 344 | assert(false); 345 | return; 346 | } 347 | 348 | void PeripheralEmulator::read_outhdr(virtio_blk_outhdr *outhdr, uint64_t outhdr_address) const { 349 | // std::cerr << "virtio_blk_outhdr address = " << std::hex << outhdr_address << std::endl; 350 | outhdr->type = memory_->Read32(outhdr_address); 351 | outhdr->reserved = memory_->Read32(outhdr_address + 4); 352 | outhdr->sector = memory_->Read64(outhdr_address + 8); 353 | // std::cerr << "virtio_blk_outhdr.type = " << outhdr->type << std::endl; 354 | // std::cerr << "virtio_blk_outhdr.sector = " << outhdr->sector << std::endl; 355 | } 356 | 357 | void PeripheralEmulator::disc_access(uint64_t sector, uint64_t buffer_address, uint32_t len, bool write) { 358 | uint64_t kSectorAddress = sector * kSectorSize; 359 | // std::cerr << (write ? "Disk Write: " : "Disk Read: "); 360 | // std::cerr << "sector = " << std::hex << sector << ", size = " << std::dec << len << std::endl; 361 | if (write) { 362 | for (uint64_t offset = 0; offset < len; ++offset) { 363 | (*disk_image_)[kSectorAddress + offset] = memory_->ReadByte(buffer_address + offset); 364 | } 365 | } else { 366 | for (uint64_t offset = 0; offset < len; ++offset) { 367 | memory_->WriteByte(buffer_address + offset, (*disk_image_)[kSectorAddress + offset]); 368 | } 369 | } 370 | } 371 | 372 | void PeripheralEmulator::process_used_buffer(uint64_t used_buffer_address, uint16_t index) { 373 | // uint16_t flag = memory_->Read16(used_buffer_address); 374 | // TODO: Add check of flag. 375 | uint16_t current_used_index = memory_->Read16(used_buffer_address + 2); 376 | memory_->Write32(used_buffer_address + 4 + current_used_index * 8, index); 377 | memory_->Write32(used_buffer_address + 4 + current_used_index * 8 + 4, 3); 378 | current_used_index = current_used_index + 1; 379 | memory_->Write16(used_buffer_address + 2, current_used_index); 380 | } 381 | 382 | } // namespace RISCV_EMULATOR -------------------------------------------------------------------------------- /Disassembler.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by moiz on 5/16/20. 3 | // 4 | 5 | #include "Disassembler.h" 6 | #include 7 | #include 8 | #include "instruction_encdec.h" 9 | 10 | namespace RISCV_EMULATOR { 11 | 12 | std::string GetRegName(int reg) { 13 | std::array kRegNames = { 14 | "ZERO", "RA", "SP", "GP", "TP", "T0", "T1", "T2", "FP", "S1", "A0", 15 | "A1", "A2", "A3", "A4", "A5", "A6", "A7", "S2", "S3", "S4", "S5", 16 | "S6", "S7", "S8", "S9", "S10", "S11", "T3", "T4", "T5", "T6", 17 | }; 18 | return kRegNames[reg]; 19 | } 20 | 21 | std::string NumberToHex(uint32_t num, int length = 0) { 22 | std::stringstream s; 23 | s << "0x" << std::hex << num; 24 | return std::string(s.str()); 25 | } 26 | 27 | // TODO: Add tests for disassembly; 28 | // Disassemble16 and GetCode16 have very similar logic. Should be combined in 29 | // some way. 30 | std::string Disassemble16(uint32_t ir, int mxl = 1) { 31 | std::string cmd = "Unsupported C Instruction"; 32 | uint32_t instruction, rd, rs1, rs2; 33 | int32_t imm; 34 | RiscvCpu::GetCode16(ir, mxl, &instruction, &rd, &rs1, &rs2, &imm); 35 | uint16_t opcode = (bitcrop(ir, 3, 13) << 2) | bitcrop(ir, 2, 0); 36 | switch (opcode) { 37 | case 0b00000: 38 | cmd = "C.ADDI4SPN " + GetRegName(rd) + ", SP, " + NumberToHex(imm); 39 | break; 40 | case 0b00001: 41 | cmd = "C.ADDI " + GetRegName(rd) + ", " + NumberToHex(imm); 42 | break; 43 | case 0b00010: 44 | cmd = "C.SLLI " + GetRegName(rd) + ", " + NumberToHex(imm); 45 | break; 46 | case 0b00101: 47 | if (mxl == 1) { 48 | cmd = "C.JAL " + NumberToHex(SignExtend(imm, 12)); 49 | } else { 50 | cmd = "C.ADDIW " + GetRegName(rd) + ", " + NumberToHex(imm); 51 | } 52 | break; 53 | case 0b01000: 54 | cmd = "C.LW " + GetRegName(rd) + ", " + NumberToHex(imm) + "(" + 55 | GetRegName(rs1) + ")"; 56 | break; 57 | case 0b01001: 58 | cmd = "C.LI " + GetRegName(rd) + ", " + NumberToHex(imm); 59 | break; 60 | case 0b01010: 61 | cmd = "C.LWSP " + GetRegName(rd) + ", " + NumberToHex(imm) + "(SP)"; 62 | break; 63 | case 0b01100: 64 | cmd = "C.LD " + GetRegName(rd) + ", " + NumberToHex(imm) + "(" + 65 | GetRegName(rs1) + ")"; 66 | break; 67 | case 0b01101: 68 | if (bitcrop(ir, 5, 7) == 0b00010) { 69 | // c.addi16sp. 70 | cmd = "C.ADDI16SP SP, SP, " + NumberToHex(imm); 71 | } else { 72 | cmd = "C.LUI " + GetRegName(rd) + ", " + NumberToHex(imm); 73 | } 74 | break; 75 | case 0b01110: 76 | cmd = "C.LDSP " + GetRegName(rd) + ", " + NumberToHex(imm) + "(SP)"; 77 | break; 78 | case 0b10010: // c.add 79 | if (bitcrop(ir, 1, 12) == 1) { 80 | if (bitcrop(ir, 5, 2) == 0 && bitcrop(ir, 5, 7) == 0) { 81 | cmd = "C.EBREAK"; 82 | } else if (bitcrop(ir, 5, 2) == 0) { 83 | cmd = "C.JALR " + GetRegName(rs1); 84 | } else if (bitcrop(ir, 5, 7) != 0) { 85 | cmd = "C.ADD " + GetRegName(rd) + ", " + GetRegName(rs2); 86 | } 87 | } else if (bitcrop(ir, 5, 2) == 0) { 88 | cmd = "C.JR " + GetRegName(rs1); 89 | } else { 90 | cmd = "C.MV " + GetRegName(rd) + ", " + GetRegName(rs2); 91 | } 92 | break; 93 | case 0b10001: 94 | if (bitcrop(ir, 3, 10) == 0b011 && bitcrop(ir, 2, 5) == 0b11) { 95 | // c.and. 96 | cmd = "C.AND " + GetRegName(rd) + ", " + GetRegName(rs2); 97 | } else if (bitcrop(ir, 3, 10) == 0b011 && bitcrop(ir, 2, 5) == 0b01) { 98 | cmd = "C.XOR " + GetRegName(rd) + ", " + GetRegName(rs2); 99 | } else if (bitcrop(ir, 3, 10) == 0b011 && bitcrop(ir, 2, 5) == 0b00) { 100 | cmd = "C.SUB " + GetRegName(rd) + ", " + GetRegName(rs2); 101 | } else if (bitcrop(ir, 3, 10) == 0b011 && bitcrop(ir, 2, 5) == 0b10) { 102 | cmd = "C.OR " + GetRegName(rd) + ", " + GetRegName(rs2); 103 | } else if (bitcrop(ir, 3, 10) == 0b111 && bitcrop(ir, 2, 5) == 0b01) { 104 | cmd = "C.ADDW " + GetRegName(rd) + ", " + GetRegName(rs2); 105 | } else if (bitcrop(ir, 3, 10) == 0b111 && bitcrop(ir, 2, 5) == 0b00) { 106 | cmd = "C.SUBW " + GetRegName(rd) + ", " + GetRegName(rs2); 107 | } else if (bitcrop(ir, 2, 10) == 0b01) { 108 | cmd = "C.SRAI " + GetRegName(rd) + ", " + NumberToHex(imm); 109 | } else if (bitcrop(ir, 2, 10) == 0b00) { 110 | cmd = "C.SRLI " + GetRegName(rd) + ", " + NumberToHex(imm); 111 | } else if (bitcrop(ir, 2, 10) == 0b10) { 112 | cmd = "C.ANDI " + GetRegName(rd) + ", " + 113 | NumberToHex(SignExtend(imm, 6)); 114 | } 115 | break; 116 | case 0b10101: 117 | cmd = "C.J " + NumberToHex(SignExtend(imm, 12)); 118 | break; 119 | case 0b11000: 120 | cmd = "C.SW " + GetRegName(rs2) + ", " + NumberToHex(imm) + "(" + 121 | GetRegName(rs1) + ")"; 122 | case 0b11001: 123 | cmd = "C.BEQZ " + GetRegName(rs1) + ", " + 124 | NumberToHex(SignExtend(imm, 9)); 125 | break; 126 | case 0b11010: 127 | cmd = "C.SWSP " + GetRegName(rs2) + ", " + NumberToHex(imm) + "(SP)"; 128 | break; 129 | case 0b11100: 130 | cmd = "C.SD " + GetRegName(rs2) + ", " + NumberToHex(imm) + "(" + 131 | GetRegName(rs1) + ")"; 132 | break; 133 | case 0b11101: 134 | cmd = "C.BNEZ " + GetRegName(rs1) + ", " + 135 | NumberToHex(SignExtend(imm, 9)); 136 | break; 137 | case 0b11110: 138 | cmd = "C.SDSP " + GetRegName(rs2) + ", " + NumberToHex(imm) + "(SP)"; 139 | } 140 | return cmd; 141 | } 142 | 143 | std::string Disassemble(uint32_t ir, int mxl) { 144 | uint16_t opcode = bitcrop(ir, 7, 0); 145 | if ((opcode & 0b11) != 0b11) { 146 | return Disassemble16(ir, mxl); 147 | } 148 | uint8_t funct3 = bitcrop(ir, 3, 12); 149 | uint8_t funct7 = bitcrop(ir, 7, 25); 150 | uint8_t funct5 = funct7 >> 2; 151 | uint32_t rd = GetRd(ir); 152 | uint32_t rs1 = GetRs1(ir); 153 | uint32_t rs2 = GetRs2(ir); 154 | int16_t imm12 = GetImm12(ir); 155 | int16_t csr = GetCsr(ir); 156 | int16_t imm13 = GetImm13(ir); 157 | int32_t imm21 = GetImm21(ir); 158 | int16_t imm12_stype = GetStypeImm12(ir); 159 | int32_t imm20 = GetImm20(ir); 160 | std::string cmd = "UNDEF"; 161 | switch (opcode) { 162 | case OPCODE_ARITHLOG: // ADD, SUB 163 | if (funct7 == FUNC_NORM || funct7 == FUNC_ALT) { 164 | if (funct3 == FUNC3_ADDSUB) { 165 | cmd = (funct7 == FUNC_NORM) ? "ADD" : "SUB"; 166 | } else if (funct3 == FUNC3_AND) { 167 | cmd = "AND"; 168 | } else if (funct3 == FUNC3_OR) { 169 | cmd = "OR"; 170 | } else if (funct3 == FUNC3_XOR) { 171 | cmd = "XOR"; 172 | } else if (funct3 == FUNC3_SR) { 173 | if (funct7 == FUNC_NORM) { 174 | cmd = "SRL"; 175 | } else if (funct7 == FUNC_ALT) { 176 | cmd = "SRA"; 177 | } 178 | } else if (funct3 == FUNC3_SL) { 179 | cmd = "SLL"; 180 | } else if (funct3 == FUNC3_SLT) { 181 | cmd = "SLT"; 182 | } else if (funct3 == FUNC3_SLTU) { 183 | cmd = "SLTU"; 184 | } 185 | } else if (funct7 == FUNC_MULT) { 186 | if (funct3 == FUNC3_MUL) { 187 | cmd = "MUL"; 188 | } else if (funct3 == FUNC3_MULH) { 189 | cmd = "MULH"; 190 | } else if (funct3 == FUNC3_MULHSU) { 191 | cmd = "MULHSU"; 192 | } else if (funct3 == FUNC3_MULHU) { 193 | cmd = "MULHU"; 194 | } else if (funct3 == FUNC3_DIV) { 195 | cmd = "DIV"; 196 | } else if (funct3 == FUNC3_DIVU) { 197 | cmd = "DIVU"; 198 | } else if (funct3 == FUNC3_REM) { 199 | cmd = "REM"; 200 | } else if (funct3 == FUNC3_REMU) { 201 | cmd = "REMU"; 202 | } 203 | cmd += " " + GetRegName(rd) + ", " + GetRegName(rs1) + ", " + 204 | GetRegName(rs2); 205 | break; 206 | case OPCODE_ARITHLOG_64: 207 | if (funct7 == FUNC_NORM || funct7 == FUNC_ALT) { 208 | if (funct3 == FUNC3_ADDSUB) { 209 | cmd = (funct7 == FUNC_NORM) ? "ADDW" : "SUBW"; 210 | } else if (funct3 == FUNC3_SL) { 211 | cmd = "SLLW"; 212 | } else if (funct3 == FUNC3_SR) { 213 | if (funct7 == FUNC_NORM) { 214 | cmd = "SRLW"; 215 | } else if (funct7 == FUNC_ALT) { 216 | cmd = "SRAW"; 217 | } 218 | } 219 | } else if (funct7 == FUNC_MULT) { 220 | if (funct3 == FUNC3_MUL) { 221 | cmd = "MULW"; 222 | } else if (funct3 == FUNC3_DIVU) { 223 | cmd = "DIVUW"; 224 | } else if (funct3 == FUNC3_DIV) { 225 | cmd = "DIVW"; 226 | } else if (funct3 == FUNC3_REMU) { 227 | cmd = "REMUW"; 228 | } else if (funct3 == FUNC3_REM) { 229 | cmd = "REMW"; 230 | } 231 | } 232 | } 233 | cmd += " " + GetRegName(rd) + ", " + GetRegName(rs1) + ", " + 234 | GetRegName(rs2); 235 | break; 236 | case OPCODE_ARITHLOG_I: // ADDI, SUBI 237 | if (funct3 == FUNC3_ADDSUB) { 238 | cmd = "ADDI"; 239 | } else if (funct3 == FUNC3_AND) { 240 | cmd = "ANDI"; 241 | } else if (funct3 == FUNC3_OR) { 242 | cmd = "ORI"; 243 | } else if (funct3 == FUNC3_XOR) { 244 | cmd = "XORI"; 245 | } else if (funct3 == FUNC3_SL) { 246 | cmd = "SLLI"; 247 | } else if (funct3 == FUNC3_SR) { 248 | if ((funct7 >> 1) == 0b000000) { 249 | cmd = "SRLI"; 250 | } else if ((funct7 >> 1) == 0b010000) { 251 | cmd = "SRAI"; 252 | } 253 | // If top 6 bits do not match, it's an error. 254 | } else if (funct3 == FUNC3_SLT) { 255 | cmd = "SLTI"; 256 | } else if (funct3 == FUNC3_SLTU) { 257 | cmd = "SLTIU"; 258 | } 259 | cmd += " " + GetRegName(rd) + ", " + GetRegName(rs1) + ", " + 260 | NumberToHex(imm12); 261 | break; 262 | case OPCODE_ARITHLOG_I64: 263 | if (funct3 == FUNC3_ADDSUB) { 264 | cmd = "ADDIW"; 265 | } else if (funct3 == FUNC3_SL) { 266 | cmd = "SLLIW"; 267 | } else if (funct3 == FUNC3_SR) { 268 | if ((funct7 >> 1) == 0b000000) { 269 | cmd = "SRLIW"; 270 | } else if ((funct7 >> 1) == 0b010000) { 271 | cmd = "SRAIW"; 272 | } 273 | } 274 | cmd += " " + GetRegName(rd) + ", " + GetRegName(rs1) + ", " + 275 | NumberToHex(imm12); 276 | break; 277 | case OPCODE_B: // beq, bltu, bge, bne 278 | if (funct3 == FUNC3_BEQ) { 279 | cmd = "BEQ"; 280 | } else if (funct3 == FUNC3_BLT) { 281 | cmd = "BLT"; 282 | } else if (funct3 == FUNC3_BLTU) { 283 | cmd = "BLTU"; 284 | } else if (funct3 == FUNC3_BGE) { 285 | cmd = "BGE"; 286 | } else if (funct3 == FUNC3_BGEU) { 287 | cmd = "BGEU"; 288 | } else if (funct3 == FUNC3_BNE) { 289 | cmd = "BNE"; 290 | } 291 | cmd += " " + GetRegName(rs1) + ", " + GetRegName(rs2) + ", " + 292 | NumberToHex(imm13); 293 | break; 294 | case OPCODE_J: // jal 295 | cmd = "JAL " + GetRegName(rd) + ", " + NumberToHex(imm21); 296 | break; 297 | case OPCODE_JALR: // jalr 298 | if (funct3 == FUNC3_JALR) { 299 | cmd = "JALR " + GetRegName(rd) + ", " + NumberToHex(imm12) + "(" + 300 | GetRegName(rs1) + ")"; 301 | } 302 | break; 303 | case OPCODE_LD: // LW 304 | if (funct3 == FUNC3_LSB) { 305 | cmd = "LB"; 306 | } else if (funct3 == FUNC3_LSBU) { 307 | cmd = "LBU"; 308 | } else if (funct3 == FUNC3_LSH) { 309 | cmd = "LH"; 310 | } else if (funct3 == FUNC3_LSHU) { 311 | cmd = "LHU"; 312 | } else if (funct3 == FUNC3_LSW) { 313 | cmd = "LW"; 314 | } else if (funct3 == FUNC3_LSWU) { 315 | cmd = "LWU"; 316 | } else if (funct3 == FUNC3_LSD) { 317 | cmd = "LD"; 318 | } 319 | cmd += " " + GetRegName(rd) + ", " + NumberToHex(imm12) + "(" + 320 | GetRegName(rs1) + ")"; 321 | break; 322 | case OPCODE_S: // SW 323 | if (funct3 == FUNC3_LSB) { 324 | cmd = "SB"; 325 | } else if (funct3 == FUNC3_LSH) { 326 | cmd = "SH"; 327 | } else if (funct3 == FUNC3_LSW) { 328 | cmd = "SW"; 329 | } else if (funct3 == FUNC3_LSD) { 330 | cmd = "SD"; 331 | } 332 | cmd += " " + GetRegName(rs2) + ", " + NumberToHex(imm12_stype) + "(" + 333 | GetRegName(rs1) + ")"; 334 | break; 335 | case OPCODE_LUI: // LUI 336 | cmd = "LUI"; 337 | cmd += " " + GetRegName(rd) + ", " + NumberToHex(imm20) + " << 12"; 338 | break; 339 | case OPCODE_AUIPC: // AUIPC 340 | cmd = "AUIPC"; 341 | cmd += " " + GetRegName(rd) + ", " + NumberToHex(imm20) + " << 12"; 342 | break; 343 | case OPCODE_SYSTEM: // EBREAK 344 | if (funct3 == FUNC3_SYSTEM) { 345 | if (imm12 == 0) { 346 | cmd = "ECALL"; 347 | } else if (imm12 == 1) { 348 | cmd = "EBREAK"; 349 | } else if (imm12 == 0b001100000010) { 350 | cmd = "MRET"; 351 | } else if (imm12 == 0b000100000010) { 352 | cmd = "SRET"; 353 | } else if (((imm12 >> 5) == 0b0001001) && (rd == 0b00000)) { 354 | cmd = "sfence.vma"; 355 | } else { 356 | cmd = "Undefined System Instruction"; 357 | } 358 | } else { 359 | if (funct3 == FUNC3_CSRRC) { 360 | cmd = "CSRRC"; 361 | } else if (funct3 == FUNC3_CSRRCI) { 362 | cmd = "CSRRCI"; 363 | } else if (funct3 == FUNC3_CSRRS) { 364 | cmd = "CSRRS"; 365 | } else if (funct3 == FUNC3_CSRRSI) { 366 | cmd = "CSRRSI"; 367 | } else if (funct3 == FUNC3_CSRRW) { 368 | cmd = "CSRRW"; 369 | } else if (funct3 == FUNC3_CSRRWI) { 370 | cmd = "CSRRWI"; 371 | } 372 | cmd += " " + GetRegName(rd) + ", " + NumberToHex(csr) + ", " + 373 | GetRegName(rs1); 374 | } 375 | break; 376 | case OPCODE_FENCE: 377 | if (funct3 == FUNC3_FENCEI) { 378 | cmd = "FENCEI"; 379 | } else if (funct3 == FUNC3_FENCE) { 380 | cmd = "FENCE"; 381 | } 382 | break; 383 | case OPCODE_AMO: 384 | if (funct5 == FUNC5_AMOADD) { 385 | cmd = funct3 == FUNC3_AMOD ? "AMOADD.D" : "AMOADD.W"; 386 | } else if (funct5 == FUNC5_AMOAND) { 387 | cmd = funct3 == FUNC3_AMOD ? "AMOAND.D" : "AMOAND.W"; 388 | } else if (funct5 == FUNC5_AMOMAX) { 389 | cmd = funct3 == FUNC3_AMOD ? "AMOMAX.D" : "AMOMAX.W"; 390 | } else if (funct5 == FUNC5_AMOMAXU) { 391 | cmd = funct3 == FUNC3_AMOD ? "AMOMAXU.D" : "AMOMAXU.W"; 392 | } else if (funct5 == FUNC5_AMOMIN) { 393 | cmd = funct3 == FUNC3_AMOD ? "AMOMIN.D" : "AMOMIN.W"; 394 | } else if (funct5 == FUNC5_AMOMINU) { 395 | cmd = funct3 == FUNC3_AMOD ? "AMOMINU.D" : "AMOMAIN.W"; 396 | } else if (funct5 == FUNC5_AMOOR) { 397 | cmd = funct3 == FUNC3_AMOD ? "AMOOR.D" : "AMOOR.W"; 398 | } else if (funct5 == FUNC5_AMOXOR) { 399 | cmd = funct3 == FUNC3_AMOD ? "AMOXOR.D" : "AMOXOR.W"; 400 | } else if (funct5 == FUNC5_AMOSWAP) { 401 | cmd = funct3 == FUNC3_AMOD ? "AMOSWAP.D" : "AMOSWAP.W"; 402 | } 403 | cmd += " " + GetRegName(rd) + ", " + GetRegName(rs2) + ", (" + 404 | GetRegName(rs1) + ")"; 405 | break; 406 | default: 407 | cmd = "Undefined instruction"; 408 | break; 409 | } 410 | return cmd; 411 | } 412 | } // namespace RISCV_EMULATOR 413 | -------------------------------------------------------------------------------- /RISCV_Emulator.cc: -------------------------------------------------------------------------------- 1 | #include "RISCV_Emulator.h" 2 | #include "memory_wrapper.h" 3 | #include "RISCV_cpu.h" 4 | #include "pte.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #ifndef _WIN32 14 | #include 15 | #else 16 | #include 17 | #endif 18 | 19 | namespace RISCV_EMULATOR { 20 | 21 | bool flag_64bit = false; 22 | 23 | std::vector ReadFile(std::string filename) { 24 | // open the file: 25 | std::streampos size; 26 | std::ifstream file(filename, std::ios::binary); 27 | 28 | // get its size: 29 | file.seekg(0, std::ios::end); 30 | size = file.tellg(); 31 | file.seekg(0, std::ios::beg); 32 | if (size > kMaxBinarySize || size <= 0) { 33 | std::cerr << "File size = " << size << "." << std::endl; 34 | exit(-1); 35 | } 36 | 37 | // read the data: 38 | std::vector fileData; 39 | fileData.resize((unsigned int) size); 40 | file.read(reinterpret_cast(fileData.data()), size); 41 | return fileData; 42 | } 43 | 44 | bool IsRightElf(Elf32_Ehdr *ehdr) { 45 | if ((ehdr->e_ident[EI_MAG0] != ELFMAG0 || ehdr->e_ident[EI_MAG1] != ELFMAG1 || 46 | ehdr->e_ident[EI_MAG2] != ELFMAG2 || 47 | ehdr->e_ident[EI_MAG3] != ELFMAG3)) { 48 | std::cerr << "Not an Elf file." << std::endl; 49 | return false; 50 | } 51 | if (ehdr->e_ident[EI_CLASS] != ELFCLASS32 && 52 | ehdr->e_ident[EI_CLASS] != ELFCLASS64) { 53 | std::cerr << "Not an 32bit or 64 bit(" 54 | << static_cast(ehdr->e_ident[EI_CLASS]) << ")" << std::endl; 55 | return false; 56 | } 57 | if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { 58 | std::cerr << "Not little endian (" 59 | << static_cast(ehdr->e_ident[EI_DATA]) << ")" << std::endl; 60 | return false; 61 | } 62 | if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) { 63 | std::cerr << "Not the current version." << std::endl; 64 | return false; 65 | } 66 | if (ehdr->e_ident[EI_OSABI] != ELFOSABI_SYSV) { 67 | std::cerr << "Not SYSV ABI (" << static_cast(ehdr->e_ident[EI_OSABI]) 68 | << ")" << std::endl; 69 | return false; 70 | } 71 | if (ehdr->e_type != ET_EXEC) { 72 | std::cerr << "Not an executable file (" << static_cast(ehdr->e_type) 73 | << ")" << std::endl; 74 | return false; 75 | } 76 | if (ehdr->e_machine != EM_RISCV) { 77 | std::cerr << "Not for RISCV (" << static_cast(ehdr->e_machine) << ")" 78 | << std::endl; 79 | return false; 80 | } 81 | return true; 82 | } 83 | 84 | bool Is64BitElf(Elf32_Ehdr *ehdr) { 85 | if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) { 86 | return true; 87 | } 88 | return false; 89 | } 90 | 91 | Elf32_Ehdr *GetElf32Ehdr(std::vector &program) { 92 | Elf32_Ehdr *ehdr = (Elf32_Ehdr *) reinterpret_cast(program.data()); 93 | return ehdr; 94 | } 95 | 96 | Elf64_Ehdr *GetElf64Ehdr(std::vector &program) { 97 | Elf64_Ehdr *ehdr = (Elf64_Ehdr *) reinterpret_cast(program.data()); 98 | return ehdr; 99 | } 100 | 101 | Elf32_Shdr *GetElf32Shdr(std::vector &program, int index) { 102 | Elf32_Ehdr *ehdr = GetElf32Ehdr(program); 103 | if (index < 0 || index >= ehdr->e_shnum) { 104 | std::cerr << "Section header " << index << " not found." << std::endl; 105 | return (NULL); 106 | } 107 | Elf32_Shdr *shdr = (Elf32_Shdr *) (program.data() + ehdr->e_shoff + 108 | ehdr->e_shentsize * index); 109 | return shdr; 110 | } 111 | 112 | Elf64_Shdr *GetElf64Shdr(std::vector &program, int index) { 113 | Elf64_Ehdr *ehdr = GetElf64Ehdr(program); 114 | if (index < 0 || index >= ehdr->e_shnum) { 115 | std::cerr << "Section header " << index << " not found." << std::endl; 116 | return (NULL); 117 | } 118 | Elf64_Shdr *shdr = (Elf64_Shdr *) (program.data() + ehdr->e_shoff + 119 | ehdr->e_shentsize * index); 120 | return shdr; 121 | } 122 | 123 | char *GetElf32SectionName(std::vector &program, Elf32_Shdr *shdr) { 124 | Elf32_Ehdr *ehdr = GetElf32Ehdr(program); 125 | Elf32_Shdr *nhdr = GetElf32Shdr(program, ehdr->e_shstrndx); 126 | return reinterpret_cast(program.data()) + nhdr->sh_offset + 127 | shdr->sh_name; 128 | } 129 | 130 | char *GetElf64SectionName(std::vector &program, Elf64_Shdr *shdr) { 131 | Elf64_Ehdr *ehdr = GetElf64Ehdr(program); 132 | Elf64_Shdr *nhdr = GetElf64Shdr(program, ehdr->e_shstrndx); 133 | return reinterpret_cast(program.data()) + nhdr->sh_offset + 134 | shdr->sh_name; 135 | } 136 | 137 | Elf32_Shdr *SearchElf32Shdr(std::vector &program, std::string name) { 138 | Elf32_Ehdr *ehdr = GetElf32Ehdr(program); 139 | 140 | // Find the last section header that has the name information. 141 | for (int i = 0; i < ehdr->e_shnum; i++) { 142 | Elf32_Shdr *shdr = GetElf32Shdr(program, i); 143 | char *section_name = GetElf32SectionName(program, shdr); 144 | if (!std::strcmp(section_name, name.c_str())) { 145 | std::cerr << "Section " << name << " found at 0x0" << std::hex 146 | << shdr->sh_offset << "." << std::endl; 147 | return shdr; 148 | } 149 | } 150 | return NULL; 151 | } 152 | 153 | Elf64_Shdr *SearchElf64Shdr(std::vector &program, std::string name) { 154 | Elf64_Ehdr *ehdr = GetElf64Ehdr(program); 155 | 156 | // Find the last section header that has the name information. 157 | for (int i = 0; i < ehdr->e_shnum; i++) { 158 | Elf64_Shdr *shdr = GetElf64Shdr(program, i); 159 | char *section_name = GetElf64SectionName(program, shdr); 160 | if (!std::strcmp(section_name, name.c_str())) { 161 | std::cerr << "Section " << name << " found at 0x0" << std::hex 162 | << shdr->sh_offset << "." << std::endl; 163 | return shdr; 164 | } 165 | } 166 | return NULL; 167 | } 168 | 169 | Elf32_Shdr *SearchElf32Shdr(std::vector &program, Elf32_Word type) { 170 | Elf32_Ehdr *ehdr = GetElf32Ehdr(program); 171 | 172 | // Find the last section header that has the name information. 173 | for (int i = 0; i < ehdr->e_shnum; i++) { 174 | Elf32_Shdr *shdr = GetElf32Shdr(program, i); 175 | if (shdr->sh_type == type) { 176 | char *section_name = GetElf32SectionName(program, shdr); 177 | std::cerr << "Section " << section_name << "(" << shdr->sh_type 178 | << ") found at 0x0" << std::hex 179 | << shdr->sh_offset << "." << std::endl; 180 | return shdr; 181 | } 182 | } 183 | return NULL; 184 | } 185 | 186 | Elf64_Shdr *SearchElf64Shdr(std::vector &program, Elf64_Word type) { 187 | Elf64_Ehdr *ehdr = GetElf64Ehdr(program); 188 | 189 | // Find the last section header that has the name information. 190 | for (int i = 0; i < ehdr->e_shnum; i++) { 191 | Elf64_Shdr *shdr = GetElf64Shdr(program, i); 192 | if (shdr->sh_type == type) { 193 | char *section_name = GetElf64SectionName(program, shdr); 194 | std::cerr << "Section " << section_name << "(" << shdr->sh_type 195 | << ") found at 0x0" << std::hex 196 | << shdr->sh_offset << "." << std::endl; 197 | return shdr; 198 | } 199 | } 200 | return NULL; 201 | } 202 | 203 | void Load32BitElfFile(std::vector &program, MemoryWrapper &memory) { 204 | Elf32_Ehdr *ehdr = GetElf32Ehdr(program); 205 | for (int i = 0; i < ehdr->e_phnum; i++) { 206 | std::cerr << "Program Header " << i << ":"; 207 | Elf32_Phdr *phdr = (Elf32_Phdr *) (program.data() + ehdr->e_phoff + 208 | ehdr->e_phentsize * i); 209 | switch (phdr->p_type) { 210 | 211 | case PT_LOAD: 212 | std::cerr << "Type: LOAD. "; 213 | std::cerr << "Copy to 0x" << std::hex << static_cast(phdr->p_vaddr) 214 | << " from 0x" << static_cast(phdr->p_offset) << std::dec 215 | << ", size " << static_cast(phdr->p_filesz) << ". "; 216 | 217 | for (Elf32_Word i = 0; i < phdr->p_filesz; i++) { 218 | memory.WriteByte(phdr->p_vaddr + i, program[phdr->p_offset + i]); 219 | } 220 | std::cerr << "Loaded" << std::endl; 221 | break; 222 | default: 223 | std::cerr << "Type: OTHER" << std::endl; 224 | break; 225 | } 226 | } 227 | 228 | // Set up BSS (/ 229 | Elf32_Shdr *shdr = SearchElf32Shdr(program, ".bss"); 230 | if (shdr) { 231 | std::cerr << "Secure BSS." << std::endl; 232 | std::cerr << "BSS start address: " << std::hex << shdr->sh_addr; 233 | std::cerr << ", end address: " << shdr->sh_addr + shdr->sh_size 234 | << std::endl; 235 | for (uint32_t i = shdr->sh_addr; i < shdr->sh_addr + shdr->sh_size; i++) { 236 | memory.WriteByte(i, 0); 237 | } 238 | } else { 239 | std::cerr << "No BSS found." << std::endl; 240 | } 241 | } 242 | 243 | void Load64BitElfFile(std::vector &program, MemoryWrapper &memory) { 244 | Elf64_Ehdr *ehdr = GetElf64Ehdr(program); 245 | for (int i = 0; i < ehdr->e_phnum; i++) { 246 | std::cerr << "Program Header " << i << ":"; 247 | Elf64_Phdr *phdr = (Elf64_Phdr *) (program.data() + ehdr->e_phoff + 248 | ehdr->e_phentsize * i); 249 | switch (phdr->p_type) { 250 | 251 | case PT_LOAD: 252 | std::cerr << "Type: LOAD. "; 253 | std::cerr << "Copy to 0x" << std::hex << static_cast(phdr->p_vaddr) 254 | << " from 0x" << static_cast(phdr->p_offset) << std::dec 255 | << ", size " << static_cast(phdr->p_filesz) << ". "; 256 | 257 | for (Elf64_Word i = 0; i < phdr->p_filesz; i++) { 258 | memory.WriteByte(phdr->p_vaddr + i, program[phdr->p_offset + i]); 259 | } 260 | std::cerr << "Loaded" << std::endl; 261 | break; 262 | default: 263 | std::cerr << "Type: OTHER" << std::endl; 264 | break; 265 | } 266 | } 267 | 268 | // Set up BSS (/ 269 | Elf64_Shdr *shdr = SearchElf64Shdr(program, ".bss"); 270 | if (shdr) { 271 | std::cerr << "Secure BSS." << std::endl; 272 | std::cerr << "BSS start address: " << std::hex << shdr->sh_addr; 273 | std::cerr << ", end address: " << shdr->sh_addr + shdr->sh_size 274 | << std::endl; 275 | for (uint32_t i = shdr->sh_addr; i < shdr->sh_addr + shdr->sh_size; i++) { 276 | memory.WriteByte(i, 0); 277 | } 278 | } else { 279 | std::cerr << "No BSS found." << std::endl; 280 | } 281 | } 282 | 283 | void LoadElfFile(std::vector &program, MemoryWrapper &memory) { 284 | Elf32_Ehdr *ehdr = GetElf32Ehdr(program); 285 | if (IsRightElf(ehdr)) { 286 | std::cerr << "This is a supported RISC-V 32bit or 64bit Elf file" 287 | << std::endl; 288 | } 289 | 290 | flag_64bit = Is64BitElf(ehdr); 291 | if (flag_64bit) { 292 | Load64BitElfFile(program, memory); 293 | } else { 294 | Load32BitElfFile(program, memory); 295 | } 296 | } 297 | 298 | Elf32_Sym * 299 | FindElf32Symbol(std::vector &program, std::string target_name) { 300 | // Find the symbol table. 301 | Elf32_Shdr *shdr = SearchElf32Shdr(program, SHT_SYMTAB); 302 | if (!shdr) { 303 | std::cerr << "Symbol table not found." << std::endl; 304 | return NULL; 305 | } 306 | 307 | int number = shdr->sh_size / sizeof(Elf32_Sym); 308 | std::cerr << "Number of symbols = " << std::dec << number << ", (" 309 | << shdr->sh_size << " bytes)" << std::endl; 310 | 311 | Elf32_Shdr *strtab_shdr = SearchElf32Shdr(program, ".strtab"); 312 | if (!strtab_shdr) { 313 | std::cerr << ".strtab not found." << std::endl; 314 | return NULL; 315 | } 316 | 317 | for (int i = 0; i < number; i++) { 318 | Elf32_Sym *symbol = (Elf32_Sym *) (program.data() + shdr->sh_offset + 319 | i * sizeof(Elf32_Sym)); 320 | // std::cerr << "Symbol name offset = " << symbol->st_name << "." << std::endl; 321 | char *symbol_name = 322 | (char *) (program.data()) + strtab_shdr->sh_offset + symbol->st_name; 323 | // std::cerr << "Symbol: " << symbol_name << " found. Size =" << symbol->st_size << std::endl; 324 | if (!strcmp(symbol_name, target_name.c_str())) { 325 | std::cerr << "Symbol \"" << target_name << "\" found at index " << i 326 | << "." << std::endl; 327 | return symbol; 328 | } 329 | } 330 | return NULL; 331 | } 332 | 333 | Elf64_Sym * 334 | FindElf64Symbol(std::vector &program, std::string target_name) { 335 | // Find the symbol table. 336 | Elf64_Shdr *shdr = SearchElf64Shdr(program, SHT_SYMTAB); 337 | if (!shdr) { 338 | std::cerr << "Symbol table not found." << std::endl; 339 | return NULL; 340 | } 341 | 342 | int number = shdr->sh_size / sizeof(Elf64_Sym); 343 | std::cerr << "Number of symbols = " << std::dec << number << ", (" 344 | << shdr->sh_size << " bytes)" << std::endl; 345 | 346 | Elf64_Shdr *strtab_shdr = SearchElf64Shdr(program, ".strtab"); 347 | if (!strtab_shdr) { 348 | std::cerr << ".strtab not found." << std::endl; 349 | return NULL; 350 | } 351 | 352 | for (int i = 0; i < number; i++) { 353 | Elf64_Sym *symbol = (Elf64_Sym *) (program.data() + shdr->sh_offset + 354 | i * sizeof(Elf64_Sym)); 355 | // std::cerr << "Symbol name offset = " << symbol->st_name << "." << std::endl; 356 | char *symbol_name = 357 | (char *) (program.data()) + strtab_shdr->sh_offset + symbol->st_name; 358 | // std::cerr << "Symbol: " << symbol_name << " found. Size =" << symbol->st_size << std::endl; 359 | if (!strcmp(symbol_name, target_name.c_str())) { 360 | std::cerr << "Symbol \"" << target_name << "\" found at index " << i 361 | << "." << std::endl; 362 | return symbol; 363 | } 364 | } 365 | return NULL; 366 | } 367 | 368 | uint32_t GetElf32GlobalPointer(std::vector &program) { 369 | std::string target_name = "__global_pointer$"; 370 | Elf32_Sym *symbol = FindElf32Symbol(program, target_name); 371 | if (symbol) { 372 | std::cerr << "Global Pointer Value = 0x" << std::hex 373 | << symbol->st_value << std::dec << "." << std::endl; 374 | return symbol->st_value; 375 | } 376 | std::cerr << "Global Pointer Value not defined." << std::endl; 377 | return -1; 378 | } 379 | 380 | uint32_t GetElf64GlobalPointer(std::vector &program) { 381 | std::string target_name = "__global_pointer$"; 382 | Elf64_Sym *symbol = FindElf64Symbol(program, target_name); 383 | if (symbol) { 384 | std::cerr << "Global Pointer Value = 0x" << std::hex 385 | << symbol->st_value << std::dec << "." << std::endl; 386 | return symbol->st_value; 387 | } 388 | std::cerr << "Global Pointer Value not defined." << std::endl; 389 | return -1; 390 | } 391 | 392 | int64_t GetGlobalPointer(std::vector &program) { 393 | if (flag_64bit) { 394 | return GetElf32GlobalPointer(program); 395 | } else { 396 | return GetElf64GlobalPointer(program); 397 | } 398 | } 399 | 400 | uint32_t GetElf32EntryPoint(std::vector &program) { 401 | Elf32_Ehdr *ehdr = GetElf32Ehdr(program); 402 | return ehdr->e_entry; 403 | } 404 | 405 | uint32_t GetElf64EntryPoint(std::vector &program) { 406 | Elf64_Ehdr *ehdr = GetElf64Ehdr(program); 407 | return ehdr->e_entry; 408 | } 409 | 410 | uint64_t GetEntryPoint(std::vector &program) { 411 | if (flag_64bit) { 412 | return GetElf64EntryPoint(program); 413 | } else { 414 | return GetElf32EntryPoint(program); 415 | } 416 | } 417 | 418 | std::tuple 419 | ParseCmd(int argc, char (***argv)) { 420 | bool error = false; 421 | bool verbose = false; 422 | bool address64bit = false; 423 | bool paging = false; 424 | bool ecall_emulation = false; 425 | bool host_emulation = false; 426 | bool device_enable = false; 427 | bool disable_machine_interrupt_delegation = false; 428 | std::string diskimage_file = ""; 429 | std::string filename = ""; 430 | if (argc < 2) { 431 | error = true; 432 | } else { 433 | for (int i = 1; i < argc && !error; ++i) { 434 | if ((*argv)[i][0] == '-') { 435 | if ((*argv)[i][1] == 'v') { 436 | verbose = true; 437 | } else if ((*argv)[i][1] == '6' && (*argv)[i][2] == '4') { 438 | address64bit = true; 439 | } else if ((*argv)[i][1] == 'p') { 440 | paging = true; 441 | } else if ((*argv)[i][1] == 'e') { 442 | ecall_emulation = true; 443 | } else if ((*argv)[i][1] == 'h') { 444 | host_emulation = true; 445 | } else if ((*argv)[i][1] == 'd') { 446 | device_enable = true; 447 | } else if ((*argv)[i][1] == 'm') { 448 | disable_machine_interrupt_delegation = true; 449 | } else if ((*argv)[i][1] == 's') { 450 | if (i < argc - 1) { 451 | diskimage_file = std::string((*argv)[++i]); 452 | } else { 453 | error = true; 454 | } 455 | } else { 456 | error = true; 457 | } 458 | } else { 459 | if (filename == "") { 460 | filename = std::string((*argv)[i]); 461 | } else { 462 | error = true; 463 | } 464 | } 465 | } 466 | } 467 | return std::make_tuple(error, filename, verbose, address64bit, paging, 468 | ecall_emulation, host_emulation, device_enable, 469 | disable_machine_interrupt_delegation, diskimage_file); 470 | } 471 | 472 | constexpr int k32BitMmuLevelOneSize = 1024; // 1024 x 4 B = 4 KiB. 473 | constexpr int k32BitMmuLevelZeroSize = 1024; // 1024 x 4 B = 4 KiB. 474 | constexpr int k32BitPteSize = 4; 475 | 476 | void SetDefaultMmuTable32(std::shared_ptr memory) { 477 | uint32_t level1 = k32BitMmuLevel1; 478 | uint32_t level0 = k32BitMmuLevel0; 479 | // Sv32. Physical address = virtual address. 480 | // Level1. 481 | Pte32 pte(0); 482 | pte.SetV(1); 483 | for (int i = 0; i < k32BitMmuLevelOneSize; ++i) { 484 | uint32_t address = level1 + i * k32BitPteSize; 485 | uint32_t ppn = (level0 + i * k32BitMmuLevelZeroSize * k32BitPteSize) >> 12; 486 | pte.SetPpn(ppn); 487 | uint32_t pte_value = pte.GetValue(); 488 | memory->Write32(address, pte_value); 489 | } 490 | // Level0. 491 | pte.SetX(1); 492 | pte.SetW(1); 493 | pte.SetR(1); 494 | for (int j = 0; j < k32BitMmuLevelOneSize; ++j) { 495 | for (int i = 0; i < k32BitMmuLevelZeroSize; ++i) { 496 | uint32_t ppn = j * k32BitMmuLevelZeroSize + i; 497 | uint32_t address = level0 + ppn * k32BitPteSize; 498 | assert(address < level1 || 499 | level1 + k32BitMmuLevelOneSize * k32BitPteSize <= address); 500 | // ((ppn << 12) + offset) will be the physical address. So, x4096 is not needed here. 501 | pte.SetPpn(ppn); 502 | memory->Write32(address, pte.GetValue()); 503 | } 504 | } 505 | return; 506 | } 507 | 508 | constexpr uint64_t k64BitMmuLevelTwoSize = 512; // 512 x 8 B = 4 KiB. 509 | constexpr uint64_t k64BitMmuLevelOneSize = 512; // 512 x 8 B = 4 KiB. 510 | constexpr uint64_t k64BitMmuLevelZeroSize = 512; // 512 x 8 B = 4 KiB. 511 | constexpr int k64BitPteSize = 8; 512 | 513 | void SetDefaultMmuTable64(std::shared_ptr memory) { 514 | uint64_t level2 = k64BitMmuLevel2; 515 | uint64_t level1 = k64BitMmuLevel1; 516 | uint64_t level0 = k64BitMmuLevel0; 517 | // Sv32. Physical address = virtual address. 518 | Pte32 pte(0); 519 | // Level2. Map only 32bit range = 4 entry at level 2. 520 | unsigned kLevel2ValidEntry = 4; 521 | for (unsigned i = 0; i < k64BitMmuLevelTwoSize; i++) { 522 | uint32_t address = level2 + i * k64BitPteSize; 523 | if (i < kLevel2ValidEntry) { 524 | pte.SetV(1); 525 | uint64_t ppn = (level1 + i * k64BitMmuLevelOneSize * k64BitPteSize) >> 12; 526 | pte.SetPpn(ppn); 527 | } else { 528 | pte.SetPpn(0); 529 | } 530 | uint64_t pte_value = pte.GetValue(); 531 | memory->Write64(address, pte_value); 532 | } 533 | // Level1. 534 | pte.SetV(1); 535 | for (unsigned j = 0; j < kLevel2ValidEntry; ++j) { 536 | for (unsigned i = 0; i < k64BitMmuLevelOneSize; ++i) { 537 | uint32_t address = 538 | level1 + j * k64BitMmuLevelOneSize * k64BitPteSize + i * k64BitPteSize; 539 | uint64_t ppn = (level0 + 540 | j * k64BitMmuLevelOneSize * k64BitMmuLevelZeroSize * 541 | k64BitPteSize + 542 | i * k64BitMmuLevelZeroSize * k64BitPteSize) >> 12; 543 | pte.SetPpn(ppn); 544 | uint64_t pte_value = pte.GetValue(); 545 | memory->Write64(address, pte_value); 546 | } 547 | } 548 | // Level0. 549 | pte.SetX(1); 550 | pte.SetW(1); 551 | pte.SetR(1); 552 | for (unsigned k = 0; k < kLevel2ValidEntry; ++k) { 553 | for (unsigned j = 0; j < k64BitMmuLevelOneSize; ++j) { 554 | for (unsigned i = 0; i < k64BitMmuLevelZeroSize; ++i) { 555 | // ((ppn << 12) + offset) will be the physical address. So, x4096 is not needed for ppn. 556 | uint64_t ppn = k * k64BitMmuLevelOneSize * k64BitMmuLevelZeroSize + 557 | j * k64BitMmuLevelZeroSize + i; 558 | uint32_t address = level0 + ppn * k64BitPteSize; 559 | pte.SetPpn(ppn); 560 | memory->Write64(address, pte.GetValue()); 561 | } 562 | } 563 | } 564 | return; 565 | } 566 | 567 | 568 | void SetDefaultMmuTable(bool address64bit, std::shared_ptr memory) { 569 | if (address64bit) { 570 | SetDefaultMmuTable64(memory); 571 | } else { 572 | SetDefaultMmuTable32(memory); 573 | } 574 | } 575 | 576 | int run(int argc, char *argv[]) { 577 | bool cmdline_error, verbose, address64bit, paging, ecall_emulation, host_emulation, 578 | device_emulation, disable_machine_interrupt_delegation; 579 | std::string disk_image_file; 580 | std::string filename; 581 | 582 | auto options = ParseCmd(argc, &argv); 583 | cmdline_error = std::get<0>(options); 584 | filename = std::get<1>(options); 585 | verbose = std::get<2>(options); 586 | address64bit = std::get<3>(options); 587 | paging = std::get<4>(options); 588 | ecall_emulation = std::get<5>(options); 589 | host_emulation = std::get<6>(options); 590 | device_emulation = std::get<7>(options); 591 | disable_machine_interrupt_delegation = std::get<8>(options); 592 | disk_image_file = std::get<9>(options); 593 | 594 | 595 | if (cmdline_error) { 596 | std::cerr << "Uasge: " << argv[0] << " elf_file " << "[-v][-64][-p][-e][-h][-m][-s disk.img]" 597 | << std::endl; 598 | std::cerr << "-v: Verbose" << std::endl; 599 | std::cerr << "-e: System Call Emulation" << std::endl; 600 | std::cerr << "-p: Paging Enabled from Start" << std::endl; 601 | std::cerr << "-64: 64 bit (RV64I) (default is 32 bit mode, RV32I)" 602 | << std::endl; 603 | std::cerr << "-d: Device emulation of UART and VirtioDisk (needed to use -s option). Press Ctrl-a to exit." << std::endl; 604 | std::cerr << "-h: Use tohost and fromhost function" << std::endl; 605 | std::cerr << "-m: disable delegation of machine interrupt (for compatibility with QEMU)" << std::endl; 606 | std::cerr << "-s disk.img: specify disk image" << std::endl; 607 | return -1; 608 | } 609 | 610 | std::cerr << "Elf file name: " << filename << std::endl; 611 | if (verbose) { 612 | std::cerr << "Verbose mode." << std::endl; 613 | } 614 | 615 | std::vector program = ReadFile(filename); 616 | 617 | auto memory = std::make_shared(MemoryWrapper()); 618 | LoadElfFile(program, *memory); 619 | uint32_t entry_point = GetEntryPoint(program); 620 | std::cerr << "Entry point is 0x" << std::hex << entry_point << std::dec 621 | << std::endl; 622 | 623 | int64_t global_pointer = GetGlobalPointer(program); 624 | if (global_pointer == -1) { 625 | global_pointer = entry_point; 626 | } 627 | std::cerr << "Global Pointer is 0x" << std::hex << global_pointer << std::dec 628 | << std::endl; 629 | 630 | uint32_t sp_value = kTop; 631 | 632 | // Read DiskImage. 633 | std::shared_ptr> disk_image; 634 | if (disk_image_file != "") { 635 | disk_image = std::make_shared>(ReadFile(disk_image_file)); 636 | } 637 | 638 | // Run CPU emulator 639 | std::cerr << "Execution start" << std::endl; 640 | 641 | RiscvCpu cpu(address64bit); 642 | cpu.SetEcallEmulationEnable(ecall_emulation); 643 | cpu.SetRegister(SP, sp_value); 644 | cpu.SetRegister(GP, global_pointer); 645 | SetDefaultMmuTable(address64bit, memory); 646 | uint64_t satp = 0; 647 | if (paging) { 648 | std::cerr << "Paging enabled." << std::endl; 649 | if (address64bit) { 650 | satp = (k64BitMmuLevel2 >> 12) | (static_cast(8) << 60); 651 | } else { 652 | satp = (k32BitMmuLevel1 >> 12) | (1 << 31); 653 | } 654 | } 655 | cpu.SetCsr(SATP, satp); 656 | cpu.SetMemory(memory); 657 | cpu.SetWorkMemory(kTop, kBottom); 658 | cpu.SetHostEmulationEnable(host_emulation); 659 | cpu.SetDeviceEmulationEnable(device_emulation); 660 | cpu.DisableMachineInterruptDelegation(disable_machine_interrupt_delegation); 661 | cpu.DeviceInitialization(); 662 | cpu.SetDiskImage(disk_image); 663 | int error = cpu.RunCpu(entry_point, verbose); 664 | if (error) { 665 | printf("CPU execution fail.\n"); 666 | } 667 | int return_value = cpu.ReadRegister(A0); 668 | 669 | std::cerr << "Return GetValue: " << return_value << "." << std::endl; 670 | 671 | return return_value; 672 | } 673 | 674 | } // namespace RISCV_EMULATOR 675 | 676 | int main(int argc, char *argv[]) { 677 | return RISCV_EMULATOR::run(argc, argv); 678 | } 679 | -------------------------------------------------------------------------------- /tests/assembler.cc: -------------------------------------------------------------------------------- 1 | #include "load_assembler.h" 2 | #include "RISCV_cpu.h" 3 | #include "instruction_encdec.h" 4 | #include 5 | #include 6 | 7 | namespace CPU_TEST { 8 | 9 | // R_TYPE 10 | uint32_t 11 | AsmRType(op_label opcode, op_funct funct, op_funct3 funct3, uint32_t rd, 12 | uint32_t rs1, uint32_t rs2) { 13 | RType cmd; 14 | cmd.opcode = opcode; 15 | cmd.funct7 = funct; 16 | cmd.funct3 = funct3; 17 | cmd.rd = rd; 18 | cmd.rs1 = rs1; 19 | cmd.rs2 = rs2; 20 | return cmd.GetValue(); 21 | } 22 | 23 | uint32_t AsmAdd(uint32_t rd, uint32_t rs1, uint32_t rs2) { 24 | return AsmRType(OPCODE_ARITHLOG, FUNC_NORM, FUNC3_ADDSUB, rd, rs1, rs2); 25 | } 26 | 27 | uint32_t AsmAddw(uint32_t rd, uint32_t rs1, uint32_t rs2) { 28 | return AsmRType(OPCODE_ARITHLOG_64, FUNC_NORM, FUNC3_ADDSUB, rd, rs1, rs2); 29 | } 30 | 31 | uint32_t AsmSub(uint32_t rd, uint32_t rs1, uint32_t rs2) { 32 | return AsmRType(OPCODE_ARITHLOG, FUNC_ALT, FUNC3_ADDSUB, rd, rs1, rs2); 33 | } 34 | 35 | uint32_t AsmSubw(uint32_t rd, uint32_t rs1, uint32_t rs2) { 36 | return AsmRType(OPCODE_ARITHLOG_64, FUNC_ALT, FUNC3_ADDSUB, rd, rs1, rs2); 37 | } 38 | 39 | uint32_t AsmAnd(uint32_t rd, uint32_t rs1, uint32_t rs2) { 40 | return AsmRType(OPCODE_ARITHLOG, FUNC_NORM, FUNC3_AND, rd, rs1, rs2); 41 | } 42 | 43 | uint32_t AsmOr(uint32_t rd, uint32_t rs1, uint32_t rs2) { 44 | return AsmRType(OPCODE_ARITHLOG, FUNC_NORM, FUNC3_OR, rd, rs1, rs2); 45 | } 46 | 47 | uint32_t AsmXor(uint32_t rd, uint32_t rs1, uint32_t rs2) { 48 | return AsmRType(OPCODE_ARITHLOG, FUNC_NORM, FUNC3_XOR, rd, rs1, rs2); 49 | } 50 | 51 | uint32_t AsmSll(uint32_t rd, uint32_t rs1, uint32_t rs2) { 52 | return AsmRType(OPCODE_ARITHLOG, FUNC_NORM, FUNC3_SL, rd, rs1, rs2); 53 | } 54 | 55 | uint32_t AsmSllw(uint32_t rd, uint32_t rs1, uint32_t rs2) { 56 | return AsmRType(OPCODE_ARITHLOG_64, FUNC_NORM, FUNC3_SL, rd, rs1, rs2); 57 | } 58 | 59 | uint32_t AsmSrl(uint32_t rd, uint32_t rs1, uint32_t rs2) { 60 | return AsmRType(OPCODE_ARITHLOG, FUNC_NORM, FUNC3_SR, rd, rs1, rs2); 61 | } 62 | 63 | uint32_t AsmSrlw(uint32_t rd, uint32_t rs1, uint32_t rs2) { 64 | return AsmRType(OPCODE_ARITHLOG_64, FUNC_NORM, FUNC3_SR, rd, rs1, rs2); 65 | } 66 | 67 | uint32_t AsmSra(uint32_t rd, uint32_t rs1, uint32_t rs2) { 68 | return AsmRType(OPCODE_ARITHLOG, FUNC_ALT, FUNC3_SR, rd, rs1, rs2); 69 | } 70 | 71 | uint32_t AsmSraw(uint32_t rd, uint32_t rs1, uint32_t rs2) { 72 | return AsmRType(OPCODE_ARITHLOG_64, FUNC_ALT, FUNC3_SR, rd, rs1, rs2); 73 | } 74 | 75 | uint32_t AsmSlt(uint32_t rd, uint32_t rs1, uint32_t rs2) { 76 | return AsmRType(OPCODE_ARITHLOG, FUNC_NORM, FUNC3_SLT, rd, rs1, rs2); 77 | } 78 | 79 | uint32_t AsmSltu(uint32_t rd, uint32_t rs1, uint32_t rs2) { 80 | return AsmRType(OPCODE_ARITHLOG, FUNC_NORM, FUNC3_SLTU, rd, rs1, rs2); 81 | } 82 | 83 | uint32_t AsmMret() { 84 | uint32_t rs2 = 0b00010; // fixed for MRET 85 | return AsmRType(OPCODE_SYSTEM, FUNC_MRET, FUNC3_SYSTEM, 0, 0, rs2); 86 | } 87 | 88 | // A_TYPE (a variation of R-Type) 89 | uint32_t 90 | AsmAType(op_label opcode, op_funct5 funct5, op_funct3 funct3, uint32_t rd, 91 | uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 92 | RType cmd; 93 | uint32_t funct7 = (funct5 << 2) | ((aq & 0b1) << 1) | (rl & 0b1); 94 | cmd.opcode = opcode; 95 | cmd.funct7 = funct7; 96 | cmd.funct3 = funct3; 97 | cmd.rd = rd; 98 | cmd.rs1 = rs1; 99 | cmd.rs2 = rs2; 100 | return cmd.GetValue(); 101 | } 102 | 103 | uint32_t AsmAmoAddd(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 104 | return AsmAType(OPCODE_AMO, FUNC5_AMOADD, FUNC3_AMOD, rd, rs1, rs2, aq, rl); 105 | } 106 | 107 | uint32_t AsmAmoAddw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 108 | return AsmAType(OPCODE_AMO, FUNC5_AMOADD, FUNC3_AMOW, rd, rs1, rs2, aq, rl); 109 | } 110 | 111 | uint32_t AsmAmoAndd(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 112 | return AsmAType(OPCODE_AMO, FUNC5_AMOAND, FUNC3_AMOD, rd, rs1, rs2, aq, rl); 113 | } 114 | uint32_t AsmAmoAndw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 115 | return AsmAType(OPCODE_AMO, FUNC5_AMOAND, FUNC3_AMOW, rd, rs1, rs2, aq, rl); 116 | } 117 | 118 | uint32_t AsmAmoMaxd(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 119 | return AsmAType(OPCODE_AMO, FUNC5_AMOMAX, FUNC3_AMOD, rd, rs1, rs2, aq, rl); 120 | } 121 | uint32_t AsmAmoMaxw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 122 | return AsmAType(OPCODE_AMO, FUNC5_AMOMAX, FUNC3_AMOW, rd, rs1, rs2, aq, rl); 123 | } 124 | 125 | uint32_t AsmAmoMaxud(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 126 | return AsmAType(OPCODE_AMO, FUNC5_AMOMAXU, FUNC3_AMOD, rd, rs1, rs2, aq, rl); 127 | } 128 | uint32_t AsmAmoMaxuw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 129 | return AsmAType(OPCODE_AMO, FUNC5_AMOMAXU, FUNC3_AMOW, rd, rs1, rs2, aq, rl); 130 | } 131 | 132 | uint32_t AsmAmoMind(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 133 | return AsmAType(OPCODE_AMO, FUNC5_AMOMIN, FUNC3_AMOD, rd, rs1, rs2, aq, rl); 134 | } 135 | uint32_t AsmAmoMinw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 136 | return AsmAType(OPCODE_AMO, FUNC5_AMOMIN, FUNC3_AMOW, rd, rs1, rs2, aq, rl); 137 | } 138 | 139 | uint32_t AsmAmoMinud(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 140 | return AsmAType(OPCODE_AMO, FUNC5_AMOMINU, FUNC3_AMOD, rd, rs1, rs2, aq, rl); 141 | } 142 | uint32_t AsmAmoMinuw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 143 | return AsmAType(OPCODE_AMO, FUNC5_AMOMINU, FUNC3_AMOW, rd, rs1, rs2, aq, rl); 144 | } 145 | 146 | uint32_t AsmAmoOrd(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 147 | return AsmAType(OPCODE_AMO, FUNC5_AMOOR, FUNC3_AMOD, rd, rs1, rs2, aq, rl); 148 | } 149 | uint32_t AsmAmoOrw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 150 | return AsmAType(OPCODE_AMO, FUNC5_AMOOR, FUNC3_AMOW, rd, rs1, rs2, aq, rl); 151 | } 152 | 153 | uint32_t AsmAmoSwapd(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 154 | return AsmAType(OPCODE_AMO, FUNC5_AMOSWAP, FUNC3_AMOD, rd, rs1, rs2, aq, rl); 155 | } 156 | uint32_t AsmAmoSwapw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 157 | return AsmAType(OPCODE_AMO, FUNC5_AMOSWAP, FUNC3_AMOW, rd, rs1, rs2, aq, rl); 158 | } 159 | 160 | uint32_t AsmAmoXord(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 161 | return AsmAType(OPCODE_AMO, FUNC5_AMOXOR, FUNC3_AMOD, rd, rs1, rs2, aq, rl); 162 | } 163 | uint32_t AsmAmoXorw(uint32_t rd, uint32_t rs1, uint32_t rs2, uint32_t aq, uint32_t rl) { 164 | return AsmAType(OPCODE_AMO, FUNC5_AMOXOR, FUNC3_AMOW, rd, rs1, rs2, aq, rl); 165 | } 166 | 167 | // I_TYPE 168 | uint32_t AsmIType(op_label opcode, op_funct3 funct3, uint32_t rd, uint32_t rs1, 169 | int32_t imm12) { 170 | IType cmd; 171 | cmd.opcode = opcode; 172 | cmd.funct3 = funct3; 173 | cmd.imm12 = imm12 & 0xFFF; 174 | cmd.rd = rd; 175 | cmd.rs1 = rs1; 176 | return cmd.GetValue(); 177 | } 178 | 179 | uint32_t AsmAddi(uint32_t rd, uint32_t rs1, int32_t imm12) { 180 | return AsmIType(OPCODE_ARITHLOG_I, FUNC3_ADDSUB, rd, rs1, imm12); 181 | } 182 | 183 | uint32_t AsmAddiw(uint32_t rd, uint32_t rs1, int32_t imm12) { 184 | return AsmIType(OPCODE_ARITHLOG_I64, FUNC3_ADDSUB, rd, rs1, imm12); 185 | } 186 | 187 | uint32_t AsmAndi(uint32_t rd, uint32_t rs1, int32_t imm12) { 188 | return AsmIType(OPCODE_ARITHLOG_I, FUNC3_AND, rd, rs1, imm12); 189 | } 190 | 191 | uint32_t AsmOri(uint32_t rd, uint32_t rs1, int32_t imm12) { 192 | return AsmIType(OPCODE_ARITHLOG_I, FUNC3_OR, rd, rs1, imm12); 193 | } 194 | 195 | uint32_t AsmXori(uint32_t rd, uint32_t rs1, int32_t imm12) { 196 | return AsmIType(OPCODE_ARITHLOG_I, FUNC3_XOR, rd, rs1, imm12); 197 | } 198 | 199 | uint32_t AsmSlli(uint32_t rd, uint32_t rs1, int32_t imm12) { 200 | // SLLI immediate is 6 bit wide. 201 | imm12 &= 0b0111111; 202 | return AsmIType(OPCODE_ARITHLOG_I, FUNC3_SL, rd, rs1, imm12); 203 | } 204 | 205 | uint32_t AsmSlliw(uint32_t rd, uint32_t rs1, int32_t imm12) { 206 | // SLLI immediate is 6 bit wide. 207 | imm12 &= 0b0111111; 208 | return AsmIType(OPCODE_ARITHLOG_I64, FUNC3_SL, rd, rs1, imm12); 209 | } 210 | 211 | uint32_t AsmSrli(uint32_t rd, uint32_t rs1, int32_t imm12) { 212 | // SRLI immediate is 6 bit wide. 213 | imm12 = imm12 & 0b0111111; 214 | return AsmIType(OPCODE_ARITHLOG_I, FUNC3_SR, rd, rs1, imm12); 215 | } 216 | 217 | uint32_t AsmSrliw(uint32_t rd, uint32_t rs1, int32_t imm12) { 218 | // SRLI immediate is 6 bit wide. 219 | imm12 = imm12 & 0b0111111; 220 | return AsmIType(OPCODE_ARITHLOG_I64, FUNC3_SR, rd, rs1, imm12); 221 | } 222 | 223 | uint32_t AsmSrai(uint32_t rd, uint32_t rs1, int32_t imm12) { 224 | IType cmd; 225 | // SRAI immediate is 6 bit wide. 226 | imm12 &= 0b0111111; 227 | // SRAI imm12 top 6 bit is same as funct7 228 | imm12 |= (FUNC_ALT >> 1) << 6; 229 | return AsmIType(OPCODE_ARITHLOG_I, FUNC3_SR, rd, rs1, imm12); 230 | } 231 | 232 | uint32_t AsmSraiw(uint32_t rd, uint32_t rs1, int32_t imm12) { 233 | IType cmd; 234 | // SRAIW immediate is 6 bit wide. 235 | imm12 &= 0b0111111; 236 | // SRAIW imm12 top 6 bit is same as funct7 237 | imm12 |= (FUNC_ALT >> 1) << 6; 238 | return AsmIType(OPCODE_ARITHLOG_I64, FUNC3_SR, rd, rs1, imm12); 239 | } 240 | 241 | uint32_t AsmSlti(uint32_t rd, uint32_t rs1, int32_t imm12) { 242 | return AsmIType(OPCODE_ARITHLOG_I, FUNC3_SLT, rd, rs1, imm12); 243 | } 244 | 245 | uint32_t AsmSltiu(uint32_t rd, uint32_t rs1, int32_t imm12) { 246 | return AsmIType(OPCODE_ARITHLOG_I, FUNC3_SLTU, rd, rs1, imm12); 247 | } 248 | 249 | uint32_t AsmJalr(uint32_t rd, uint32_t rs1, int32_t offset12) { 250 | return AsmIType(OPCODE_JALR, FUNC3_JALR, rd, rs1, offset12); 251 | } 252 | 253 | uint32_t AsmEbreak() { 254 | return AsmIType(OPCODE_SYSTEM, FUNC3_SYSTEM, 0, 0, 1); 255 | } 256 | 257 | uint32_t AsmEcall() { 258 | return AsmIType(OPCODE_SYSTEM, FUNC3_SYSTEM, 0, 0, 0); 259 | } 260 | 261 | uint32_t AsmCsrrc(uint32_t rd, uint32_t rs1, int32_t offset12) { 262 | return AsmIType(OPCODE_SYSTEM, FUNC3_CSRRC, rd, rs1, offset12); 263 | } 264 | 265 | uint32_t AsmCsrrci(uint32_t rd, uint32_t zimm, int32_t offset12) { 266 | return AsmIType(OPCODE_SYSTEM, FUNC3_CSRRCI, rd, zimm, offset12); 267 | } 268 | 269 | uint32_t AsmCsrrs(uint32_t rd, uint32_t rs1, int32_t offset12) { 270 | return AsmIType(OPCODE_SYSTEM, FUNC3_CSRRS, rd, rs1, offset12); 271 | } 272 | 273 | uint32_t AsmCsrrsi(uint32_t rd, uint32_t zimm, int32_t offset12) { 274 | return AsmIType(OPCODE_SYSTEM, FUNC3_CSRRSI, rd, zimm, offset12); 275 | } 276 | 277 | uint32_t AsmCsrrw(uint32_t rd, uint32_t rs1, int32_t offset12) { 278 | return AsmIType(OPCODE_SYSTEM, FUNC3_CSRRW, rd, rs1, offset12); 279 | } 280 | 281 | uint32_t AsmCsrrwi(uint32_t rd, uint32_t zimm, int32_t offset12) { 282 | return AsmIType(OPCODE_SYSTEM, FUNC3_CSRRWI, rd, zimm, offset12); 283 | } 284 | 285 | uint32_t AsmFence(uint32_t pred, uint32_t succ) { 286 | assert((pred & ~0xF) == 0); 287 | assert((succ & ~0xF) == 0); 288 | uint32_t imm12 = (pred << 4) | succ; 289 | return AsmIType(OPCODE_FENCE, FUNC3_FENCE, 0, 0, imm12); 290 | } 291 | 292 | uint32_t AsmFencei() { 293 | return AsmIType(OPCODE_FENCE, FUNC3_FENCEI, 0, 0, 0); 294 | } 295 | 296 | uint32_t AsmLb(uint32_t rd, uint32_t rs1, int32_t offset12) { 297 | return AsmIType(OPCODE_LD, FUNC3_LSB, rd, rs1, offset12); 298 | } 299 | 300 | uint32_t AsmLbu(uint32_t rd, uint32_t rs1, int32_t offset12) { 301 | return AsmIType(OPCODE_LD, FUNC3_LSBU, rd, rs1, offset12); 302 | } 303 | 304 | uint32_t AsmLh(uint32_t rd, uint32_t rs1, int32_t offset12) { 305 | return AsmIType(OPCODE_LD, FUNC3_LSH, rd, rs1, offset12); 306 | } 307 | 308 | uint32_t AsmLhu(uint32_t rd, uint32_t rs1, int32_t offset12) { 309 | return AsmIType(OPCODE_LD, FUNC3_LSHU, rd, rs1, offset12); 310 | } 311 | 312 | uint32_t AsmLw(uint32_t rd, uint32_t rs1, int32_t offset12) { 313 | return AsmIType(OPCODE_LD, FUNC3_LSW, rd, rs1, offset12); 314 | } 315 | 316 | uint32_t AsmLd(uint32_t rd, uint32_t rs1, int32_t offset12) { 317 | return AsmIType(OPCODE_LD, FUNC3_LSD, rd, rs1, offset12); 318 | } 319 | 320 | uint32_t AsmLwu(uint32_t rd, uint32_t rs1, int32_t offset12) { 321 | return AsmIType(OPCODE_LD, FUNC3_LSWU, rd, rs1, offset12); 322 | } 323 | 324 | // B TYPE 325 | uint32_t AsmBType(op_label opcode, op_funct3 funct3, uint32_t rs1, uint32_t rs2, 326 | int32_t offset13) { 327 | BType cmd; 328 | cmd.opcode = opcode; 329 | cmd.funct3 = funct3; 330 | cmd.rs1 = rs1; 331 | cmd.rs2 = rs2; 332 | cmd.imm13 = offset13; 333 | return cmd.GetValue(); 334 | } 335 | 336 | uint32_t AsmBeq(uint32_t rs1, uint32_t rs2, int32_t offset13) { 337 | return AsmBType(OPCODE_B, FUNC3_BEQ, rs1, rs2, offset13); 338 | } 339 | 340 | uint32_t AsmBge(uint32_t rs1, uint32_t rs2, int32_t offset13) { 341 | return AsmBType(OPCODE_B, FUNC3_BGE, rs1, rs2, offset13); 342 | } 343 | 344 | uint32_t AsmBgeu(uint32_t rs1, uint32_t rs2, int32_t offset13) { 345 | return AsmBType(OPCODE_B, FUNC3_BGEU, rs1, rs2, offset13); 346 | } 347 | 348 | uint32_t AsmBlt(uint32_t rs1, uint32_t rs2, int32_t offset13) { 349 | return AsmBType(OPCODE_B, FUNC3_BLT, rs1, rs2, offset13); 350 | } 351 | 352 | uint32_t AsmBltu(uint32_t rs1, uint32_t rs2, int32_t offset13) { 353 | return AsmBType(OPCODE_B, FUNC3_BLTU, rs1, rs2, offset13); 354 | } 355 | 356 | uint32_t AsmBne(uint32_t rs1, uint32_t rs2, int32_t offset13) { 357 | return AsmBType(OPCODE_B, FUNC3_BNE, rs1, rs2, offset13); 358 | } 359 | 360 | // J TYPE 361 | uint32_t AsmJType(op_label opcode, uint32_t rd, int32_t offset21) { 362 | JType cmd; 363 | cmd.opcode = opcode; 364 | cmd.rd = rd; 365 | cmd.imm21 = offset21; 366 | return cmd.GetValue(); 367 | } 368 | 369 | uint32_t AsmJal(uint32_t rd, int32_t offset21) { 370 | return AsmJType(OPCODE_J, rd, offset21); 371 | } 372 | 373 | // S TYPE 374 | uint32_t AsmSType(op_label opcode, op_funct3 funct3, uint32_t rs1, uint32_t rs2, 375 | int32_t offset12) { 376 | SType cmd; 377 | cmd.opcode = opcode; 378 | cmd.funct3 = funct3; 379 | cmd.rs2 = rs2; 380 | cmd.rs1 = rs1; 381 | cmd.imm12 = offset12; 382 | return cmd.GetValue(); 383 | } 384 | 385 | uint32_t AsmSw(uint32_t rs1, uint32_t rs2, int32_t offset12) { 386 | return AsmSType(OPCODE_S, FUNC3_LSW, rs1, rs2, offset12); 387 | } 388 | 389 | uint32_t AsmSh(uint32_t rs1, uint32_t rs2, int32_t offset12) { 390 | return AsmSType(OPCODE_S, FUNC3_LSH, rs1, rs2, offset12); 391 | } 392 | 393 | uint32_t AsmSb(uint32_t rs1, uint32_t rs2, int32_t offset12) { 394 | return AsmSType(OPCODE_S, FUNC3_LSB, rs1, rs2, offset12); 395 | } 396 | 397 | uint32_t AsmSd(uint32_t rs1, uint32_t rs2, int32_t offset12) { 398 | return AsmSType(OPCODE_S, FUNC3_LSD, rs1, rs2, offset12); 399 | } 400 | 401 | // U TYPE. 402 | uint32_t AsmUType(op_label opcode, uint32_t rd, int32_t imm20) { 403 | UType cmd; 404 | cmd.opcode = opcode; 405 | cmd.rd = rd; 406 | cmd.imm20 = imm20; 407 | return cmd.GetValue(); 408 | } 409 | 410 | uint32_t AsmLui(uint32_t rd, int32_t imm20) { 411 | return AsmUType(OPCODE_LUI, rd, imm20); 412 | } 413 | 414 | uint32_t AsmAuipc(uint32_t rd, int32_t imm20) { 415 | return AsmUType(OPCODE_AUIPC, rd, imm20); 416 | } 417 | 418 | // MULT Instructions (R-Type). 419 | uint32_t AsmMul(uint32_t rd, uint32_t rs1, uint32_t rs2) { 420 | return AsmRType(OPCODE_ARITHLOG, FUNC_MULT, FUNC3_MUL, rd, rs1, rs2); 421 | } 422 | 423 | uint32_t AsmMulh(uint32_t rd, uint32_t rs1, uint32_t rs2) { 424 | return AsmRType(OPCODE_ARITHLOG, FUNC_MULT, FUNC3_MULH, rd, rs1, rs2); 425 | } 426 | 427 | uint32_t AsmMulhsu(uint32_t rd, uint32_t rs1, uint32_t rs2) { 428 | return AsmRType(OPCODE_ARITHLOG, FUNC_MULT, FUNC3_MULHSU, rd, rs1, rs2); 429 | } 430 | 431 | uint32_t AsmMulhu(uint32_t rd, uint32_t rs1, uint32_t rs2) { 432 | return AsmRType(OPCODE_ARITHLOG, FUNC_MULT, FUNC3_MULHU, rd, rs1, rs2); 433 | } 434 | 435 | uint32_t AsmMulw(uint32_t rd, uint32_t rs1, uint32_t rs2) { 436 | return AsmRType(OPCODE_ARITHLOG_64, FUNC_MULT, FUNC3_MUL, rd, rs1, rs2); 437 | } 438 | 439 | uint32_t AsmDiv(uint32_t rd, uint32_t rs1, uint32_t rs2) { 440 | return AsmRType(OPCODE_ARITHLOG, FUNC_MULT, FUNC3_DIV, rd, rs1, rs2); 441 | } 442 | 443 | uint32_t AsmDivu(uint32_t rd, uint32_t rs1, uint32_t rs2) { 444 | return AsmRType(OPCODE_ARITHLOG, FUNC_MULT, FUNC3_DIVU, rd, rs1, rs2); 445 | } 446 | 447 | uint32_t AsmDivuw(uint32_t rd, uint32_t rs1, uint32_t rs2) { 448 | return AsmRType(OPCODE_ARITHLOG_64, FUNC_MULT, FUNC3_DIVU, rd, rs1, rs2); 449 | } 450 | 451 | uint32_t AsmDivw(uint32_t rd, uint32_t rs1, uint32_t rs2) { 452 | return AsmRType(OPCODE_ARITHLOG_64, FUNC_MULT, FUNC3_DIV, rd, rs1, rs2); 453 | } 454 | 455 | uint32_t AsmRem(uint32_t rd, uint32_t rs1, uint32_t rs2) { 456 | return AsmRType(OPCODE_ARITHLOG, FUNC_MULT, FUNC3_REM, rd, rs1, rs2); 457 | } 458 | 459 | uint32_t AsmRemu(uint32_t rd, uint32_t rs1, uint32_t rs2) { 460 | return AsmRType(OPCODE_ARITHLOG, FUNC_MULT, FUNC3_REMU, rd, rs1, rs2); 461 | } 462 | 463 | uint32_t AsmRemw(uint32_t rd, uint32_t rs1, uint32_t rs2) { 464 | return AsmRType(OPCODE_ARITHLOG_64, FUNC_MULT, FUNC3_REM, rd, rs1, rs2); 465 | } 466 | 467 | uint32_t AsmRemuw(uint32_t rd, uint32_t rs1, uint32_t rs2) { 468 | return AsmRType(OPCODE_ARITHLOG_64, FUNC_MULT, FUNC3_REMU, rd, rs1, rs2); 469 | } 470 | 471 | uint16_t AsmCAddType(uint16_t cmd, uint32_t rd, uint32_t rs2) { 472 | cmd |= ((rd & 0x1F) << 7) | ((rs2 & 0x1F) << 2); 473 | return cmd; 474 | } 475 | 476 | uint16_t AsmCAdd(uint32_t rd, uint32_t rs2) { 477 | uint16_t cmd = 0b1001000000000010; 478 | return AsmCAddType(cmd, rd, rs2); 479 | } 480 | 481 | uint16_t AsmCEbreak() { 482 | uint16_t cmd = 0b1001000000000010; 483 | return cmd; 484 | } 485 | 486 | uint16_t AsmCFldsp(uint32_t rd, uint32_t uimm) { 487 | uint16_t cmd = 0b0010000000000010; 488 | cmd |= (rd & 0x1F) << 7; 489 | cmd |= ((uimm >> 5) & 1) << 12; 490 | cmd |= ((uimm >> 3) & 0b11) << 5; 491 | cmd |= ((uimm >> 6) & 0b111) << 2; 492 | return cmd; 493 | } 494 | 495 | uint16_t AsmCFlwsp(uint32_t rd, uint32_t uimm) { 496 | uint16_t cmd = 0b0110000000000010; 497 | cmd |= (rd & 0x1F) << 7; 498 | cmd |= ((uimm >> 5) & 1) << 12; 499 | cmd |= ((uimm >> 2) & 0b111) << 4; 500 | cmd |= ((uimm >> 6) & 0b11) << 2; 501 | return cmd; 502 | } 503 | 504 | uint16_t AsmCFsdsp(uint32_t rs2, uint32_t uimm) { 505 | uint16_t cmd = 0b1010000000000010; 506 | cmd |= (rs2 & 0x1F) << 2; 507 | cmd |= ((uimm >> 3) & 0b111) << 10; 508 | cmd |= ((uimm >> 6) & 0b111) << 7; 509 | return cmd; 510 | } 511 | 512 | uint16_t AsmCFswsp(uint32_t rs2, uint32_t uimm) { 513 | uint16_t cmd = 0b1110000000000010; 514 | cmd |= (rs2 & 0x1F) << 2; 515 | cmd |= ((uimm >> 2) & 0b1111) << 9; 516 | cmd |= ((uimm >> 6) & 0b11) << 7; 517 | return cmd; 518 | } 519 | 520 | uint16_t AsmCJalr(uint32_t rs1) { 521 | uint16_t cmd = 0b1001000000000010; 522 | cmd |= (rs1 & 0b011111) << 7; 523 | return cmd; 524 | } 525 | 526 | uint16_t AsmCJr(uint32_t rs1) { 527 | uint16_t cmd = 0b1000000000000010; 528 | cmd |= (rs1 & 0b011111) << 7; 529 | return cmd; 530 | } 531 | 532 | uint16_t AsmCLdsp(uint32_t rd, uint32_t uimm) { 533 | uint16_t cmd = 0b0110000000000010; 534 | cmd |= (rd & 0x1F) << 7; 535 | cmd |= ((uimm >> 5) & 1) << 12; 536 | cmd |= ((uimm >> 3) & 0b11) << 5; 537 | cmd |= ((uimm >> 6) & 0b111) << 2; 538 | return cmd; 539 | } 540 | 541 | uint16_t AsmCLwsp(uint32_t rd, uint32_t uimm) { 542 | uint16_t cmd = 0b0100000000000010; 543 | cmd |= (rd & 0x1F) << 7; 544 | cmd |= ((uimm >> 5) & 1) << 12; 545 | cmd |= ((uimm >> 2) & 0b111) << 4; 546 | cmd |= ((uimm >> 6) & 0b11) << 2; 547 | return cmd; 548 | } 549 | 550 | uint16_t AsmCMv(uint32_t rd, uint32_t rs2) { 551 | uint16_t cmd = 0b01000000000000010; 552 | return AsmCAddType(cmd, rd, rs2); 553 | } 554 | 555 | uint16_t AsmCSdsp(uint32_t rs2, uint32_t uimm) { 556 | uint16_t cmd = 0b1110000000000010; 557 | cmd |= (rs2 & 0x1F) << 2; 558 | cmd |= ((uimm >> 3) & 0b111) << 10; 559 | cmd |= ((uimm >> 6) & 0b111) << 7; 560 | return cmd; 561 | } 562 | 563 | uint16_t AsmCSlli(uint32_t rd, uint32_t uimm) { 564 | uint16_t cmd = 0b0000000000000010; 565 | cmd |= (rd & 0x1F) << 7; 566 | cmd |= ((uimm >> 5) & 1) << 12; 567 | cmd |= (uimm & 0b11111) << 2; 568 | return cmd; 569 | } 570 | 571 | uint16_t AsmCSwsp(uint32_t rs2, uint32_t uimm) { 572 | uint16_t cmd = 0b1100000000000010; 573 | cmd |= (rs2 & 0x1F) << 2; 574 | cmd |= ((uimm >> 2) & 0b1111) << 9; 575 | cmd |= ((uimm >> 6) & 0b11) << 7; 576 | return cmd; 577 | } 578 | 579 | uint16_t AsmCAddiType(uint16_t cmd, uint32_t rd, int32_t imm) { 580 | cmd |= (rd & 0x1F) << 7; 581 | cmd |= ((imm >> 5) & 1) << 12; 582 | cmd |= (imm & 0b11111) << 2; 583 | return cmd; 584 | } 585 | 586 | uint16_t AsmCAddi(uint32_t rd, int32_t imm) { 587 | uint16_t cmd = 0b0000000000000001; 588 | return AsmCAddiType(cmd, rd, imm); 589 | } 590 | 591 | uint16_t AsmCLi(uint32_t rd, int32_t imm) { 592 | uint16_t cmd = 0b0100000000000001; 593 | return AsmCAddiType(cmd, rd, imm); 594 | } 595 | 596 | uint16_t AsmCAddi16sp(int32_t imm) { 597 | uint16_t cmd = 0b0110000100000001; 598 | cmd |= ((imm >> 9) & 1) << 12; 599 | cmd |= ((imm >> 5) & 1) << 2; 600 | cmd |= ((imm >> 7) & 0b11) << 3; 601 | cmd |= ((imm >> 6) & 1) << 5; 602 | cmd |= ((imm >> 4) & 1) << 6; 603 | return cmd; 604 | } 605 | 606 | uint16_t AsmCAddiw(uint32_t rd, int32_t imm) { 607 | uint16_t cmd = 0b0010000000000001; 608 | cmd |= (rd & 0x1F) << 7; 609 | cmd |= ((imm >> 5) & 1) << 12; 610 | cmd |= (imm & 0b11111) << 2; 611 | return cmd; 612 | } 613 | 614 | uint16_t AsmCAndType(uint16_t cmd, uint32_t rd, uint32_t rs2) { 615 | cmd |= (rd & 0b111) << 7; 616 | cmd |= (rs2 & 0b111) << 2; 617 | return cmd; 618 | } 619 | 620 | uint16_t AsmCAnd(uint32_t rd, uint32_t rs2) { 621 | uint16_t cmd = 0b1000110001100001; 622 | return AsmCAndType(cmd, rd, rs2); 623 | } 624 | 625 | uint16_t AsmCAddw(uint32_t rd, uint32_t rs2) { 626 | uint16_t cmd = 0b1001110000100001; 627 | return AsmCAndType(cmd, rd, rs2); 628 | } 629 | 630 | uint16_t AsmCOr(uint32_t rd, uint32_t rs2) { 631 | uint16_t cmd = 0b1000110001000001; 632 | return AsmCAndType(cmd, rd, rs2); 633 | } 634 | 635 | uint16_t AsmCSub(uint32_t rd, uint32_t rs2) { 636 | uint16_t cmd = 0b1000110000000001; 637 | return AsmCAndType(cmd, rd, rs2); 638 | } 639 | 640 | uint16_t AsmCSubw(uint32_t rd, uint32_t rs2) { 641 | uint16_t cmd = 0b1001110000000001; 642 | return AsmCAndType(cmd, rd, rs2); 643 | } 644 | 645 | uint16_t AsmCXor(uint32_t rd, uint32_t rs2) { 646 | uint16_t cmd = 0b1000110000100001; 647 | return AsmCAndType(cmd, rd, rs2); 648 | } 649 | 650 | uint16_t AsmCAndiType(uint16_t cmd, uint32_t rd, int32_t imm) { 651 | cmd |= (rd & 0b111) << 7; 652 | cmd |= ((imm >> 5) & 1) << 12; 653 | cmd |= (imm & 0b11111) << 2; 654 | return cmd; 655 | } 656 | 657 | uint16_t AsmCAndi(uint32_t rd, int32_t imm) { 658 | uint16_t cmd = 0b1000100000000001; 659 | return AsmCAndiType(cmd, rd, imm); 660 | } 661 | 662 | uint16_t AsmCSrai(uint32_t rd, int32_t imm) { 663 | uint16_t cmd = 0b1000010000000001; 664 | return AsmCAndiType(cmd, rd, imm); 665 | } 666 | 667 | uint16_t AsmCSrli(uint32_t rd, int32_t imm) { 668 | uint16_t cmd = 0b1000000000000001; 669 | return AsmCAndiType(cmd, rd, imm); 670 | } 671 | 672 | uint16_t AsmCBType(uint16_t cmd, uint32_t rs1, int32_t offset) { 673 | cmd |= (rs1 & 0b111) << 7; 674 | cmd |= ((offset >> 8) & 1) << 12; 675 | cmd |= ((offset >> 3) & 0b11) << 10; 676 | cmd |= ((offset >> 6) & 0b11) << 5; 677 | cmd |= ((offset >> 1) & 0b11) << 3; 678 | cmd |= ((offset >> 5) & 1) << 2; 679 | return cmd; 680 | } 681 | 682 | uint16_t AsmCBeqz(uint32_t rs1, int32_t offset) { 683 | uint16_t cmd = 0b1100000000000001; 684 | return AsmCBType(cmd, rs1, offset); 685 | } 686 | 687 | uint16_t AsmCBnez(uint32_t rs1, int32_t offset) { 688 | uint16_t cmd = 0b1110000000000001; 689 | return AsmCBType(cmd, rs1, offset); 690 | } 691 | 692 | uint16_t AsmCJType(uint16_t cmd, int32_t offset) { 693 | cmd |= ((offset >> 11) & 1) << 12; 694 | cmd |= ((offset >> 4) & 1) << 11; 695 | cmd |= ((offset >> 8) & 0b11) << 9; 696 | cmd |= ((offset >> 10) & 1) << 8; 697 | cmd |= ((offset >> 6) & 1) << 7; 698 | cmd |= ((offset >> 7) & 1) << 6; 699 | cmd |= ((offset >> 1) & 0b111) << 3; 700 | cmd |= ((offset >> 5) & 1) << 2; 701 | return cmd; 702 | } 703 | 704 | uint16_t AsmCJ(int32_t offset) { 705 | uint16_t cmd = 0b1010000000000001; 706 | return AsmCJType(cmd, offset); 707 | } 708 | 709 | uint16_t AsmCJal(int32_t offset) { 710 | uint16_t cmd = 0b0010000000000001; 711 | return AsmCJType(cmd, offset); 712 | } 713 | 714 | uint16_t AsmCLui(uint32_t rd, int32_t imm) { 715 | uint16_t cmd = 0b0110000000000001; 716 | cmd |= ((imm >> 17) & 1) << 12; 717 | cmd |= ((imm >> 12) & 0b11111) << 2; 718 | return cmd; 719 | } 720 | 721 | uint16_t AsmCAddi4spn(uint32_t rd, uint32_t uimm) { 722 | uint16_t cmd = 0b0000000000000000; 723 | cmd |= ((rd & 0b111) << 2); 724 | cmd |= ((uimm >> 4) & 0b11) << 11; 725 | cmd |= ((uimm >> 6) & 0b1111) << 7; 726 | cmd |= ((uimm >> 2) & 0b1) << 6; 727 | cmd |= ((uimm >> 3) & 0b1) << 5; 728 | return cmd; 729 | } 730 | 731 | uint16_t AsmCLdType(uint16_t cmd, uint32_t r4to2, uint32_t r9to7, uint32_t uimm) { 732 | cmd |= ((r4to2 & 0b111) << 2); 733 | cmd |= ((r9to7 & 0b111) << 7); 734 | cmd |= ((uimm >> 3) & 0b111) << 10; 735 | cmd |= ((uimm >> 6) & 0b11) << 5; 736 | return cmd; 737 | } 738 | 739 | uint16_t AsmCLwType(uint16_t cmd, uint32_t r4to2, uint32_t r9to7, uint32_t uimm) { 740 | cmd |= ((r4to2 & 0b111) << 2); 741 | cmd |= ((r9to7 & 0b111) << 7); 742 | cmd |= ((uimm >> 3) & 0b111) << 10; 743 | cmd |= ((uimm >> 2) & 0b1) << 6; 744 | cmd |= ((uimm >> 6) & 0b1) << 5; 745 | return cmd; 746 | } 747 | 748 | uint16_t AsmCFld(uint32_t rd, uint32_t rs1, uint32_t uimm) { 749 | uint16_t cmd = 0b0010000000000000; 750 | return AsmCLdType(cmd, rd, rs1, uimm); 751 | } 752 | 753 | uint16_t AsmCFlw(uint32_t rd, uint32_t rs1, uint32_t uimm) { 754 | uint16_t cmd = 0b0110000000000000; 755 | return AsmCLwType(cmd, rd, rs1, uimm); 756 | } 757 | 758 | uint16_t AsmCFsd(uint32_t rs1, uint32_t rs2, uint32_t uimm) { 759 | uint16_t cmd = 0b01010000000000000; 760 | return AsmCLdType(cmd, rs2, rs1, uimm); 761 | } 762 | 763 | uint16_t AsmCFsw(uint32_t rs1, uint32_t rs2, uint32_t uimm) { 764 | uint16_t cmd = 0b1110000000000000; 765 | return AsmCLwType(cmd, rs2, rs1, uimm); 766 | } 767 | 768 | uint16_t AsmCLd(uint32_t rd, uint32_t rs1, uint32_t uimm) { 769 | uint16_t cmd = 0b0110000000000000; 770 | return AsmCLdType(cmd, rd, rs1, uimm); 771 | } 772 | 773 | uint16_t AsmCLw(uint32_t rd, uint32_t rs1, uint32_t uimm) { 774 | uint16_t cmd = 0b0100000000000000; 775 | return AsmCLwType(cmd, rd, rs1, uimm); 776 | } 777 | 778 | uint16_t AsmCSd(uint32_t rs1, uint32_t rs2, uint32_t uimm) { 779 | uint16_t cmd = 0b01110000000000000; 780 | return AsmCLdType(cmd, rs2, rs1, uimm); 781 | } 782 | 783 | uint16_t AsmCsw(uint32_t rs1, uint32_t rs2, uint32_t uimm) { 784 | uint16_t cmd = 0b1100000000000000; 785 | return AsmCLwType(cmd, rs2, rs1, uimm); 786 | } 787 | 788 | } // namespace RISCV_EMULATOR --------------------------------------------------------------------------------