├── .circleci ├── config.yml └── images │ └── primary │ └── Dockerfile ├── .clang-format ├── .dockerignore ├── .github └── ISSUE_TEMPLATE │ ├── simeng-bug-or-issue-report.md │ └── simeng-instruction-NYI-or-Alias.md ├── .gitignore ├── .jenkins ├── build_arm22.sh ├── build_gcc10.sh ├── build_gcc7.sh ├── build_gcc8.sh ├── build_gcc9.sh ├── build_intel19.sh ├── build_test_run.sh ├── format.sh └── run.sh ├── CMakeLists.txt ├── CONTRIBUTORS.txt ├── Dockerfile ├── LICENSE.txt ├── LICENSE_CAPSTONE.txt ├── LICENSE_GTEST.txt ├── LICENSE_LLVM.txt ├── LICENSE_RYML.txt ├── README.md ├── RELEASE-NOTES.txt ├── SimEngDefaultProgram ├── configs ├── DEMO_RISCV.yaml ├── a64fx.yaml ├── a64fx_SME.yaml ├── m1_firestorm.yaml ├── sst-cores │ ├── a64fx-sst.yaml │ ├── m1_firestorm-sst.yaml │ └── tx2-sst.yaml └── tx2.yaml ├── docs ├── CMakeLists.txt ├── requirements.txt └── sphinx │ ├── assets │ ├── elfstruct.png │ ├── expectations.png │ ├── instruction_groups.png │ ├── instruction_groups_AArch64.png │ ├── instruction_groups_RISCV.png │ ├── modsim22_poster.pdf │ ├── modsim23_poster.pdf │ ├── simeng_arm_cpus.pdf │ ├── simeng_generic_core_model.png │ ├── simeng_modsim_2019.pdf │ └── sst_mem_model.png │ ├── conf.py │ ├── developer │ ├── arch │ │ ├── abstract.rst │ │ ├── index.rst │ │ └── supported │ │ │ ├── aarch64.rst │ │ │ └── riscv.rst │ ├── components │ │ ├── branchPred.rst │ │ ├── coreinstance.rst │ │ ├── index.rst │ │ ├── pipeline │ │ │ ├── components.rst │ │ │ ├── index.rst │ │ │ └── units.rst │ │ └── registerFiles.rst │ ├── concepts │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── kernel.rst │ │ ├── memory.rst │ │ ├── registers.rst │ │ └── syscalls.rst │ ├── developerInfo.rst │ ├── index.rst │ ├── models │ │ └── index.rst │ └── test │ │ └── index.rst │ ├── index.rst │ ├── sst │ ├── building_simeng_with_sst.rst │ ├── index.rst │ ├── running_simeng_with_sst.rst │ └── understanding_sst.rst │ └── user │ ├── building_simeng.rst │ ├── configuring_simeng.rst │ ├── creating_binaries.rst │ ├── docker.rst │ ├── index.rst │ └── running_simeng.rst ├── src ├── CMakeLists.txt ├── include │ └── simeng │ │ ├── AlwaysNotTakenPredictor.hh │ │ ├── ArchitecturalRegisterFileSet.hh │ │ ├── BranchPredictor.hh │ │ ├── Core.hh │ │ ├── CoreInstance.hh │ │ ├── Elf.hh │ │ ├── GenericPredictor.hh │ │ ├── Instruction.hh │ │ ├── PerceptronPredictor.hh │ │ ├── Pool.hh │ │ ├── Register.hh │ │ ├── RegisterFileSet.hh │ │ ├── RegisterValue.hh │ │ ├── SpecialFileDirGen.hh │ │ ├── arch │ │ ├── ArchInfo.hh │ │ ├── Architecture.hh │ │ ├── ProcessStateChange.hh │ │ ├── aarch64 │ │ │ ├── ArchInfo.hh │ │ │ ├── Architecture.hh │ │ │ ├── ExceptionHandler.hh │ │ │ ├── Instruction.hh │ │ │ ├── InstructionGroups.hh │ │ │ ├── MicroDecoder.hh │ │ │ ├── helpers │ │ │ │ ├── arithmetic.hh │ │ │ │ ├── auxiliaryFunctions.hh │ │ │ │ ├── bitmanip.hh │ │ │ │ ├── comparison.hh │ │ │ │ ├── conditional.hh │ │ │ │ ├── divide.hh │ │ │ │ ├── float.hh │ │ │ │ ├── logical.hh │ │ │ │ ├── multiply.hh │ │ │ │ ├── neon.hh │ │ │ │ └── sve.hh │ │ │ └── operandContainer.hh │ │ └── riscv │ │ │ ├── ArchInfo.hh │ │ │ ├── Architecture.hh │ │ │ ├── ExceptionHandler.hh │ │ │ ├── Instruction.hh │ │ │ └── InstructionGroups.hh │ │ ├── config │ │ ├── ExpectationNode.hh │ │ ├── ModelConfig.hh │ │ ├── SimInfo.hh │ │ └── yaml │ │ │ ├── .clang-format │ │ │ └── ryml.hh │ │ ├── kernel │ │ ├── Linux.hh │ │ └── LinuxProcess.hh │ │ ├── memory │ │ ├── FixedLatencyMemoryInterface.hh │ │ ├── FlatMemoryInterface.hh │ │ ├── MemoryAccessTarget.hh │ │ ├── MemoryInterface.hh │ │ └── MemoryReadResult.hh │ │ ├── models │ │ ├── emulation │ │ │ └── Core.hh │ │ ├── inorder │ │ │ └── Core.hh │ │ └── outoforder │ │ │ └── Core.hh │ │ ├── pipeline │ │ ├── A64FXPortAllocator.hh │ │ ├── BalancedPortAllocator.hh │ │ ├── DecodeUnit.hh │ │ ├── DispatchIssueUnit.hh │ │ ├── ExecuteUnit.hh │ │ ├── FetchUnit.hh │ │ ├── LoadStoreQueue.hh │ │ ├── M1PortAllocator.hh │ │ ├── MappedRegisterFileSet.hh │ │ ├── PipelineBuffer.hh │ │ ├── PortAllocator.hh │ │ ├── RegisterAliasTable.hh │ │ ├── RenameUnit.hh │ │ ├── ReorderBuffer.hh │ │ └── WritebackUnit.hh │ │ ├── span.hh │ │ └── version.hh.in ├── lib │ ├── AlwaysNotTakenPredictor.cc │ ├── ArchitecturalRegisterFileSet.cc │ ├── CMakeLists.txt │ ├── CoreInstance.cc │ ├── Elf.cc │ ├── GenericPredictor.cc │ ├── PerceptronPredictor.cc │ ├── RegisterFileSet.cc │ ├── RegisterValue.cc │ ├── SpecialFileDirGen.cc │ ├── arch │ │ ├── aarch64 │ │ │ ├── Architecture.cc │ │ │ ├── ExceptionHandler.cc │ │ │ ├── Instruction.cc │ │ │ ├── InstructionMetadata.cc │ │ │ ├── InstructionMetadata.hh │ │ │ ├── Instruction_address.cc │ │ │ ├── Instruction_decode.cc │ │ │ ├── Instruction_execute.cc │ │ │ └── MicroDecoder.cc │ │ └── riscv │ │ │ ├── Architecture.cc │ │ │ ├── ExceptionHandler.cc │ │ │ ├── Instruction.cc │ │ │ ├── InstructionMetadata.cc │ │ │ ├── InstructionMetadata.hh │ │ │ ├── Instruction_address.cc │ │ │ ├── Instruction_decode.cc │ │ │ └── Instruction_execute.cc │ ├── config │ │ └── ModelConfig.cc │ ├── kernel │ │ ├── Linux.cc │ │ └── LinuxProcess.cc │ ├── memory │ │ ├── FixedLatencyMemoryInterface.cc │ │ └── FlatMemoryInterface.cc │ ├── models │ │ ├── emulation │ │ │ └── Core.cc │ │ ├── inorder │ │ │ └── Core.cc │ │ └── outoforder │ │ │ └── Core.cc │ └── pipeline │ │ ├── A64FXPortAllocator.cc │ │ ├── BalancedPortAllocator.cc │ │ ├── DecodeUnit.cc │ │ ├── DispatchIssueUnit.cc │ │ ├── ExecuteUnit.cc │ │ ├── FetchUnit.cc │ │ ├── LoadStoreQueue.cc │ │ ├── M1PortAllocator.cc │ │ ├── MappedRegisterFileSet.cc │ │ ├── RegisterAliasTable.cc │ │ ├── RenameUnit.cc │ │ ├── ReorderBuffer.cc │ │ └── WritebackUnit.cc └── tools │ ├── CMakeLists.txt │ └── simeng │ ├── CMakeLists.txt │ └── main.cc ├── sst ├── Assemble.cc ├── CMakeLists.txt ├── SimEngCoreWrapper.cc ├── SimEngMemInterface.cc ├── config │ ├── L1-example-config.py │ ├── L1L2-example-config.py │ └── a64fx-config.py ├── include │ ├── Assemble.hh │ ├── SimEngCoreWrapper.hh │ └── SimEngMemInterface.hh └── test │ ├── CMakeLists.txt │ ├── include │ ├── framework │ │ ├── context.hh │ │ ├── expression.hh │ │ ├── handlers.hh │ │ ├── macros │ │ │ ├── eval.hh │ │ │ ├── group.hh │ │ │ └── util.hh │ │ ├── output.hh │ │ ├── parser.hh │ │ ├── process.hh │ │ ├── registry.hh │ │ ├── runner.hh │ │ ├── stats.hh │ │ └── uid.hh │ └── sstsimengtest.hh │ ├── main.cc │ ├── sstconfigs │ └── fastL1WithParams_config.py │ └── test_files │ ├── tg0_llvm_assemble.cc │ ├── tg1_load_store.cc │ ├── tg2_cache_access.cc │ ├── tg3_request_split.cc │ └── tg4_request_misaligned.cc └── test ├── CMakeLists.txt ├── integration ├── CMakeLists.txt └── ConfigTest.cc ├── regression ├── CMakeLists.txt ├── RegressionTest.cc ├── RegressionTest.hh ├── aarch64 │ ├── AArch64RegressionTest.cc │ ├── AArch64RegressionTest.hh │ ├── CMakeLists.txt │ ├── Exception.cc │ ├── LoadStoreQueue.cc │ ├── MicroOperation.cc │ ├── SmokeTest.cc │ ├── Syscall.cc │ ├── SystemRegisters.cc │ ├── data │ │ ├── input.txt │ │ └── truncate-test.txt │ └── instructions │ │ ├── arithmetic.cc │ │ ├── bitmanip.cc │ │ ├── comparison.cc │ │ ├── conditional.cc │ │ ├── divide.cc │ │ ├── float.cc │ │ ├── load.cc │ │ ├── logical.cc │ │ ├── misc.cc │ │ ├── multiply.cc │ │ ├── neon.cc │ │ ├── sme.cc │ │ ├── store.cc │ │ └── sve.cc └── riscv │ ├── CMakeLists.txt │ ├── Exception.cc │ ├── InorderPipeline.cc │ ├── LoadStoreQueue.cc │ ├── RISCVRegressionTest.cc │ ├── RISCVRegressionTest.hh │ ├── SmokeTest.cc │ ├── Syscall.cc │ ├── data │ ├── input.txt │ └── truncate-test.txt │ └── instructions │ ├── arithmetic.cc │ ├── atomic.cc │ ├── branch.cc │ ├── compressed.cc │ ├── float.cc │ ├── jump.cc │ ├── load.cc │ ├── multiplyDivide.cc │ └── store.cc └── unit ├── ArchitecturalRegisterFileSetTest.cc ├── CMakeLists.txt ├── ConfigInit.hh ├── ElfTest.cc ├── FixedLatencyMemoryInterfaceTest.cc ├── FlatMemoryInterfaceTest.cc ├── GenericPredictorTest.cc ├── MockArchitecture.hh ├── MockBranchPredictor.hh ├── MockCore.hh ├── MockInstruction.hh ├── MockMemoryInterface.hh ├── MockPortAllocator.hh ├── OSTest.cc ├── PerceptronPredictorTest.cc ├── PoolTest.cc ├── ProcessTest.cc ├── RegisterFileSetTest.cc ├── RegisterValueTest.cc ├── SpecialFileDirGenTest.cc ├── aarch64 ├── ArchInfoTest.cc ├── ArchitectureTest.cc ├── AuxiliaryFunctionsTest.cc ├── ExceptionHandlerTest.cc ├── InstructionTest.cc └── OperandContainerTest.cc ├── data ├── stream-aarch64.elf └── stream.rv32ima.elf ├── pipeline ├── A64FXPortAllocatorTest.cc ├── BalancedPortAllocatorTest.cc ├── DecodeUnitTest.cc ├── DispatchIssueUnitTest.cc ├── ExecuteUnitTest.cc ├── FetchUnitTest.cc ├── LoadStoreQueueTest.cc ├── M1PortAllocatorTest.cc ├── MappedRegisterFileSetTest.cc ├── PipelineBufferTest.cc ├── RegisterAliasTableTest.cc ├── RenameUnitTest.cc ├── ReorderBufferTest.cc └── WritebackUnitTest.cc └── riscv ├── ArchInfoTest.cc ├── ArchitectureTest.cc ├── ExceptionHandlerTest.cc └── InstructionTest.cc /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | jobs: 4 | docs-build: 5 | docker: 6 | - image: python:3.7 7 | steps: 8 | - checkout 9 | - run: 10 | name: Install dependencies 11 | command: pip install -r docs/requirements.txt 12 | - run: 13 | name: Build docs 14 | command: cd docs/ && sphinx-build -b html sphinx/ _build/html 15 | - persist_to_workspace: 16 | root: docs/_build 17 | paths: html 18 | 19 | docs-deploy: 20 | docker: 21 | - image: node:8.10.0 22 | steps: 23 | - checkout 24 | - attach_workspace: 25 | at: docs/_build 26 | - run: 27 | name: Disable jekyll builds 28 | command: touch docs/_build/html/.nojekyll 29 | - run: 30 | name: Install and configure dependencies 31 | command: | 32 | npm install -g --silent gh-pages@2.0.1 33 | git config user.email "jj16791@bristol.ac.uk" 34 | git config user.name "jj16791" 35 | - add_ssh_keys: 36 | fingerprints: 37 | - "84:b0:fe:c6:8e:4a:bd:c0:ae:57:85:4b:26:8f:db:54" 38 | - run: 39 | name: Deploy docs to gh-pages branch 40 | command: gh-pages --dotfiles --message "[skip ci] Updates" --dist docs/_build/html 41 | 42 | workflows: 43 | version: 2 44 | build: 45 | jobs: 46 | - docs-build 47 | - docs-deploy: 48 | requires: 49 | - docs-build 50 | filters: 51 | branches: 52 | only: 53 | - main -------------------------------------------------------------------------------- /.circleci/images/primary/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM buildpack-deps:bionic 2 | 3 | # Install dependencies 4 | RUN \ 5 | echo deb https://apt.kitware.com/ubuntu/ bionic main \ 6 | >> /etc/apt/sources.list && \ 7 | wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc \ 8 | | apt-key add - && \ 9 | echo deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main \ 10 | >> /etc/apt/sources.list && \ 11 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \ 12 | apt-get update -yqq && \ 13 | apt-get install -y cmake g++-7 g++-8 clang-5.0 clang-7 && \ 14 | apt-get install -y llvm-8-dev && \ 15 | apt-get install -y clang-format-8 && \ 16 | ln -s /usr/bin/clang-format-8 /usr/bin/clang-format 17 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | 3 | DerivePointerAlignment: false 4 | PointerAlignment: true 5 | Standard: C++11 6 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | build 2 | external 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/simeng-bug-or-issue-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: SimEng Bug or Issue Report 3 | about: Report a bug or un-expected SimEng behaviour 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Check List** 11 | - [ ] The binary I am trying to run has been compiled statically for either RV64 or AArch64. 12 | - [ ] The compiled binary is a Linux Elf file. 13 | - [ ] I have provided both a config file and a binary to SimEng as runtime arguments. 14 | 15 | **Binary File Information** 16 | Please run `file` on the binary used and paste the output below (i.e. `file myBinary.elf`). 17 | ```bash 18 | ``` 19 | 20 | **System Description** 21 | Please provide the following as a list: 22 | - The Operating System of the system you are running SimEng on 23 | - The compiler used to compile SimEng and its version 24 | - The compiler used to compile the static binary and its version 25 | - The ISA or specific processor that the binary was compiled for 26 | - For example, if `-march=armv8.4-a+sve` was used, then present `armv8.4-a+sve` 27 | - If `-mcpu=neoverse-v1` or similar was used, then present `neoverse-v1` 28 | - The processor of the system you are running SimEng on 29 | - The main memory capacity of the system you are running SimEng on 30 | 31 | **SimEng Version** 32 | Provide the SimEng repository branch, commit hash, and version tag (if relevant) that the issue is present on. 33 | 34 | **SimEng CMAKE Options Used** 35 | Provide a bullet list of all CMAKE options used. E.g. `-DCMAKE_BUILD_TYPE=Release`. 36 | 37 | **Binary Compilation Instructions** 38 | Provide a bullet list of how the binary in question was compiled, including all compiler flags used. 39 | 40 | **SimEng Command Line Expression** 41 | Provide the command line expression used to run SimEng e.g. `./simeng /path/to/configs/a64fx.yaml /path/to/myBinary.elf` 42 | 43 | **SimEng Metadata Output** 44 | If your simulation begins to execute the binary, please provide the metadata that SimEng prints at the start of execution. 45 | E.g. 46 | ```bash 47 | ./simeng configs/a64fx.yaml myStaticBinary.elf 48 | [SimEng] Build metadata: 49 | [SimEng] Version: 0.9.6 50 | [SimEng] Compile Time - Date: 14:01:44 - Jun 19 2024 51 | [SimEng] Build type: Debug 52 | [SimEng] Compile options: $<$:-fno-rtti>;-Wall;-pedantic;-Werror 53 | [SimEng] Test suite: ON 54 | 55 | [SimEng] Running in Out-of-Order mode 56 | [SimEng] Workload: /home/SimEng/myStaticBinary.elf 57 | [SimEng] Config file: /home/SimEng/configs/a64fx.yaml 58 | [SimEng] ISA: AArch64 59 | [SimEng] Auto-generated Special File directory: True 60 | [SimEng] Special File directory used: /home/SimEng/build/specialFiles/ 61 | [SimEng] Number of Cores: 1 62 | [SimEng] Starting... 63 | ``` 64 | 65 | **Problem Description** 66 | Explain what you think should happen, and what actually happens. 67 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/simeng-instruction-NYI-or-Alias.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: SimEng Instruction Not-Yet-Imlpemented, Alias, or Missing System Register 3 | about: Report an instruction which either does not yet have execution logic implemented (NYI), an instruction who's alias needs resolving, or an instruction which targets a system register which is not yet supported. 4 | title: '[NYI|ALIAS|SYSREG]' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Check List** 11 | - [ ] I have selected the appropriate title prefix from the available options within `[NYI|ALIAS|SYSREG]`, and added a suitable title after this. 12 | - [ ] The binary I am trying to run has been compiled statically for either RV64 or AArch64. 13 | - [ ] The compiled binary is a Linux Elf file. 14 | 15 | **Binary Compilation Instructions** 16 | Provide a bullet list of how the binary in question was compiled, including all compiler flags used. 17 | 18 | **SimEng CMAKE Options Used** 19 | Provide a bullet list of all CMAKE options used. E.g. `-DCMAKE_BUILD_TYPE=Release`. 20 | 21 | **SimEng Command Line Expression** 22 | Provide the command line expression used to run SimEng e.g. `./simeng /path/to/configs/a64fx.yaml /path/to/myBinary.elf` 23 | 24 | **Console Printout** 25 | Please provide a copy of SimEng's console printout prior to its end-of-execution statistics. Please do not include any binary specific output. 26 | E.g. 27 | ```bash 28 | ./simeng configs/a64fx.yaml myStaticBinary.elf 29 | [SimEng] Build metadata: 30 | [SimEng] Version: 0.9.6 31 | [SimEng] Compile Time - Date: 14:01:44 - Jun 19 2024 32 | [SimEng] Build type: Debug 33 | [SimEng] Compile options: $<$:-fno-rtti>;-Wall;-pedantic;-Werror 34 | [SimEng] Test suite: ON 35 | 36 | [SimEng] Running in Out-of-Order mode 37 | [SimEng] Workload: /home/SimEng/myStaticBinary.elf 38 | [SimEng] Config file: /home/SimEng/configs/a64fx.yaml 39 | [SimEng] ISA: AArch64 40 | [SimEng] Auto-generated Special File directory: True 41 | [SimEng] Special File directory used: /home/SimEng/build/specialFiles/ 42 | [SimEng] Number of Cores: 1 43 | [SimEng] Starting... 44 | 45 | 46 | [SimEng:ExceptionHandler] Encountered execution not-yet-implemented exception 47 | [SimEng:ExceptionHandler] Generated by instruction: 48 | [SimEng:ExceptionHandler] 0x0000000000402dc8: 1d 00 80 d2 mov x29, #0 49 | [SimEng:ExceptionHandler] opcode ID: 3680 50 | [SimEng:Core] Halting due to fatal exception 51 | ``` 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .clang_complete 2 | .vscode 3 | .idea 4 | .DS_Store 5 | obj 6 | build 7 | xcode-build 8 | cmake-build-debug 9 | cmake-build-release 10 | docs/sphinx/_build 11 | binaries 12 | install 13 | install-debug 14 | capstone.pc 15 | version.hh 16 | **/specialFiles/ 17 | .cache 18 | 19 | CMakeCache.txt 20 | CPackConfig.cmake 21 | CPackSourceConfig.cmake 22 | CMakeFiles/ 23 | 24 | **/capstone-config-version.cmake 25 | **/capstone-config.cmake 26 | 27 | **/simeng-fileio-test.txt -------------------------------------------------------------------------------- /.jenkins/build_arm22.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source .jenkins/build_test_run.sh 4 | 5 | ## Download/clean and checkout pull request 6 | checkout 7 | 8 | ## Load compilers/libraries 9 | echo "Compiler Armclang 22.0.2" 10 | module use /software/arm64/modulefiles 11 | module load tools/arm-compiler-sles 12 | module load tools/cmake 13 | 14 | ## Build, test, and run SimEng 15 | build armclang armclang++ 16 | test 17 | run 18 | 19 | buildRelease armclang armclang++ 20 | test 21 | run 22 | -------------------------------------------------------------------------------- /.jenkins/build_gcc10.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source .jenkins/build_test_run.sh 4 | 5 | ## Download/clean and checkout pull request 6 | checkout 7 | 8 | ## Load compilers/libraries 9 | echo "Compiler GCC 10" 10 | module swap PrgEnv-cray PrgEnv-gnu 11 | module swap gcc gcc/10.3.0 12 | module load tools/cmake 13 | 14 | ## Build, test, and run SimEng 15 | build gcc g++ 16 | test 17 | run 18 | 19 | buildRelease gcc g++ 20 | test 21 | run 22 | -------------------------------------------------------------------------------- /.jenkins/build_gcc7.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source .jenkins/build_test_run.sh 4 | 5 | ## Download/clean and checkout pull request 6 | checkout 7 | 8 | ## Load compilers/libraries 9 | echo "Compiler GCC 7" 10 | module swap PrgEnv-cray PrgEnv-gnu 11 | module swap gcc gcc/7.3.0 12 | module load tools/cmake 13 | 14 | ## Build, test, and run SimEng 15 | build gcc g++ 16 | test 17 | run 18 | 19 | buildRelease gcc g++ 20 | test 21 | run 22 | -------------------------------------------------------------------------------- /.jenkins/build_gcc8.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source .jenkins/build_test_run.sh 4 | 5 | ## Download/clean and checkout pull request 6 | checkout 7 | 8 | ## Load compilers/libraries 9 | echo "Compiler GCC 8" 10 | module swap PrgEnv-cray PrgEnv-gnu 11 | module swap gcc gcc/8.3.0 12 | module load tools/cmake 13 | 14 | ## Build, test, and run SimEng 15 | build gcc g++ 16 | test 17 | run 18 | 19 | buildRelease gcc g++ 20 | test 21 | run 22 | -------------------------------------------------------------------------------- /.jenkins/build_gcc9.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source .jenkins/build_test_run.sh 4 | 5 | ## Download/clean and checkout pull request 6 | checkout 7 | 8 | ## Load compilers/libraries 9 | echo "Compiler GCC 9" 10 | module swap PrgEnv-cray PrgEnv-gnu 11 | module swap gcc gcc/9.3.0 12 | module load tools/cmake 13 | 14 | ## Build, test, and run SimEng 15 | build gcc g++ 16 | test 17 | run 18 | 19 | buildRelease gcc g++ 20 | test 21 | run 22 | -------------------------------------------------------------------------------- /.jenkins/build_intel19.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source .jenkins/build_test_run.sh 4 | 5 | ## Download/clean and checkout pull request 6 | checkout 7 | 8 | ## Setup environment 9 | PATH=/cm/shared/apps/intel_parallel_studio_xe_2019_update4/compilers_and_libraries_2019.4.243/linux/bin/intel64/:$PATH 10 | export CMAKE_C_COMPILER=icc 11 | export CC=icc 12 | export CMAKE_CXX_COMPILER=icpc 13 | export CXX=icpc 14 | 15 | ## Load compilers/libraries 16 | echo "Compiler INTEL 19" 17 | #module load intel-parallel-studio-xe/compilers/64/2019u4/19.0.4 intel-parallel-studio-xe/mpi/64/2019u4/4.243 18 | export PATH=/home/br-hwaugh/installations/cmake-3.18.5/bin/:$PATH 19 | 20 | ## Build, test, and run SimEng 21 | build 22 | test 23 | run 24 | -------------------------------------------------------------------------------- /.jenkins/build_test_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script is not intended be to run direct but rather to be sourced from other scripts 3 | 4 | ## Set up file structure 5 | export SIMENG_TOP="$PWD" 6 | export SIMENG_BUILD="$PWD"/build 7 | export SIMENG_INSTALL="$PWD"/install 8 | 9 | debug () { 10 | echo "MODULES" 11 | module li 12 | echo "CURRENT DIRECTORY" 13 | echo "$PWD" 14 | echo "GIT BRANCH" 15 | git branch 16 | 17 | echo "SIMENG TOP $SIMENG_TOP" 18 | echo "SIMENG BUILD $SIMENG_BUILD" 19 | echo "SIMENG INSTALL $SIMENG_INSTALL" 20 | } 21 | 22 | # If source available clean and checkout, otherwise download 23 | checkout () { 24 | cd "$SIMENG_TOP" || exit 25 | rm -rf build install 26 | mkdir build install 27 | } 28 | 29 | # Build common function 30 | build () { 31 | cd "$SIMENG_TOP" || exit 32 | rm -rf build/* install/* 33 | 34 | cmake -B build -S . -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="$SIMENG_INSTALL" -DSIMENG_ENABLE_TESTS=ON -DSIMENG_USE_EXTERNAL_LLVM=ON -DLLVM_DIR=/home/br-simeng/llvm14.0.5/install-gcc7/lib/cmake/llvm/ -DCMAKE_C_COMPILER=$1 -DCMAKE_CXX_COMPILER=$2 35 | cmake --build build -j 36 | cmake --build build --target install 37 | } 38 | 39 | # Build common function 40 | buildRelease () { 41 | cd "$SIMENG_TOP" || exit 42 | rm -rf build/* install/* 43 | 44 | cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$SIMENG_INSTALL" -DSIMENG_ENABLE_TESTS=ON -DSIMENG_USE_EXTERNAL_LLVM=ON -DLLVM_DIR=/home/br-simeng/llvm14.0.5/install-gcc7/lib/cmake/llvm/ -DCMAKE_C_COMPILER=$1 -DCMAKE_CXX_COMPILER=$2 45 | cmake --build build -j 46 | cmake --build build --target install 47 | } 48 | 49 | # Run tests 50 | test () { 51 | cd "$SIMENG_BUILD" || exit 52 | ./test/unit/unittests --gtest_output=xml:unittests.xml || true 53 | ./test/regression/aarch64/regression-aarch64 --gtest_output=xml:regressiontests.xml || true 54 | ./test/regression/riscv/regression-riscv --gtest_output=xml:regressiontests.xml || true 55 | } 56 | 57 | # Run default program with and without specified configuration 58 | run () { 59 | cd "$SIMENG_INSTALL" || exit 60 | 61 | ./bin/simeng > run 62 | echo "Simulation without configuration file argument:" 63 | cat run 64 | echo "" 65 | compare_outputs "$(grep "retired:" run | rev | cut -d ' ' -f1 | rev)" "6721" "retired instructions" 66 | compare_outputs "$(grep "cycles:" run | rev | cut -d ' ' -f1 | rev)" "6721" "simulated cycles" 67 | echo "" 68 | 69 | ./bin/simeng "$SIMENG_TOP"/configs/tx2.yaml > run 70 | echo "Simulation with configuration file argument:" 71 | cat run 72 | echo "" 73 | compare_outputs "$(grep "retired:" run | rev | cut -d ' ' -f1 | rev)" "6724" "retired instructions" 74 | compare_outputs "$(grep "cycles:" run | rev | cut -d ' ' -f1 | rev)" "8677" "simulated cycles" 75 | echo "" 76 | } 77 | 78 | # Helper function for checking outputs 79 | compare_outputs() { 80 | if [[ $1 != $2 ]] 81 | then 82 | echo "ERROR: ${STAGE_NAME} run failed due to an incorrect number of $3." 83 | echo -e "\tExpect \"$2\"" 84 | echo -e "\tGot \"$1\"" 85 | exit 1 86 | fi 87 | } 88 | -------------------------------------------------------------------------------- /.jenkins/format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | module swap PrgEnv-cray PrgEnv-gnu 4 | module use /lustre/projects/bristol/modules-a64fx/modulefiles/ 5 | module load llvm/11.0 6 | 7 | git-clang-format --diff origin/main --extensions cc,hh > FORMATTING 8 | 9 | # Check whether any source files were modified 10 | if grep 'no modified files to format' FORMATTING 11 | then 12 | exit 0 13 | fi 14 | 15 | # Check whether any formatting changes are necessary 16 | if grep 'clang-format did not modify any files' FORMATTING 17 | then 18 | exit 0 19 | fi 20 | 21 | echo "" 22 | echo "Code formatting issues detected (see below)." 23 | echo "" 24 | cat FORMATTING 25 | exit 1 -------------------------------------------------------------------------------- /.jenkins/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pwd 4 | 5 | source .jenkins/build_test_run.sh 6 | 7 | run 8 | -------------------------------------------------------------------------------- /CONTRIBUTORS.txt: -------------------------------------------------------------------------------- 1 | Major contributors to SimEng to date include: 2 | 3 | Project leader: 4 | Simon McIntosh-Smith 5 | 6 | Original SimEng design and implementation: 7 | Hal Jones 8 | James Price 9 | 10 | Current development team: 11 | Jack Jones (Lead Developer) 12 | Finn Wilkinson 13 | Rahat Muneeb 14 | Daniel Weaver 15 | Alex Cockrean 16 | Joseph Moore 17 | 18 | Additional Contributors: 19 | Ainsley Rutterford 20 | Andrei Poenaru 21 | Harry Waugh 22 | Mutalib Mohammed 23 | Seunghun Lee 24 | Tom Hepworth 25 | Tom Lin 26 | Will Robinson 27 | 28 | SimEng's development has been funded by the UKRI/EPSRC ASiMoV project, EP/S005072/1. 29 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Builder container 2 | FROM ubuntu:20.04 AS dev 3 | 4 | RUN apt-get update && \ 5 | DEBIAN_FRONTEND=noninteractive apt-get install -y \ 6 | build-essential \ 7 | cmake \ 8 | git \ 9 | llvm-9-dev \ 10 | ninja-build \ 11 | zlib1g-dev && \ 12 | apt-get clean && \ 13 | rm -rf /var/lib/apt/lists/* 14 | 15 | RUN git clone https://github.com/UoB-HPC/SimEng.git /root/SimEng && \ 16 | cd /root/SimEng && \ 17 | rm -rf build && \ 18 | CC=gcc CXX=g++ cmake -Bbuild -S. -GNinja -DSIMENG_OPTIMIZE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local -DSIMENG_ENABLE_TESTS=ON -DSIMENG_USE_EXTERNAL_LLVM=ON -DLLVM_DIR=/usr/lib/llvm-9/cmake && \ 19 | cd build && \ 20 | ninja && \ 21 | ninja install 22 | 23 | # Tar file to preserve links when copying to the release container 24 | RUN cd /usr/local && \ 25 | tar -cf simeng.tar.gz bin/simeng lib/libsimeng* include/simeng/* 26 | 27 | 28 | ## Release container 29 | FROM ubuntu:20.04 30 | 31 | COPY --from=dev /usr/local/simeng.tar.gz /root/ 32 | 33 | WORKDIR /root 34 | 35 | RUN cd /usr/local && tar xf /root/simeng.tar.gz 36 | -------------------------------------------------------------------------------- /LICENSE_CAPSTONE.txt: -------------------------------------------------------------------------------- 1 | This is the software license for Capstone disassembly framework. 2 | Capstone has been designed & implemented by Nguyen Anh Quynh 3 | 4 | See http://www.capstone-engine.org for further information. 5 | 6 | Copyright (c) 2013, COSEINC. 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | * Redistributions of source code must retain the above copyright notice, 13 | this list of conditions and the following disclaimer. 14 | * Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | * Neither the name of the developer(s) nor the names of its 18 | contributors may be used to endorse or promote products derived from this 19 | software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /LICENSE_GTEST.txt: -------------------------------------------------------------------------------- 1 | Copyright 2008, Google Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following disclaimer 12 | in the documentation and/or other materials provided with the 13 | distribution. 14 | * Neither the name of Google Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /LICENSE_RYML.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, Joao Paulo Magalhaes 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included 11 | in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimEng 2 | 3 | The Simulation Engine (SimEng) is a framework for building modern cycle-accurate processor simulators. It aims to be: 4 | 5 | - Fast 6 | - Easy to use and modify to desired configurations 7 | - Scalable, supporting simulation of simple scalar cores, up to superscalar out-of-order designs 8 | - Capable of supporting a wide range of ISAs, starting with AArch64 but eventually including x86, RISC-V, POWER, etc. 9 | - Open source, with a permissive license to enable collaboration across academia and industry 10 | 11 | SimEng places an emphasis on performance and scalability, whilst maintaining a clean, modern, and well-documented code base. 12 | 13 | ## Getting started with SimEng 14 | 15 | To get started with SimEng, please follow the instructions set out in our [User Documentation](https://uob-hpc.github.io/SimEng/user/index.html). This will cover how to download, build and run SimEng along with a brief overview on how it works. 16 | 17 | If you are interested in developing further SimEng features, and are already familiar with the User Documentation please refer to our [Developer Documentation](https://uob-hpc.github.io/SimEng/developer/index.html). This offers further depth on how SimEng works and the reasoning behind design choices. 18 | -------------------------------------------------------------------------------- /SimEngDefaultProgram: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UoB-HPC/SimEng/7af3bc3fe7deb703756def31a208839bc5bdc4c2/SimEngDefaultProgram -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_program(SPHINX_EXECUTABLE NAMES sphinx-build 2 | DOC "Sphinx documentation generator") 3 | if (SPHINX_EXECUTABLE) 4 | message(STATUS "Sphinx found: ${SPHINX_EXECUTABLE}") 5 | add_custom_target(docs VERBATIM 6 | COMMAND ${SPHINX_EXECUTABLE} 7 | -c ${CMAKE_CURRENT_SOURCE_DIR}/sphinx 8 | ${CMAKE_CURRENT_SOURCE_DIR}/sphinx 9 | ${CMAKE_CURRENT_BINARY_DIR}/sphinx) 10 | else() 11 | message(STATUS "Sphinx NOT found (set SPHINX_EXECUTABLE if needed)") 12 | endif() 13 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | Sphinx==5.0.0 2 | sphinx-rtd-theme==0.5.2 3 | mistune==0.8.4 4 | m2r2==0.3.1 5 | Jinja2==3.1.3 -------------------------------------------------------------------------------- /docs/sphinx/assets/elfstruct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UoB-HPC/SimEng/7af3bc3fe7deb703756def31a208839bc5bdc4c2/docs/sphinx/assets/elfstruct.png -------------------------------------------------------------------------------- /docs/sphinx/assets/expectations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UoB-HPC/SimEng/7af3bc3fe7deb703756def31a208839bc5bdc4c2/docs/sphinx/assets/expectations.png -------------------------------------------------------------------------------- /docs/sphinx/assets/instruction_groups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UoB-HPC/SimEng/7af3bc3fe7deb703756def31a208839bc5bdc4c2/docs/sphinx/assets/instruction_groups.png -------------------------------------------------------------------------------- /docs/sphinx/assets/instruction_groups_AArch64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UoB-HPC/SimEng/7af3bc3fe7deb703756def31a208839bc5bdc4c2/docs/sphinx/assets/instruction_groups_AArch64.png -------------------------------------------------------------------------------- /docs/sphinx/assets/instruction_groups_RISCV.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UoB-HPC/SimEng/7af3bc3fe7deb703756def31a208839bc5bdc4c2/docs/sphinx/assets/instruction_groups_RISCV.png -------------------------------------------------------------------------------- /docs/sphinx/assets/modsim22_poster.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UoB-HPC/SimEng/7af3bc3fe7deb703756def31a208839bc5bdc4c2/docs/sphinx/assets/modsim22_poster.pdf -------------------------------------------------------------------------------- /docs/sphinx/assets/modsim23_poster.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UoB-HPC/SimEng/7af3bc3fe7deb703756def31a208839bc5bdc4c2/docs/sphinx/assets/modsim23_poster.pdf -------------------------------------------------------------------------------- /docs/sphinx/assets/simeng_arm_cpus.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UoB-HPC/SimEng/7af3bc3fe7deb703756def31a208839bc5bdc4c2/docs/sphinx/assets/simeng_arm_cpus.pdf -------------------------------------------------------------------------------- /docs/sphinx/assets/simeng_generic_core_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UoB-HPC/SimEng/7af3bc3fe7deb703756def31a208839bc5bdc4c2/docs/sphinx/assets/simeng_generic_core_model.png -------------------------------------------------------------------------------- /docs/sphinx/assets/simeng_modsim_2019.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UoB-HPC/SimEng/7af3bc3fe7deb703756def31a208839bc5bdc4c2/docs/sphinx/assets/simeng_modsim_2019.pdf -------------------------------------------------------------------------------- /docs/sphinx/assets/sst_mem_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UoB-HPC/SimEng/7af3bc3fe7deb703756def31a208839bc5bdc4c2/docs/sphinx/assets/sst_mem_model.png -------------------------------------------------------------------------------- /docs/sphinx/developer/arch/index.rst: -------------------------------------------------------------------------------- 1 | Architectures 2 | ============= 3 | 4 | SimEng architecture definitions are responsible for describing the features and behaviour of specific Instruction Set Architectures (ISAs) in a standardised way, allowing SimEng to model processors that use these ISAs while itself remaining ISA agnostic. 5 | 6 | To achieve this, SimEng defines a set of abstract architecture-related classes. Discrete implementations of these classes are provided for each of the ISAs SimEng supports by default, and must also be implemented for adding support for new or custom ISAs. 7 | 8 | ISA support is achieved through the use of the `Capstone `_ disassembly framework, which disassembles a binary instruction into a C/C++ object that include operand registers, access types, and immediate values to name a few. In order to update SimEng's AArch64 support from Armv8.4-a to Armv9.2-a, we undertook a Capstone update to allow for disassembly of the Armv9.2-a ISA. The work done for this can be found `here `_, and other useful ISA updating tools present in Capstone can be found `here `_. 9 | 10 | Below provides more information on the abstract structure of a SimEng architecture and currently supported ISAs. 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | 15 | abstract 16 | supported/aarch64 17 | supported/riscv -------------------------------------------------------------------------------- /docs/sphinx/developer/components/coreinstance.rst: -------------------------------------------------------------------------------- 1 | Core Instance 2 | ============= 3 | 4 | The ``CoreInstance`` component supplies the functionality for instantiating all simulation objects and linking them together. 5 | 6 | The standard process taken to create an instance of the modelled core is as follows: 7 | 8 | Process the config file 9 | Either the passed configuration file path, or default configuration string, is used to generate the model configuration class. All subsequent parameterised instantiations of simulation objects utilise this configuration class. 10 | 11 | Create the image process 12 | From the passed workload path, or default binary, a process image is created. A region of host memory is populated with workload data (e.g. instructions), a region for the HEAP, and an initial stack frame. References to it are then passed between various simulation objects to serve as the underlying process memory space. 13 | 14 | Construct on-chip cache interfaces 15 | Based on the supplied configuration options, the on-chip cache interfaces are constructed. These interfaces sit on top of a reference to the process memory space constructed prior. Currently, only L1 instruction and data caches are supported and the interfaces are defined under the :ref:`L1-Data-Memory ` and :ref:`L1-Instruction-Memory ` config options. 16 | 17 | Construct the core simulation object 18 | After all the general components are created, the simulated core object is constructed. The architecture, branch predictor, and issue port allocator are first constructed and subsequently passed to the core object. Within the core object itself, relevant simulation objects are constructed using the instantiations carried out in the ``CoreInstance`` class. The exact simulation objects created are dependent on the core :ref:`archetype ` in use. 19 | 20 | Special File Directory 21 | Finally, SimEng's special file directory is constructed if enabled within the passed configuration. More information about its usage can be found :ref:`here `. 22 | 23 | The ``CoreInstance`` class also contains a selection of getter functions for obtaining information about the simulation objects constructed. -------------------------------------------------------------------------------- /docs/sphinx/developer/components/index.rst: -------------------------------------------------------------------------------- 1 | Simulation Components 2 | ===================== 3 | 4 | To support the default simulation models SimEng supplies, as well as improve the ease with which new models may be created, the SimEng library maintains a set of simulation components which represent discrete parts of a processor core. 5 | 6 | The following information is concerned with those simulation components. 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | 11 | coreinstance 12 | pipeline/index 13 | branchPred 14 | registerFiles 15 | 16 | -------------------------------------------------------------------------------- /docs/sphinx/developer/components/pipeline/index.rst: -------------------------------------------------------------------------------- 1 | Pipeline 2 | ======== 3 | 4 | The SimEng pipeline consists of a set of standard self-contained "units", which represent the logic for the various stages of a processor pipeline. These units are designed to be generic representations of the typical functionality found in their hardware counterparts to encourage reusability between models. 5 | 6 | Beyond these pipeline units, appropriate instructions may flow to other objects termed "common components". These components provide additional logic that may not have such typical functionality, as the pipeline units do, and thus may require alterations to better suit the model. These components include: 7 | 8 | ``ReorderBuffer``: Holds an ordered list of ‘in-flight’ instructions, with facilities for committing instructions and rewinding register allocations. 9 | 10 | ``LoadStoreQueue``: Holds ordered queues of load/store instructions, with facilities for servicing memory accesses and checking for address clashes. 11 | 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | 16 | units 17 | components -------------------------------------------------------------------------------- /docs/sphinx/developer/concepts/index.rst: -------------------------------------------------------------------------------- 1 | Simulation Concepts 2 | =================== 3 | 4 | A SimEng simulation consists of two core components: a :doc:`model <../models/index>` and an :doc:`architecture <../arch/index>`. The model describes the type and structure of the processor being simulated, while the architecture describes the instruction set and related mechanisms and features. This separation allows for reusing the same basic instruction set across a wide range of simulated processors, such as modelling in-order and out-of-order processors which both support Armv9.2-a. Similarly, similar processor designs with different instruction sets may use the same model, such as a ThunderX2 versus an Ivy Bridge or similar Intel Core processor; while one is ARMv8 and the other x86, they are both out-of-order processors with a unified reservation station and a similar arrangement of execution units. 5 | 6 | The following information is concerned with those concepts whose interactions flow beyond the scope of the processor core or don't fit the description of a :doc:`Simulation Component <../components/index>`. 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | 11 | instructions 12 | registers 13 | memory 14 | syscalls 15 | kernel -------------------------------------------------------------------------------- /docs/sphinx/developer/concepts/memory.rst: -------------------------------------------------------------------------------- 1 | Memory 2 | ====== 3 | 4 | The SimEng core models simulate everything in a core up to and including the load/store units, but stop short of the memory system. This is to allow integration and interoperability with a wide range of external memory models, and to focus the development of SimEng primarily on the simulation of the core itself. 5 | 6 | .. _memInt: 7 | 8 | MemoryInterface 9 | --------------- 10 | 11 | All SimEng components that interact with the memory system make memory access requests using supplied instances of the abstract ``MemoryInterface`` class. 12 | 13 | ``MemoryInterface`` access requests are asynchronous, and may be either read or write. All requests must supply a ``MemoryAccessTarget``, containing the memory address and the number of bytes to access 14 | 15 | While write requests receive no response, a read request may be responded to an indeterminate number of cycles later. The ``MemoryInterface::getCompletedReads`` function may be used to retrieve a list of the read requests that completed during the previous cycle. Once processed, responses should be dismissed using the ``MemoryInterface::clearCompletedReads`` function. 16 | 17 | .. Note:: Future versions may update the interface to remove the need for the component to manually clear completed reads. 18 | 19 | In addition to the ``MemoryAccessTarget``, a write request must be supplied with the data to be stored via a ``RegisterValue`` class instance, and a read request must be supplied with a unique identifier via a ``uint64_t`` value. 20 | 21 | It is expected that all implementations of ``MemoryInterface`` should respect the order that requests are made: a read request following a write request to the same address should respond with the newly written value, rather than returning the old, stale result. 22 | 23 | FlatMemoryInterface 24 | ******************* 25 | 26 | For simpler models, a ``FlatMemoryInterface`` implementation is supplied. This is a simple wrapper around a byte array representing the process memory, and will always respond to all requests instantly and synchronously. 27 | 28 | FixedMemoryInterface 29 | ******************** 30 | 31 | For more complex models, a ``FixedMemoryInterface`` implementation is supplied. Similar to the ``FlatMemoryInterface``, a simple wrapper around a byte array is used to represent the process memory. However, a ``pendingRequests_`` queue is utilised in combination with an internal clock, ``tickCounter_``, to support memory requests with a predefined fixed latency value named ``latency_``. 32 | 33 | A ``MemoryAccessTarget`` is transformed into a ``FixedLatencyMemoryInterfaceRequest`` when pushed onto the ``pendingRequests_`` queue. Each ``FixedLatencyMemoryInterfaceRequest`` contains the original ``MemoryAccessTarget``, an optional ``data`` or ``requestId`` value to hold a write's ``RegisterValue`` or read's unique id respectively, and a ``readyAt`` value. The ``readyAt`` value defines when the request is ready to be performed in relation to the ``tickCounter_``, with ``readyAt = tickCounter_ + latency_`` at the time of the initial request. When ``tickCounter_`` is equivalent to the ``readyAt`` value, the request is performed. -------------------------------------------------------------------------------- /docs/sphinx/developer/concepts/registers.rst: -------------------------------------------------------------------------------- 1 | .. _registers: 2 | 3 | Registers 4 | ========= 5 | 6 | Instructions in SimEng primarily operate upon registers. Each instruction may request data from any number of registers, and may update any number of registers upon completion. 7 | 8 | Registers are uniquely identified within SimEng using a ``Register`` object, consisting of a "type"---corresponding to a specific set of registers (i.e., general purpose, floating point, etc.)---and a "tag", which identifies an individual register within that set. The number of different types of registers, as well as the number and size of registers of each type, is defined by the architecture used. 9 | 10 | 11 | RegisterValue 12 | ------------- 13 | 14 | As the type of data stored in registers and passed between instructions is architecture-dependent, all data is represented using the ``RegisterValue`` class. This is essentially a thin wrapper around a byte array, and provides templated helper functions to read the data as the desired datatype. 15 | 16 | A default-initialised ``RegisterValue`` has size 0, and will return ``false`` if evaluated as a bool. 17 | -------------------------------------------------------------------------------- /docs/sphinx/developer/concepts/syscalls.rst: -------------------------------------------------------------------------------- 1 | System Calls 2 | ============ 3 | 4 | To support the simulation of programs that perform system calls, SimEng implements system call emulation. When a supervisor call exception is raised by the program, the :doc:`exception handler <../arch/abstract>` invokes the emulated operating system :doc:`kernel ` instance to perform the syscall. 5 | 6 | Many syscalls are emulated entirely inside SimEng, with some exceptions detailed below. 7 | 8 | File input/output 9 | ----------------- 10 | 11 | Syscalls that interact with files are passed through to the host, in order to allow the simulated program to read and write data files on the host filesystem and interact with ``stdin``, ``stdout`` and ``stderr``. When a file is opened (e.g. with ``open`` or ``openat``), the emulated kernel maps the host file descriptor to a virtual file descriptor (``fileDescriptorTable``) which is returned to the simulated program. When handling syscalls that operate on file descriptors (e.g. ``lseek`` or ``writev``), the kernel looks up the corresponding host file descriptor in the map before passing the call onwards to the host. 12 | 13 | .. _specialDir: 14 | 15 | The kernel detects attempts to open special files (such as those in ``/dev/`` or ``/proc``) and emulates their access inside SimEng rather than passing the call through to the host. This is achieved by generating the most commonly accessed special files at runtime via information provided in the model :ref:`config file `. The generated special files directory can be found at ``simeng/build/specialFiles/...``. Alternatively, a user can disable the special file generation in the model config file and copy in their own directory to the same location. -------------------------------------------------------------------------------- /docs/sphinx/developer/index.rst: -------------------------------------------------------------------------------- 1 | Overview 2 | ======== 3 | 4 | The following information is intended for those users of SimEng who wish to modify the simulated architecture beyond that capable through configuration options. For those users who wish to concern themselves only with configurable options, please refer to the :doc:`User Documentation <../user/index>` instead. 5 | 6 | * :doc:`Developer Information ` 7 | 8 | * :doc:`Simulation Concepts ` 9 | 10 | * :doc:`Instructions ` 11 | * :doc:`Registers ` 12 | * :doc:`Memory ` 13 | * :doc:`Kernel ` 14 | * :doc:`System Calls ` 15 | 16 | * :doc:`Simulation Components ` 17 | 18 | * :doc:`Core Instance ` 19 | * :doc:`Pipeline ` 20 | * :doc:`Branch Prediction ` 21 | * :doc:`Register Files ` 22 | 23 | * :doc:`Models ` 24 | * :doc:`Architectures ` 25 | 26 | * :doc:`Abstract Structure ` 27 | * :doc:`AArch64 ` 28 | * :doc:`RISC-V ` 29 | 30 | * :doc:`Testing ` 31 | 32 | 33 | -------------------------------------------------------------------------------- /docs/sphinx/sst/building_simeng_with_sst.rst: -------------------------------------------------------------------------------- 1 | Building SimEng with SST 2 | ======================== 3 | 4 | Prerequisites 5 | ************* 6 | In addition to CMake and a compiler that supports C++17, version **12.0.x** of SST-Core and SST-Elements are required. 7 | 8 | .. warning:: 9 | Please ensure that while installing SST-Elements, the initial configuration step should include the 10 | ``--with-sst-core=`` flag so that all components present in **SST-Elements** are registered with the **SST-Core**. 11 | This is important because **SimEng** relies on components present in **SST-Elements**. 12 | However, if these components aren't registered, **SST-Core** will fail to locate the components to use during simulation. 13 | 14 | .. note:: 15 | If the path: ``/bin`` is added to the ``$PATH`` environment variable then all sst executables will be 16 | available globally. i.e. you will be to use the commands ``sst`` , ``sst-info``, ``sst-register`` and ``sst-config`` without having to navigate 17 | to the SST install directory. 18 | 19 | Build Steps 20 | *********** 21 | Two flags have been added to SimEng's CMake configuration step to enable integration with SST: 22 | 23 | .. code-block:: text 24 | 25 | -DSIMENG_ENABLE_SST={ON, OFF} // Defaults to OFF 26 | -DSST_INSTALL_DIR= // Path to the SST-Core install location 27 | 28 | The rest of the steps for building and installing SimEng with SST integration remain the same as a standalone SimEng :ref:`installation` i.e the build step and the install step. 29 | 30 | Validation 31 | ********** 32 | A successful SST installation should also install the ``sst-info`` executable. This executable is used to print all the components registered with SST-Core which 33 | can be used in an SST simulation. To verify a successful installation of SimEng with SST integration enabled, the command ``sst-info --libs=libsstsimeng`` 34 | should output: 35 | 36 | .. code-block:: text 37 | 38 | ================================================================================ 39 | ELEMENT 0 = sstsimeng() 40 | Num Components = 1 41 | Component 0: simengcore 42 | CATEGORY: PROCESSOR COMPONENT 43 | NUM STATISTICS = 0 44 | NUM PORTS = 0 45 | NUM SUBCOMPONENT SLOTS = 0 46 | NUM PARAMETERS = 5 47 | PARAMETER 0 = config_path (Path to Simeng YAML config file (string)) [] 48 | PARAMETER 1 = executable_path (Path to executable binary to be run by SimEng (string)) [] 49 | PARAMETER 2 = executable_args (argument to be passed to the executable binary (string)) [] 50 | PARAMETER 3 = clock (Clock rate of the SST clock (string)) [] 51 | PARAMETER 4 = max_addr_memory (Maximum address that memory can access (int)) [] 52 | simengcore: Simeng core wrapper for SST 53 | Using ELI version 0.9.0 54 | Compiled on: Aug 25 2022 11:55:05, using file: /sst/include/SimengCoreWrapper.hh 55 | Num SubComponents = 0 56 | Num Modules = 0 57 | Num SSTPartitioners = 0 58 | -------------------------------------------------------------------------------- /docs/sphinx/sst/index.rst: -------------------------------------------------------------------------------- 1 | Overview 2 | ======== 3 | 4 | The following provides information on how to build, run, and configure SimEng with SST. SimEng uses SST for simulating the memory heirarchy. 5 | 6 | * :doc:`Building SimEng with the SST ` 7 | 8 | * :doc:`Understanding SST ` 9 | 10 | * :doc:`Running SimEng with SST ` 11 | -------------------------------------------------------------------------------- /docs/sphinx/user/building_simeng.rst: -------------------------------------------------------------------------------- 1 | .. _Building_SimEng: 2 | 3 | Building SimEng 4 | =============== 5 | 6 | Prerequisites 7 | ------------- 8 | 9 | Building SimEng requires CMake and a compiler that supports C++17. 10 | 11 | Building 12 | -------- 13 | 14 | 1. Obtain the source code using git: 15 | 16 | .. code-block:: text 17 | 18 | git clone https://github.com/UoB-HPC/SimEng.git 19 | 20 | 21 | 2. Configure with CMake, specifying the path to your desired installation directory if necessary:: 22 | 23 | .. code-block:: text 24 | 25 | cd SimEng 26 | cmake -B build -S . 27 | -DCMAKE_BUILD_TYPE={Release, Debug, RelWithDebInfo, MinSizeRel} 28 | -DCMAKE_INSTALL_PREFIX= 29 | -DSIMENG_ENABLE_TESTS={ON, OFF} #Defaults to OFF 30 | 31 | With this configuration, the build files will be generated in a directory called "build". 32 | 33 | .. 34 | 35 | a. The SimEng test suites depend on a selection of LLVM libraries. Building this dependency can take a while, therefore, the use of prebuilt LLVM libraries is supported. The following CMake configuration flags should be set to enable this:: 36 | 37 | -DSIMENG_USE_EXTERNAL_LLVM=ON 38 | -DLLVM_DIR= # directory of LLVM libraries as CMake targets 39 | 40 | .. Note:: 41 | More information about the LLVM_DIR value can be found `here `_. 42 | 43 | .. Note:: 44 | LLVM versions greater than 14 or less than 8 are not supported. We'd recommend using LLVM 14.0.5 where possible as this has been verified by us to work correctly. 45 | 46 | b. Two additional flags are available when building SimEng. Firstly is ``-DSIMENG_SANITIZE={ON, OFF}`` which adds a selection of sanitisation compilation flags (primarily used during the development of the framework). Secondly is ``-SIMENG_OPTIMIZE={ON, OFF}`` which attempts to optimise the framework's compilation for the host machine through a set of compiler flags and options. 47 | 48 | We recommend using the `Ninja `_ build system for faster builds, especially if not using pre-built LLVM libraries. After installation, it can be enabled through the addition of the ``-GNinja`` flag in the above CMake build command. 49 | 50 | 1. Once configured, use ``cmake --build build`` or whichever generator you have selected for CMake to build. Append the ``-j{Num_Cores}`` flag to build in parallel, keep in mind that building without a linked external LLVM library usually has very high (1.5GB per core) memory requirements. 51 | 52 | 2. (Optional) Run ``cmake --build build --target test`` to run the SimEng regression tests and unit tests. Please report any test failures as `a GitHub issue `_. 53 | 54 | 3. Finally, run ``cmake --build build --target install`` to install SimEng to the directory specified with CMake. 55 | 56 | .. Docker 57 | .. ------ 58 | 59 | .. We have also created a SimEng docker container, offering pre-built images with the SimEng source code and binary. More details on the docker container can be found :doc:`here`. 60 | 61 | -------------------------------------------------------------------------------- /docs/sphinx/user/creating_binaries.rst: -------------------------------------------------------------------------------- 1 | Creating Binaries 2 | ================= 3 | 4 | SimEng uses real AArch64 and RISC-V statically linked binaries for its workloads. To date, the following compilers have been used to compile our codes of interest: 5 | 6 | AArch64 7 | 8 | - GCC 8.3.0 9 | - GCC 9.3.0 10 | - GCC 10.3.0 11 | - ArmClang 20.0 12 | 13 | RISC-V 14 | 15 | - GCC 12.2.0 16 | -------------------------------------------------------------------------------- /docs/sphinx/user/docker.rst: -------------------------------------------------------------------------------- 1 | Docker 2 | ====== 3 | 4 | You can find Docker containers of SimEng `on DockerHub 5 | `_ (finalise docker containers and correct broken link if necessary). 6 | To get an interactive session in a container with SimEng installed, run: 7 | 8 | .. code-block:: text 9 | 10 | docker run -it uobhpc/simeng 11 | 12 | The `simeng` binary is on the default path. 13 | 14 | A development version is also available, which contains the SimEng source code and all the build dependencies installed: 15 | 16 | .. code-block:: text 17 | 18 | docker run -it uobhpc/simeng:dev 19 | 20 | In the development container, the source code can be found in `/root/SimEng`. 21 | 22 | If you don't want to use the pre-built container images, you can build your own: 23 | 24 | .. code-block:: text 25 | 26 | docker build -t uobhpc/simeng . # For the release container 27 | docker build --target dev -t uobhpc/simeng:dev . # For the development container 28 | -------------------------------------------------------------------------------- /docs/sphinx/user/index.rst: -------------------------------------------------------------------------------- 1 | Overview 2 | ======== 3 | 4 | The following provides information on how to build, run, and configure SimEng. For those users seeking further information about SimEng's codebase, please refer to the :doc:`Developer Documentation <../developer/index>` instead. 5 | 6 | * :doc:`Building SimEng ` 7 | 8 | * :doc:`Running SimEng ` 9 | 10 | * :doc:`Configuring SimEng ` 11 | 12 | * :doc:`Creating Binaries ` 13 | 14 | .. * :doc:`SimEng docker container ` 15 | -------------------------------------------------------------------------------- /docs/sphinx/user/running_simeng.rst: -------------------------------------------------------------------------------- 1 | Running SimEng 2 | ============== 3 | 4 | SimEng uses a configuration file and a program binary to produce a cycle-accurate simulation of a modern processor. These options are passed to SimEng through the following command line arguments: 5 | 6 | .. code-block:: text 7 | 8 | /bin/simeng 9 | 10 | If no arguments are passed to SimEng, default options are used. The default configuration file is tuned to a ThunderX2 processor. The default program is a binary compiled to AArch64 found at ``SimEng/SimEngDefaultProgram``. This prints a welcome message to the console. 11 | 12 | Whilst a configuration file can be specified without a program (will use default program), a specified program must be accompanied by a configuration file. 13 | 14 | Simulation Output 15 | ----------------- 16 | 17 | For a successful simulation, SimEng's output can be split into 4 parts; 18 | 19 | Build Metadata 20 | A summary of the build options set and general information about the SimEng framework built. 21 | 22 | Workload Output 23 | All outputs from the supplied workload under simulation. 24 | 25 | Exit Clause 26 | The reason why the simulation has halted. Most commonly this is due to the invoking of the ``exit()`` system call by the workload under simulation. 27 | 28 | Statistics 29 | A selection of simulation statistics describing the emergent simulated PMU-style hardware events. 30 | 31 | All non-workload outputs from SimEng are prefixed with a tag of the format ``[SimEng:Object]`` (e.g. ``[SimEng:ExceptionHandler]``). If the output came from the root of the framework, the ``Object`` field is omitted. 32 | 33 | Configuration files 34 | ------------------- 35 | 36 | SimEng provides several configuration files that parameterise the simulated model. These can be found in ``/configs`` and more information about their content can be found :doc:`here `. 37 | 38 | The following examples illustrate the use of both the ThunderX2 and A64FX configurations: 39 | 40 | ThunderX2 processor 41 | ``/bin/simeng /configs/tx2.yaml `` 42 | 43 | A64FX processor 44 | ``/bin/simeng /configs/a64fx.yaml `` 45 | 46 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(lib) 2 | add_subdirectory(tools) 3 | -------------------------------------------------------------------------------- /src/include/simeng/AlwaysNotTakenPredictor.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "simeng/BranchPredictor.hh" 4 | 5 | namespace simeng { 6 | 7 | /** An "Always Not Taken" branch predictor; predicts all branches as not taken. 8 | */ 9 | class AlwaysNotTakenPredictor : public BranchPredictor { 10 | public: 11 | /** Generate a branch prediction for the specified instruction address; will 12 | * always predict not taken. */ 13 | BranchPrediction predict(uint64_t address, BranchType type, 14 | int64_t knownOffset) override; 15 | 16 | /** Provide branch results to update the prediction model for the specified 17 | * instruction address. As this model is static, this does nothing. */ 18 | void update(uint64_t address, bool taken, uint64_t targetAddress, 19 | BranchType type) override; 20 | 21 | /** Provide flush logic for branch prediction scheme. As there's no flush 22 | * logic for an always taken predictor, this does nothing. */ 23 | void flush(uint64_t address) override; 24 | }; 25 | 26 | } // namespace simeng 27 | -------------------------------------------------------------------------------- /src/include/simeng/ArchitecturalRegisterFileSet.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "simeng/RegisterFileSet.hh" 4 | 5 | namespace simeng { 6 | 7 | /** An extendable architectural register file set class, for mapping 8 | * architectural registers to an underlying physical register file. This default 9 | * implementation provides a 1:1 mapping to a RegisterFileSet instance. */ 10 | class ArchitecturalRegisterFileSet { 11 | public: 12 | /** Create an architectural register file set that maps 1:1 to the supplied 13 | * physical register file. */ 14 | ArchitecturalRegisterFileSet(RegisterFileSet& physicalRegisterFileSet); 15 | 16 | /** Read the value of the specified architectural register. */ 17 | virtual const RegisterValue& get(Register reg) const; 18 | 19 | /** Set an architectural register as the specified value. */ 20 | virtual void set(Register reg, const RegisterValue& value); 21 | 22 | private: 23 | /** The physical register file set to map onto. */ 24 | RegisterFileSet& physicalRegisterFileSet_; 25 | }; 26 | 27 | } // namespace simeng 28 | -------------------------------------------------------------------------------- /src/include/simeng/BranchPredictor.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace simeng { 7 | 8 | /** The types of branches recognised. */ 9 | enum class BranchType { 10 | Conditional = 0, 11 | LoopClosing, 12 | Return, 13 | SubroutineCall, 14 | Unconditional, 15 | Unknown 16 | }; 17 | 18 | /** A branch result prediction for an instruction. */ 19 | struct BranchPrediction { 20 | /** Whether the branch will be taken. */ 21 | bool taken; 22 | 23 | /** The branch instruction's target address. If `taken = false`, the value 24 | * will be ignored. */ 25 | uint64_t target; 26 | 27 | /** Check for equality of two branch predictions . */ 28 | bool operator==(const BranchPrediction& other) { 29 | if ((taken == other.taken) && (target == other.target)) 30 | return true; 31 | else 32 | return false; 33 | } 34 | 35 | /** Check for inequality of two branch predictions . */ 36 | bool operator!=(const BranchPrediction& other) { 37 | if ((taken != other.taken) || (target != other.target)) 38 | return true; 39 | else 40 | return false; 41 | } 42 | }; 43 | 44 | /** An abstract branch predictor interface. */ 45 | class BranchPredictor { 46 | public: 47 | virtual ~BranchPredictor(){}; 48 | 49 | /** Generate a branch prediction for the specified instruction address with a 50 | * branch type and possible known branch offset. */ 51 | virtual BranchPrediction predict(uint64_t address, BranchType type, 52 | int64_t knownOffset) = 0; 53 | 54 | /** Provide branch results to update the prediction model for the specified 55 | * instruction address. */ 56 | virtual void update(uint64_t address, bool taken, uint64_t targetAddress, 57 | BranchType type) = 0; 58 | 59 | /** Provides flushing behaviour for the implemented branch prediction schemes 60 | * via the instruction address. 61 | */ 62 | virtual void flush(uint64_t address) = 0; 63 | }; 64 | 65 | } // namespace simeng -------------------------------------------------------------------------------- /src/include/simeng/GenericPredictor.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "simeng/BranchPredictor.hh" 8 | #include "simeng/config/SimInfo.hh" 9 | 10 | namespace simeng { 11 | 12 | /** A generic branch predictor implementing well known/text book branch 13 | * predictor logic. The following predictors have been included: 14 | * 15 | * - Static predictor based on pre-allocated branch type. 16 | * 17 | * - A Branch Target Buffer (BTB) with a local and global indexing scheme and a 18 | * 2-bit saturating counter. 19 | * 20 | * - A Return Address Stack (RAS) is also in use. 21 | */ 22 | 23 | class GenericPredictor : public BranchPredictor { 24 | public: 25 | /** Initialise predictor models. */ 26 | GenericPredictor(ryml::ConstNodeRef config = config::SimInfo::getConfig()); 27 | ~GenericPredictor(); 28 | 29 | /** Generate a branch prediction for the supplied instruction address, a 30 | * branch type, and a known branch offset; defaults to 0 meaning offset is not 31 | * known. Returns a branch direction and branch target address. */ 32 | BranchPrediction predict(uint64_t address, BranchType type, 33 | int64_t knownOffset = 0) override; 34 | 35 | /** Updates appropriate predictor model objects based on the address and 36 | * outcome of the branch instruction. */ 37 | void update(uint64_t address, bool taken, uint64_t targetAddress, 38 | BranchType type) override; 39 | 40 | /** Provides RAS rewinding behaviour. */ 41 | void flush(uint64_t address) override; 42 | 43 | private: 44 | /** The bitlength of the BTB index; BTB will have 2^bits entries. */ 45 | uint8_t btbBits_; 46 | 47 | /** A 2^bits length vector of pairs containing a satCntBits_-bit saturating 48 | * counter and a branch target. */ 49 | std::vector> btb_; 50 | 51 | /** The previous BTB index calculated for an address. */ 52 | std::map btbHistory_; 53 | 54 | /** The number of bits used to form the saturating counter in a BTB entry. */ 55 | uint8_t satCntBits_; 56 | 57 | /** A n-bit history of previous branch directions where n is equal to 58 | * globalHistoryLength_. */ 59 | uint64_t globalHistory_ = 0; 60 | 61 | /** The number of previous branch directions recorded globally. */ 62 | uint16_t globalHistoryLength_; 63 | 64 | /** A return address stack. */ 65 | std::deque ras_; 66 | 67 | /** RAS history with instruction address as the keys. A non-zero value 68 | * represents the target prediction for a return instruction and a 0 entry for 69 | * a branch-and-link instruction. */ 70 | std::map rasHistory_; 71 | 72 | /** The size of the RAS. */ 73 | uint16_t rasSize_; 74 | }; 75 | 76 | } // namespace simeng 77 | -------------------------------------------------------------------------------- /src/include/simeng/Register.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace simeng { 5 | 6 | /** A generic register identifier. */ 7 | struct Register { 8 | /** An identifier representing the type of register - e.g. 0 = general, 1 = 9 | * vector. Used to determine which register file to access. */ 10 | uint8_t type; 11 | 12 | /** A tag identifying the register. May correspond to either physical or 13 | * architectural register, depending on point of usage. */ 14 | uint16_t tag; 15 | 16 | /** A boolean identifier for whether the creation of this register has been a 17 | * result of a register renaming scheme. */ 18 | bool renamed = false; 19 | 20 | /** Check for equality of two register identifiers. */ 21 | bool operator==(const Register& other) const { 22 | return (other.type == type && other.tag == tag); 23 | } 24 | 25 | /** Check for inequality of two register identifiers. */ 26 | bool operator!=(const Register& other) const { return !(other == *this); } 27 | }; 28 | 29 | } // namespace simeng -------------------------------------------------------------------------------- /src/include/simeng/RegisterFileSet.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "simeng/Register.hh" 6 | #include "simeng/RegisterValue.hh" 7 | 8 | namespace simeng { 9 | 10 | /** Defines the structure of a register file. */ 11 | struct RegisterFileStructure { 12 | /** The number of bytes per register. */ 13 | uint16_t bytes; 14 | /** The number of registers. */ 15 | uint16_t quantity; 16 | /** Check for the equality of two RegisterFileStructure structs. */ 17 | bool operator==(const RegisterFileStructure& other) const { 18 | return (bytes == other.bytes) && (quantity == other.quantity); 19 | } 20 | }; 21 | 22 | /** A processor register file set. Holds the physical registers for each 23 | * register file. */ 24 | class RegisterFileSet { 25 | public: 26 | /** Constructs a set of register files, defined by `registerFileStructures`. 27 | */ 28 | RegisterFileSet(std::vector registerFileStructures); 29 | 30 | /** Read the value of the specified register. */ 31 | const RegisterValue& get(Register reg) const; 32 | 33 | /** Set a register as the specified value. */ 34 | void set(Register reg, const RegisterValue& value); 35 | 36 | private: 37 | /** The set of register files. Each entry in the outer vector corresponds to a 38 | * register file, and the inner vectors are the registers. */ 39 | std::vector> registerFiles; 40 | }; 41 | 42 | } // namespace simeng 43 | -------------------------------------------------------------------------------- /src/include/simeng/SpecialFileDirGen.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "simeng/config/SimInfo.hh" 7 | 8 | namespace simeng { 9 | class SpecialFileDirGen { 10 | public: 11 | /** Construct a SpecialFileDirGen class by reading in the YAML file and 12 | * running it through checks and formatting. */ 13 | SpecialFileDirGen(ryml::ConstNodeRef config = config::SimInfo::getConfig()); 14 | 15 | /** Removes all files inside the '/src.lib/kernel/specialFiles' directory. */ 16 | void RemoveExistingSFDir(); 17 | 18 | /** Creates necessary file structure to support needed special files inside 19 | * the '/src.lib/kernel/specialFiles' directory. */ 20 | void GenerateSFDir(); 21 | 22 | private: 23 | /** Path to the root of the SimEng special files directory. */ 24 | const std::string specialFilesDir_; 25 | 26 | /** Values declared in YAML config file needed to create the Special Files 27 | * Directory tree. */ 28 | uint64_t coreCount_; 29 | uint64_t socketCount_; 30 | uint64_t smt_; 31 | float bogoMIPS_; 32 | std::string features_; 33 | std::string cpuImplementer_; 34 | uint64_t cpuArchitecture_; 35 | std::string cpuVariant_; 36 | std::string cpuPart_; 37 | uint64_t cpuRevision_; 38 | uint64_t packageCount_; 39 | 40 | }; // namespace SpecialFilesDirGen 41 | 42 | } // namespace simeng -------------------------------------------------------------------------------- /src/include/simeng/arch/ArchInfo.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "capstone/capstone.h" 6 | #include "simeng/RegisterFileSet.hh" 7 | #include "simeng/config/yaml/ryml.hh" 8 | 9 | namespace simeng { 10 | namespace arch { 11 | 12 | /** A class to hold and generate architecture specific configuration options. */ 13 | class ArchInfo { 14 | public: 15 | virtual ~ArchInfo(){}; 16 | 17 | /** Get the set of system register enums currently supported. */ 18 | virtual const std::vector& getSysRegEnums() const = 0; 19 | 20 | /** Get the structure of the architecture register fileset(s). */ 21 | virtual const std::vector& getArchRegStruct() 22 | const = 0; 23 | 24 | /** Get the structure of the physical register fileset(s) as defined in the 25 | * simulation configuration. */ 26 | virtual const std::vector& getPhysRegStruct() 27 | const = 0; 28 | 29 | /** Get the quantities of the physical register in each fileset as defined in 30 | * the simulation configuration. */ 31 | virtual const std::vector& getPhysRegQuantities() const = 0; 32 | }; 33 | 34 | } // namespace arch 35 | } // namespace simeng -------------------------------------------------------------------------------- /src/include/simeng/arch/ProcessStateChange.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "simeng/Register.hh" 6 | #include "simeng/RegisterValue.hh" 7 | #include "simeng/memory/MemoryAccessTarget.hh" 8 | 9 | namespace simeng { 10 | 11 | namespace arch { 12 | 13 | /** The types of changes that can be made to values within the process state. */ 14 | enum class ChangeType { REPLACEMENT, INCREMENT, DECREMENT }; 15 | 16 | /** A structure describing a set of changes to the process state. */ 17 | struct ProcessStateChange { 18 | /** Type of changes to be made */ 19 | ChangeType type; 20 | /** Registers to modify */ 21 | std::vector modifiedRegisters; 22 | /** Values to set modified registers to */ 23 | std::vector modifiedRegisterValues; 24 | /** Memory address/width pairs to modify */ 25 | std::vector memoryAddresses; 26 | /** Values to write to memory */ 27 | std::vector memoryAddressValues; 28 | }; 29 | 30 | } // namespace arch 31 | } // namespace simeng -------------------------------------------------------------------------------- /src/include/simeng/arch/aarch64/helpers/bitmanip.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "auxiliaryFunctions.hh" 4 | 5 | namespace simeng { 6 | namespace arch { 7 | namespace aarch64 { 8 | 9 | /** Helper function for instructions with the format `bfm rd, rn, #immr, 10 | * #imms`. 11 | * T represents the type of sourceValues (e.g. for xn, T = uint64_t). 12 | * Returns single value of type T. */ 13 | template 14 | T bfm_2imms(srcValContainer& sourceValues, 15 | const simeng::arch::aarch64::InstructionMetadata& metadata, 16 | bool signExtend, bool zeroDestReg) { 17 | uint8_t r = metadata.operands[2].imm; 18 | uint8_t s = metadata.operands[3].imm; 19 | T dest, source; 20 | if (!zeroDestReg) { 21 | dest = sourceValues[0].get(); 22 | source = sourceValues[1].get(); 23 | } else { 24 | dest = 0; 25 | source = sourceValues[0].get(); 26 | } 27 | return bitfieldManipulate(source, dest, r, s, signExtend); 28 | } 29 | 30 | /** Helper function for instructions with the format `extr rd, rn, rm, #lsb`. 31 | * T represents the type of sourceValues (e.g. for xn, T = uint64_t). 32 | * Returns single value of type T. */ 33 | template 34 | T extrLSB_registers( 35 | srcValContainer& sourceValues, 36 | const simeng::arch::aarch64::InstructionMetadata& metadata) { 37 | T n = sourceValues[0].get(); 38 | T m = sourceValues[1].get(); 39 | int64_t lsb = metadata.operands[3].imm; 40 | if (lsb == 0) return m; 41 | return (m >> lsb) | (n << ((sizeof(T) * 8) - lsb)); 42 | } 43 | 44 | /** Helper function for instructions with the format `rbit rd, rn`. 45 | * T represents the type of sourceValues (e.g. for xn, T = uint64_t). 46 | * Returns single value of type uint64_t. */ 47 | template 48 | uint64_t rbit(srcValContainer& sourceValues, 49 | const simeng::arch::aarch64::InstructionMetadata& metadata) { 50 | int width = sizeof(T) * 8; 51 | 52 | uint8_t reversedNibble[16] = {0b0000, 0b1000, 0b0100, 0b1100, 0b0010, 0b1010, 53 | 0b0110, 0b1110, 0b0001, 0b1001, 0b0101, 0b1101, 54 | 0b0011, 0b1011, 0b0111, 0b1111}; 55 | 56 | uint64_t n = sourceValues[0].get(); 57 | uint64_t result = 0; 58 | for (int i = 0; i < width; i += 4) { 59 | result <<= 4; 60 | result |= reversedNibble[n & 0b1111]; 61 | n >>= 4; 62 | } 63 | return result; 64 | } 65 | 66 | /** Helper function for instructions with the format `rev rd, rn`. 67 | * T represents the type of sourceValues (e.g. for xn, T = uint64_t). 68 | * Returns array of uint8_t with number of elements = bytes in T. */ 69 | template 70 | std::array rev(srcValContainer& sourceValues) { 71 | auto bytes = sourceValues[0].getAsVector(); 72 | std::array reversed; 73 | // Copy `bytes` backwards onto `reversed` 74 | std::copy(bytes, bytes + sizeof(T), std::rbegin(reversed)); 75 | return reversed; 76 | } 77 | 78 | } // namespace aarch64 79 | } // namespace arch 80 | } // namespace simeng -------------------------------------------------------------------------------- /src/include/simeng/arch/aarch64/helpers/comparison.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "auxiliaryFunctions.hh" 4 | 5 | namespace simeng { 6 | namespace arch { 7 | namespace aarch64 { 8 | 9 | /** Helper function for instructions with the format `orr rd, rn, rm {shift 10 | * #amount}`. 11 | * T represents the type of sourceValues (e.g. for xn, T = uint64_t). 12 | * Returns single value of type T. */ 13 | template 14 | T orrShift_3ops(srcValContainer& sourceValues, 15 | const simeng::arch::aarch64::InstructionMetadata& metadata) { 16 | const T n = sourceValues[0].get(); 17 | const T m = 18 | shiftValue(sourceValues[1].get(), metadata.operands[2].shift.type, 19 | metadata.operands[2].shift.value); 20 | return (n | m); 21 | } 22 | 23 | } // namespace aarch64 24 | } // namespace arch 25 | } // namespace simeng -------------------------------------------------------------------------------- /src/include/simeng/arch/aarch64/helpers/divide.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "auxiliaryFunctions.hh" 4 | 5 | namespace simeng { 6 | namespace arch { 7 | namespace aarch64 { 8 | 9 | /** Helper function for instructions with the format `div rd, rn, rm`. 10 | * T represents the type of sourceValues (e.g. for xd, T = uint64_t). 11 | * Returns single value of type T. */ 12 | template 13 | T div_3ops(srcValContainer& sourceValues) { 14 | const T n = sourceValues[0].get(); 15 | const T m = sourceValues[1].get(); 16 | if (m == 0) return 0; 17 | return (n / m); 18 | } 19 | 20 | } // namespace aarch64 21 | } // namespace arch 22 | } // namespace simeng -------------------------------------------------------------------------------- /src/include/simeng/arch/aarch64/helpers/multiply.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "auxiliaryFunctions.hh" 4 | 5 | namespace simeng { 6 | namespace arch { 7 | namespace aarch64 { 8 | 9 | /** Helper function for instructions with the format `madd rd, rn, rm, ra`. 10 | * T represents the type of sourceValues (e.g. for xn, T = uint64_t). 11 | * Returns single value of type T. */ 12 | template 13 | T madd_4ops(srcValContainer& sourceValues) { 14 | const T n = sourceValues[0].get(); 15 | const T m = sourceValues[1].get(); 16 | const T a = sourceValues[2].get(); 17 | return (a + (n * m)); 18 | } 19 | 20 | /** Helper function for instructions with the format `maddl xd, wn, wm, xa`. 21 | * D represents the type of the destination register (either int64_t or 22 | * uint64_t). 23 | * N represents the type of the first source register (either 24 | * int32_t or uint32_t). 25 | * Returns single value of type D. */ 26 | template 27 | D maddl_4ops(srcValContainer& sourceValues) { 28 | const D n = static_cast(sourceValues[0].get()); 29 | const D m = static_cast(sourceValues[1].get()); 30 | const D a = sourceValues[2].get(); 31 | return (a + (n * m)); 32 | } 33 | 34 | /** Helper function for instructions with the format `mul rd, rn, rm`. 35 | * T represents the type of sourceValues (e.g. for xn, T = uint64_t). 36 | * Returns single value of type T. */ 37 | template 38 | T mul_3ops(srcValContainer& sourceValues) { 39 | const T n = sourceValues[0].get(); 40 | const T m = sourceValues[1].get(); 41 | return (n * m); 42 | } 43 | 44 | /** Helper function for instructions with the format `msub rd, rn, rm, ra`. 45 | * T represents the type of sourceValues (e.g. for xn, T = uint64_t). 46 | * Returns single value of type T. */ 47 | template 48 | T msub_4ops(srcValContainer& sourceValues) { 49 | const T n = sourceValues[0].get(); 50 | const T m = sourceValues[1].get(); 51 | const T a = sourceValues[2].get(); 52 | return (a - (n * m)); 53 | } 54 | 55 | } // namespace aarch64 56 | } // namespace arch 57 | } // namespace simeng -------------------------------------------------------------------------------- /src/include/simeng/arch/riscv/InstructionGroups.hh: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace simeng { 4 | namespace arch { 5 | namespace riscv { 6 | 7 | /** The IDs of the instruction groups for RISC-V instructions. */ 8 | namespace InstructionGroups { 9 | const uint16_t INT = 0; 10 | const uint16_t INT_SIMPLE = 1; 11 | const uint16_t INT_SIMPLE_ARTH = 2; 12 | const uint16_t INT_SIMPLE_CMP = 3; 13 | const uint16_t INT_SIMPLE_LOGICAL = 4; 14 | const uint16_t INT_SIMPLE_SHIFT = 5; 15 | const uint16_t INT_MUL = 6; 16 | const uint16_t INT_DIV_OR_SQRT = 7; 17 | const uint16_t LOAD_INT = 8; 18 | const uint16_t STORE_INT = 9; 19 | const uint16_t FLOAT = 10; 20 | const uint16_t FLOAT_SIMPLE = 11; 21 | const uint16_t FLOAT_SIMPLE_ARTH = 12; 22 | const uint16_t FLOAT_SIMPLE_CMP = 13; 23 | const uint16_t FLOAT_SIMPLE_LOGICAL = 14; 24 | const uint16_t FLOAT_SIMPLE_CVT = 15; 25 | const uint16_t FLOAT_MUL = 16; 26 | const uint16_t FLOAT_DIV_OR_SQRT = 17; 27 | const uint16_t LOAD_FLOAT = 18; 28 | const uint16_t STORE_FLOAT = 19; 29 | const uint16_t LOAD = 20; 30 | const uint16_t STORE = 21; 31 | const uint16_t BRANCH = 22; 32 | const uint16_t ALL = 23; 33 | const uint16_t NONE = 24; 34 | } // namespace InstructionGroups 35 | 36 | static constexpr uint8_t NUM_GROUPS = 25; 37 | 38 | const std::unordered_map> groupInheritance_ = { 39 | {InstructionGroups::ALL, 40 | {InstructionGroups::INT, InstructionGroups::FLOAT, InstructionGroups::LOAD, 41 | InstructionGroups::STORE, InstructionGroups::BRANCH}}, 42 | {InstructionGroups::INT, 43 | {InstructionGroups::INT_SIMPLE, InstructionGroups::INT_MUL, 44 | InstructionGroups::INT_DIV_OR_SQRT}}, 45 | {InstructionGroups::INT_SIMPLE, 46 | {InstructionGroups::INT_SIMPLE_ARTH, InstructionGroups::INT_SIMPLE_CMP, 47 | InstructionGroups::INT_SIMPLE_LOGICAL, 48 | InstructionGroups::INT_SIMPLE_SHIFT}}, 49 | {InstructionGroups::LOAD, 50 | {InstructionGroups::LOAD_INT, InstructionGroups::LOAD_FLOAT}}, 51 | {InstructionGroups::STORE, 52 | {InstructionGroups::STORE_INT, InstructionGroups::STORE_FLOAT}}, 53 | {InstructionGroups::FLOAT, 54 | {InstructionGroups::FLOAT_SIMPLE, InstructionGroups::FLOAT_MUL, 55 | InstructionGroups::FLOAT_DIV_OR_SQRT}}, 56 | {InstructionGroups::FLOAT_SIMPLE, 57 | {InstructionGroups::FLOAT_SIMPLE_ARTH, 58 | InstructionGroups::FLOAT_SIMPLE_LOGICAL, 59 | InstructionGroups::FLOAT_SIMPLE_CMP, 60 | InstructionGroups::FLOAT_SIMPLE_CVT}}}; 61 | 62 | } // namespace riscv 63 | } // namespace arch 64 | } // namespace simeng -------------------------------------------------------------------------------- /src/include/simeng/config/yaml/.clang-format: -------------------------------------------------------------------------------- 1 | DisableFormat: true 2 | SortIncludes: false 3 | -------------------------------------------------------------------------------- /src/include/simeng/memory/FixedLatencyMemoryInterface.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "simeng/memory/MemoryInterface.hh" 7 | 8 | namespace simeng { 9 | 10 | namespace memory { 11 | 12 | /** A fixed-latency memory interface request. */ 13 | struct FixedLatencyMemoryInterfaceRequest { 14 | /** Is this a write request? */ 15 | bool write; 16 | 17 | /** The memory target to access. */ 18 | const MemoryAccessTarget target; 19 | 20 | /** The value to write to the target (writes only) */ 21 | const RegisterValue data; 22 | 23 | /** The cycle count this request will be ready at. */ 24 | uint64_t readyAt; 25 | 26 | /** A unique request identifier for read operations. */ 27 | uint64_t requestId; 28 | 29 | /** Construct a write request. */ 30 | FixedLatencyMemoryInterfaceRequest(const MemoryAccessTarget& target, 31 | const RegisterValue& data, 32 | uint64_t readyAt) 33 | : write(true), target(target), data(data), readyAt(readyAt) {} 34 | 35 | /** Construct a read request. */ 36 | FixedLatencyMemoryInterfaceRequest(const MemoryAccessTarget& target, 37 | uint64_t readyAt, uint64_t requestId) 38 | : write(false), target(target), readyAt(readyAt), requestId(requestId) {} 39 | }; 40 | 41 | /** A memory interface where all requests respond with a fixed latency. */ 42 | class FixedLatencyMemoryInterface : public MemoryInterface { 43 | public: 44 | FixedLatencyMemoryInterface(char* memory, size_t size, uint16_t latency); 45 | 46 | /** Queue a read request from the supplied target location. 47 | * 48 | * The caller can optionally provide an ID that will be attached to completed 49 | * read results. 50 | */ 51 | void requestRead(const MemoryAccessTarget& target, 52 | uint64_t requestId = 0) override; 53 | /** Queue a write request of `data` to the target location. */ 54 | void requestWrite(const MemoryAccessTarget& target, 55 | const RegisterValue& data) override; 56 | /** Retrieve all completed requests. */ 57 | const span getCompletedReads() const override; 58 | 59 | /** Clear the completed reads. */ 60 | void clearCompletedReads() override; 61 | 62 | /** Returns true if there are any outstanding memory requests in-flight. */ 63 | bool hasPendingRequests() const override; 64 | 65 | /** Tick the memory model to process the request queue. */ 66 | void tick() override; 67 | 68 | private: 69 | /** The array representing the memory system to access. */ 70 | char* memory_; 71 | /** The size of accessible memory. */ 72 | size_t size_; 73 | /** A vector containing all completed read requests. */ 74 | std::vector completedReads_; 75 | 76 | /** A queue containing all pending memory requests. */ 77 | std::queue pendingRequests_; 78 | 79 | /** The latency all requests are completed after. */ 80 | uint16_t latency_; 81 | 82 | /** The number of times this interface has been ticked. */ 83 | uint64_t tickCounter_ = 0; 84 | 85 | /** Returns true if unsigned overflow occurs. */ 86 | bool unsignedOverflow_(uint64_t a, uint64_t b) const { 87 | return (a + b) < a || (a + b) < b; 88 | } 89 | }; 90 | 91 | } // namespace memory 92 | } // namespace simeng 93 | -------------------------------------------------------------------------------- /src/include/simeng/memory/FlatMemoryInterface.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "simeng/memory/MemoryInterface.hh" 6 | 7 | namespace simeng { 8 | 9 | namespace memory { 10 | 11 | /** A memory interface to a flat memory system. */ 12 | class FlatMemoryInterface : public MemoryInterface { 13 | public: 14 | FlatMemoryInterface(char* memory, size_t size); 15 | 16 | /** Request a read from the supplied target location. 17 | * 18 | * The caller can optionally provide an ID that will be attached to completed 19 | * read results. 20 | */ 21 | void requestRead(const MemoryAccessTarget& target, 22 | uint64_t requestId = 0) override; 23 | /** Request a write of `data` to the target location. */ 24 | void requestWrite(const MemoryAccessTarget& target, 25 | const RegisterValue& data) override; 26 | /** Retrieve all completed requests. */ 27 | const span getCompletedReads() const override; 28 | 29 | /** Clear the completed reads. */ 30 | void clearCompletedReads() override; 31 | 32 | /** Returns true if there are any outstanding memory requests in-flight. */ 33 | bool hasPendingRequests() const override; 34 | 35 | /** Tick: do nothing */ 36 | void tick() override; 37 | 38 | private: 39 | /** The array representing the flat memory system to access. */ 40 | char* memory_; 41 | /** The size of accessible memory. */ 42 | size_t size_; 43 | /** A vector containing all completed read requests. */ 44 | std::vector completedReads_; 45 | }; 46 | 47 | } // namespace memory 48 | } // namespace simeng 49 | -------------------------------------------------------------------------------- /src/include/simeng/memory/MemoryAccessTarget.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace simeng { 4 | 5 | namespace memory { 6 | 7 | /** A generic memory access target; describes a region of memory to access. */ 8 | struct MemoryAccessTarget { 9 | /** The address to access. */ 10 | uint64_t address; 11 | /** The number of bytes to access at `address`. */ 12 | uint16_t size; 13 | 14 | /** Check for equality of two access targets. */ 15 | bool operator==(const MemoryAccessTarget& other) const { 16 | return (address == other.address && size == other.size); 17 | }; 18 | 19 | /** Check for inequality of two access targets. */ 20 | bool operator!=(const MemoryAccessTarget& other) const { 21 | return !(other == *this); 22 | } 23 | }; 24 | 25 | } // namespace memory 26 | } // namespace simeng -------------------------------------------------------------------------------- /src/include/simeng/memory/MemoryInterface.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "simeng/RegisterValue.hh" 4 | #include "simeng/memory/MemoryReadResult.hh" 5 | #include "simeng/span.hh" 6 | 7 | namespace simeng { 8 | 9 | namespace memory { 10 | 11 | /** The available memory interface types. */ 12 | enum class MemInterfaceType { 13 | Flat, // A zero access latency interface 14 | Fixed, // A fixed, non-zero, access latency interface 15 | External // An interface generated outside of the standard SimEng 16 | // instantiation 17 | }; 18 | 19 | /** An abstract memory interface. Describes a connection to a memory system to 20 | * which data read/write requests may be made. */ 21 | class MemoryInterface { 22 | public: 23 | virtual ~MemoryInterface() {} 24 | 25 | /** Request a read from the supplied target location. 26 | * 27 | * The caller can optionally provide an ID that will be attached to completed 28 | * read results. 29 | */ 30 | virtual void requestRead(const MemoryAccessTarget& target, 31 | uint64_t requestId = 0) = 0; 32 | /** Request a write of `data` to the target location. */ 33 | virtual void requestWrite(const MemoryAccessTarget& target, 34 | const RegisterValue& data) = 0; 35 | /** Retrieve all completed read requests. */ 36 | virtual const span getCompletedReads() const = 0; 37 | 38 | /** Clear the completed reads. */ 39 | virtual void clearCompletedReads() = 0; 40 | 41 | /** Returns true if there are any outstanding memory requests in-flight. */ 42 | virtual bool hasPendingRequests() const = 0; 43 | 44 | /** Tick the memory interface to allow it to process internal tasks. 45 | * 46 | * TODO: Move ticking out of the memory interface and into a central "memory 47 | * system" covering a set of related interfaces. 48 | */ 49 | virtual void tick() = 0; 50 | }; 51 | 52 | } // namespace memory 53 | } // namespace simeng 54 | -------------------------------------------------------------------------------- /src/include/simeng/memory/MemoryReadResult.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "simeng/RegisterValue.hh" 4 | #include "simeng/memory/MemoryAccessTarget.hh" 5 | 6 | namespace simeng { 7 | 8 | namespace memory { 9 | 10 | /** A structure used for the result of memory read operations. */ 11 | struct MemoryReadResult { 12 | /** The memory access that was requested. */ 13 | MemoryAccessTarget target; 14 | /** The data returned by the request. */ 15 | RegisterValue data; 16 | /** The request identifier provided by the requester. */ 17 | uint64_t requestId; 18 | }; 19 | 20 | } // namespace memory 21 | } // namespace simeng -------------------------------------------------------------------------------- /src/include/simeng/models/emulation/Core.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "simeng/ArchitecturalRegisterFileSet.hh" 8 | #include "simeng/Core.hh" 9 | #include "simeng/arch/Architecture.hh" 10 | #include "simeng/span.hh" 11 | 12 | namespace simeng { 13 | namespace models { 14 | namespace emulation { 15 | 16 | /** An emulation-style core model. Executes each instruction in turn. */ 17 | class Core : public simeng::Core { 18 | public: 19 | /** Construct an emulation-style core, providing memory interfaces for 20 | * instructions and data, along with the instruction entry point and an ISA to 21 | * use. */ 22 | Core(memory::MemoryInterface& instructionMemory, 23 | memory::MemoryInterface& dataMemory, uint64_t entryPoint, 24 | uint64_t programByteLength, const arch::Architecture& isa); 25 | 26 | /** Tick the core. */ 27 | void tick() override; 28 | 29 | /** Check whether the program has halted. */ 30 | bool hasHalted() const override; 31 | 32 | /** Retrieve the architectural register file set. */ 33 | const ArchitecturalRegisterFileSet& getArchitecturalRegisterFileSet() 34 | const override; 35 | 36 | /** Retrieve the number of instructions retired. */ 37 | uint64_t getInstructionsRetiredCount() const override; 38 | 39 | /** Retrieve a map of statistics to report. */ 40 | std::map getStats() const override; 41 | 42 | private: 43 | /** Execute an instruction. */ 44 | void execute(std::shared_ptr& uop); 45 | 46 | /** Handle an encountered exception. */ 47 | void handleException(const std::shared_ptr& instruction); 48 | 49 | /** Process an active exception handler. */ 50 | void processExceptionHandler(); 51 | 52 | /** A memory interface to access instructions. */ 53 | memory::MemoryInterface& instructionMemory_; 54 | 55 | /** An architectural register file set, serving as a simple wrapper around the 56 | * register file set. */ 57 | ArchitecturalRegisterFileSet architecturalRegisterFileSet_; 58 | 59 | /** A reusable macro-op vector to fill with uops. */ 60 | MacroOp macroOp_; 61 | 62 | /** The previously generated addresses. */ 63 | std::vector previousAddresses_; 64 | 65 | /** The current program counter. */ 66 | uint64_t pc_ = 0; 67 | 68 | /** The length of the available instruction memory. */ 69 | uint64_t programByteLength_ = 0; 70 | 71 | /** The number of instructions executed. */ 72 | uint64_t instructionsExecuted_ = 0; 73 | 74 | /** The number of branches executed. */ 75 | uint64_t branchesExecuted_ = 0; 76 | }; 77 | 78 | } // namespace emulation 79 | } // namespace models 80 | } // namespace simeng 81 | -------------------------------------------------------------------------------- /src/include/simeng/pipeline/A64FXPortAllocator.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "simeng/pipeline/PortAllocator.hh" 6 | 7 | namespace simeng { 8 | namespace pipeline { 9 | 10 | /** The A64FX defined instruction attribute groups. */ 11 | namespace InstructionAttribute { 12 | const uint8_t RSX = 0; 13 | const uint8_t RSE = 1; 14 | const uint8_t RSA = 2; 15 | const uint8_t RSE0 = 3; 16 | const uint8_t RSE1 = 4; 17 | const uint8_t BR = 5; 18 | } // namespace InstructionAttribute 19 | 20 | /** An A64FX port allocator implementation. Follows the functionality 21 | * described in the A64FX Microarchitecture manual. */ 22 | class A64FXPortAllocator : public PortAllocator { 23 | public: 24 | /** Constructor for the A64FXPortAllocator object. */ 25 | A64FXPortAllocator(const std::vector>& portArrangement); 26 | 27 | /** Allocate a port for the specified instruction group; returns the allocated 28 | * port. */ 29 | uint16_t allocate(const std::vector& ports) override; 30 | 31 | /** Inform the allocator that an instruction was issued to the specified port. 32 | */ 33 | void issued(uint16_t port) override; 34 | 35 | /** Inform the allocator that an instruction will not issue to its 36 | * allocated port. */ 37 | void deallocate(uint16_t port) override; 38 | 39 | /** Set function from DispatchIssueUnit to retrieve reservation 40 | * station sizes during execution. */ 41 | void setRSSizeGetter( 42 | std::function&)> rsSizes) override; 43 | 44 | /** Tick the port allocator to allow it to process internal tasks. */ 45 | void tick() override; 46 | 47 | private: 48 | /** A mapping from issue ports to instruction attribute */ 49 | uint8_t attributeMapping(const std::vector& ports); 50 | 51 | /** An approximate estimation of the index of an instruction within the input 52 | * buffer of the dispatch unit. Increments slot at each allocation thus cannot 53 | * account for nullptr entries in buffer.*/ 54 | uint8_t dispatchSlot_; 55 | 56 | /** Get the current sizes an capacity of the reservation stations. */ 57 | std::function&)> rsSizes_; 58 | 59 | /** Mapping from reservation station to ports. */ 60 | std::vector> rsToPort_; 61 | 62 | /** Vector of free entries across all reservation stations. */ 63 | std::vector freeEntries_; 64 | 65 | /** Reservation station classifications as detailed in manual. */ 66 | /** RSE with most free entries. */ 67 | uint8_t RSEm_; 68 | /** RSE with least free entries. */ 69 | uint8_t RSEf_; 70 | /** RSA with most free entries. */ 71 | uint8_t RSAm_; 72 | /** RSA with least free entries. */ 73 | uint8_t RSAf_; 74 | 75 | const std::vector EXA_EXB_EAGA_EAGB = {2, 4, 5, 6}; 76 | const std::vector EXA_EXB = {2, 4}; 77 | const std::vector FLA_FLB = {0, 3}; 78 | const std::vector EAGA_EAGB = {5, 6}; 79 | const std::vector EXA = {2}; 80 | const std::vector FLA = {0}; 81 | const std::vector PR = {1}; 82 | const std::vector EXB = {4}; 83 | const std::vector FLB = {3}; 84 | const std::vector BR = {7}; 85 | }; 86 | 87 | } // namespace pipeline 88 | } // namespace simeng 89 | -------------------------------------------------------------------------------- /src/include/simeng/pipeline/BalancedPortAllocator.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "simeng/pipeline/PortAllocator.hh" 6 | 7 | namespace simeng { 8 | namespace pipeline { 9 | 10 | /** A load-balancing port allocator implementation. Maintains demand weightings 11 | * for each port, and allocates instructions to the suitable port with the 12 | * lowest weighting. */ 13 | class BalancedPortAllocator : public PortAllocator { 14 | public: 15 | /** Construct a load-balancing port allocator, providing a port arrangement 16 | * specification. Each element of the port arrangement should represent a 17 | * port, and contain a list of the instruction groups that port supports and 18 | * a port type which denotes the matching requirements of said instruction 19 | * groups. */ 20 | BalancedPortAllocator( 21 | const std::vector>& portArrangement); 22 | 23 | /** Allocate the lowest weighted port available for the specified instruction 24 | * group. Returns the allocated port, and increases the weight of the port. 25 | */ 26 | uint16_t allocate(const std::vector& ports) override; 27 | 28 | /** Decrease the weight for the specified port. */ 29 | void issued(uint16_t port) override; 30 | 31 | /** Decrease the weight for the specified port. */ 32 | void deallocate(uint16_t port) override; 33 | 34 | /** Set function from DispatchIssueUnit to retrieve reservation 35 | * station sizes during execution. */ 36 | void setRSSizeGetter( 37 | std::function&)> rsSizes) override; 38 | 39 | /** Tick the port allocator to allow it to process internal tasks. */ 40 | void tick() override; 41 | 42 | private: 43 | /** The instruction group support matrix. An instruction-group-indexed map 44 | * containing lists of the ports that support each instruction group. */ 45 | std::vector> supportMatrix; 46 | 47 | /** The port weighting map. Each element corresponds to a port, and contains a 48 | * weighting representing the number of in-flight instructions allocated to 49 | * that port. */ 50 | std::vector weights; 51 | 52 | /** Get the current sizes an capacity of the reservation stations */ 53 | std::function&)> rsSizes_; 54 | }; 55 | 56 | } // namespace pipeline 57 | } // namespace simeng 58 | -------------------------------------------------------------------------------- /src/include/simeng/pipeline/DecodeUnit.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "simeng/arch/Architecture.hh" 6 | #include "simeng/pipeline/PipelineBuffer.hh" 7 | 8 | namespace simeng { 9 | namespace pipeline { 10 | 11 | /** A decode unit for a pipelined processor. Splits pre-decoded macro-ops into 12 | * uops. */ 13 | class DecodeUnit { 14 | public: 15 | /** Constructs a decode unit with references to input/output buffers and the 16 | * current branch predictor. */ 17 | DecodeUnit(PipelineBuffer& input, 18 | PipelineBuffer>& output, 19 | BranchPredictor& predictor); 20 | 21 | /** Ticks the decode unit. Breaks macro-ops into uops, and performs early 22 | * branch misprediction checks. */ 23 | void tick(); 24 | 25 | /** Check whether the core should be flushed this cycle. */ 26 | bool shouldFlush() const; 27 | 28 | /** Retrieve the target instruction address associated with the most recently 29 | * discovered misprediction. */ 30 | uint64_t getFlushAddress() const; 31 | 32 | /** Retrieve the number of times that the decode unit requested a flush due to 33 | * discovering a branch misprediction early. */ 34 | uint64_t getEarlyFlushes() const; 35 | 36 | /** Clear the microOps_ queue. */ 37 | void purgeFlushed(); 38 | 39 | private: 40 | /** A buffer of macro-ops to split into uops. */ 41 | PipelineBuffer& input_; 42 | /** An internal buffer for storing one or more uops. */ 43 | std::deque> microOps_; 44 | /** A buffer for writing decoded uops into. */ 45 | PipelineBuffer>& output_; 46 | 47 | /** A reference to the current branch predictor. */ 48 | BranchPredictor& predictor_; 49 | 50 | /** Whether the core should be flushed after this cycle. */ 51 | bool shouldFlush_; 52 | 53 | /** The target instruction address the PC should be updated to upon flush. */ 54 | uint64_t pc_; 55 | 56 | /** The number of times that the decode unit requested a flush due to 57 | * discovering a branch misprediction early. */ 58 | uint64_t earlyFlushes_ = 0; 59 | }; 60 | 61 | } // namespace pipeline 62 | } // namespace simeng 63 | -------------------------------------------------------------------------------- /src/include/simeng/pipeline/M1PortAllocator.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "simeng/pipeline/PortAllocator.hh" 7 | 8 | namespace simeng { 9 | namespace pipeline { 10 | 11 | /** A load-balancing port allocator implementation. Maintains demand weightings 12 | * for each port, and allocates instructions to the suitable port with the 13 | * lowest weighting. */ 14 | class M1PortAllocator : public PortAllocator { 15 | public: 16 | /** Construct a load-balancing port allocator, providing a port arrangement 17 | * specification. Each element of the port arrangement should represent a 18 | * port, and contain a list of the instruction groups that port supports and 19 | * a port type which denotes the matching requirements of said instruction 20 | * groups. */ 21 | M1PortAllocator(const std::vector>& portArrangement, 22 | std::vector> rsArrangement); 23 | 24 | /** Allocate the lowest weighted port available for the specified instruction 25 | * group. Returns the allocated port, and increases the weight of the port. 26 | */ 27 | uint16_t allocate(const std::vector& ports) override; 28 | 29 | /** Decrease the weight for the specified port. */ 30 | void issued(uint16_t port) override; 31 | 32 | /** Decrease the weight for the specified port. */ 33 | void deallocate(uint16_t port) override; 34 | 35 | /** Set function from DispatchIssueUnit to retrieve reservation 36 | * station sizes during execution. */ 37 | void setRSSizeGetter( 38 | std::function&)> rsSizes) override; 39 | 40 | /** Tick the port allocator to allow it to process internal tasks. */ 41 | void tick() override; 42 | 43 | private: 44 | /** The instruction group support matrix. An instruction-group-indexed map 45 | * containing lists of the ports that support each instruction group. */ 46 | std::vector> supportMatrix; 47 | 48 | /** The port weighting map. Each element corresponds to a port, and contains a 49 | * weighting representing the number of in-flight instructions allocated to 50 | * that port. */ 51 | std::vector weights; 52 | 53 | std::vector rsFreeSpaces; 54 | 55 | /** Get the current capacity of the reservation stations */ 56 | std::function&)> rsSizes_; 57 | 58 | /** Mapping from port index to reservation station */ 59 | std::vector> rsArrangement_; 60 | }; 61 | 62 | } // namespace pipeline 63 | } // namespace simeng 64 | -------------------------------------------------------------------------------- /src/include/simeng/pipeline/MappedRegisterFileSet.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "simeng/ArchitecturalRegisterFileSet.hh" 4 | #include "simeng/pipeline/RegisterAliasTable.hh" 5 | 6 | namespace simeng { 7 | namespace pipeline { 8 | 9 | /** An ArchitecturalRegisterFileSet implementation which maps architectural 10 | * registers to their corresponding physical registers, according to a register 11 | * alias table's current mapping table. */ 12 | class MappedRegisterFileSet : public ArchitecturalRegisterFileSet { 13 | public: 14 | /** Create a mapped register file set which maps architectural registers to 15 | * their corresponding physical registers in `physicalRegisterFileSet`, 16 | * according to the mapping table in `rat`. */ 17 | MappedRegisterFileSet(RegisterFileSet& physicalRegisterFileSet, 18 | const RegisterAliasTable& rat); 19 | 20 | /** Read the value of the physical register currently mapped to the 21 | * architectural register `reg`. 22 | * 23 | * NOTE: The physical register mapped to depends on the current state of the 24 | * register alias table; if there are any allocated but uncommitted registers, 25 | * this mapping will not represent the committed architectural state. */ 26 | virtual const RegisterValue& get(Register reg) const override; 27 | 28 | /** Set the physical register currently mapped to the architectural register 29 | * `reg` to the specified value. 30 | * 31 | * NOTE: The physical register mapped to depends on the current state of the 32 | * register alias table; if there are any allocated but uncommitted registers, 33 | * this mapping will not represent the committed architectural state. */ 34 | virtual void set(Register reg, const RegisterValue& value) override; 35 | 36 | private: 37 | /** A reference to the register alias table to use for mapping architectural 38 | * to physical registers. */ 39 | const RegisterAliasTable& rat_; 40 | }; 41 | 42 | } // namespace pipeline 43 | } // namespace simeng 44 | -------------------------------------------------------------------------------- /src/include/simeng/pipeline/PipelineBuffer.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace simeng { 8 | namespace pipeline { 9 | 10 | // TODO: Extend to allow specifying the number of cycles it will take for 11 | // information to move from tail to head (currently fixed at 1 by 12 | // implementation) 13 | 14 | /** A tickable pipelined buffer. Values are shifted from the tail slot to the 15 | * head slot each time `tick()` is called. */ 16 | template 17 | class PipelineBuffer { 18 | public: 19 | /** Construct a pipeline buffer of width `width`, and fill all slots with 20 | * `initialValue`. */ 21 | PipelineBuffer(uint16_t width, const T& initialValue) 22 | : width(width), buffer(width * length, initialValue) {} 23 | 24 | /** Tick the buffer and move head/tail pointers, or do nothing if it's 25 | * stalled. */ 26 | void tick() { 27 | if (isStalled_) return; 28 | 29 | headIsStart = !headIsStart; 30 | } 31 | 32 | /** Return the slots waiting to be processed by the next pipeline unit */ 33 | const T* getPendingSlots() const { 34 | // If stalled head and tail slots won't have been swapped 35 | if (isStalled_) { 36 | return getTailSlots(); 37 | } else { 38 | return getHeadSlots(); 39 | } 40 | } 41 | 42 | /** Get a tail slots pointer. */ 43 | T* getTailSlots() { 44 | T* ptr = buffer.data(); 45 | return &ptr[headIsStart * width]; 46 | } 47 | /** Get a const tail slots pointer. */ 48 | const T* getTailSlots() const { 49 | const T* ptr = buffer.data(); 50 | return &ptr[headIsStart * width]; 51 | } 52 | 53 | /** Get a head slots pointer. */ 54 | T* getHeadSlots() { 55 | T* ptr = buffer.data(); 56 | return &ptr[!headIsStart * width]; 57 | } 58 | /** Get a const head slots pointer. */ 59 | const T* getHeadSlots() const { 60 | const T* ptr = buffer.data(); 61 | return &ptr[!headIsStart * width]; 62 | } 63 | 64 | /** Check if the buffer is stalled. */ 65 | bool isStalled() const { return isStalled_; } 66 | 67 | /** Set the buffer's stall flag to `stalled`. */ 68 | void stall(bool stalled) { isStalled_ = stalled; } 69 | 70 | /** Fill the buffer with a specified value. */ 71 | void fill(const T& value) { std::fill(buffer.begin(), buffer.end(), value); } 72 | 73 | /** Get the width of the buffer slots. */ 74 | uint16_t getWidth() const { return width; } 75 | 76 | private: 77 | /** The width of each row of slots. */ 78 | uint16_t width; 79 | 80 | /** The buffer. */ 81 | std::vector buffer; 82 | 83 | /** The offset of the head pointer; either 0 or 1. */ 84 | bool headIsStart = 0; 85 | 86 | /** Whether the buffer is stalled or not. */ 87 | bool isStalled_ = false; 88 | 89 | /** The number of stages in the pipeline. */ 90 | static const unsigned int length = 2; 91 | }; 92 | 93 | } // namespace pipeline 94 | } // namespace simeng 95 | -------------------------------------------------------------------------------- /src/include/simeng/pipeline/PortAllocator.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace simeng { 7 | namespace pipeline { 8 | 9 | namespace PortType { 10 | /** Instructions have to match the exact group(s) in set. */ 11 | const uint8_t COMPULSORY = 0; 12 | /** Instructions can optional match group(s) in set. */ 13 | const uint8_t OPTIONAL = 1; 14 | } // namespace PortType 15 | 16 | /** An abstract execution port allocator interface. */ 17 | class PortAllocator { 18 | public: 19 | virtual ~PortAllocator(){}; 20 | 21 | /** Allocate a port for the specified instruction group; returns the allocated 22 | * port. */ 23 | virtual uint16_t allocate(const std::vector& ports) = 0; 24 | 25 | /** Inform the allocator that an instruction was issued to the specified port. 26 | */ 27 | virtual void issued(uint16_t port) = 0; 28 | 29 | /** Inform the allocator that an instruction will not issue to its 30 | * allocated port. */ 31 | virtual void deallocate(uint16_t port) = 0; 32 | 33 | /** Set function from DispatchIssueUnit to retrieve reservation 34 | * station sizes during execution. */ 35 | virtual void setRSSizeGetter( 36 | std::function&)> rsSizes) = 0; 37 | 38 | /** Tick the port allocator to allow it to process internal tasks. */ 39 | virtual void tick() = 0; 40 | }; 41 | 42 | } // namespace pipeline 43 | } // namespace simeng 44 | -------------------------------------------------------------------------------- /src/include/simeng/pipeline/RegisterAliasTable.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "simeng/RegisterFileSet.hh" 6 | 7 | namespace simeng { 8 | namespace pipeline { 9 | 10 | /** A Register Alias Table (RAT) implementation. Contains information on 11 | * the current register renaming state. */ 12 | class RegisterAliasTable { 13 | public: 14 | /** Construct a RAT, supplying a description of the architectural register 15 | * structure, and the corresponding numbers of physical registers that should 16 | * be available. */ 17 | RegisterAliasTable(std::vector architecturalStructure, 18 | std::vector physicalRegisterCounts); 19 | 20 | /** Retrieve the current physical register assigned to the provided 21 | * architectural register. */ 22 | Register getMapping(Register architectural) const; 23 | 24 | /** Determine whether it's possible to allocate `quantity` physical registers 25 | * of type `type` this cycle. */ 26 | bool canAllocate(uint8_t type, unsigned int quantity) const; 27 | 28 | /** Check whether registers of type `type` can be renamed by this RAT. */ 29 | bool canRename(uint8_t type) const; 30 | 31 | /** Allocate a physical register for the provided architectural register. */ 32 | Register allocate(Register architectural); 33 | 34 | /** Get the number of free registers available for allocation this cycle. */ 35 | unsigned int freeRegistersAvailable(uint8_t type) const; 36 | 37 | /** Commit the provided physical register. This register now holds the 38 | * committed state of the corresponding architectural register, and previous 39 | * physical register is freed. */ 40 | void commit(Register physical); 41 | 42 | /** Rewind the allocation of a physical register. The former physical register 43 | * is reinstated to the mapping table, and the provided register is freed. */ 44 | void rewind(Register physical); 45 | 46 | private: 47 | /** The register mapping tables. Holds a map of architectural -> physical 48 | * register mappings for each register type. */ 49 | std::vector> mappingTable_; 50 | 51 | /** The register history tables. Each table holds an entry for each physical 52 | * register, recording the physical register formerly assigned to its 53 | * architectural register; one table is available per register type. */ 54 | std::vector> historyTable_; 55 | 56 | /** The register destination tables. Holds a map of physical -> architectural 57 | * register mappings for each register type. Used for rewind behaviour. */ 58 | std::vector> destinationTable_; 59 | 60 | /** The free register queues. Holds a list of unallocated physical registers 61 | * for each register type. */ 62 | std::vector> freeQueues_; 63 | }; 64 | 65 | } // namespace pipeline 66 | } // namespace simeng 67 | -------------------------------------------------------------------------------- /src/include/simeng/pipeline/RenameUnit.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "simeng/Instruction.hh" 4 | #include "simeng/pipeline/LoadStoreQueue.hh" 5 | #include "simeng/pipeline/PipelineBuffer.hh" 6 | #include "simeng/pipeline/RegisterAliasTable.hh" 7 | #include "simeng/pipeline/ReorderBuffer.hh" 8 | 9 | namespace simeng { 10 | namespace pipeline { 11 | 12 | /** A rename unit for an out-of-order pipelined processor. Renames the input 13 | * operands of instructions, allocates registers for destination operands, and 14 | * reserves slots in the Reorder Buffer. */ 15 | class RenameUnit { 16 | public: 17 | /** Construct a rename unit with a reference to input/output buffers, the 18 | * reorder buffer, and the register alias table. */ 19 | RenameUnit(PipelineBuffer>& input, 20 | PipelineBuffer>& output, 21 | ReorderBuffer& rob, RegisterAliasTable& rat, LoadStoreQueue& lsq, 22 | uint16_t registerTypes); 23 | 24 | /** Ticks this unit. Renames registers of instructions, and allocates ROB 25 | * space. */ 26 | void tick(); 27 | 28 | /** Retrieve the number of cycles this unit stalled due to an inability to 29 | * allocate enough destination registers. */ 30 | uint64_t getAllocationStalls() const; 31 | 32 | /** Retrieve the number of cycles this unit stalled due to insufficient ROB 33 | * space. */ 34 | uint64_t getROBStalls() const; 35 | 36 | /** Retrieve the number of cycles stalled due to insufficient load/store queue 37 | * space for a load operation. */ 38 | uint64_t getLoadQueueStalls() const; 39 | 40 | /** Retrieve the number of cycles stalled due to insufficient load/store queue 41 | * space for a store operation. */ 42 | uint64_t getStoreQueueStalls() const; 43 | 44 | private: 45 | /** A buffer of instructions to rename. */ 46 | PipelineBuffer>& input_; 47 | 48 | /** A buffer to write renamed instructions to. */ 49 | PipelineBuffer>& output_; 50 | 51 | /** The reorder buffer. */ 52 | ReorderBuffer& reorderBuffer_; 53 | 54 | /** The register alias table. */ 55 | RegisterAliasTable& rat_; 56 | 57 | /** A reference to the load/store queue. */ 58 | LoadStoreQueue& lsq_; 59 | 60 | /** A table recording the numbers of free physical registers for each register 61 | * file. */ 62 | std::vector freeRegistersAvailable_; 63 | 64 | /** The number of cycles stalled due to inability to allocate enough 65 | * destination registers. */ 66 | uint64_t allocationStalls_ = 0; 67 | 68 | /** The number of cycles stalled due to insufficient ROB space. */ 69 | uint64_t robStalls_ = 0; 70 | 71 | /** The number of cycles stalled due to insufficient load/store queue space 72 | * for a load operation. */ 73 | uint64_t lqStalls_ = 0; 74 | 75 | /** The number of cycles stalled due to insufficient load/store queue space 76 | * for a store operation. */ 77 | uint64_t sqStalls_ = 0; 78 | }; 79 | 80 | } // namespace pipeline 81 | } // namespace simeng 82 | -------------------------------------------------------------------------------- /src/include/simeng/pipeline/WritebackUnit.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "simeng/Instruction.hh" 6 | #include "simeng/RegisterFileSet.hh" 7 | #include "simeng/pipeline/PipelineBuffer.hh" 8 | 9 | namespace simeng { 10 | namespace pipeline { 11 | 12 | /** A writeback pipeline unit. Responsible for writing instruction results to 13 | * the register files. */ 14 | class WritebackUnit { 15 | public: 16 | /** Constructs a writeback unit with references to an input buffer and 17 | * register file to write to. */ 18 | WritebackUnit(std::vector>>& 19 | completionSlots, 20 | RegisterFileSet& registerFileSet, 21 | std::function flagMicroOpCommits); 22 | 23 | /** Tick the writeback unit to perform its operation for this cycle. */ 24 | void tick(); 25 | 26 | /** Retrieve a count of the number of instructions retired. */ 27 | uint64_t getInstructionsWrittenCount() const; 28 | 29 | private: 30 | /** Buffers of completed instructions to process. */ 31 | std::vector>>& completionSlots_; 32 | 33 | /** The register file set to write results into. */ 34 | RegisterFileSet& registerFileSet_; 35 | 36 | /** A function handle called to determine if uops associated to an instruction 37 | * ID can now be committed. */ 38 | std::function flagMicroOpCommits_; 39 | 40 | /** The number of instructions processed and retired by this stage. */ 41 | uint64_t instructionsWritten_ = 0; 42 | }; 43 | 44 | } // namespace pipeline 45 | } // namespace simeng 46 | -------------------------------------------------------------------------------- /src/include/simeng/span.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace simeng { 7 | 8 | /** A partial implementation of the C++20 `std::span`. Provides an iterable 9 | * wrapper around a pointer of type T, with a known number of elements. 10 | * 11 | * https://en.cppreference.com/w/cpp/container/span */ 12 | template 13 | class span { 14 | public: 15 | using pointer = T*; 16 | using value_type = T; 17 | using index_type = size_t; 18 | using reference = T&; 19 | using iterator = pointer; 20 | using const_iterator = const T*; 21 | using reverse_iterator = std::reverse_iterator; 22 | using const_reverse_iterator = std::reverse_iterator; 23 | 24 | constexpr span() noexcept : pointer_(nullptr), size_(0) {} 25 | constexpr span(pointer ptr, index_type count) : pointer_(ptr), size_(count) {} 26 | 27 | template 28 | constexpr span(std::array& arr) : pointer_(arr.data()), size_(N) {} 29 | 30 | constexpr index_type size() const { return size_; } 31 | [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; } 32 | 33 | constexpr iterator begin() const noexcept { return pointer_; } 34 | constexpr const_iterator cbegin() const noexcept { return begin(); } 35 | 36 | constexpr reverse_iterator rbegin() const noexcept { 37 | return reverse_iterator(end()); 38 | } 39 | constexpr const_reverse_iterator crbegin() const noexcept { 40 | return const_reverse_iterator(cend()); 41 | } 42 | 43 | constexpr iterator end() const noexcept { return pointer_ + size_; } 44 | constexpr const_iterator cend() const noexcept { return end(); } 45 | 46 | constexpr reverse_iterator rend() const noexcept { 47 | return reverse_iterator(begin()); 48 | } 49 | constexpr const_reverse_iterator crend() const noexcept { 50 | return const_reverse_iterator(cbegin()); 51 | } 52 | 53 | constexpr reference front() const { return pointer_[0]; } 54 | constexpr reference back() const { return pointer_[size_ - 1]; } 55 | constexpr pointer data() const noexcept { return pointer_; } 56 | 57 | constexpr reference operator[](index_type idx) const { 58 | return pointer_[idx]; 59 | }; 60 | 61 | private: 62 | T* pointer_; 63 | index_type size_; 64 | }; 65 | 66 | } // namespace simeng 67 | -------------------------------------------------------------------------------- /src/include/simeng/version.hh.in: -------------------------------------------------------------------------------- 1 | #ifndef VERSION_H_INCLUDED 2 | #define VERSION_H_INCLUDED 3 | 4 | #define SIMENG_VERSION "${SIMENG_VERSION}" 5 | #define SIMENG_COMPILE_OPTIONS "${SIMENG_COMPILE_OPTIONS}" 6 | #define SIMENG_COMPILE_DEFINITIONS "${SIMENG_COMPILE_DEFINITIONS}" 7 | #define SIMENG_BUILD_TYPE "${CMAKE_BUILD_TYPE}" 8 | #define SIMENG_SOURCE_DIR "${PROJECT_SOURCE_DIR}" 9 | #define SIMENG_LLVM_VERSION @SIMENG_LLVM_VERSION@ 10 | #define SIMENG_ENABLE_TESTS "${SIMENG_ENABLE_TESTS}" 11 | #define SIMENG_BUILD_DIR "${CMAKE_BINARY_DIR}" 12 | 13 | #endif -------------------------------------------------------------------------------- /src/lib/AlwaysNotTakenPredictor.cc: -------------------------------------------------------------------------------- 1 | #include "simeng/AlwaysNotTakenPredictor.hh" 2 | 3 | namespace simeng { 4 | 5 | BranchPrediction AlwaysNotTakenPredictor::predict( 6 | [[maybe_unused]] uint64_t address, BranchType type, int64_t knownOffset) { 7 | return {false, 0}; 8 | } 9 | 10 | void AlwaysNotTakenPredictor::update(uint64_t address, bool taken, 11 | uint64_t targetAddress, BranchType type) {} 12 | 13 | void AlwaysNotTakenPredictor::flush(uint64_t address) {} 14 | 15 | } // namespace simeng 16 | -------------------------------------------------------------------------------- /src/lib/ArchitecturalRegisterFileSet.cc: -------------------------------------------------------------------------------- 1 | #include "simeng/ArchitecturalRegisterFileSet.hh" 2 | 3 | namespace simeng { 4 | 5 | ArchitecturalRegisterFileSet::ArchitecturalRegisterFileSet( 6 | RegisterFileSet& physicalRegisterFileSet) 7 | : physicalRegisterFileSet_(physicalRegisterFileSet) {} 8 | 9 | const RegisterValue& ArchitecturalRegisterFileSet::get(Register reg) const { 10 | return physicalRegisterFileSet_.get(reg); 11 | } 12 | 13 | void ArchitecturalRegisterFileSet::set(Register reg, 14 | const RegisterValue& value) { 15 | return physicalRegisterFileSet_.set(reg, value); 16 | } 17 | 18 | } // namespace simeng 19 | -------------------------------------------------------------------------------- /src/lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SIMENG_SOURCES 2 | arch/aarch64/Architecture.cc 3 | arch/aarch64/ExceptionHandler.cc 4 | arch/aarch64/Instruction.cc 5 | arch/aarch64/Instruction_address.cc 6 | arch/aarch64/Instruction_decode.cc 7 | arch/aarch64/Instruction_execute.cc 8 | arch/aarch64/InstructionMetadata.cc 9 | arch/aarch64/MicroDecoder.cc 10 | arch/riscv/Architecture.cc 11 | arch/riscv/ExceptionHandler.cc 12 | arch/riscv/Instruction.cc 13 | arch/riscv/Instruction_address.cc 14 | arch/riscv/Instruction_decode.cc 15 | arch/riscv/Instruction_execute.cc 16 | arch/riscv/InstructionMetadata.cc 17 | config/ModelConfig.cc 18 | kernel/Linux.cc 19 | kernel/LinuxProcess.cc 20 | memory/FixedLatencyMemoryInterface.cc 21 | memory/FlatMemoryInterface.cc 22 | models/emulation/Core.cc 23 | models/inorder/Core.cc 24 | models/outoforder/Core.cc 25 | pipeline/A64FXPortAllocator.cc 26 | pipeline/BalancedPortAllocator.cc 27 | pipeline/M1PortAllocator.cc 28 | pipeline/DecodeUnit.cc 29 | pipeline/DispatchIssueUnit.cc 30 | pipeline/ExecuteUnit.cc 31 | pipeline/FetchUnit.cc 32 | pipeline/LoadStoreQueue.cc 33 | pipeline/MappedRegisterFileSet.cc 34 | pipeline/RegisterAliasTable.cc 35 | pipeline/RenameUnit.cc 36 | pipeline/ReorderBuffer.cc 37 | pipeline/WritebackUnit.cc 38 | AlwaysNotTakenPredictor.cc 39 | ArchitecturalRegisterFileSet.cc 40 | CMakeLists.txt 41 | CoreInstance.cc 42 | Elf.cc 43 | GenericPredictor.cc 44 | PerceptronPredictor.cc 45 | RegisterFileSet.cc 46 | RegisterValue.cc 47 | SpecialFileDirGen.cc 48 | ) 49 | 50 | configure_file(${capstone_SOURCE_DIR}/arch/AArch64/AArch64GenInstrInfo.inc AArch64GenInstrInfo.inc COPYONLY) 51 | configure_file(${capstone_SOURCE_DIR}/arch/RISCV/RISCVGenInstrInfo.inc RISCVGenInstrInfo.inc COPYONLY) 52 | 53 | add_library(libsimeng SHARED ${SIMENG_SOURCES} ${SIMENG_HEADERS}) 54 | set_target_properties(libsimeng PROPERTIES OUTPUT_NAME simeng) 55 | 56 | target_include_directories(libsimeng PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) 57 | target_include_directories(libsimeng PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) 58 | target_link_libraries(libsimeng capstone) 59 | # Only enable compiler warnings for our code 60 | target_compile_options(libsimeng PRIVATE ${SIMENG_COMPILE_OPTIONS}) 61 | 62 | set_target_properties(libsimeng PROPERTIES VERSION ${SimEng_VERSION}) 63 | set_target_properties(libsimeng PROPERTIES SOVERSION ${SimEng_VERSION_MAJOR}) 64 | 65 | install(TARGETS libsimeng DESTINATION lib) 66 | 67 | # Store build metadata in version.hh 68 | get_target_property(SIMENG_COMPILE_OPTIONS libsimeng COMPILE_OPTIONS) 69 | get_target_property(SIMENG_COMPILE_DEFINITIONS libsimeng COMPILE_DEFINITIONS) 70 | get_target_property(SIMENG_VERSION libsimeng VERSION) 71 | # TODO move this to build folder. Cmake may not reconfigure when rebuilding after a change, therefor version.hh and cmake options can differ. This is happens especially when using an IDEs when changing build types 72 | configure_file(${PROJECT_SOURCE_DIR}/src/include/simeng/version.hh.in ${PROJECT_SOURCE_DIR}/src/include/simeng/version.hh) 73 | -------------------------------------------------------------------------------- /src/lib/RegisterFileSet.cc: -------------------------------------------------------------------------------- 1 | #include "simeng/RegisterFileSet.hh" 2 | 3 | #include 4 | 5 | namespace simeng { 6 | 7 | RegisterFileSet::RegisterFileSet( 8 | std::vector registerFileStructures) 9 | : registerFiles(registerFileStructures.size()) { 10 | for (size_t type = 0; type < registerFileStructures.size(); type++) { 11 | const auto& structure = registerFileStructures[type]; 12 | registerFiles[type] = std::vector( 13 | structure.quantity, RegisterValue(0, structure.bytes)); 14 | } 15 | } 16 | 17 | const RegisterValue& RegisterFileSet::get(Register reg) const { 18 | return registerFiles[reg.type][reg.tag]; 19 | } 20 | 21 | void RegisterFileSet::set(Register reg, const RegisterValue& value) { 22 | assert(value.size() != 0 && 23 | "Attempted to write an zero sized value to a register"); 24 | assert(value.size() == registerFiles[reg.type][reg.tag].size() && 25 | "Attempted to write an incorrectly sized value to a register"); 26 | registerFiles[reg.type][reg.tag] = value; 27 | } 28 | 29 | } // namespace simeng 30 | -------------------------------------------------------------------------------- /src/lib/RegisterValue.cc: -------------------------------------------------------------------------------- 1 | #include "simeng/RegisterValue.hh" 2 | 3 | #include 4 | 5 | namespace simeng { 6 | 7 | Pool pool = Pool(); 8 | 9 | RegisterValue::RegisterValue() : bytes(0) {} 10 | 11 | RegisterValue::operator bool() const { return (bytes > 0); } 12 | 13 | RegisterValue RegisterValue::zeroExtend(uint16_t fromBytes, 14 | uint16_t toBytes) const { 15 | assert(bytes > 0 && "Attempted to extend an uninitialised RegisterValue"); 16 | assert(fromBytes <= bytes && 17 | "Attempted to copy more data from a RegisterValue than it held"); 18 | 19 | auto extended = RegisterValue(0, toBytes); 20 | 21 | // Get the appropriate source/destination pointers and copy the data 22 | const char* src = (isLocal() ? value : ptr.get()); 23 | char* dest = (extended.isLocal() ? extended.value : extended.ptr.get()); 24 | 25 | std::memcpy(dest, src, fromBytes); 26 | 27 | return extended; 28 | } 29 | 30 | } // namespace simeng 31 | -------------------------------------------------------------------------------- /src/lib/memory/FixedLatencyMemoryInterface.cc: -------------------------------------------------------------------------------- 1 | #include "simeng/memory/FixedLatencyMemoryInterface.hh" 2 | 3 | #include 4 | 5 | namespace simeng { 6 | 7 | namespace memory { 8 | 9 | FixedLatencyMemoryInterface::FixedLatencyMemoryInterface(char* memory, 10 | size_t size, 11 | uint16_t latency) 12 | : memory_(memory), size_(size), latency_(latency) {} 13 | 14 | void FixedLatencyMemoryInterface::tick() { 15 | tickCounter_++; 16 | 17 | while (pendingRequests_.size() > 0) { 18 | const auto& request = pendingRequests_.front(); 19 | 20 | if (request.readyAt > tickCounter_) { 21 | // Head of queue isn't ready yet; end cycle 22 | break; 23 | } 24 | 25 | const auto& target = request.target; 26 | 27 | if (request.write) { 28 | // Write: write data directly to memory 29 | if (target.address + target.size > size_) { 30 | std::cerr << "[SimEng:FixedLatencyMemoryInterface] Attempted to write " 31 | "beyond memory limit." 32 | << std::endl; 33 | exit(1); 34 | } 35 | 36 | auto ptr = memory_ + target.address; 37 | // Copy the data from the RegisterValue to memory 38 | memcpy(ptr, request.data.getAsVector(), target.size); 39 | } else { 40 | // Read: read data into `completedReads` 41 | if (target.address + target.size > size_ || 42 | unsignedOverflow_(target.address, target.size)) { 43 | // Read outside of memory; return an invalid value to signal a fault 44 | completedReads_.push_back({target, RegisterValue(), request.requestId}); 45 | } else { 46 | const char* ptr = memory_ + target.address; 47 | 48 | // Copy the data at the requested memory address into a RegisterValue 49 | completedReads_.push_back( 50 | {target, RegisterValue(ptr, target.size), request.requestId}); 51 | } 52 | } 53 | 54 | // Remove the request from the queue 55 | pendingRequests_.pop(); 56 | } 57 | } 58 | 59 | void FixedLatencyMemoryInterface::requestRead(const MemoryAccessTarget& target, 60 | uint64_t requestId) { 61 | pendingRequests_.push({target, tickCounter_ + latency_, requestId}); 62 | } 63 | 64 | void FixedLatencyMemoryInterface::requestWrite(const MemoryAccessTarget& target, 65 | const RegisterValue& data) { 66 | pendingRequests_.push({target, data, tickCounter_ + latency_}); 67 | } 68 | 69 | const span FixedLatencyMemoryInterface::getCompletedReads() 70 | const { 71 | return {const_cast(completedReads_.data()), 72 | completedReads_.size()}; 73 | } 74 | 75 | void FixedLatencyMemoryInterface::clearCompletedReads() { 76 | completedReads_.clear(); 77 | } 78 | 79 | bool FixedLatencyMemoryInterface::hasPendingRequests() const { 80 | return !pendingRequests_.empty(); 81 | } 82 | 83 | } // namespace memory 84 | } // namespace simeng 85 | -------------------------------------------------------------------------------- /src/lib/memory/FlatMemoryInterface.cc: -------------------------------------------------------------------------------- 1 | #include "simeng/memory/FlatMemoryInterface.hh" 2 | 3 | #include 4 | 5 | namespace simeng { 6 | 7 | namespace memory { 8 | 9 | FlatMemoryInterface::FlatMemoryInterface(char* memory, size_t size) 10 | : memory_(memory), size_(size) {} 11 | 12 | void FlatMemoryInterface::requestRead(const MemoryAccessTarget& target, 13 | uint64_t requestId) { 14 | if (target.address + target.size > size_) { 15 | // Read outside of memory; return an invalid value to signal a fault 16 | completedReads_.push_back({target, RegisterValue(), requestId}); 17 | return; 18 | } 19 | 20 | const char* ptr = memory_ + target.address; 21 | 22 | // Copy the data at the requested memory address into a RegisterValue 23 | completedReads_.push_back( 24 | {target, RegisterValue(ptr, target.size), requestId}); 25 | } 26 | 27 | void FlatMemoryInterface::requestWrite(const MemoryAccessTarget& target, 28 | const RegisterValue& data) { 29 | if (target.address + target.size > size_) { 30 | std::cerr << "[SimEng:FlatLatencyMemoryInterface] Attempted to write " 31 | "beyond memory limit." 32 | << std::endl; 33 | exit(1); 34 | } 35 | 36 | auto ptr = memory_ + target.address; 37 | // Copy the data from the RegisterValue to memory 38 | memcpy(ptr, data.getAsVector(), target.size); 39 | } 40 | 41 | const span FlatMemoryInterface::getCompletedReads() const { 42 | return {const_cast(completedReads_.data()), 43 | completedReads_.size()}; 44 | } 45 | 46 | void FlatMemoryInterface::clearCompletedReads() { completedReads_.clear(); } 47 | 48 | bool FlatMemoryInterface::hasPendingRequests() const { return false; } 49 | 50 | void FlatMemoryInterface::tick() {} 51 | 52 | } // namespace memory 53 | } // namespace simeng 54 | -------------------------------------------------------------------------------- /src/lib/pipeline/BalancedPortAllocator.cc: -------------------------------------------------------------------------------- 1 | #include "simeng/pipeline/BalancedPortAllocator.hh" 2 | 3 | #include 4 | 5 | namespace simeng { 6 | namespace pipeline { 7 | 8 | BalancedPortAllocator::BalancedPortAllocator( 9 | const std::vector>& portArrangement) 10 | : weights(portArrangement.size(), 0) {} 11 | 12 | uint16_t BalancedPortAllocator::allocate(const std::vector& ports) { 13 | assert(ports.size() && 14 | "No supported ports supplied; cannot allocate from a empty set"); 15 | bool foundPort = false; 16 | uint16_t bestWeight = 0xFFFF; 17 | uint16_t bestPort = 0; 18 | for (const auto& portIndex : ports) { 19 | // Search for the lowest-weighted port available 20 | if (!foundPort || weights[portIndex] < bestWeight) { 21 | foundPort = true; 22 | bestWeight = weights[portIndex]; 23 | bestPort = portIndex; 24 | } 25 | } 26 | 27 | assert(foundPort && "Unsupported group; cannot allocate a port"); 28 | 29 | // Increment the weight of the allocated port 30 | weights[bestPort]++; 31 | return bestPort; 32 | } 33 | 34 | void BalancedPortAllocator::issued(uint16_t port) { 35 | assert(weights[port] > 0); 36 | weights[port]--; 37 | } 38 | void BalancedPortAllocator::deallocate(uint16_t port) { issued(port); } 39 | 40 | void BalancedPortAllocator::setRSSizeGetter( 41 | std::function&)> rsSizes) { 42 | rsSizes_ = rsSizes; 43 | } 44 | 45 | void BalancedPortAllocator::tick() {} 46 | 47 | } // namespace pipeline 48 | } // namespace simeng 49 | -------------------------------------------------------------------------------- /src/lib/pipeline/M1PortAllocator.cc: -------------------------------------------------------------------------------- 1 | #include "simeng/pipeline/M1PortAllocator.hh" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace simeng { 8 | namespace pipeline { 9 | 10 | M1PortAllocator::M1PortAllocator( 11 | const std::vector>& portArrangement, 12 | std::vector> rsArrangement) 13 | : weights(portArrangement.size(), 0), rsArrangement_(rsArrangement) {} 14 | 15 | uint16_t M1PortAllocator::allocate(const std::vector& ports) { 16 | assert(ports.size() && 17 | "No supported ports supplied; cannot allocate from a empty set"); 18 | bool foundPort = false; 19 | uint16_t bestPort = 0; 20 | uint16_t bestWeight = 0xFFFF; 21 | 22 | uint16_t bestRSQueueSize = 0xFFFF; 23 | // Only used in assertions so produces warning in release mode 24 | [[maybe_unused]] bool foundRS = false; 25 | 26 | // Update the reference for number of free spaces in the reservation 27 | // stations 28 | rsFreeSpaces.clear(); 29 | rsSizes_(rsFreeSpaces); 30 | 31 | for (const auto& portIndex : ports) { 32 | auto rsIndex = rsArrangement_[portIndex].first; 33 | auto rsSize = rsArrangement_[portIndex].second; 34 | auto rsFreeSpace = rsFreeSpaces[rsIndex]; 35 | auto rsQueueSize = (rsSize - rsFreeSpace); 36 | 37 | if (rsQueueSize < bestRSQueueSize) { 38 | bestRSQueueSize = rsQueueSize; 39 | foundRS = true; 40 | 41 | // Search for the lowest-weighted port available 42 | if (!foundPort || weights[portIndex] < bestWeight) { 43 | foundPort = true; 44 | bestWeight = weights[portIndex]; 45 | bestPort = portIndex; 46 | } 47 | } 48 | } 49 | 50 | assert(foundPort && foundRS && "Unsupported group; cannot allocate a port"); 51 | 52 | // Increment the weight of the allocated port 53 | weights[bestPort]++; 54 | return bestPort; 55 | } 56 | 57 | void M1PortAllocator::issued(uint16_t port) { 58 | assert(weights[port] > 0); 59 | weights[port]--; 60 | } 61 | 62 | void M1PortAllocator::deallocate(uint16_t port) { issued(port); } 63 | 64 | void M1PortAllocator::setRSSizeGetter( 65 | std::function&)> rsSizes) { 66 | rsSizes_ = rsSizes; 67 | } 68 | 69 | void M1PortAllocator::tick() {} 70 | } // namespace pipeline 71 | } // namespace simeng 72 | -------------------------------------------------------------------------------- /src/lib/pipeline/MappedRegisterFileSet.cc: -------------------------------------------------------------------------------- 1 | #include "simeng/pipeline/MappedRegisterFileSet.hh" 2 | 3 | namespace simeng { 4 | namespace pipeline { 5 | 6 | MappedRegisterFileSet::MappedRegisterFileSet( 7 | RegisterFileSet& physicalRegisterFileSet, const RegisterAliasTable& rat) 8 | : ArchitecturalRegisterFileSet(physicalRegisterFileSet), rat_(rat) {} 9 | 10 | const RegisterValue& MappedRegisterFileSet::get(Register reg) const { 11 | return ArchitecturalRegisterFileSet::get(rat_.getMapping(reg)); 12 | } 13 | 14 | void MappedRegisterFileSet::set(Register reg, const RegisterValue& value) { 15 | return ArchitecturalRegisterFileSet::set(rat_.getMapping(reg), value); 16 | } 17 | 18 | } // namespace pipeline 19 | } // namespace simeng 20 | -------------------------------------------------------------------------------- /src/lib/pipeline/WritebackUnit.cc: -------------------------------------------------------------------------------- 1 | #include "simeng/pipeline/WritebackUnit.hh" 2 | 3 | #include 4 | 5 | namespace simeng { 6 | namespace pipeline { 7 | 8 | WritebackUnit::WritebackUnit( 9 | std::vector>>& completionSlots, 10 | RegisterFileSet& registerFileSet, 11 | std::function flagMicroOpCommits) 12 | : completionSlots_(completionSlots), 13 | registerFileSet_(registerFileSet), 14 | flagMicroOpCommits_(flagMicroOpCommits) {} 15 | 16 | void WritebackUnit::tick() { 17 | for (size_t slot = 0; slot < completionSlots_.size(); slot++) { 18 | auto& uop = completionSlots_[slot].getHeadSlots()[0]; 19 | 20 | if (uop == nullptr) { 21 | continue; 22 | } 23 | 24 | auto& results = uop->getResults(); 25 | auto& destinations = uop->getDestinationRegisters(); 26 | for (size_t i = 0; i < results.size(); i++) { 27 | // Write results to register file 28 | registerFileSet_.set(destinations[i], results[i]); 29 | } 30 | if (uop->isMicroOp()) { 31 | uop->setWaitingCommit(); 32 | flagMicroOpCommits_(uop->getInstructionId()); 33 | if (uop->isLastMicroOp()) instructionsWritten_++; 34 | } else { 35 | uop->setCommitReady(); 36 | instructionsWritten_++; 37 | } 38 | 39 | completionSlots_[slot].getHeadSlots()[0] = nullptr; 40 | } 41 | } 42 | 43 | uint64_t WritebackUnit::getInstructionsWrittenCount() const { 44 | return instructionsWritten_; 45 | } 46 | 47 | } // namespace pipeline 48 | } // namespace simeng 49 | -------------------------------------------------------------------------------- /src/tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(simeng) 2 | -------------------------------------------------------------------------------- /src/tools/simeng/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(simeng main.cc) 2 | 3 | # Setting the YAML_OUTPUT macro prints out build metadata and core statistics 4 | # in YAML format at the end of the program. 5 | if( YAML_OUTPUT ) 6 | target_compile_definitions(simeng PRIVATE -DYAML_OUTPUT) 7 | endif() 8 | 9 | target_include_directories(simeng PUBLIC ${PROJECT_SOURCE_DIR}/src/lib) 10 | target_link_libraries(simeng libsimeng) 11 | 12 | install(TARGETS simeng DESTINATION bin) -------------------------------------------------------------------------------- /sst/config/L1-example-config.py: -------------------------------------------------------------------------------- 1 | import sst 2 | 3 | DEBUG_L1 = 0 4 | DEBUG_MEM = 0 5 | DEBUG_LEVEL = 0 6 | 7 | clw = "64" 8 | 9 | # Define the simulation components 10 | cpu = sst.Component("core", "sstsimeng.simengcore") 11 | cpu.addParams({ 12 | "simeng_config_path": "", 13 | "executable_path": "", 14 | "executable_args": "", 15 | "clock" : "1GHz", 16 | "max_addr_memory": 2*1024*1024*1024-1, 17 | "cache_line_width": clw, 18 | "source": "", 19 | "assemble_with_source": False, 20 | "heap": "", 21 | "debug": False 22 | }) 23 | 24 | iface = cpu.setSubComponent("memory", "memHierarchy.standardInterface") 25 | 26 | l1cache = sst.Component("l1cache.mesi", "memHierarchy.Cache") 27 | l1cache.addParams({ 28 | "access_latency_cycles" : "2", 29 | "cache_frequency" : "2Ghz", 30 | "replacement_policy" : "nmru", 31 | "coherence_protocol" : "MESI", 32 | "associativity" : "4", 33 | "cache_line_size" : clw, 34 | "debug" : DEBUG_L1, 35 | "debug_level" : DEBUG_LEVEL, 36 | "L1" : "1", 37 | "cache_size" : "200KiB" 38 | }) 39 | 40 | # Explicitly set the link subcomponents instead of having cache figure them out based on connected port names 41 | l1toC = l1cache.setSubComponent("cpulink", "memHierarchy.MemLink") 42 | l1toM = l1cache.setSubComponent("memlink", "memHierarchy.MemLink") 43 | 44 | # Memory controller 45 | memctrl = sst.Component("memory", "memHierarchy.MemController") 46 | memctrl.addParams({ 47 | "clock" : "1GHz", 48 | "request_width" : "64", 49 | "debug" : DEBUG_MEM, 50 | "debug_level" : DEBUG_LEVEL, 51 | "addr_range_end" : 2*1024*1024*1024-1, 52 | }) 53 | Mtol1 = memctrl.setSubComponent("cpulink", "memHierarchy.MemLink") 54 | 55 | # Memory model 56 | memory = memctrl.setSubComponent("backend", "memHierarchy.simpleMem") 57 | memory.addParams({ 58 | "access_time" : "1ns", 59 | "mem_size" : "2GiB", 60 | "request_width": "64" 61 | }) 62 | 63 | # Define the simulation links 64 | link_cpu_cache_link = sst.Link("link_cpu_cache_link") 65 | link_cpu_cache_link.connect( (iface, "port", "100ps"), (l1toC, "port", "100ps") ) 66 | link_mem_bus_link = sst.Link("link_mem_bus_link") 67 | link_mem_bus_link.connect( (l1toM, "port", "50ps"), (Mtol1, "port", "50ps") ) 68 | 69 | -------------------------------------------------------------------------------- /sst/config/L1L2-example-config.py: -------------------------------------------------------------------------------- 1 | import sst 2 | import sys 3 | 4 | DEBUG_L1 = 0 5 | DEBUG_MEM = 0 6 | DEBUG_LEVEL = 10 7 | 8 | clw = "64" 9 | 10 | # Define the simulation components 11 | cpu = sst.Component("core", "sstsimeng.simengcore") 12 | cpu.addParams({ 13 | "simeng_config_path": "", 14 | "executable_path": "", 15 | "executable_args": "", 16 | "clock" : "2GHz", 17 | "max_addr_memory": 2*1024*1024*1024-1, 18 | "cache_line_width": clw, 19 | "source": "", 20 | "assemble_with_source": False, 21 | "heap": "", 22 | "debug": False 23 | }) 24 | 25 | iface = cpu.setSubComponent("memory", "memHierarchy.standardInterface") 26 | 27 | l1cache = sst.Component("l1cache.msi", "memHierarchy.Cache") 28 | l1cache.addParams({ 29 | "access_latency_cycles" : "4", 30 | "cache_frequency" : "2Ghz", 31 | "replacement_policy" : "lru", 32 | "coherence_protocol" : "MSI", 33 | "associativity" : "4", 34 | "cache_line_size" : clw, 35 | "cache_size" : "1KiB", 36 | "L1" : "1", 37 | "debug" : DEBUG_L1, 38 | "debug_level" : DEBUG_LEVEL, 39 | "verbose": "2" 40 | }) 41 | l2cache = sst.Component("l2cache.msi.inclus", "memHierarchy.Cache") 42 | l2cache.addParams({ 43 | "access_latency_cycles" : "10", 44 | "cache_frequency" : "1.8Ghz", 45 | "replacement_policy" : "lru", 46 | "coherence_protocol" : "MSI", 47 | "associativity" : "8", 48 | "cache_line_size" : clw, 49 | "cache_size" : "16 KiB", 50 | "debug_level" : "10", 51 | "debug": "1" 52 | }) 53 | memctrl = sst.Component("memory", "memHierarchy.MemController") 54 | memctrl.addParams({ 55 | "clock" : "1GHz", 56 | "backend.access_time" : "100 ns", 57 | "debug" : DEBUG_MEM, 58 | "debug_level" : DEBUG_LEVEL, 59 | "addr_range_end" : 2*1024*1024*1024-1, 60 | }) 61 | 62 | memory = memctrl.setSubComponent("backend", "memHierarchy.simpleMem") 63 | memory.addParams({ 64 | "access_time" : "100 ns", 65 | "mem_size" : "2GiB", 66 | }) 67 | 68 | 69 | # Define the simulation links 70 | link_cpu_l1cache = sst.Link("link_cpu_l1cache_link") 71 | link_cpu_l1cache.connect( (iface, "port", "10ps"), (l1cache, "high_network_0", "10ps") ) 72 | link_l1cache_l2cache = sst.Link("link_l1cache_l2cache_link") 73 | link_l1cache_l2cache.connect( (l1cache, "low_network_0", "100ps"), (l2cache, "high_network_0", "100ps") ) 74 | link_mem_bus = sst.Link("link_mem_bus_link") 75 | link_mem_bus.connect( (l2cache, "low_network_0", "100ps"), (memctrl, "direct_link", "100ps") ) 76 | -------------------------------------------------------------------------------- /sst/include/Assemble.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "simeng/version.hh" 9 | 10 | namespace SST { 11 | 12 | namespace SSTSimEng { 13 | 14 | class Assembler { 15 | private: 16 | /** The flat binary produced by assembling the test source. */ 17 | uint8_t* code_ = nullptr; 18 | 19 | /** The size of the assembled flat binary in bytes. */ 20 | size_t codeSize_ = 0; 21 | 22 | /** Assemble test source to a flat binary for the given triple. */ 23 | void assemble(const char* source, const char* triple); 24 | 25 | public: 26 | /** Constructor for Assembler class which takes in source code. */ 27 | Assembler(std::string source); 28 | ~Assembler(); 29 | 30 | /** Returns the assembled source as a char array. */ 31 | uint8_t* getAssembledSource(); 32 | 33 | /** Returns the size of the assembled source. */ 34 | size_t getAssembledSourceSize(); 35 | }; 36 | 37 | } // namespace SSTSimEng 38 | } // namespace SST -------------------------------------------------------------------------------- /sst/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SIMENG_SST_TEST_SOURCES 2 | main.cc 3 | test_files/tg0_llvm_assemble.cc 4 | test_files/tg1_load_store.cc 5 | test_files/tg2_cache_access.cc 6 | test_files/tg3_request_split.cc 7 | test_files/tg4_request_misaligned.cc 8 | ) 9 | add_executable(sstsimengtest ${SIMENG_SST_TEST_SOURCES}) 10 | 11 | add_compile_options(-Wall) 12 | 13 | set(SSTTESTDIR ${CMAKE_CURRENT_SOURCE_DIR}) 14 | if(SST_TEST_CMD) 15 | 16 | target_compile_definitions( 17 | sstsimengtest PUBLIC 18 | SST_INSTALL_DIR="${SST_INSTALL_DIR}" 19 | SST_TEST_CMD="${SST_TEST_CMD}" 20 | SST_TEST_DIR="${SSTTESTDIR}" 21 | SST_TESTS_MODEL_CONFIG_PATH="${SST_TESTS_MODEL_CONFIG_PATH}" 22 | ) 23 | 24 | else() 25 | target_compile_definitions( 26 | sstsimengtest PUBLIC 27 | SST_INSTALL_DIR="${SST_INSTALL_DIR}" 28 | SST_TEST_DIR="${SSTTESTDIR}" 29 | SST_TESTS_MODEL_CONFIG_PATH="${SST_TESTS_MODEL_CONFIG_PATH}" 30 | ) 31 | endif() 32 | 33 | target_include_directories(libsimeng PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) 34 | target_include_directories(sstsimengtest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) 35 | 36 | add_test(NAME simeng_sst_tests COMMAND sstsimengtest) 37 | -------------------------------------------------------------------------------- /sst/test/include/framework/context.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | using TestFunc = std::function; 7 | 8 | /** 9 | * This struct stores the filename and line number of the 10 | * TEST_CASE. 11 | */ 12 | struct SourceInfo { 13 | SourceInfo(const char* fl_name, uint64_t ln_num) 14 | : fname_(fl_name), lnum_(ln_num){}; 15 | SourceInfo() : fname_(NULL), lnum_(0){}; 16 | 17 | const char* fname_; 18 | uint64_t lnum_; 19 | }; 20 | 21 | /** 22 | * This class represents a test case, it stores all contextual information 23 | * regarding the test case. The TEST_CASE Macro ultimately leads to the creation 24 | * of a TestContext. TextContext(s) are run inside Runner(s). 25 | */ 26 | class TestContext { 27 | private: 28 | /** The name of the test case passed using the TEST_CASE macro. */ 29 | std::string tname_; 30 | /** The function which contains all the testable logic. */ 31 | TestFunc tfn_; 32 | /** The source of the test case. */ 33 | SourceInfo tsinfo_; 34 | 35 | public: 36 | /** Constructor used to a TestContext by the TEST_CASE macro. */ 37 | TestContext(TestFunc fn, const SourceInfo& info, std::string tname) { 38 | tsinfo_ = info; 39 | tname_ = tname; 40 | tfn_ = fn; 41 | }; 42 | 43 | /** Constructor used to create an empty TestContext. */ 44 | TestContext() { 45 | tsinfo_ = SourceInfo{}; 46 | tname_ = ""; 47 | }; 48 | /** Returns the name of the test case. */ 49 | std::string getTestCaseName() const { return tname_; } 50 | 51 | /** Returns the name of the file the test is written in. */ 52 | std::string getTestCaseSrcFile() const { return std::string(tsinfo_.fname_); } 53 | 54 | /** Returns the TestFunc of the test case. */ 55 | TestFunc getTestCaseFn() const { return tfn_; } 56 | 57 | /** Returns the line where the test exists. */ 58 | uint64_t getTestCaseLineNum() const { return tsinfo_.lnum_; } 59 | }; 60 | -------------------------------------------------------------------------------- /sst/test/include/framework/macros/util.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // The MACRO concatenates any two arguments. 4 | #define CONCAT(A, B) A##B 5 | // The MACRO converts any argument into a string. 6 | #define STRINGIFY(STR) #STR 7 | -------------------------------------------------------------------------------- /sst/test/include/framework/stats.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "framework/output.hh" 6 | 7 | /** 8 | * A Singleton class which records total number of tests, total failures and 9 | * total successes. 10 | */ 11 | class Stats { 12 | private: 13 | Stats() {} 14 | /** Total number of test cases. */ 15 | uint64_t testCount_ = 0; 16 | /** Total number of test case failures. */ 17 | uint64_t failures_ = 0; 18 | /** Total number test case successes. */ 19 | uint64_t success_ = 0; 20 | 21 | public: 22 | /** This method returns the singleton instance of the Stats class. */ 23 | static std::unique_ptr& getInstance() { 24 | static std::unique_ptr ptr; 25 | if (ptr == nullptr) { 26 | ptr = std::unique_ptr(new Stats()); 27 | } 28 | return ptr; 29 | } 30 | /** This method increments the total test count. */ 31 | void recordTest() { testCount_++; } 32 | /** This method increments the total failure count. */ 33 | void recordFailure() { failures_++; } 34 | /** This method increments the total sucess count. */ 35 | void recordSuccess() { success_++; } 36 | /** This method returns the total test count. */ 37 | uint64_t getTestCount() { return testCount_; } 38 | /** This method returns the total failure count. */ 39 | uint64_t getFailureCount() { return failures_; } 40 | /** This method returns the total success count. */ 41 | uint64_t getSuccessCount() { return success_; } 42 | /** This method prints the all statistics stored by the Stats class. */ 43 | void printStats() { 44 | Output output; 45 | output.output("", 0, Formatter::bold("\nStats:")); 46 | output.output(" ", 4, 47 | "Total Tests:", Formatter::blue(std::to_string(testCount_))); 48 | output.output(" ", 4, "Tests Passed:", 49 | Formatter::bright_green(std::to_string(success_))); 50 | output.output(" ", 4, "Tests Failed:", 51 | Formatter::bright_red(std::to_string(failures_))); 52 | } 53 | }; -------------------------------------------------------------------------------- /sst/test/include/framework/uid.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "framework/output.hh" 8 | /** 9 | * This Singleton class ensures that all TEST_GROUP(s) have unique names, and 10 | * all TEST_CASE(s) in a TEST_GROUP have unique names. 11 | */ 12 | class UidRegistry { 13 | private: 14 | UidRegistry(){}; 15 | /** This method checks if an id is unique. */ 16 | static bool isUnique(std::string id) { 17 | std::unique_ptr>& uidReg = getUidReg(); 18 | auto itr = uidReg->find(id); 19 | if (itr != uidReg->end()) { 20 | return false; 21 | } 22 | uidReg->insert(id); 23 | return true; 24 | } 25 | /** This method returns the singleton reference to the UidRegistry class. */ 26 | static std::unique_ptr>& getUidReg() { 27 | static std::unique_ptr> set; 28 | if (set == nullptr) { 29 | set = std::unique_ptr>(new std::set()); 30 | } 31 | return set; 32 | } 33 | 34 | public: 35 | /** This method validates the uniqueness of a TEST_GROUP name.*/ 36 | static void validateGroupName(std::string gname, std::string fname, 37 | uint64_t line) { 38 | Output output; 39 | if (!isUnique(gname)) { 40 | output.output(" ", 0, 41 | Formatter::bold_bright_red("Duplicate TestGroup name:"), 42 | Formatter::bold("\"" + gname + "\"")); 43 | output.output("", 4, Formatter::bold("Source: "), fname, ':', line); 44 | exit(EXIT_FAILURE); 45 | } 46 | } 47 | /** 48 | * This method validates the uniqueness of the TEST_CASE name within a 49 | * TEST_GROUP. 50 | */ 51 | static void validateTestName(std::string gname, std::string tname, 52 | std::string fname, uint64_t line) { 53 | Output output; 54 | std::string uid = gname + '.' + tname; 55 | if (!isUnique(uid)) { 56 | output.output( 57 | " ", 0, 58 | Formatter::bold_bright_red("Duplicate TestCase name in TestGroup:"), 59 | Formatter::bold("\"" + gname + "\""), "-", 60 | Formatter::bold("\"" + tname + "\"")); 61 | output.output("", 4, Formatter::bold("Source: "), fname, ':', line); 62 | exit(EXIT_FAILURE); 63 | } 64 | } 65 | }; -------------------------------------------------------------------------------- /sst/test/include/sstsimengtest.hh: -------------------------------------------------------------------------------- 1 | #include "framework/macros/eval.hh" 2 | #include "framework/macros/group.hh" 3 | #include "framework/parser.hh" 4 | #include "framework/registry.hh" 5 | #include "framework/runner.hh" 6 | #include "framework/stats.hh" 7 | 8 | inline std::string appendBinDirPath(std::string binName) { 9 | return ("execBin=" + std::string(SST_TEST_DIR) + "/sstbinaries/" + binName); 10 | } -------------------------------------------------------------------------------- /sst/test/main.cc: -------------------------------------------------------------------------------- 1 | #include "sstsimengtest.hh" 2 | 3 | int main(void) { 4 | Registry* reg = Registry::getInstance(); 5 | auto map = reg->getMap(); 6 | for (auto itr = map->begin(); itr != map->end(); itr++) { 7 | std::vector fvec = itr->second; 8 | for (auto itrr = fvec.begin(); itrr != fvec.end(); itrr++) { 9 | std::unique_ptr rn = (*itrr)(); 10 | rn->run(); 11 | } 12 | } 13 | auto& stats = Stats::getInstance(); 14 | stats->printStats(); 15 | if (stats->getFailureCount() > 0) { 16 | exit(EXIT_FAILURE); 17 | } 18 | return 0; 19 | }; -------------------------------------------------------------------------------- /sst/test/sstconfigs/fastL1WithParams_config.py: -------------------------------------------------------------------------------- 1 | import sst 2 | import sys 3 | import os 4 | 5 | DEBUG_L1 = 0 6 | DEBUG_MEM = 0 7 | DEBUG_LEVEL = 10 8 | 9 | def split(param: str) -> list[str]: 10 | return param.split("=") 11 | 12 | def parseParams(params: list[str]): 13 | out = { 14 | "withSrc": False, 15 | "source": "", 16 | "clw": 8, 17 | "heap": "", 18 | "model": "", 19 | "args": "", 20 | "execBin": "" 21 | } 22 | for param in params: 23 | key, value = split(param) 24 | if (key == "withSrc"): 25 | out[key] = value == "True" 26 | else: 27 | out[key] = value 28 | return out 29 | 30 | DEBUG_L1 = 0 31 | DEBUG_MEM = 0 32 | DEBUG_LEVEL = 10 33 | 34 | params = parseParams(sys.argv[1:]) 35 | 36 | cpu = sst.Component("core", "sstsimeng.simengcore") 37 | cpu.addParams({ 38 | "simeng_config_path": params["model"], 39 | "executable_path": "", 40 | "executable_args": "", 41 | "clock" : "1.8GHz", 42 | "max_addr_memory": 2*1024*1024*1024-1, 43 | "cache_line_width": params["clw"], 44 | "source": params["source"], 45 | "assemble_with_source": params["withSrc"], 46 | "heap": params["heap"], 47 | "debug": True 48 | }) 49 | 50 | iface = cpu.setSubComponent("memory", "memHierarchy.standardInterface") 51 | 52 | l1cache = sst.Component("l1cache.mesi", "memHierarchy.Cache") 53 | l1cache.addParams({ 54 | "access_latency_cycles" : "2", 55 | "cache_frequency" : "1.8Ghz", 56 | "replacement_policy" : "nmru", 57 | "coherence_protocol" : "MESI", 58 | "associativity" : "4", 59 | "cache_line_size" : params["clw"], 60 | "debug" : DEBUG_L1, 61 | "debug_level" : DEBUG_LEVEL, 62 | "verbose": "2", 63 | "L1" : "1", 64 | "cache_size" : "64KiB" 65 | }) 66 | 67 | # Explicitly set the link subcomponents instead of having cache figure them out based on connected port names 68 | l1toC = l1cache.setSubComponent("cpulink", "memHierarchy.MemLink") 69 | l1toM = l1cache.setSubComponent("memlink", "memHierarchy.MemLink") 70 | 71 | # Memory controller 72 | memctrl = sst.Component("memory", "memHierarchy.MemController") 73 | memctrl.addParams({ 74 | "clock" : "1.8GHz", 75 | "request_width" : "64", 76 | "debug" : DEBUG_MEM, 77 | "debug_level" : DEBUG_LEVEL, 78 | "addr_range_end" : 2*1024*1024*1024-1, 79 | }) 80 | Mtol1 = memctrl.setSubComponent("cpulink", "memHierarchy.MemLink") 81 | 82 | # Memory model 83 | memory = memctrl.setSubComponent("backend", "memHierarchy.simpleMem") 84 | memory.addParams({ 85 | "access_time" : "0ps", 86 | "mem_size" : "2GiB", 87 | "request_width": "64" 88 | }) 89 | 90 | # Define the simulation links 91 | link_cpu_cache_link = sst.Link("link_cpu_cache_link") 92 | link_cpu_cache_link.connect( (iface, "port", "0ps"), (l1toC, "port", "0ps") ) 93 | link_mem_bus_link = sst.Link("link_mem_bus_link") 94 | link_mem_bus_link.connect( (l1toM, "port", "0ps"), (Mtol1, "port", "0ps") ) 95 | 96 | -------------------------------------------------------------------------------- /sst/test/test_files/tg0_llvm_assemble.cc: -------------------------------------------------------------------------------- 1 | #include "sstsimengtest.hh" 2 | 3 | // Only load and store instructions of various types and basic arithmetic 4 | // instructions are checked as LLVM assembly will only be used by the SST 5 | // testing framework, which only tests loads and stores. 6 | 7 | TEST_GROUP(TG0, "SSTSimEng_correctly_assembles_instructions_using_LLVM", 8 | "fastL1WithParams_config.py", "withSrc=True", 9 | R"(source= 10 | mov x1, #1 11 | mov x0, #0 12 | add x1, x1, x1 13 | sub x2, x1, x1 14 | mul x2, x1, x1 15 | sdiv x2, x1, x1 16 | cmp x1, x1 17 | fmov s0, 0.5 18 | fmov s1, 1.5 19 | fadd s2, s0, s1 20 | fsub s2, s1, s0 21 | fmul s2, s0, s1 22 | fdiv s2, s1, s0 23 | str s0, [x1] 24 | str x0, [x1] 25 | str w0, [x1] 26 | strh w0, [x1] 27 | strb w0, [x1] 28 | ldr s0, [x1] 29 | ldr x0, [x1] 30 | ldr w2, [x1] 31 | ldrh w2, [x1] 32 | ldrb w2, [x1] 33 | ldp s0, s1, [x0] 34 | ldp x3, x4, [x0] 35 | ldp w3, w4, [x0] 36 | st1 {v0.b}[8], [x0], #1 37 | ptrue p0.d 38 | st1b {z0.b}, p0, [x0, x1] 39 | st1d {z2.d}, p0, [z1.d] 40 | st1w {z2.s}, p0, [x4] 41 | addvl x1, x1, #1 42 | str z1, [x1, #4, mul vl] 43 | ld1r {v0.16b}, [x0] 44 | ld1r {v1.8b}, [x0], 1 45 | ld1 {v0.16b}, [x0] 46 | ld1 {v2.16b, v3.16b}, [x0] 47 | hlt #0 48 | )") 49 | TEST_CASE(TG0, "Test_asssembly_of_simple_instructions") { 50 | size_t pos = capturedStdout.find("[SimEng] retired:"); 51 | std::string retired = ""; 52 | // Extract the retired: string from capturedStdout. 53 | for (size_t y = pos; y < capturedStdout.length(); y++) { 54 | if (capturedStdout[y] != '\n') { 55 | retired += capturedStdout[y]; 56 | } else { 57 | break; 58 | } 59 | } 60 | // Extract retired instruction count from "retired: " string and cast 61 | // to uint64_t. 62 | // Subtract 18 (length of the prefix: "[SimEng] retired:") from retired string 63 | // to obtain the length of the substring containing the numeric value 64 | // representing the total number of retired instructions. 65 | size_t len = retired.length() - 18; 66 | uint64_t retiredCount = std::stoull(retired.substr(18, len)); 67 | std::cout << "Total instructions retired: " << retiredCount << std::endl; 68 | // This should be equal to the total number of instructions in the test case. 69 | EXPECT_EQ(retiredCount, (uint64_t)38); 70 | std::cout << capturedStdout << std::endl; 71 | } 72 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(unit) 2 | add_subdirectory(regression) 3 | add_subdirectory(integration) 4 | -------------------------------------------------------------------------------- /test/integration/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TEST_SOURCES 2 | ConfigTest.cc 3 | ) 4 | 5 | add_executable(integrationtests ${TEST_SOURCES}) 6 | 7 | target_include_directories(integrationtests PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) 8 | target_include_directories(integrationtests PUBLIC ${PROJECT_SOURCE_DIR}/src/lib) 9 | target_link_libraries(integrationtests libsimeng) 10 | target_link_libraries(integrationtests gmock_main) 11 | target_compile_options(integrationtests PRIVATE ${SIMENG_COMPILE_OPTIONS}) 12 | 13 | add_test(NAME integration_tests COMMAND integrationtests) 14 | -------------------------------------------------------------------------------- /test/regression/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Add object library for base regression test fixture class 2 | add_library(regression-test-base OBJECT RegressionTest.cc RegressionTest.hh) 3 | 4 | # Include and link to libsimeng 5 | target_include_directories(regression-test-base 6 | PUBLIC ${PROJECT_SOURCE_DIR}/src/lib) 7 | target_link_libraries(regression-test-base libsimeng) 8 | 9 | # Link to gtest 10 | target_link_libraries(regression-test-base gtest_main) 11 | 12 | # Add LLVM includes 13 | target_include_directories(regression-test-base PUBLIC ${LLVM_INCLUDE_DIRS}) 14 | 15 | # Link to LLVM libraries 16 | llvm_map_components_to_libnames(LLVM_LIBS aarch64asmparser riscvasmparser object) 17 | target_link_libraries(regression-test-base ${LLVM_LIBS}) 18 | 19 | # Add regression test directories for each architecture 20 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 21 | add_subdirectory(aarch64) 22 | add_subdirectory(riscv) 23 | -------------------------------------------------------------------------------- /test/regression/aarch64/AArch64RegressionTest.cc: -------------------------------------------------------------------------------- 1 | #include "AArch64RegressionTest.hh" 2 | 3 | #include "simeng/arch/aarch64/Architecture.hh" 4 | #include "simeng/pipeline/BalancedPortAllocator.hh" 5 | 6 | using namespace simeng::arch::aarch64; 7 | 8 | void AArch64RegressionTest::run(const char* source) { 9 | // Initialise LLVM 10 | LLVMInitializeAArch64TargetInfo(); 11 | LLVMInitializeAArch64TargetMC(); 12 | LLVMInitializeAArch64AsmParser(); 13 | 14 | const char* subtargetFeatures; 15 | #if SIMENG_LLVM_VERSION < 14 16 | subtargetFeatures = "+sve,+lse"; 17 | #else 18 | subtargetFeatures = "+sve,+lse,+sve2,+sme,+sme-f64"; 19 | #endif 20 | 21 | RegressionTest::run(source, "aarch64", subtargetFeatures); 22 | } 23 | 24 | void AArch64RegressionTest::generateConfig() const { 25 | // Re-generate the default config for the AArch64 ISA 26 | simeng::config::SimInfo::generateDefault(simeng::config::ISA::AArch64, true); 27 | 28 | // Add the base additional AArch64 test suite config options 29 | simeng::config::SimInfo::addToConfig(AARCH64_ADDITIONAL_CONFIG); 30 | std::string mode; 31 | switch (std::get<0>(GetParam())) { 32 | case EMULATION: 33 | mode = "emulation"; 34 | break; 35 | case INORDER: 36 | mode = "inorderpipelined"; 37 | break; 38 | case OUTOFORDER: 39 | mode = "outoforder"; 40 | break; 41 | } 42 | simeng::config::SimInfo::addToConfig("{Core: {Simulation-Mode: " + mode + 43 | "}}"); 44 | 45 | // Add the test specific config options 46 | simeng::config::SimInfo::addToConfig(std::get<1>(GetParam())); 47 | } 48 | 49 | std::unique_ptr 50 | AArch64RegressionTest::createArchitecture(simeng::kernel::Linux& kernel) const { 51 | return std::make_unique(kernel); 52 | } 53 | 54 | std::unique_ptr 55 | AArch64RegressionTest::createPortAllocator(ryml::ConstNodeRef config) const { 56 | // Extract the port arrangement from the config file 57 | std::vector> portArrangement( 58 | config["Ports"].num_children()); 59 | for (size_t i = 0; i < config["Ports"].num_children(); i++) { 60 | auto config_groups = config["Ports"][i]["Instruction-Group-Support-Nums"]; 61 | // Read groups in associated port 62 | for (size_t j = 0; j < config_groups.num_children(); j++) { 63 | portArrangement[i].push_back(config_groups[j].as()); 64 | } 65 | } 66 | return std::make_unique( 67 | portArrangement); 68 | } 69 | 70 | uint8_t AArch64RegressionTest::getNZCV() const { 71 | return getRegister({RegisterType::NZCV, 0}); 72 | } 73 | 74 | bool AArch64RegressionTest::getNegativeFlag() const { 75 | return (getNZCV() >> 3) & 1; 76 | } 77 | 78 | bool AArch64RegressionTest::getZeroFlag() const { return (getNZCV() >> 2) & 1; } 79 | 80 | bool AArch64RegressionTest::getCarryFlag() const { 81 | return (getNZCV() >> 1) & 1; 82 | } 83 | 84 | bool AArch64RegressionTest::getOverflowFlag() const { 85 | return (getNZCV() >> 0) & 1; 86 | } 87 | -------------------------------------------------------------------------------- /test/regression/aarch64/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(regression-aarch64 2 | AArch64RegressionTest.cc 3 | AArch64RegressionTest.hh 4 | Exception.cc 5 | LoadStoreQueue.cc 6 | MicroOperation.cc 7 | SmokeTest.cc 8 | Syscall.cc 9 | SystemRegisters.cc 10 | instructions/arithmetic.cc 11 | instructions/bitmanip.cc 12 | instructions/comparison.cc 13 | instructions/conditional.cc 14 | instructions/divide.cc 15 | instructions/float.cc 16 | instructions/load.cc 17 | instructions/logical.cc 18 | instructions/misc.cc 19 | instructions/multiply.cc 20 | instructions/neon.cc 21 | instructions/sme.cc 22 | instructions/store.cc 23 | instructions/sve.cc 24 | ) 25 | 26 | configure_file(${capstone_SOURCE_DIR}/arch/AArch64/AArch64GenInstrInfo.inc AArch64GenInstrInfo.inc COPYONLY) 27 | 28 | target_include_directories(regression-aarch64 PRIVATE 29 | ${CMAKE_CURRENT_SOURCE_DIR}) 30 | target_include_directories(regression-aarch64 PRIVATE 31 | ${CMAKE_CURRENT_BINARY_DIR}) 32 | 33 | target_link_libraries(regression-aarch64 regression-test-base) 34 | target_compile_options(regression-aarch64 PRIVATE ${SIMENG_COMPILE_OPTIONS}) 35 | 36 | # Define a macro so that tests can find data files 37 | target_compile_definitions(regression-aarch64 PRIVATE 38 | "SIMENG_AARCH64_TEST_ROOT=\"${CMAKE_CURRENT_SOURCE_DIR}\"") 39 | 40 | add_test(NAME regression-aarch64-test COMMAND regression-aarch64) -------------------------------------------------------------------------------- /test/regression/aarch64/LoadStoreQueue.cc: -------------------------------------------------------------------------------- 1 | #include "AArch64RegressionTest.hh" 2 | 3 | namespace { 4 | 5 | using LoadStoreQueue = AArch64RegressionTest; 6 | 7 | // Test reading from an address immediately after storing to it. 8 | TEST_P(LoadStoreQueue, RAW) { 9 | initialHeapData_.resize(8); 10 | reinterpret_cast(initialHeapData_.data())[0] = -1; 11 | 12 | RUN_AARCH64(R"( 13 | # Get heap address 14 | mov x0, 0 15 | mov x8, 214 16 | svc #0 17 | 18 | # Write a value and try to read it immediately. 19 | mov x1, #42 20 | str x1, [x0] 21 | ldr x2, [x0] 22 | )"); 23 | EXPECT_EQ(getGeneralRegister(2), 42u); 24 | } 25 | 26 | // Test multiple simultaneous RAW violations are flushed correctly. 27 | TEST_P(LoadStoreQueue, RAWx2) { 28 | initialHeapData_.resize(8); 29 | reinterpret_cast(initialHeapData_.data())[0] = -1; 30 | 31 | RUN_AARCH64(R"( 32 | # Get heap address 33 | mov x0, 0 34 | mov x8, 214 35 | svc #0 36 | 37 | # Write a value and try to read it immediately, twice. 38 | mov x1, #42 39 | str x1, [x0] 40 | ldr x2, [x0] 41 | ldr x3, [x0] 42 | )"); 43 | EXPECT_EQ(getGeneralRegister(2), 42u); 44 | EXPECT_EQ(getGeneralRegister(3), 42u); 45 | } 46 | 47 | // Test with two load instructions that will complete on the same cycle. 48 | TEST_P(LoadStoreQueue, SimultaneousLoadCompletion) { 49 | initialHeapData_.resize(8); 50 | reinterpret_cast(initialHeapData_.data())[0] = 0xDEADBEEF; 51 | reinterpret_cast(initialHeapData_.data())[1] = 0x12345678; 52 | 53 | maxTicks_ = 30; 54 | RUN_AARCH64(R"( 55 | # Get heap address 56 | mov x0, 0 57 | mov x8, 214 58 | svc #0 59 | 60 | # Perform two loads that should complete on the same cycle 61 | # (assuming superscalar core with at least two load units) 62 | ldr w1, [x0] 63 | ldr w2, [x0, 4] 64 | )"); 65 | EXPECT_EQ(getGeneralRegister(1), 0xDEADBEEF); 66 | EXPECT_EQ(getGeneralRegister(2), 0x12345678); 67 | } 68 | 69 | // Test that a speculative load from an invalid address does not crash. 70 | TEST_P(LoadStoreQueue, SpeculativeInvalidLoad) { 71 | initialHeapData_.resize(16); 72 | reinterpret_cast(initialHeapData_.data())[0] = 0.0; 73 | reinterpret_cast(initialHeapData_.data())[1] = 0.0; 74 | 75 | RUN_AARCH64(R"( 76 | # Fill pipelines to delay branch execution 77 | fadd v1.2d, v1.2d, v1.2d 78 | fadd v2.2d, v2.2d, v2.2d 79 | fadd v1.2d, v1.2d, v1.2d 80 | fadd v2.2d, v2.2d, v2.2d 81 | fcmp d0, d0 82 | b.eq .end 83 | 84 | # Load from an invalid address 85 | movk x0, 0xFFFF, lsl 48 86 | ldr x1, [x0] 87 | 88 | .end: 89 | nop 90 | )"); 91 | } 92 | 93 | INSTANTIATE_TEST_SUITE_P(AArch64, LoadStoreQueue, 94 | ::testing::Values(std::make_tuple(OUTOFORDER, "{}")), 95 | paramToString); 96 | 97 | } // namespace 98 | -------------------------------------------------------------------------------- /test/regression/aarch64/SmokeTest.cc: -------------------------------------------------------------------------------- 1 | #include "AArch64RegressionTest.hh" 2 | 3 | namespace { 4 | 5 | using SmokeTest = AArch64RegressionTest; 6 | 7 | // Test that a trivial instruction will execute 8 | TEST_P(SmokeTest, instruction) { 9 | RUN_AARCH64(R"( 10 | orr x0, xzr, #7 11 | )"); 12 | EXPECT_EQ(getGeneralRegister(0), 7u); 13 | } 14 | 15 | // Test a loop executing 1024 times, adding 3 to w1 each time 16 | TEST_P(SmokeTest, loop) { 17 | RUN_AARCH64(R"( 18 | orr w0, wzr, #1024 19 | mov w1, wzr 20 | add w1, w1, #3 21 | subs w0, w0, #1 22 | b.ne -8 23 | )"); 24 | EXPECT_TRUE(getZeroFlag()); 25 | EXPECT_EQ(getGeneralRegister(0), 0u); 26 | EXPECT_EQ(getGeneralRegister(1), 1024 * 3u); 27 | } 28 | 29 | // Test that we can store values to the stack 30 | TEST_P(SmokeTest, stack) { 31 | RUN_AARCH64(R"( 32 | mov w0, #7 33 | mov w1, #42 34 | str w0, [sp, -4] 35 | str w1, [sp, -8] 36 | )"); 37 | EXPECT_EQ(getMemoryValue(process_->getInitialStackPointer() - 4), 38 | 7u); 39 | EXPECT_EQ(getMemoryValue(process_->getInitialStackPointer() - 8), 40 | 42u); 41 | } 42 | 43 | // Test that we can store values to the heap 44 | TEST_P(SmokeTest, heap) { 45 | RUN_AARCH64(R"( 46 | # Use brk syscall to move program brk by eight bytes 47 | mov w0, 0 48 | mov w8, 214 49 | svc #0 50 | add w0, w0, 8 51 | svc #0 52 | # Write a couple of values into the allocated region 53 | mov w1, #7 54 | mov w2, #42 55 | str w1, [x0, -8] 56 | str w2, [x0, -4] 57 | )"); 58 | EXPECT_EQ(getMemoryValue(process_->getHeapStart()), 7u); 59 | EXPECT_EQ(getMemoryValue(process_->getHeapStart() + 4), 42u); 60 | } 61 | 62 | INSTANTIATE_TEST_SUITE_P( 63 | AArch64, SmokeTest, 64 | ::testing::Values(std::make_tuple(EMULATION, "{}"), 65 | std::make_tuple(INORDER, "{}"), 66 | std::make_tuple(OUTOFORDER, 67 | "{L1-Data-Memory: " 68 | "{Interface-Type: Fixed}}")), 69 | paramToString); 70 | 71 | } // namespace 72 | -------------------------------------------------------------------------------- /test/regression/aarch64/SystemRegisters.cc: -------------------------------------------------------------------------------- 1 | #include "AArch64RegressionTest.hh" 2 | 3 | namespace { 4 | 5 | using SystemRegister = AArch64RegressionTest; 6 | 7 | TEST_P(SystemRegister, sysreg_access) { 8 | maxTicks_ = 100; 9 | 10 | // Simple system register write and read. 11 | RUN_AARCH64(R"( 12 | mov x0, 42 13 | msr TPIDR_EL0, x0 14 | mrs x2, TPIDR_EL0 15 | )"); 16 | EXPECT_EQ(getGeneralRegister(2), 42); 17 | 18 | // Test system register WAW hazard. 19 | // The first write will be delayed by the prior sequence of instructions. 20 | // The second write should not execute until the first has retired. 21 | RUN_AARCH64(R"( 22 | mov x0, 7 23 | mov x1, 6 24 | mul x0, x0, x1 25 | msr TPIDR_EL0, x1 26 | msr TPIDR_EL0, x0 27 | mrs x2, TPIDR_EL0 28 | )"); 29 | EXPECT_EQ(getGeneralRegister(2), 42); 30 | EXPECT_EQ(getSystemRegister(0xde82), 42); 31 | 32 | // Test system register RAW hazard. 33 | // The first write will be delayed by the prior sequence of instructions. 34 | // The second write should not execute until the read has retired. 35 | RUN_AARCH64(R"( 36 | mov x0, 7 37 | mov x1, 6 38 | mul x0, x0, x1 39 | msr TPIDR_EL0, x0 40 | mrs x2, TPIDR_EL0 41 | msr TPIDR_EL0, x1 42 | )"); 43 | EXPECT_EQ(getGeneralRegister(2), 42); 44 | EXPECT_EQ(getSystemRegister(0xde82), 6); 45 | 46 | // Test writing and reading multiple system registers. 47 | RUN_AARCH64(R"( 48 | mov x0, 42 49 | mov x1, 7 50 | msr TPIDR_EL0, x0 51 | msr FPCR, x1 52 | mrs x2, TPIDR_EL0 53 | mrs x3, FPCR 54 | )"); 55 | EXPECT_EQ(getGeneralRegister(2), 42); 56 | EXPECT_EQ(getGeneralRegister(3), 7); 57 | 58 | // Test that writing to a system register isn't done speculatively. 59 | // The system register is initially set to 42. 60 | // We perform a load which will cause a data abort exception, and then write 61 | // the value 7 to the system register. 62 | // This second write should not happen due to the exception. 63 | RUN_AARCH64(R"( 64 | mov x0, 42 65 | msr TPIDR_EL0, x0 66 | mov x1, 7 67 | sub x2, x1, 100 68 | ldr x2, [x2] 69 | msr TPIDR_EL0, x1 70 | )"); 71 | EXPECT_EQ(getSystemRegister(0xde82), 42); 72 | } 73 | 74 | TEST_P(SystemRegister, counter_timers) { 75 | // Ensure that the VCT is incremented at correct rate : once per ((2.5 * 1e9) 76 | // / (100 * 1e6)) cycles (i.e. once per 25 cycles). 77 | RUN_AARCH64(R"( 78 | mov x2, xzr 79 | mov x1, #16 80 | # Loop of 3 instructions * 16 iterations = 48, + 2 mov instructions = 50 total instructions & ~50 cycles 81 | sub x1, x1, #1 82 | cmp x1, x2 83 | b.ne #-8 84 | )"); 85 | EXPECT_EQ(getSystemRegister(0xdf02), 2); 86 | } 87 | 88 | INSTANTIATE_TEST_SUITE_P( 89 | AArch64, SystemRegister, 90 | ::testing::Values(std::make_tuple(EMULATION, "{}"), 91 | std::make_tuple(INORDER, "{}"), 92 | std::make_tuple(OUTOFORDER, 93 | "{L1-Data-Memory: " 94 | "{Interface-Type: Fixed}}")), 95 | paramToString); 96 | 97 | } // namespace 98 | -------------------------------------------------------------------------------- /test/regression/aarch64/data/input.txt: -------------------------------------------------------------------------------- 1 | ABCDEFGHIJKLMNOPQRSTUVWXYZ 2 | -------------------------------------------------------------------------------- /test/regression/aarch64/data/truncate-test.txt: -------------------------------------------------------------------------------- 1 | This is a test file for the ftruncate syscall -------------------------------------------------------------------------------- /test/regression/aarch64/instructions/divide.cc: -------------------------------------------------------------------------------- 1 | #include "AArch64RegressionTest.hh" 2 | 3 | namespace { 4 | 5 | using InstDiv = AArch64RegressionTest; 6 | 7 | TEST_P(InstDiv, sdiv) { 8 | // 42 / 6 = 7 9 | RUN_AARCH64(R"( 10 | movz x0, #42 11 | movz x1, #6 12 | sdiv x2, x0, x1 13 | sdiv w3, w0, w1 14 | )"); 15 | EXPECT_EQ(getGeneralRegister(2), 7u); 16 | EXPECT_EQ(getGeneralRegister(3), 7u); 17 | 18 | // 42 / -7 = -6 19 | RUN_AARCH64(R"( 20 | movz x0, #42 21 | mov x1, xzr 22 | sub x1, x1, #1 23 | movz x2, #7 24 | mul x1, x1, x2 25 | sdiv x2, x0, x1 26 | sdiv w3, w0, w1 27 | )"); 28 | EXPECT_EQ(getGeneralRegister(2), -6); 29 | EXPECT_EQ(getGeneralRegister(3), -6); 30 | 31 | // Divide-by-zero should not crash 32 | // 42 / 0 = 0 33 | RUN_AARCH64(R"( 34 | movz x0, #42 35 | sdiv x2, x0, xzr 36 | sdiv w3, w0, wzr 37 | )"); 38 | EXPECT_EQ(getGeneralRegister(2), 0u); 39 | EXPECT_EQ(getGeneralRegister(3), 0u); 40 | } 41 | 42 | TEST_P(InstDiv, udiv) { 43 | // 42 / 6 = 7 44 | RUN_AARCH64(R"( 45 | movz x0, #42 46 | movz x1, #6 47 | udiv x2, x0, x1 48 | udiv w3, w0, w1 49 | )"); 50 | EXPECT_EQ(getGeneralRegister(2), 7u); 51 | EXPECT_EQ(getGeneralRegister(3), 7u); 52 | 53 | // Divide-by-zero should not crash 54 | // 42 / 0 = 0 55 | RUN_AARCH64(R"( 56 | movz x0, #42 57 | udiv x2, x0, xzr 58 | udiv w3, w0, wzr 59 | )"); 60 | EXPECT_EQ(getGeneralRegister(2), 0u); 61 | EXPECT_EQ(getGeneralRegister(3), 0u); 62 | } 63 | 64 | INSTANTIATE_TEST_SUITE_P(AArch64, InstDiv, 65 | ::testing::Values(std::make_tuple(EMULATION, "{}")), 66 | paramToString); 67 | 68 | } // namespace 69 | -------------------------------------------------------------------------------- /test/regression/aarch64/instructions/misc.cc: -------------------------------------------------------------------------------- 1 | #include "AArch64RegressionTest.hh" 2 | 3 | namespace { 4 | 5 | using InstMisc = AArch64RegressionTest; 6 | 7 | TEST_P(InstMisc, adr) { 8 | RUN_AARCH64(R"( 9 | adr x0, #0 10 | adr x1, #4 11 | adr x2, #-4 12 | )"); 13 | EXPECT_EQ(getGeneralRegister(0), 0u); 14 | EXPECT_EQ(getGeneralRegister(1), 8u); 15 | EXPECT_EQ(getGeneralRegister(2), 4u); 16 | } 17 | 18 | TEST_P(InstMisc, ret) { 19 | RUN_AARCH64(R"( 20 | bl #20 21 | b.al #28 22 | nop 23 | add w2, w2, #1 24 | nop 25 | add w1, w1, #1 26 | ret 27 | nop 28 | )"); 29 | EXPECT_EQ(getGeneralRegister(1), 1); 30 | EXPECT_EQ(getGeneralRegister(2), 0); 31 | 32 | RUN_AARCH64(R"( 33 | mov x15, #36 34 | bl #20 35 | add w2, w2, #1 36 | nop 37 | nop 38 | nop 39 | add w1, w1, #1 40 | ret x15 41 | add w2, w2, #1 42 | nop 43 | )"); 44 | EXPECT_EQ(getGeneralRegister(1), 1); 45 | EXPECT_EQ(getGeneralRegister(2), 0); 46 | } 47 | 48 | INSTANTIATE_TEST_SUITE_P(AArch64, InstMisc, 49 | ::testing::Values(std::make_tuple(EMULATION, "{}")), 50 | paramToString); 51 | 52 | } // namespace 53 | -------------------------------------------------------------------------------- /test/regression/aarch64/instructions/multiply.cc: -------------------------------------------------------------------------------- 1 | #include "AArch64RegressionTest.hh" 2 | 3 | namespace { 4 | 5 | using InstMul = AArch64RegressionTest; 6 | 7 | TEST_P(InstMul, maddw) { 8 | RUN_AARCH64(R"( 9 | movz w0, #7 10 | movz w1, #6 11 | movz w2, #5 12 | madd w3, w0, w1, w2 13 | )"); 14 | EXPECT_EQ(getGeneralRegister(3), 47u); 15 | } 16 | 17 | TEST_P(InstMul, msub) { 18 | // 32-bit 19 | RUN_AARCH64(R"( 20 | movz w0, #7 21 | movz w1, #6 22 | movz w2, #47 23 | msub w3, w0, w1, w2 24 | )"); 25 | EXPECT_EQ(getGeneralRegister(3), 5u); 26 | 27 | // 64-bit 28 | RUN_AARCH64(R"( 29 | movz x0, #7 30 | movz x1, #6 31 | movz x2, #47 32 | msub x3, x0, x1, x2 33 | )"); 34 | EXPECT_EQ(getGeneralRegister(3), 5u); 35 | } 36 | 37 | TEST_P(InstMul, mulw) { 38 | RUN_AARCH64(R"( 39 | movz w0, #7 40 | movz w1, #6 41 | mul w2, w0, w1 42 | )"); 43 | EXPECT_EQ(getGeneralRegister(2), 42u); 44 | } 45 | 46 | TEST_P(InstMul, smaddl) { 47 | RUN_AARCH64(R"( 48 | mov w0, 0x2A 49 | orr w0, wzr, w0, lsl 24 50 | movz w1, 0x100 51 | movz x2, 0x05, lsl 48 52 | smaddl x3, w0, w1, x2 53 | 54 | mov w4, #-1 55 | movz w5, 0xFF, lsl 16 56 | mov x6, 0x100 57 | smaddl x7, w4, w5, x6 58 | )"); 59 | EXPECT_EQ(getGeneralRegister(3), 0x0005002A00000000); 60 | EXPECT_EQ(getGeneralRegister(7), -0xFEFF00); 61 | } 62 | 63 | TEST_P(InstMul, smulh) { 64 | RUN_AARCH64(R"( 65 | movz x0, 0x2AB3 66 | orr x0, xzr, x0, lsl 48 67 | movz x1, 0x100 68 | smulh x2, x0, x1 69 | )"); 70 | EXPECT_EQ(getGeneralRegister(2), 0x000000000000002A); 71 | } 72 | 73 | TEST_P(InstMul, smull) { 74 | RUN_AARCH64(R"( 75 | mov w0, 0x2A 76 | orr w0, wzr, w0, lsl 24 77 | movz w1, 0x100 78 | smull x3, w0, w1 79 | )"); 80 | EXPECT_EQ(getGeneralRegister(3), 0x0000002A00000000); 81 | } 82 | 83 | TEST_P(InstMul, umaddl) { 84 | RUN_AARCH64(R"( 85 | mov w0, 0x2A 86 | orr w0, wzr, w0, lsl 24 87 | movz w1, 0x100 88 | movz x2, 0x05, lsl 48 89 | umaddl x3, w0, w1, x2 90 | 91 | # Test umull alias 92 | umull x4, w0, w1 93 | )"); 94 | EXPECT_EQ(getGeneralRegister(3), 0x0005002A00000000); 95 | EXPECT_EQ(getGeneralRegister(4), 0x0000002A00000000); 96 | } 97 | 98 | INSTANTIATE_TEST_SUITE_P(AArch64, InstMul, 99 | ::testing::Values(std::make_tuple(EMULATION, "{}")), 100 | paramToString); 101 | 102 | } // namespace 103 | -------------------------------------------------------------------------------- /test/regression/riscv/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(regression-riscv 2 | RISCVRegressionTest.cc 3 | RISCVRegressionTest.hh 4 | Exception.cc 5 | LoadStoreQueue.cc 6 | InorderPipeline.cc 7 | SmokeTest.cc 8 | Syscall.cc 9 | instructions/arithmetic.cc 10 | instructions/multiplyDivide.cc 11 | instructions/load.cc 12 | instructions/store.cc 13 | instructions/jump.cc 14 | instructions/branch.cc 15 | instructions/atomic.cc 16 | instructions/float.cc 17 | instructions/compressed.cc 18 | ) 19 | 20 | configure_file(${capstone_SOURCE_DIR}/arch/RISCV/RISCVGenInstrInfo.inc RISCVGenInstrInfo.inc COPYONLY) 21 | 22 | target_include_directories(regression-riscv PRIVATE 23 | ${CMAKE_CURRENT_SOURCE_DIR}) 24 | target_include_directories(regression-riscv PRIVATE 25 | ${CMAKE_CURRENT_BINARY_DIR}) 26 | 27 | target_link_libraries(regression-riscv regression-test-base) 28 | target_compile_options(regression-riscv PRIVATE ${SIMENG_COMPILE_OPTIONS}) 29 | 30 | # Define a macro so that tests can find data files 31 | target_compile_definitions(regression-riscv PRIVATE 32 | "SIMENG_RISCV_TEST_ROOT=\"${CMAKE_CURRENT_SOURCE_DIR}\"") 33 | 34 | add_test(NAME regression-riscv-test COMMAND regression-riscv) -------------------------------------------------------------------------------- /test/regression/riscv/InorderPipeline.cc: -------------------------------------------------------------------------------- 1 | #include "RISCVRegressionTest.hh" 2 | 3 | namespace { 4 | 5 | using inorderPipeline = RISCVRegressionTest; 6 | 7 | TEST_P(inorderPipeline, prematureMulticycleHalting) { 8 | RUN_RISCV(R"( 9 | li a1, 2 10 | li a2, 1 11 | li a4, 5 12 | 13 | beq a1, a2, end # mispredict with target out of programByteLength creates 14 | # pipeline bubble 15 | div a3, a1, a2 # multicycle instruction ties up execution unit causing 16 | # decode to halt and next instruction to be stuck in the 17 | # tail of pipelined buffer 18 | beq a1, a2, end # mispredict with target out of programByteLength halts 19 | # fetch unit this only occurs because the instruction 20 | # address is set after decode therefor a garbage value is 21 | # used sometimes causing the halt at ~FetchUnit.cc::177 22 | 23 | # This sequence of instructions with this inorder pipeline causes all 24 | # buffers to appear empty and the fetch unit to halt causing the inorder 25 | # core to halt early which is incorrect behaviour. This is fixed in PR 294 26 | 27 | li a4, 10 # Occurs if core does not halt 28 | end: 29 | )"); 30 | EXPECT_EQ(getGeneralRegister(14), 10); 31 | } 32 | 33 | INSTANTIATE_TEST_SUITE_P(RISCV, inorderPipeline, 34 | ::testing::Values(std::make_tuple(INORDER, "{}")), 35 | paramToString); 36 | 37 | } // namespace 38 | -------------------------------------------------------------------------------- /test/regression/riscv/RISCVRegressionTest.cc: -------------------------------------------------------------------------------- 1 | #include "RISCVRegressionTest.hh" 2 | 3 | #include "simeng/arch/riscv/Architecture.hh" 4 | #include "simeng/pipeline/BalancedPortAllocator.hh" 5 | 6 | using namespace simeng::arch::riscv; 7 | 8 | void RISCVRegressionTest::run(const char* source, const char* extensions) { 9 | // Initialise LLVM 10 | LLVMInitializeRISCVTargetInfo(); 11 | LLVMInitializeRISCVTargetMC(); 12 | LLVMInitializeRISCVAsmParser(); 13 | 14 | RegressionTest::run(source, "riscv64", extensions); 15 | } 16 | 17 | void RISCVRegressionTest::generateConfig() const { 18 | // Re-generate the default config for the rv64 ISA 19 | simeng::config::SimInfo::generateDefault(simeng::config::ISA::RV64, true); 20 | 21 | // Add the base additional RISCV test suite config options 22 | simeng::config::SimInfo::addToConfig(RISCV_ADDITIONAL_CONFIG); 23 | std::string mode; 24 | switch (std::get<0>(GetParam())) { 25 | case EMULATION: 26 | mode = "emulation"; 27 | break; 28 | case INORDER: 29 | mode = "inorderpipelined"; 30 | break; 31 | case OUTOFORDER: 32 | mode = "outoforder"; 33 | break; 34 | } 35 | 36 | simeng::config::SimInfo::addToConfig("{Core: {Simulation-Mode: " + mode + 37 | "}}"); 38 | 39 | // Add the test specific config options 40 | simeng::config::SimInfo::addToConfig(std::get<1>(GetParam())); 41 | } 42 | 43 | std::unique_ptr 44 | RISCVRegressionTest::createArchitecture(simeng::kernel::Linux& kernel) const { 45 | return std::make_unique(kernel); 46 | } 47 | 48 | std::unique_ptr 49 | RISCVRegressionTest::createPortAllocator(ryml::ConstNodeRef config) const { 50 | // Extract the port arrangement from the config file 51 | std::vector> portArrangement( 52 | config["Ports"].num_children()); 53 | for (size_t i = 0; i < config["Ports"].num_children(); i++) { 54 | auto config_groups = config["Ports"][i]["Instruction-Group-Support-Nums"]; 55 | // Read groups in associated port 56 | for (size_t j = 0; j < config_groups.num_children(); j++) { 57 | portArrangement[i].push_back(config_groups[j].as()); 58 | } 59 | } 60 | return std::make_unique( 61 | portArrangement); 62 | } 63 | -------------------------------------------------------------------------------- /test/regression/riscv/SmokeTest.cc: -------------------------------------------------------------------------------- 1 | #include "RISCVRegressionTest.hh" 2 | 3 | namespace { 4 | 5 | using SmokeTest = RISCVRegressionTest; 6 | 7 | // Test that a trivial instruction will execute 8 | TEST_P(SmokeTest, instruction) { 9 | RUN_RISCV(R"( 10 | addi a5,a5,32 11 | )"); 12 | EXPECT_EQ(getGeneralRegister(15), 32u); 13 | } 14 | 15 | INSTANTIATE_TEST_SUITE_P( 16 | RISCV, SmokeTest, 17 | ::testing::Values(std::make_tuple(EMULATION, "{}"), 18 | std::make_tuple(INORDER, "{}"), 19 | std::make_tuple(OUTOFORDER, 20 | "{L1-Data-Memory: " 21 | "{Interface-Type: Fixed}}")), 22 | paramToString); 23 | 24 | } // namespace 25 | -------------------------------------------------------------------------------- /test/regression/riscv/data/input.txt: -------------------------------------------------------------------------------- 1 | ABCDEFGHIJKLMNOPQRSTUVWXYZ 2 | -------------------------------------------------------------------------------- /test/regression/riscv/data/truncate-test.txt: -------------------------------------------------------------------------------- 1 | This is a test file for the ftruncate syscall -------------------------------------------------------------------------------- /test/regression/riscv/instructions/jump.cc: -------------------------------------------------------------------------------- 1 | #include "RISCVRegressionTest.hh" 2 | 3 | namespace { 4 | 5 | using InstJump = RISCVRegressionTest; 6 | 7 | TEST_P(InstJump, jalr) { 8 | RUN_RISCV(R"( 9 | li t1, 4 10 | jalr t0, t1, 12 11 | addi t6, t6, 10 12 | jalr ra, t1, 20 13 | addi t5, t5, 5 14 | jalr ra, t1, 4 15 | addi t4, t4, 3 16 | )"); 17 | EXPECT_EQ(getGeneralRegister(30), 5); 18 | EXPECT_EQ(getGeneralRegister(31), 10); 19 | EXPECT_EQ(getGeneralRegister(29), 3); 20 | EXPECT_EQ(getGeneralRegister(1), 16); 21 | EXPECT_EQ(getGeneralRegister(5), 8); 22 | } 23 | 24 | TEST_P(InstJump, jalrAlias) { 25 | RUN_RISCV(R"( 26 | addi t0, t0, 12 27 | jalr t0 28 | addi t6, t6, 10 29 | addi t6, t6, 3 30 | )"); 31 | EXPECT_EQ(getGeneralRegister(31), 3); 32 | EXPECT_EQ(getGeneralRegister(1), 8); 33 | 34 | RUN_RISCV(R"( 35 | addi ra, ra, 12 36 | ret # jalr zero, ra, 0 37 | addi t6, t6, 10 38 | addi t6, t6, 3 39 | )"); 40 | EXPECT_EQ(getGeneralRegister(31), 3); 41 | EXPECT_EQ(getGeneralRegister(1), 12); 42 | EXPECT_EQ(getGeneralRegister(0), 0); 43 | 44 | RUN_RISCV(R"( 45 | addi t0, t0, 12 46 | jr t0 # jalr zero, t0, 0 47 | addi t6, t6, 10 48 | addi t6, t6, 3 49 | )"); 50 | EXPECT_EQ(getGeneralRegister(31), 3); 51 | EXPECT_EQ(getGeneralRegister(1), 0); 52 | EXPECT_EQ(getGeneralRegister(0), 0); 53 | } 54 | 55 | TEST_P(InstJump, jal) { 56 | RUN_RISCV(R"( 57 | jal t0, 12 58 | addi t6, t6, 10 59 | jal ra, 12 60 | addi t5, t5, 5 61 | jal ra, -12 62 | addi t4, t4, 3 63 | )"); 64 | EXPECT_EQ(getGeneralRegister(30), 5); 65 | EXPECT_EQ(getGeneralRegister(31), 10); 66 | EXPECT_EQ(getGeneralRegister(29), 3); 67 | EXPECT_EQ(getGeneralRegister(1), 12); 68 | EXPECT_EQ(getGeneralRegister(5), 4); 69 | } 70 | 71 | TEST_P(InstJump, jalAlias) { 72 | RUN_RISCV(R"( 73 | j 12 #j 0xc 74 | addi t6, t6, 10 75 | jal t1, 12 #jal t1, 0xc 76 | addi t5, t5, 5 77 | jal -12 #jal -0xc 78 | addi t4, t4, 3 79 | )"); 80 | EXPECT_EQ(getGeneralRegister(30), 5); 81 | EXPECT_EQ(getGeneralRegister(31), 10); 82 | EXPECT_EQ(getGeneralRegister(29), 3); 83 | EXPECT_EQ(getGeneralRegister(6), 12); 84 | EXPECT_EQ(getGeneralRegister(5), 0); 85 | EXPECT_EQ(getGeneralRegister(1), 20); 86 | EXPECT_EQ(getGeneralRegister(0), 0); 87 | } 88 | 89 | INSTANTIATE_TEST_SUITE_P(RISCV, InstJump, 90 | ::testing::Values(std::make_tuple(EMULATION, "{}")), 91 | paramToString); 92 | 93 | } // namespace 94 | -------------------------------------------------------------------------------- /test/unit/ArchitecturalRegisterFileSetTest.cc: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "simeng/ArchitecturalRegisterFileSet.hh" 3 | 4 | namespace simeng { 5 | namespace pipeline { 6 | 7 | class ArchitecturalRegisterFileSetTest : public ::testing::Test { 8 | public: 9 | ArchitecturalRegisterFileSetTest() 10 | : physRegFileSet(regFileStruct), archRegFileSet(physRegFileSet) {} 11 | 12 | protected: 13 | const std::vector regFileStruct = { 14 | {8, 10}, {24, 15}, {256, 31}}; 15 | 16 | RegisterFileSet physRegFileSet; 17 | 18 | ArchitecturalRegisterFileSet archRegFileSet; 19 | }; 20 | 21 | // Ensure we can read and write values to the architectural register file 22 | TEST_F(ArchitecturalRegisterFileSetTest, readWrite) { 23 | for (uint8_t i = 0; i < regFileStruct.size(); i++) { 24 | const uint16_t regSize = regFileStruct[i].bytes; 25 | const uint16_t maxRegTag = regFileStruct[i].quantity - 1; 26 | const Register r0 = {i, 0}; 27 | const Register rMax = {i, maxRegTag}; 28 | 29 | EXPECT_EQ(archRegFileSet.get(r0), RegisterValue(0, regSize)); 30 | EXPECT_EQ(archRegFileSet.get(rMax), RegisterValue(0, regSize)); 31 | 32 | archRegFileSet.set(r0, RegisterValue(20, regSize)); 33 | archRegFileSet.set(rMax, RegisterValue(40, regSize)); 34 | 35 | EXPECT_EQ(archRegFileSet.get(r0), RegisterValue(20, regSize)); 36 | EXPECT_EQ(archRegFileSet.get(rMax), RegisterValue(40, regSize)); 37 | } 38 | } 39 | 40 | } // namespace pipeline 41 | } // namespace simeng -------------------------------------------------------------------------------- /test/unit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TEST_SOURCES 2 | aarch64/ArchInfoTest.cc 3 | aarch64/ArchitectureTest.cc 4 | aarch64/AuxiliaryFunctionsTest.cc 5 | aarch64/ExceptionHandlerTest.cc 6 | aarch64/InstructionTest.cc 7 | aarch64/OperandContainerTest.cc 8 | riscv/ArchInfoTest.cc 9 | riscv/ArchitectureTest.cc 10 | riscv/ExceptionHandlerTest.cc 11 | riscv/InstructionTest.cc 12 | pipeline/A64FXPortAllocatorTest.cc 13 | pipeline/BalancedPortAllocatorTest.cc 14 | pipeline/DecodeUnitTest.cc 15 | pipeline/DispatchIssueUnitTest.cc 16 | pipeline/ExecuteUnitTest.cc 17 | pipeline/FetchUnitTest.cc 18 | pipeline/LoadStoreQueueTest.cc 19 | pipeline/M1PortAllocatorTest.cc 20 | pipeline/MappedRegisterFileSetTest.cc 21 | pipeline/PipelineBufferTest.cc 22 | pipeline/RegisterAliasTableTest.cc 23 | pipeline/RenameUnitTest.cc 24 | pipeline/ReorderBufferTest.cc 25 | pipeline/WritebackUnitTest.cc 26 | ArchitecturalRegisterFileSetTest.cc 27 | ElfTest.cc 28 | FixedLatencyMemoryInterfaceTest.cc 29 | FlatMemoryInterfaceTest.cc 30 | GenericPredictorTest.cc 31 | OSTest.cc 32 | PoolTest.cc 33 | ProcessTest.cc 34 | RegisterFileSetTest.cc 35 | RegisterValueTest.cc 36 | PerceptronPredictorTest.cc 37 | SpecialFileDirGenTest.cc 38 | ) 39 | 40 | add_executable(unittests ${TEST_SOURCES}) 41 | 42 | configure_file(${capstone_SOURCE_DIR}/arch/AArch64/AArch64GenInstrInfo.inc AArch64GenInstrInfo.inc COPYONLY) 43 | configure_file(${capstone_SOURCE_DIR}/arch/RISCV/RISCVGenInstrInfo.inc RISCVGenInstrInfo.inc COPYONLY) 44 | 45 | target_include_directories(unittests PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) 46 | target_include_directories(unittests PUBLIC ${PROJECT_SOURCE_DIR}/src/lib) 47 | target_link_libraries(unittests libsimeng) 48 | target_link_libraries(unittests gmock_main) 49 | target_compile_options(unittests PRIVATE ${SIMENG_COMPILE_OPTIONS}) 50 | 51 | add_test(NAME unit_tests COMMAND unittests) 52 | -------------------------------------------------------------------------------- /test/unit/ConfigInit.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "simeng/config/SimInfo.hh" 4 | #include "simeng/version.hh" 5 | 6 | namespace simeng { 7 | 8 | // This small class' purpose is to initialise the SimInfo config before the 9 | // initialisation of a test class 10 | class ConfigInit { 11 | public: 12 | ConfigInit(config::ISA isa, std::string configAdditions) { 13 | config::SimInfo::generateDefault(isa, true); 14 | config::SimInfo::addToConfig(configAdditions); 15 | } 16 | }; 17 | 18 | } // namespace simeng -------------------------------------------------------------------------------- /test/unit/ElfTest.cc: -------------------------------------------------------------------------------- 1 | #include "gmock/gmock.h" 2 | #include "simeng/Elf.hh" 3 | #include "simeng/version.hh" 4 | 5 | using ::testing::_; 6 | using ::testing::HasSubstr; 7 | using ::testing::Return; 8 | 9 | namespace simeng { 10 | 11 | class ElfTest : public testing::Test { 12 | public: 13 | ElfTest() {} 14 | 15 | protected: 16 | const std::string knownElfFilePath = 17 | SIMENG_SOURCE_DIR "/test/unit/data/stream-aarch64.elf"; 18 | 19 | const uint64_t known_entryPoint = 4206008; 20 | const uint16_t known_e_phentsize = 56; 21 | const uint16_t known_e_phnum = 6; 22 | const uint64_t known_phdrTableAddress = 4194368; 23 | const uint64_t known_processImageSize = 5040480; 24 | 25 | char* unwrappedProcImgPtr; 26 | }; 27 | 28 | // Test that a valid ELF file can be created 29 | TEST_F(ElfTest, validElf) { 30 | Elf elf(knownElfFilePath, &unwrappedProcImgPtr); 31 | 32 | EXPECT_TRUE(elf.isValid()); 33 | EXPECT_EQ(elf.getEntryPoint(), known_entryPoint); 34 | EXPECT_EQ(elf.getPhdrEntrySize(), known_e_phentsize); 35 | EXPECT_EQ(elf.getNumPhdr(), known_e_phnum); 36 | EXPECT_EQ(elf.getPhdrTableAddress(), known_phdrTableAddress); 37 | EXPECT_EQ(elf.getProcessImageSize(), known_processImageSize); 38 | } 39 | 40 | // Test that wrong filepath results in invalid ELF 41 | TEST_F(ElfTest, invalidElf) { 42 | Elf elf(SIMENG_SOURCE_DIR "/test/bogus_file_path___--__--__", 43 | &unwrappedProcImgPtr); 44 | EXPECT_FALSE(elf.isValid()); 45 | } 46 | 47 | // Test that non-ELF file is not accepted 48 | TEST_F(ElfTest, nonElf) { 49 | testing::internal::CaptureStderr(); 50 | Elf elf(SIMENG_SOURCE_DIR "/test/unit/ElfTest.cc", &unwrappedProcImgPtr); 51 | EXPECT_FALSE(elf.isValid()); 52 | EXPECT_THAT(testing::internal::GetCapturedStderr(), 53 | HasSubstr("[SimEng:Elf] Elf magic does not match")); 54 | } 55 | 56 | // Check that 32-bit ELF is not accepted 57 | TEST_F(ElfTest, format32Elf) { 58 | testing::internal::CaptureStderr(); 59 | Elf elf(SIMENG_SOURCE_DIR "/test/unit/data/stream.rv32ima.elf", 60 | &unwrappedProcImgPtr); 61 | EXPECT_FALSE(elf.isValid()); 62 | EXPECT_THAT( 63 | testing::internal::GetCapturedStderr(), 64 | HasSubstr("[SimEng:Elf] Unsupported architecture detected in Elf")); 65 | } 66 | 67 | } // namespace simeng -------------------------------------------------------------------------------- /test/unit/FlatMemoryInterfaceTest.cc: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "simeng/memory/FlatMemoryInterface.hh" 3 | 4 | namespace { 5 | 6 | class FlatMemoryInterfaceTest : public testing::Test { 7 | public: 8 | FlatMemoryInterfaceTest() : memory(memoryData.data(), memorySize) {} 9 | 10 | protected: 11 | static constexpr uint16_t memorySize = 4; 12 | std::array memoryData = {(char)0xFE, (char)0xCA, (char)0xBA, 13 | (char)0xAB}; 14 | 15 | simeng::RegisterValue value = {0xDEADBEEF, 4}; 16 | simeng::RegisterValue value_oversized = {0xDEADBEEFDEADBEEF, 8}; 17 | simeng::memory::MemoryAccessTarget target = {0, 4}; 18 | simeng::memory::MemoryAccessTarget target_OutOfBound1 = {1000, 4}; 19 | simeng::memory::MemoryAccessTarget target_OutOfBound2 = {0, 8}; 20 | 21 | const std::string writeOverflowStr = 22 | "Attempted to write beyond memory limit."; 23 | 24 | simeng::memory::FlatMemoryInterface memory; 25 | }; 26 | 27 | // Test that we can read data and it completes after zero cycles. 28 | TEST_F(FlatMemoryInterfaceTest, FixedReadData) { 29 | // Read a 32-bit value 30 | memory.requestRead(target, 1); 31 | auto entries = memory.getCompletedReads(); 32 | EXPECT_EQ(entries.size(), 1); 33 | EXPECT_EQ(entries[0].requestId, 1); 34 | EXPECT_EQ(entries[0].data, simeng::RegisterValue(0xABBACAFE, 4)); 35 | EXPECT_EQ(entries[0].target, target); 36 | } 37 | 38 | // Test that we can write data and it completes after zero cycles. 39 | TEST_F(FlatMemoryInterfaceTest, FixedWriteData) { 40 | // Write a 32-bit value to memory 41 | memory.requestWrite(target, value); 42 | EXPECT_EQ(reinterpret_cast(memoryData.data())[0], 0xDEADBEEF); 43 | } 44 | 45 | // Test that out-of-bounds memory reads are correctly handled. 46 | TEST_F(FlatMemoryInterfaceTest, OutofBoundsRead) { 47 | // Create a target such that address + size will overflow 48 | memory.requestRead(target_OutOfBound1, 1); 49 | 50 | // Create a regular out-of-bounds target 51 | memory.requestRead(target_OutOfBound2, 2); 52 | 53 | auto entries = memory.getCompletedReads(); 54 | EXPECT_EQ(entries.size(), 2); 55 | 56 | auto overflowResult = entries[0]; 57 | EXPECT_EQ(overflowResult.requestId, 1); 58 | EXPECT_FALSE(overflowResult.data); 59 | EXPECT_EQ(overflowResult.target, target_OutOfBound1); 60 | 61 | overflowResult = entries[1]; 62 | EXPECT_EQ(overflowResult.requestId, 2); 63 | EXPECT_FALSE(overflowResult.data); 64 | EXPECT_EQ(overflowResult.target, target_OutOfBound2); 65 | } 66 | 67 | // Test that out-of-bounds memory writes are correctly handled. 68 | TEST_F(FlatMemoryInterfaceTest, OutofBoundsWrite_1) { 69 | // Create a target such that address + size will overflow 70 | ASSERT_DEATH(memory.requestWrite(target_OutOfBound1, value), 71 | writeOverflowStr); 72 | } 73 | 74 | // Test that out-of-bounds memory writes are correctly handled. 75 | TEST_F(FlatMemoryInterfaceTest, OutofBoundsWrite_2) { 76 | // Create a regular out-of-bounds target 77 | ASSERT_DEATH(memory.requestWrite(target_OutOfBound2, value_oversized), 78 | writeOverflowStr); 79 | } 80 | 81 | } // namespace 82 | -------------------------------------------------------------------------------- /test/unit/MockArchitecture.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gmock/gmock.h" 4 | #include "simeng/arch/Architecture.hh" 5 | 6 | namespace simeng { 7 | 8 | /** Mock implementation of the `Architecture` interface. */ 9 | class MockArchitecture : public arch::Architecture { 10 | public: 11 | MockArchitecture(kernel::Linux& kernel) : arch::Architecture(kernel) {} 12 | MOCK_CONST_METHOD4(predecode, 13 | uint8_t(const uint8_t* ptr, uint16_t bytesAvailable, 14 | uint64_t instructionAddress, MacroOp& output)); 15 | MOCK_CONST_METHOD1(canRename, bool(Register reg)); 16 | MOCK_CONST_METHOD1(getSystemRegisterTag, int32_t(uint16_t reg)); 17 | MOCK_CONST_METHOD3(handleException, 18 | std::shared_ptr( 19 | const std::shared_ptr& instruction, 20 | const Core& core, memory::MemoryInterface& memory)); 21 | MOCK_CONST_METHOD0(getInitialState, arch::ProcessStateChange()); 22 | MOCK_CONST_METHOD0(getMaxInstructionSize, uint8_t()); 23 | MOCK_CONST_METHOD0(getMinInstructionSize, uint8_t()); 24 | MOCK_CONST_METHOD2(updateSystemTimerRegisters, 25 | void(RegisterFileSet* regFile, const uint64_t iterations)); 26 | }; 27 | 28 | } // namespace simeng 29 | -------------------------------------------------------------------------------- /test/unit/MockBranchPredictor.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gmock/gmock.h" 4 | #include "simeng/BranchPredictor.hh" 5 | 6 | namespace simeng { 7 | 8 | /** Mock implementation of the `BranchPredictor` interface. */ 9 | class MockBranchPredictor : public BranchPredictor { 10 | public: 11 | MOCK_METHOD3(predict, BranchPrediction(uint64_t address, BranchType type, 12 | int64_t knownTarget)); 13 | MOCK_METHOD4(update, void(uint64_t address, bool taken, 14 | uint64_t targetAddress, BranchType type)); 15 | MOCK_METHOD1(flush, void(uint64_t address)); 16 | }; 17 | 18 | } // namespace simeng 19 | -------------------------------------------------------------------------------- /test/unit/MockCore.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gmock/gmock.h" 4 | #include "simeng/Core.hh" 5 | 6 | namespace simeng { 7 | 8 | /** Mock implementation of the `Core` interface. */ 9 | class MockCore : public Core { 10 | public: 11 | MockCore(memory::MemoryInterface& dataMemory, const arch::Architecture& isa, 12 | const std::vector& regFileStructure) 13 | : Core(dataMemory, isa, regFileStructure) {} 14 | MOCK_METHOD0(tick, void()); 15 | MOCK_CONST_METHOD0(hasHalted, bool()); 16 | MOCK_CONST_METHOD0(getArchitecturalRegisterFileSet, 17 | const ArchitecturalRegisterFileSet&()); 18 | MOCK_CONST_METHOD0(getInstructionsRetiredCount, uint64_t()); 19 | MOCK_CONST_METHOD0(getSystemTimer, uint64_t()); 20 | MOCK_CONST_METHOD0(getStats, std::map()); 21 | }; 22 | 23 | } // namespace simeng 24 | -------------------------------------------------------------------------------- /test/unit/MockInstruction.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gmock/gmock.h" 4 | #include "simeng/Instruction.hh" 5 | 6 | namespace simeng { 7 | 8 | /** Mock implementation of the `Instruction` interface. */ 9 | class MockInstruction : public Instruction { 10 | public: 11 | MOCK_CONST_METHOD0(getSourceRegisters, const span()); 12 | MOCK_CONST_METHOD0(getSourceOperands, const span()); 13 | MOCK_CONST_METHOD0(getDestinationRegisters, const span()); 14 | MOCK_METHOD2(renameSource, void(uint16_t i, Register renamed)); 15 | MOCK_METHOD2(renameDestination, void(uint16_t i, Register renamed)); 16 | MOCK_METHOD2(supplyOperand, void(uint16_t i, const RegisterValue& value)); 17 | MOCK_CONST_METHOD1(isOperandReady, bool(int i)); 18 | MOCK_CONST_METHOD0(canExecute, bool()); 19 | MOCK_METHOD0(execute, void()); 20 | MOCK_CONST_METHOD0(getResults, const span()); 21 | MOCK_METHOD0(generateAddresses, span()); 22 | MOCK_METHOD2(supplyData, void(uint64_t address, const RegisterValue& data)); 23 | MOCK_CONST_METHOD0(getGeneratedAddresses, 24 | span()); 25 | MOCK_CONST_METHOD0(hasAllData, bool()); 26 | MOCK_CONST_METHOD0(getData, span()); 27 | 28 | MOCK_CONST_METHOD0(checkEarlyBranchMisprediction, 29 | std::tuple()); 30 | MOCK_CONST_METHOD0(getBranchType, BranchType()); 31 | MOCK_CONST_METHOD0(getKnownOffset, int64_t()); 32 | 33 | MOCK_CONST_METHOD0(isStoreAddress, bool()); 34 | MOCK_CONST_METHOD0(isStoreData, bool()); 35 | MOCK_CONST_METHOD0(isLoad, bool()); 36 | MOCK_CONST_METHOD0(isBranch, bool()); 37 | MOCK_CONST_METHOD0(getGroup, uint16_t()); 38 | 39 | MOCK_CONST_METHOD0(getLSQLatency, uint16_t()); 40 | 41 | MOCK_METHOD0(getSupportedPorts, const std::vector&()); 42 | 43 | MOCK_METHOD1(setExecutionInfo, void(const ExecutionInfo& info)); 44 | 45 | void setBranchResults(bool wasTaken, uint64_t targetAddress) { 46 | branchTaken_ = wasTaken; 47 | branchAddress_ = targetAddress; 48 | } 49 | 50 | void setExecuted(bool executed) { executed_ = executed; } 51 | 52 | void setExceptionEncountered(bool exceptionEncountered) { 53 | exceptionEncountered_ = exceptionEncountered; 54 | } 55 | 56 | void setDataPending(uint8_t value) { dataPending_ = value; } 57 | 58 | void setLatency(uint16_t cycles) { latency_ = cycles; } 59 | 60 | void setLSQLatency(uint16_t cycles) { lsqExecutionLatency_ = cycles; } 61 | 62 | void setStallCycles(uint16_t cycles) { stallCycles_ = cycles; } 63 | 64 | void setIsMicroOp(bool isMicroOp) { isMicroOp_ = isMicroOp; } 65 | 66 | void setIsLastMicroOp(bool isLastOp) { isLastMicroOp_ = isLastOp; } 67 | }; 68 | 69 | } // namespace simeng 70 | -------------------------------------------------------------------------------- /test/unit/MockMemoryInterface.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gmock/gmock.h" 4 | #include "simeng/memory/MemoryInterface.hh" 5 | 6 | namespace simeng { 7 | 8 | /** Mock implementation of memory::MemoryInterface */ 9 | class MockMemoryInterface : public memory::MemoryInterface { 10 | public: 11 | MOCK_METHOD2(requestRead, void(const memory::MemoryAccessTarget& target, 12 | uint64_t requestId)); 13 | 14 | MOCK_METHOD2(requestWrite, void(const memory::MemoryAccessTarget& target, 15 | const RegisterValue& data)); 16 | 17 | MOCK_CONST_METHOD0(getCompletedReads, const span()); 18 | 19 | MOCK_METHOD0(clearCompletedReads, void()); 20 | 21 | MOCK_CONST_METHOD0(hasPendingRequests, bool()); 22 | 23 | MOCK_METHOD0(tick, void()); 24 | }; 25 | 26 | } // namespace simeng 27 | -------------------------------------------------------------------------------- /test/unit/MockPortAllocator.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gmock/gmock.h" 4 | #include "simeng/pipeline/PortAllocator.hh" 5 | 6 | namespace simeng { 7 | namespace pipeline { 8 | 9 | /** Mock implementation of the `PortAllocator` interface. */ 10 | class MockPortAllocator : public pipeline::PortAllocator { 11 | public: 12 | MOCK_METHOD1(allocate, uint16_t(const std::vector& ports)); 13 | MOCK_METHOD1(issued, void(uint16_t port)); 14 | MOCK_METHOD1(deallocate, void(uint16_t port)); 15 | MOCK_METHOD1(setRSSizeGetter, 16 | void(std::function&)> rsSizes)); 17 | MOCK_METHOD0(tick, void()); 18 | }; 19 | 20 | } // namespace pipeline 21 | } // namespace simeng 22 | -------------------------------------------------------------------------------- /test/unit/PoolTest.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "gtest/gtest.h" 5 | #include "simeng/Pool.hh" 6 | 7 | namespace { 8 | 9 | // Tests that memory is reused correctly 10 | TEST(FixedPoolTest, MemoryReused) { 11 | auto p = simeng::fixedPool_<10, 2>(); 12 | void* ptr = p.allocate(); 13 | void* ptr2 = p.allocate(); 14 | // The pool will grow by 4. Total size is 6. 15 | void* ptr3 = p.allocate(); 16 | 17 | ASSERT_NE(ptr, nullptr); 18 | ASSERT_NE(ptr2, nullptr); 19 | ASSERT_NE(ptr3, nullptr); 20 | 21 | p.deallocate(ptr); 22 | p.deallocate(ptr2); 23 | p.deallocate(ptr3); 24 | 25 | void* ptr4 = p.allocate(); 26 | void* ptr5 = p.allocate(); 27 | void* ptr6 = p.allocate(); 28 | 29 | EXPECT_EQ(ptr3, ptr4); 30 | EXPECT_EQ(ptr5, ptr2); 31 | EXPECT_EQ(ptr6, ptr); 32 | } 33 | 34 | // Tests that the pointer returned by allocate is sufficiently aligned 35 | TEST(FixedPoolTest, Alignment) { 36 | auto p = simeng::fixedPool_<25>(); 37 | uintptr_t ptr = reinterpret_cast(p.allocate()); 38 | 39 | EXPECT_EQ(ptr & (alignof(std::max_align_t) - 1), 0); 40 | } 41 | 42 | // Tests general usage works correctly. To be tested with sanitizers 43 | TEST(FixedPoolTest, GeneralUsage) { 44 | std::mt19937 gen; 45 | std::uniform_int_distribution<> distribution(0, 1); 46 | 47 | auto p = simeng::fixedPool_<10>(); 48 | for (size_t i = 0; i < 65535; i++) { 49 | void* ptr = p.allocate(); 50 | 51 | // Allocation was successful. 52 | ASSERT_NE(ptr, nullptr); 53 | 54 | // Test that we can access all the bytes. 55 | memset(ptr, 0, 10); 56 | 57 | // Randomly deallocate to simulate real usage. 58 | if (distribution(gen)) p.deallocate(ptr); 59 | } 60 | } 61 | 62 | } // namespace 63 | -------------------------------------------------------------------------------- /test/unit/RegisterFileSetTest.cc: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "simeng/RegisterFileSet.hh" 3 | 4 | namespace simeng { 5 | namespace pipeline { 6 | 7 | class RegisterFileSetTest : public ::testing::Test { 8 | public: 9 | RegisterFileSetTest() : regFileSet(regFileStruct) {} 10 | 11 | protected: 12 | const std::vector regFileStruct = { 13 | {8, 10}, {24, 15}, {256, 31}}; 14 | 15 | RegisterFileSet regFileSet; 16 | }; 17 | 18 | // Ensure RegisterFileSet is constructed correctly 19 | TEST_F(RegisterFileSetTest, validConstruction) { 20 | for (uint8_t i = 0; i < regFileStruct.size(); i++) { 21 | for (uint16_t j = 0; j < regFileStruct[i].quantity; j++) { 22 | const Register reg = {i, j}; 23 | EXPECT_EQ(regFileSet.get(reg), RegisterValue(0, regFileStruct[i].bytes)); 24 | } 25 | } 26 | } 27 | 28 | // Ensure we can read and write values to the register file 29 | TEST_F(RegisterFileSetTest, readWrite) { 30 | for (uint8_t i = 0; i < regFileStruct.size(); i++) { 31 | const uint16_t regSize = regFileStruct[i].bytes; 32 | const uint16_t maxRegTag = regFileStruct[i].quantity - 1; 33 | const Register r0 = {i, 0}; 34 | const Register rMax = {i, maxRegTag}; 35 | 36 | EXPECT_EQ(regFileSet.get(r0), RegisterValue(0, regSize)); 37 | EXPECT_EQ(regFileSet.get(rMax), RegisterValue(0, regSize)); 38 | 39 | regFileSet.set(r0, RegisterValue(20, regSize)); 40 | regFileSet.set(rMax, RegisterValue(40, regSize)); 41 | 42 | EXPECT_EQ(regFileSet.get(r0), RegisterValue(20, regSize)); 43 | EXPECT_EQ(regFileSet.get(rMax), RegisterValue(40, regSize)); 44 | } 45 | } 46 | 47 | } // namespace pipeline 48 | } // namespace simeng -------------------------------------------------------------------------------- /test/unit/RegisterValueTest.cc: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "simeng/RegisterValue.hh" 3 | 4 | namespace { 5 | 6 | // Tests that we can create a RegisterValue 7 | TEST(RegisterValueTest, Create) { 8 | auto registerValue = simeng::RegisterValue(0, 8); 9 | EXPECT_EQ(registerValue.get(), 0); 10 | } 11 | 12 | // Tests that we can check that a RegisterValue holds no data 13 | TEST(RegisterValueTest, False) { EXPECT_FALSE(simeng::RegisterValue()); } 14 | 15 | // Tests that we can check that a RegisterValue holds data 16 | TEST(RegisterValueTest, True) { EXPECT_TRUE(simeng::RegisterValue(0, 8)); } 17 | 18 | // Tests that we can cast to different datatypes 19 | TEST(RegisterValueTest, Cast) { 20 | uint32_t value = 1; 21 | auto registerValue = simeng::RegisterValue(value, 8); 22 | EXPECT_EQ(registerValue.get(), 1); 23 | } 24 | 25 | // Tests that high bits are zeroed when initialised with a smaller datatype 26 | TEST(RegisterValueTest, MismatchedSizesZeroed) { 27 | uint32_t value = 0; 28 | auto registerValue = simeng::RegisterValue(value, 8); 29 | EXPECT_EQ(registerValue.get(), 0); 30 | } 31 | 32 | // Tests that low bits of stored values can be read correctly 33 | TEST(RegisterValueTest, Reinterpret) { 34 | uint32_t value = 0x101; 35 | auto registerValue = simeng::RegisterValue(value, 8); 36 | EXPECT_EQ(registerValue.get(), 1); 37 | } 38 | 39 | // Tests that larger datatypes can be read as vectors of smaller datatypes 40 | TEST(RegisterValueTest, Vector) { 41 | uint64_t value = 0x0000000200000001; 42 | auto registerValue = simeng::RegisterValue(value, 8); 43 | auto vector = registerValue.getAsVector(); 44 | EXPECT_EQ(vector[0], 1); 45 | EXPECT_EQ(vector[1], 2); 46 | } 47 | 48 | // Tests a register value can be zero-extended 49 | TEST(RegisterValueTest, ZeroExtend) { 50 | auto small = simeng::RegisterValue(1, 1); 51 | auto extended = small.zeroExtend(1, 8); 52 | EXPECT_EQ(extended.get(), 1); 53 | } 54 | 55 | TEST(RegisterValueTest, Array) { 56 | uint64_t arr[] = {0, 0xffffffffffffffff}; 57 | simeng::RegisterValue z_reg = {arr, 256}; 58 | EXPECT_EQ(z_reg.size(), 256); 59 | auto ptr = z_reg.getAsVector(); 60 | EXPECT_EQ(ptr[0], 0); 61 | EXPECT_EQ(ptr[1], 0xffffffffffffffff); 62 | EXPECT_EQ(ptr[2], 0); 63 | EXPECT_EQ(ptr[3], 0); 64 | } 65 | } // namespace 66 | -------------------------------------------------------------------------------- /test/unit/aarch64/ArchInfoTest.cc: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "simeng/arch/aarch64/ArchInfo.hh" 3 | #include "simeng/config/SimInfo.hh" 4 | #include "simeng/version.hh" 5 | 6 | namespace simeng { 7 | namespace arch { 8 | namespace aarch64 { 9 | 10 | class AArch64ArchInfoTest : public ::testing::Test { 11 | public: 12 | AArch64ArchInfoTest() { 13 | simeng::config::SimInfo::setConfig(SIMENG_SOURCE_DIR 14 | "/configs/a64fx_SME.yaml"); 15 | } 16 | 17 | protected: 18 | const std::vector sysRegisterEnums = { 19 | arm64_sysreg::ARM64_SYSREG_DCZID_EL0, 20 | arm64_sysreg::ARM64_SYSREG_FPCR, 21 | arm64_sysreg::ARM64_SYSREG_FPSR, 22 | arm64_sysreg::ARM64_SYSREG_TPIDR_EL0, 23 | arm64_sysreg::ARM64_SYSREG_MIDR_EL1, 24 | arm64_sysreg::ARM64_SYSREG_CNTVCT_EL0, 25 | arm64_sysreg::ARM64_SYSREG_PMCCNTR_EL0, 26 | arm64_sysreg::ARM64_SYSREG_SVCR}; 27 | 28 | const std::vector archRegStruct = { 29 | {8, 32}, 30 | {256, 32}, 31 | {32, 17}, 32 | {1, 1}, 33 | {8, static_cast(sysRegisterEnums.size())}, 34 | {256, 64}}; 35 | 36 | const std::vector physRegStruct = { 37 | {8, 96}, 38 | {256, 128}, 39 | {32, 48}, 40 | {1, 128}, 41 | {8, static_cast(sysRegisterEnums.size())}, 42 | {256, 128}}; 43 | 44 | const std::vector physRegQuants = { 45 | 96, 128, 48, 128, static_cast(sysRegisterEnums.size()), 128}; 46 | }; 47 | 48 | // Test for the getSysRegEnums() function 49 | TEST_F(AArch64ArchInfoTest, getSysRegEnums) { 50 | ArchInfo info = ArchInfo(config::SimInfo::getConfig()); 51 | EXPECT_EQ(info.getSysRegEnums(), sysRegisterEnums); 52 | } 53 | 54 | // Test for the getArchRegStruct() function 55 | TEST_F(AArch64ArchInfoTest, getArchRegStruct) { 56 | ArchInfo info = ArchInfo(config::SimInfo::getConfig()); 57 | EXPECT_EQ(info.getArchRegStruct(), archRegStruct); 58 | } 59 | 60 | // Test for the getPhysRegStruct() function 61 | TEST_F(AArch64ArchInfoTest, getPhysRegStruct) { 62 | ArchInfo info = ArchInfo(config::SimInfo::getConfig()); 63 | EXPECT_EQ(info.getPhysRegStruct(), physRegStruct); 64 | } 65 | 66 | // Test for the getPhysRegQuantities() function 67 | TEST_F(AArch64ArchInfoTest, getPhysRegQuantities) { 68 | ArchInfo info = ArchInfo(config::SimInfo::getConfig()); 69 | EXPECT_EQ(info.getPhysRegQuantities(), physRegQuants); 70 | } 71 | 72 | } // namespace aarch64 73 | } // namespace arch 74 | } // namespace simeng -------------------------------------------------------------------------------- /test/unit/data/stream-aarch64.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UoB-HPC/SimEng/7af3bc3fe7deb703756def31a208839bc5bdc4c2/test/unit/data/stream-aarch64.elf -------------------------------------------------------------------------------- /test/unit/data/stream.rv32ima.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UoB-HPC/SimEng/7af3bc3fe7deb703756def31a208839bc5bdc4c2/test/unit/data/stream.rv32ima.elf -------------------------------------------------------------------------------- /test/unit/pipeline/BalancedPortAllocatorTest.cc: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "simeng/pipeline/BalancedPortAllocator.hh" 3 | 4 | namespace simeng { 5 | namespace pipeline { 6 | 7 | // Tests that the balanced port allocator can correctly allocate a port 8 | TEST(BalancedPortAllocatorTest, Allocate) { 9 | std::vector> arrangement = {{0}}; 10 | auto simple = BalancedPortAllocator(arrangement); 11 | EXPECT_EQ(simple.allocate({0}), 0); 12 | } 13 | 14 | // Tests that the balanced port allocator selects the correct port when there's 15 | // multiple ports 16 | TEST(BalancedPortAllocatorTest, AllocateLimited) { 17 | auto limited = BalancedPortAllocator({{0}, {1}, {2}}); 18 | EXPECT_EQ(limited.allocate({1}), 1); 19 | } 20 | 21 | // Tests that the balanced port allocator will balance across two equal ports 22 | // when allocated in sequence 23 | TEST(BalancedPortAllocatorTest, BalanceEven) { 24 | auto portAllocator = BalancedPortAllocator({{0}, {1}}); 25 | auto first = portAllocator.allocate({0, 1}); 26 | auto second = portAllocator.allocate({0, 1}); 27 | EXPECT_NE(first, second); 28 | } 29 | 30 | // Tests that the balanced port allocator will choose the less-contended port in 31 | // an uneven port allocation 32 | TEST(BalancedPortAllocatorTest, BalanceUneven) { 33 | auto portAllocator = BalancedPortAllocator({{0}, {1}}); 34 | // Allocate for port 0 twice 35 | EXPECT_EQ(portAllocator.allocate({0}), 0); 36 | EXPECT_EQ(portAllocator.allocate({0}), 0); 37 | 38 | // Port 0 and 1 allocation should go to port 1 to be balanced 39 | EXPECT_EQ(portAllocator.allocate({0, 1}), 1); 40 | } 41 | 42 | // Tests that the balanced port allocator will take deallocations into account 43 | // when balancing 44 | TEST(BalancedPortAllocatorTest, Deallocate) { 45 | auto portAllocator = BalancedPortAllocator({{0, 1}, {0, 2}}); 46 | // Allocate to port 0 twice 47 | EXPECT_EQ(portAllocator.allocate({0}), 0); 48 | EXPECT_EQ(portAllocator.allocate({0}), 0); 49 | 50 | // Port 1 allocation 51 | EXPECT_EQ(portAllocator.allocate({1}), 1); 52 | 53 | // Deallocate twice from port 0 54 | portAllocator.deallocate(0); 55 | portAllocator.deallocate(0); 56 | 57 | // Next allocation should go to port 0, rather than port 1, if deallocation 58 | // was respected 59 | EXPECT_EQ(portAllocator.allocate({0, 1}), 0); 60 | } 61 | 62 | // Tests correct allocation when multiple ports support an instruction 63 | TEST(BalancedPortAllocatorTest, MultipleSupportedPorts) { 64 | auto portAllocator = 65 | BalancedPortAllocator({{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}}); 66 | 67 | // Ensure multi-port support is correctly balanced 68 | for (int i = 0; i < 10; i++) { 69 | EXPECT_EQ(portAllocator.allocate({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}), i); 70 | } 71 | } 72 | 73 | } // namespace pipeline 74 | } // namespace simeng 75 | -------------------------------------------------------------------------------- /test/unit/pipeline/MappedRegisterFileSetTest.cc: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "simeng/pipeline/MappedRegisterFileSet.hh" 3 | 4 | namespace simeng { 5 | namespace pipeline { 6 | 7 | class MappedRegisterFileSetTest : public ::testing::Test { 8 | public: 9 | MappedRegisterFileSetTest() 10 | : regFileSet(physRegFileStruct), 11 | rat(archRegFileStruct, physRegCounts), 12 | mappedRegFile(regFileSet, rat) {} 13 | 14 | protected: 15 | const std::vector archRegFileStruct = { 16 | {8, 10}, {24, 15}, {256, 31}}; 17 | const std::vector physRegFileStruct = { 18 | {8, 20}, {24, 30}, {256, 62}}; 19 | const std::vector physRegCounts = {20, 30, 62}; 20 | 21 | RegisterFileSet regFileSet; 22 | RegisterAliasTable rat; 23 | 24 | MappedRegisterFileSet mappedRegFile; 25 | }; 26 | 27 | // Ensure that with continually changing physical-architectural register mapping 28 | // changes, the correct register is being updated with set(). 29 | TEST_F(MappedRegisterFileSetTest, getSet) { 30 | // Loop through all register types 31 | for (uint8_t i = 0; i < archRegFileStruct.size(); i++) { 32 | // Keep allocating the same register to a) keep past values and b) more 33 | // easily verify correct functionality 34 | const uint16_t maxRegTag = archRegFileStruct[i].quantity - 1; 35 | const uint16_t regSize = archRegFileStruct[i].bytes; 36 | const Register rMax = {i, maxRegTag}; 37 | 38 | std::vector physRegs; 39 | for (int j = 2; j < 12; j++) { 40 | physRegs.push_back(rat.allocate(rMax)); 41 | RegisterValue regVal = RegisterValue(j, regSize); 42 | mappedRegFile.set(rMax, regVal); 43 | EXPECT_EQ(mappedRegFile.get(rMax), regVal); 44 | } 45 | 46 | for (int k = 0; k < 10; k++) { 47 | // RAT constructed where Arch-Phys mapping is 1:1. So, first re-mapped 48 | // value will be to maxArchRegRag + 1 49 | EXPECT_EQ(physRegs[k].tag, maxRegTag + k + 1); 50 | EXPECT_EQ(physRegs[k].type, i); 51 | EXPECT_EQ(regFileSet.get(physRegs[k]), RegisterValue(k + 2, regSize)); 52 | } 53 | } 54 | } 55 | } // namespace pipeline 56 | } // namespace simeng -------------------------------------------------------------------------------- /test/unit/pipeline/PipelineBufferTest.cc: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "simeng/pipeline/PipelineBuffer.hh" 3 | 4 | namespace simeng { 5 | namespace pipeline { 6 | 7 | class PipelineBufferTest : public ::testing::TestWithParam {}; 8 | 9 | // Test that we can create a pipeline buffer with a specified initial value 10 | TEST_P(PipelineBufferTest, Create) { 11 | auto pipelineBuffer = PipelineBuffer(GetParam(), 1); 12 | for (size_t i = 0; i < GetParam(); i++) { 13 | EXPECT_EQ(pipelineBuffer.getTailSlots()[i], 1); 14 | EXPECT_EQ(pipelineBuffer.getHeadSlots()[i], 1); 15 | } 16 | } 17 | 18 | // Test that values move when ticked 19 | TEST_P(PipelineBufferTest, Tick) { 20 | auto pipelineBuffer = PipelineBuffer(GetParam(), 0); 21 | for (size_t i = 0; i < GetParam(); i++) { 22 | pipelineBuffer.getTailSlots()[i] = i; 23 | } 24 | 25 | pipelineBuffer.tick(); 26 | 27 | for (size_t i = 0; i < GetParam(); i++) { 28 | EXPECT_EQ(pipelineBuffer.getTailSlots()[i], 0); 29 | EXPECT_EQ(pipelineBuffer.getHeadSlots()[i], i); 30 | } 31 | } 32 | 33 | // Test that values don't move once stalled 34 | TEST_P(PipelineBufferTest, Stall) { 35 | auto pipelineBuffer = PipelineBuffer(GetParam(), 0); 36 | for (size_t i = 0; i < GetParam(); i++) { 37 | pipelineBuffer.getTailSlots()[i] = i; 38 | } 39 | 40 | pipelineBuffer.stall(true); 41 | EXPECT_TRUE(pipelineBuffer.isStalled()); 42 | pipelineBuffer.tick(); 43 | 44 | for (size_t i = 0; i < GetParam(); i++) { 45 | EXPECT_EQ(pipelineBuffer.getTailSlots()[i], i); 46 | EXPECT_EQ(pipelineBuffer.getHeadSlots()[i], 0); 47 | } 48 | } 49 | 50 | // Test that filling the buffer works 51 | TEST_P(PipelineBufferTest, Fill) { 52 | auto pipelineBuffer = PipelineBuffer(GetParam(), 1); 53 | 54 | pipelineBuffer.fill(0); 55 | 56 | for (size_t i = 0; i < GetParam(); i++) { 57 | EXPECT_EQ(pipelineBuffer.getTailSlots()[i], 0); 58 | EXPECT_EQ(pipelineBuffer.getHeadSlots()[i], 0); 59 | } 60 | } 61 | 62 | INSTANTIATE_TEST_SUITE_P(PipelineBufferTests, PipelineBufferTest, 63 | ::testing::Range(1, 9, 1)); 64 | 65 | } // namespace pipeline 66 | } // namespace simeng 67 | -------------------------------------------------------------------------------- /test/unit/pipeline/WritebackUnitTest.cc: -------------------------------------------------------------------------------- 1 | #include "../MockInstruction.hh" 2 | #include "gmock/gmock.h" 3 | #include "gtest/gtest.h" 4 | #include "simeng/Instruction.hh" 5 | #include "simeng/RegisterFileSet.hh" 6 | #include "simeng/pipeline/PipelineBuffer.hh" 7 | #include "simeng/pipeline/WritebackUnit.hh" 8 | 9 | using ::testing::_; 10 | using ::testing::DoAll; 11 | using ::testing::Field; 12 | using ::testing::Return; 13 | using ::testing::SetArgReferee; 14 | 15 | namespace simeng { 16 | namespace pipeline { 17 | 18 | class PipelineWritebackUnitTest : public testing::Test { 19 | public: 20 | PipelineWritebackUnitTest() 21 | : input(1, {1, nullptr}), 22 | registerFileSet({{8, 2}}), 23 | uop(new MockInstruction), 24 | uopPtr(uop), 25 | writebackUnit(input, registerFileSet, [](auto insnId) {}) {} 26 | 27 | protected: 28 | std::vector>> input; 29 | RegisterFileSet registerFileSet; 30 | 31 | MockInstruction* uop; 32 | std::shared_ptr uopPtr; 33 | WritebackUnit writebackUnit; 34 | }; 35 | 36 | // Tests that a value is correctly written back, and the uop is cleared from the 37 | // buffer 38 | TEST_F(PipelineWritebackUnitTest, Tick) { 39 | input[0].getHeadSlots()[0] = uopPtr; 40 | uint64_t result = 1; 41 | std::vector results = {result}; 42 | std::vector destinations = {{0, 1}}; 43 | 44 | EXPECT_CALL(*uop, getResults()) 45 | .WillOnce(Return(span(results.data(), results.size()))); 46 | EXPECT_CALL(*uop, getDestinationRegisters()) 47 | .WillOnce( 48 | Return(span(destinations.data(), destinations.size()))); 49 | 50 | writebackUnit.tick(); 51 | 52 | EXPECT_EQ(registerFileSet.get(destinations[0]).get(), result); 53 | EXPECT_EQ(input[0].getHeadSlots()[0], nullptr); 54 | } 55 | 56 | } // namespace pipeline 57 | } // namespace simeng 58 | -------------------------------------------------------------------------------- /test/unit/riscv/ArchInfoTest.cc: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "simeng/arch/riscv/ArchInfo.hh" 3 | #include "simeng/config/SimInfo.hh" 4 | #include "simeng/version.hh" 5 | 6 | namespace simeng { 7 | namespace arch { 8 | namespace riscv { 9 | 10 | class RiscVArchInfoTest : public ::testing::Test { 11 | public: 12 | RiscVArchInfoTest() { 13 | simeng::config::SimInfo::setConfig(SIMENG_SOURCE_DIR 14 | "/configs/DEMO_RISCV.yaml"); 15 | } 16 | 17 | protected: 18 | const std::vector sysRegisterEnums = { 19 | simeng::arch::riscv::riscv_sysreg::RISCV_SYSREG_FFLAGS, 20 | simeng::arch::riscv::riscv_sysreg::RISCV_SYSREG_FRM, 21 | simeng::arch::riscv::riscv_sysreg::RISCV_SYSREG_FCSR, 22 | simeng::arch::riscv::riscv_sysreg::RISCV_SYSREG_CYCLE, 23 | simeng::arch::riscv::riscv_sysreg::RISCV_SYSREG_TIME, 24 | simeng::arch::riscv::riscv_sysreg::RISCV_SYSREG_INSTRET}; 25 | 26 | const std::vector archRegStruct = { 27 | {8, 32}, {8, 32}, {8, static_cast(sysRegisterEnums.size())}}; 28 | 29 | const std::vector physRegStruct = { 30 | {8, 154}, {8, 90}, {8, static_cast(sysRegisterEnums.size())}}; 31 | 32 | const std::vector physRegQuants = { 33 | 154, 90, static_cast(sysRegisterEnums.size())}; 34 | }; 35 | 36 | // Test for the getSysRegEnums() function 37 | TEST_F(RiscVArchInfoTest, getSysRegEnums) { 38 | ArchInfo info = ArchInfo(config::SimInfo::getConfig()); 39 | EXPECT_EQ(info.getSysRegEnums(), sysRegisterEnums); 40 | } 41 | 42 | // Test for the getArchRegStruct() function 43 | TEST_F(RiscVArchInfoTest, getArchRegStruct) { 44 | ArchInfo info = ArchInfo(config::SimInfo::getConfig()); 45 | EXPECT_EQ(info.getArchRegStruct(), archRegStruct); 46 | } 47 | 48 | // Test for the getPhysRegStruct() function 49 | TEST_F(RiscVArchInfoTest, getPhysRegStruct) { 50 | ArchInfo info = ArchInfo(config::SimInfo::getConfig()); 51 | EXPECT_EQ(info.getPhysRegStruct(), physRegStruct); 52 | } 53 | 54 | // Test for the getPhysRegQuantities() function 55 | TEST_F(RiscVArchInfoTest, getPhysRegQuantities) { 56 | ArchInfo info = ArchInfo(config::SimInfo::getConfig()); 57 | EXPECT_EQ(info.getPhysRegQuantities(), physRegQuants); 58 | } 59 | 60 | } // namespace riscv 61 | } // namespace arch 62 | } // namespace simeng --------------------------------------------------------------------------------