diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 799591c5ce..2605198ccf 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -29,3 +29,9 @@ c3bd8eb1214cbebbc92c7958b80aa06913bce3ba # A commit which ran flynt all Python files. e73655d038cdfa68964109044e33c9a6e7d85ac9 + +# A commit which ran pre-commit on ext/testlib +9e1afdecefaf910fa6e266f29dc480a32b0fa83e + +# Updated black from 22.6.0 to 23.9.1 +ddf6cb88e48df4ac7de4a9e4b612daf2e7e635c8 diff --git a/.gitignore b/.gitignore index 229a0d5ae9..d1904756d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build +gem5_build parser.out parsetab.py cscope.files @@ -32,3 +33,4 @@ configs/example/memcheck.cfg configs/dram/lowp_sweep.cfg .pyenv .vscode +typings diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8cbc6afdb7..5183f38110 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,4 @@ +--- # Copyright (c) 2022 Arm Limited # All rights reserved. # @@ -33,57 +34,77 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -minimum_pre_commit_version: "2.18" +minimum_pre_commit_version: '2.18' default_language_version: - python: python3 + python: python3 exclude: | - (?x)^( - ext/.*| - build/.*| - src/systemc/ext/.*| - src/systemc/tests/.*/.*| - src/python/m5/ext/pyfdt/.*| - tests/.*/ref/.* - )$ + (?x)^( + ext/(?!testlib/).*| + build/.*| + src/systemc/ext/.*| + src/systemc/tests/.*/.*| + src/python/m5/ext/pyfdt/.*| + tests/.*/ref/.* + )$ default_stages: [commit] repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-json - - id: check-yaml - - id: check-added-large-files - - id: mixed-line-ending - args: [--fix=lf] - - id: check-case-conflict -- repo: https://github.com/psf/black - rev: 22.6.0 - hooks: - - id: black -- repo: local - hooks: - - id: gem5-style-checker - name: gem5 style checker - entry: util/git-pre-commit.py - always_run: true - exclude: ".*" - language: system - description: 'The gem5 style checker hook.' - - id: gem5-commit-msg-checker - name: gem5 commit msg checker - entry: ext/git-commit-msg - language: system - stages: [commit-msg] - description: 'The gem5 commit message checker hook.' - - id: gerrit-commit-msg-job - name: gerrit commit message job - entry: util/gerrit-commit-msg-hook - language: system - stages: [commit-msg] - description: 'Adds Change-ID to the commit message. Needed by Gerrit.' + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-json + - id: check-yaml + - id: check-added-large-files + - id: mixed-line-ending + args: [--fix=lf] + - id: check-ast + - id: check-case-conflict + - id: check-merge-conflict + - id: check-symlinks + - id: destroyed-symlinks + - id: requirements-txt-fixer + - repo: https://github.com/PyCQA/isort + rev: 5.11.5 + hooks: + - id: isort + - repo: https://github.com/jumanjihouse/pre-commit-hook-yamlfmt + rev: 0.2.3 + hooks: + - id: yamlfmt + - repo: https://github.com/psf/black + rev: 23.9.1 + hooks: + - id: black + - repo: https://github.com/asottile/pyupgrade + rev: v3.14.0 + hooks: + - id: pyupgrade + # Python 3.8 is the earliest version supported. + # We therefore conform to the standards compatible with 3.8+. + args: [--py38-plus] + - repo: local + hooks: + - id: gem5-style-checker + name: gem5 style checker + entry: util/git-pre-commit.py + always_run: true + exclude: .* + language: system + description: The gem5 style checker hook. + - id: gem5-commit-msg-checker + name: gem5 commit msg checker + entry: ext/git-commit-msg + language: system + stages: [commit-msg] + description: The gem5 commit message checker hook. + - id: gerrit-commit-msg-job + name: gerrit commit message job + entry: util/gerrit-commit-msg-hook + language: system + stages: [commit-msg] + description: Adds Change-ID to the commit message. Needed by Gerrit. diff --git a/MAINTAINERS.yaml b/MAINTAINERS.yaml index c8b8957496..ebf5e3a5e7 100644 --- a/MAINTAINERS.yaml +++ b/MAINTAINERS.yaml @@ -1,23 +1,23 @@ +--- # See CONTRIBUTING.md for details of gem5's contribution process. # # This file contains a list of gem5's subsystems and their -# maintainers. The key used to identifity a subsystem should be used -# as a tag in commit messages targetting that subsystem. At least one -# (not all) of these maintainers must review the patch before it can -# be pushed. These people will automatically be emailed when you -# upload the patch to Gerrit (https://gem5-review.googlesource.com). -# These subsystem keys mostly follow the directory structure. +# maintainers. The key used to identify a subsystem should be used +# as a tag in commit messages targeting that subsystem. Via our GitHub +# Pull Request system (https://github.com/gem5/gem5/pulls) a maintainer +# of the subsystem impacted by a pull request contribution will be added +# as an assignee to that pull request. Their role is be to referee the +# contribution (add a review, assign reviewers, suggest changes, etc.), then +# merge the contribution into the gem5 develop branch when they are satisfied +# with the change. # -# Maintainers have the following responsibilities: -# 1. That at least one maintainer of each subsystem reviews all -# changes to that subsystem (they will be automatically tagged and -# emailed on each new change). -# 2. They will complete your reviews in a timely manner (within a few -# business days). -# 3. They pledge to uphold gem5's community standards and its code of -# conduct by being polite and professional in their code -# reviews. See CODE-OF-CONDUCT.md. +# Maintainers assigned to a pull request are expected to acknowledge their +# assignment in 2 business days and to fully begin refereeing the contribution +# within a business week. # +# Maintainers pledge to uphold gem5's community standards and its code of +# conduct by being polite and professional in their interactions with +# contributors. See CODE-OF-CONDUCT.md. # # Entries in this file have the following format: # key: @@ -27,310 +27,260 @@ # maintainers: # - John Doe # - Jane Doe -# +# experts: +# - Jack Doe +# - Jill Doe # # The status field should have one of the following values: # - maintained: The component has an active maintainer. # - orphaned: The component is looking for a new owner. - - -pmc: - desc: >- - PMC Members (general maintainers): - status: maintained - maintainers: - - Andreas Sandberg - - Brad Beckmann - - David Wood - - Gabe Black - - Giacomo Travaglini - - Jason Lowe-Power (chair) - - Matt Sinclair - - Tony Gutierrez - - Steve Reinhardt +# +# The experts field is optional and used to identify people who are +# knowledgeable about the subsystem but are not responsible for it. Those +# listed as an expert are typically good to add as a reviewer for pull requests +# targeting that subsystem. arch: - desc: >- - General architecture-specific components - status: maintained - maintainers: - - Gabe Black + desc: >- + General architecture-specific components + status: orphaned arch-arm: - status: maintained - maintainers: - - Andreas Sandberg - - Giacomo Travaglini + status: maintained + maintainers: + - Giacomo Travaglini + - Andreas Sandberg arch-gcn3: - status: maintained - maintainers: - - Matt Poremba - - Matt Sinclair + status: maintained + maintainers: + - Matt Sinclair + - Matt Poremba arch-vega: - status: maintained - maintainers: - - Matt Poremba - - Matt Sinclair + status: maintained + maintainers: + - Matt Sinclair + - Matt Poremba arch-mips: - status: orphaned + status: orphaned arch-power: - status: maintained - maintainers: - - Boris Shingarov + status: orphaned arch-riscv: - status: orphaned + status: orphaned arch-sparc: - status: maintained - maintainers: - - Gabe Black + status: orphaned arch-x86: - status: maintained - maintainers: - - Gabe Black + status: orphaned base: - status: maintained - maintainers: - - Bobby Bruce - - Daniel Carvalho + status: orphaned base-stats: - status: orphaned + status: orphaned configs: - status: maintained - maintainers: - - Jason Lowe-Power + status: orphaned + experts: + - Jason Lowe-Power cpu: - desc: >- - General changes to all CPU models (e.g., BaseCPU) - status: maintained - maintainers: - - Gabe Black - - Jason Lowe-Power + desc: >- + General changes to all CPU models (e.g., BaseCPU) + status: orphaned + experts: + - Jason Lowe-Power cpu-kvm: - status: maintained - maintainers: - - Andreas Sandberg + status: maintained + maintainers: + - Andreas Sandberg cpu-minor: - status: maintained - maintainers: - - Zhengrong Wang + status: orphaned cpu-o3: - status: orphaned + status: orphaned cpu-simple: - status: maintained - maintainers: - - Jason Lowe-Power - - Gabe Black + status: orphaned + experts: + - Jason Lowe-Power dev: - status: maintained - maintainers: - - Gabe Black + status: orphaned dev-hsa: - status: maintained - maintainers: - - Matt Poremba + status: maintained + maintainers: + - Matt Poremba dev-amdgpu: - status: maintained - maintainers: - - Matt Poremba + status: maintained + maintainers: + - Matt Poremba dev-virtio: - status: maintained - maintainers: - - Andreas Sandberg + status: maintained + maintainers: + - Andreas Sandberg dev-arm: - status: maintained - maintainers: - - Andreas Sandberg - - Giacomo Travaglini + status: maintained + maintainers: + - Giacomo Travaglini + - Andreas Sandberg doc: - status: maintained - maintainers: - - Bobby Bruce + status: orphaned ext: - desc: >- - Components external to gem5 - status: maintained - maintainers: - - Bobby Bruce - - Jason Lowe-Power + desc: >- + Components external to gem5 + status: orphaned + experts: + - Jason Lowe-Power ext-testlib: - status: maintained - maintainers: - - Bobby Bruce - - Hoa Nguyen + status: orphaned + experts: + - Bobby R. Bruce fastmodel: - desc: >- - Changes relating to ARM Fast Models - status: maintained - maintainers: - - Gabe Black + desc: >- + Changes relating to ARM Fast Models + status: orphaned gpu-compute: - status: maintained - maintainers: - - Matt Poremba - - Matt Sinclair + status: maintained + maintainers: + - Matt Poremba learning-gem5: - desc: >- - The code and configs for the Learning gem5 book - status: maintained - maintainers: - - Jason Lowe-Power + desc: >- + The code and configs for the Learning gem5 book + status: orphaned + experts: + - Jason Lowe-Power + - Bobby R. Bruce stdlib: - desc: >- - The gem5 standard library found under `src/python/gem5` - status: maintained - maintainers: - - Bobby R. Bruce + desc: >- + The gem5 standard library found under `src/python/gem5` + status: maintained + maintainers: + - Bobby R. Bruce mem: - desc: >- - General memory system (e.g., XBar, Packet) - status: maintained - maintainers: - - Nikos Nikoleris + desc: >- + General memory system (e.g., XBar, Packet) + status: orphaned mem-cache: - desc: >- - Classic caches and coherence - status: maintained - maintainers: - - Nikos Nikoleris - - Daniel Carvalho + desc: >- + Classic caches and coherence + status: orphaned mem-dram: - status: maintained - maintainers: - - Nikos Nikoleris + status: orphaned mem-garnet: - desc: >- - Garnet subcomponent of Ruby - status: maintained - maintainers: - - Srikant Bharadwaj + desc: >- + Garnet subcomponent of Ruby + status: orphaned mem-ruby: - desc: >- - Ruby structures and protocols - status: maintained - maintainers: - - Jason Lowe-Power - - Matt Sinclair + desc: >- + Ruby structures and protocols + status: maintained + maintainers: + - Matt Sinclair + experts: + - Jason Lowe-Power misc: - desc: >- - Anything outside of the other categories - status: maintained - maintainers: - - Bobby Bruce - - Jason Lowe-Power + desc: >- + Anything outside of the other categories + status: orphaned + experts: + - Jason Lowe-Power python: - desc: >- - Python SimObject wrapping and infrastructure - status: maintained - maintainers: - - Andreas Sandberg - - Jason Lowe-Power + desc: >- + Python SimObject wrapping and infrastructure + status: orphaned + experts: + - Jason Lowe-Power + - Andreas Sandberg resources: - desc: >- - The gem5-resources repo with auxiliary resources for simulation - status: maintained - maintainers: - - Bobby Bruce - - Jason Lowe-Power + desc: >- + The gem5-resources repo with auxiliary resources for simulation + status: maintained + maintainers: + - Bobby R. Bruce + experts: + - Jason Lowe-Power scons: - desc: >- - Build system - status: maintained - maintainers: - - Gabe Black + desc: >- + Build system + status: orphaned sim: - desc: >- - General simulation components - status: maintained - maintainers: - - Jason Lowe-Power + desc: >- + General simulation components + status: orphaned + experts: + - Jason Lowe-Power sim-se: - desc: >- - Syscall emulation - status: orphaned + desc: >- + Syscall emulation + status: orphaned system-arm: - status: maintained - maintainers: - - Andreas Sandberg - - Giacomo Travaglini + status: maintained + maintainers: + - Giacomo Travaglini + - Andreas Sandberg systemc: - desc: >- - Code for the gem5 SystemC implementation and interface - status: maintained - maintainers: - - Gabe Black + desc: >- + Code for the gem5 SystemC implementation and interface + status: orphaned tests: - desc: >- - testing changes - status: maintained - maintainers: - - Bobby Bruce + desc: >- + testing changes + status: maintained + maintainers: + - Bobby R. Bruce util: - status: maintained - maintainers: - - Gabe Black + status: orphaned util-docker: - status: maintained - maintainers: - - Bobby Bruce + status: maintained + maintainers: + - Bobby R. Bruce util-m5: - status: maintained - maintainers: - - Gabe Black + status: orphaned util-gem5art: - status: maintained - maintainers: - - Bobby Bruce - - Jason Lowe-Power + status: orphaned website: - desc: >- - The gem5-website repo which contains the gem5.org site - status: maintained - maintainers: - - Bobby Bruce - - Hoa Nguyen + desc: >- + The gem5-website repo which contains the gem5.org site + status: maintained + maintainers: + - Bobby R. Bruce + experts: + - Jason Lowe-Power diff --git a/README.md b/README.md index bd6db2fbd7..6ca80bd0ef 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,15 @@ system software changes, and compile-time and run-time system optimizations. The main website can be found at . +## Testing status + +**Note**: These regard tests run on the develop branch of gem5: +. + +[![Daily Tests](https://github.com/gem5/gem5/actions/workflows/daily-tests.yaml/badge.svg)](https://github.com/gem5/gem5/actions/workflows/daily-tests.yaml) +[![Weekly Tests](https://github.com/gem5/gem5/actions/workflows/weekly-tests.yaml/badge.svg)](https://github.com/gem5/gem5/actions/workflows/weekly-tests.yaml) +[![Compiler Tests](https://github.com/gem5/gem5/actions/workflows/compiler-tests.yaml/badge.svg)](https://github.com/gem5/gem5/actions/workflows/compiler-tests.yaml) + ## Getting started A good starting point is , and for @@ -29,8 +38,8 @@ Once you have all dependencies resolved, execute `scons build/ALL/gem5.opt` to build an optimized version of the gem5 binary (`gem5.opt`) containing all gem5 ISAs. If you only wish to compile gem5 to include a single ISA, you can replace `ALL` with the name of the ISA. Valid -options include `ARM`, `NULL`, `MIPS`, `POWER`, `SPARC`, and `X86` The complete -list of options can be found in the build_opts directory. +options include `ARM`, `NULL`, `MIPS`, `POWER`, `RISCV`, `SPARC`, and `X86` +The complete list of options can be found in the build_opts directory. See https://www.gem5.org/documentation/general_docs/building for more information on building gem5. diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 1e3a7fbadb..d99e7226f3 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,3 +1,133 @@ +# Version 23.1 + +gem5 Version 23.1 is our first release where the development has been on GitHub. +During this release, there have been 362 pull requests merged which comprise 416 commits with 51 unique contributors. + +## Significant API and user-facing changes + +### The gem5 build can is now configured with `kconfig` + +- Most gem5 builds without customized options (excluding double dash options) (e.g. , build/X86/gem5.opt) are backwards compatible and require no changes to your current workflows. +- All of the default builds in `build_opts` are unchanged and still available. +- However, if you want to specialize your build. For example, use customized ruby protocol. The command `scons PROTOCOL= build/ALL/gem5.opt` will not work anymore. you now have to use `scons ` to update the ruby protocol as example. The double dash options (`--without-tcmalloc`, `--with-asan` and so on) are still continue to work as normal. +- For more details refer to the documentation here: [kconfig documentation](https://www.gem5.org/documentation/general_docs/kconfig_build_system/) + +### Standard library improvements + +#### `WorkloadResource` added to resource specialization + +- The `Workload` and `CustomWorkload` classes are now deprecated. They have been transformed into wrappers for the `obtain_resource` and `WorkloadResource` classes in `resource.py`, respectively. +- Code utilizing the older API will continue to function as expected but will trigger a warning message. To update code using the `Workload` class, change the call from `Workload(id='resource_id', resource_version='1.0.0')` to `obtain_resource(id='resource_id', resource_version='1.0.0')`. Similarly, to update code using the `CustomWorkload` class, change the call from `CustomWorkload(function=func, parameters=params)` to `WorkloadResource(function=func, parameters=params)`. +- Workload resources in gem5 can now be directly acquired using the `obtain_resource` function, just like other resources. + +#### Introducing Suites + +Suites is a new category of resource being introduced in gem5. Documentation of suites can be found here: [suite documentation](https://www.gem5.org/documentation/gem5-stdlib/suites). + +#### Other API changes + +- All resource object now have their own `id` and `category`. Each resource class has its own `__str__()` function which return its information in the form of **category(id, version)** like **BinaryResource(id='riscv-hello', resource_version='1.0.0')**. +- Users can use GEM5_RESOURCE_JSON and GEM5_RESOURCE_JSON_APPEND env variables to overwrite all the data sources with the provided JSON and append a JSON file to all the data source respectively. More information can be found [here](https://www.gem5.org/documentation/gem5-stdlib/using-local-resources). + +### Other user-facing changes + +- Added support for clang 15 and clang 16 +- gem5 no longer supports building on Ubuntu 18.04 +- GCC 7, GCC 9, and clang 6 are no longer supported +- Two `DRAMInterface` stats have changed names (`bytesRead` and `bytesWritten`). For instance, `board.memory.mem_ctrl.dram.bytesRead` and `board.memory.mem_ctrl.dram.bytesWritten`. These are changed to `dramBytesRead` and `dramBytesWritten` so they don't collide with the stat with the same name in `AbstractMemory`. +- The stats for `NVMInterface` (`bytesRead` and `bytesWritten`) have been change to `nvmBytesRead` and `nvmBytesWritten` as well. + +## Full-system GPU model improvements + +- Support for up to latest ROCm 5.7.1. +- Various changes to enable PyTorch/TensorFlow simulations. +- New packer disk image script containing ROCm 5.4.2, PyTorch 2.0.1, and Tensorflow 2.11. +- GPU instructions can now perform atomics on host addresses. +- The provided configs scripts can now run KVM on more restrictive setups. +- Add support to checkpoint and restore between kernels in GPUFS, including adding various AQL, HSA Queue, VMID map, MQD attributes, GART translations, and PM4Queues to GPU checkpoints +- move GPU cache recorder code to RubyPort instead of Sequencer/GPUCoalescer to allow checkpointing to occur +- add support for flushing GPU caches, as well as cache cooldown/warmup support, for checkpoints +- Update vega10_kvm.py to add checkpointing instructions + +## SE mode GPU model improvements + +- started adding support for mmap'ing inputs for GPUSE tests, which reduces their runtime by 8-15% per run + +## GPU model improvements + +- update GPU VIPER and Coalescer support to ensure correct replacement policy behavior when multiple requests from the same CU are concurrently accessing the same line +- fix bug with GPU VIPER to resolve a race conflict for loads that bypass the TCP (L1D$) +- fix bug with MRU replacement policy updates in GPU SQC (I$) +- update GPU and Ruby debug prints to resolve various small errors +- Add configurable GPU L1,L2 num banks and L2 latencies +- Add decodings for new MI100 VOP2 insts +- Add GPU GLC Atomic Resource Constraints to better model how atomic resources are shared at GPU TCC (L2$) +- Update GPU tester to work with both requests that bypass all caches (SLC) and requests that bypass only the TCP (L1D$) +- Fixes for how write mask works for GPU WB L2 caches +- Added support for WB and WT GPU atomics +- Added configurable support to better model the latency of GPU atomic requests +- fix GPU's default number of HW barrier/CU to better model amount of concurrency GPU CUs should have + +## RISC-V RVV 1.0 implemented + +This was a huge undertaking by a large number of people! +Some of these people include Adrià Armejach who pushed it over the finish line, Xuan Hu who pushed the most recent version to gerrit that Adrià picked up, +Jerin Joy who did much of the initial work, and many others who contributed to the implementation including Roger Chang, Hoa Nguyen who put significant effort into testing and reviewing the code. + +- Most of the instructions in the 1.0 spec implemented +- Works with both FS and SE mode +- Compatible with Simple CPUs, the O3, and the minor CPU models +- User can specify the width of the vector units +- Future improvements + - Widening/narrowing instructions are *not* implemented + - The model for executing memory instructions is not very high performance + - The statistics are not correct for counting vector instruction execution + +## ArmISA changes/improvements + +- Architectural support for the following extensions: +* FEAT_TLBIRANGE +* FEAT_FGT +* FEAT_TCR2 +* FEAT_SCTLR2 + +- Arm support for SVE instructions improved +- Fixed some FEAT_SEL2 related issues: + - [Fix virtual interrupt logic in secure mode](https://github.com/gem5/gem5/pull/584) + - [Make interrupt masking handle VHE/SEL2 cases](https://github.com/gem5/gem5/pull/430) +- Removed support for Arm Jazelle and ThumbEE +- Implementation of an Arm Capstone Disassembler + +## Other notable changes/improvements + +- Improvements to the CHI coherence protocol implementation +- Far atomics implemented in CHI +- Ruby now supports using the prefetchers from the classic caches, if the protocol supports it. CHI has been extended to support the classic prefetchers. +- Bug in RISC-V TLB to fixed to correctly count misses and hits +- Added new RISC-V Zcb instructions https://github.com/gem5/gem5/pull/399 +- RISC-V can now use a separate binary for the bootloader and kernel in FS mode +- DRAMSys integration updated to latest DRAMSys version (5.0) +- Improved support for RISC-V privilege modes +- Fixed bug in switching CPUs with RISC-V +- CPU branch preditor refactoring to prepare for decoupled front end support +- Perf is now optional when using the KVM CPU model +- Improvements to the gem5-SST bridge including updating to SST 13.0 +- Improved formatting of documentation in stdlib +- By default use isort for python imports in style +- Many, many testing improvements during the migration to GitHub actions +- Fixed the elastic trace replaying logic (TraceCPU) + +## Known Bugs/Issues + +- [RISC-V RVV Bad execution of riscv rvv vss instruction](https://github.com/gem5/gem5/issues/594) +- [RISC-V Vector Extension float32_t bugs/unsupported widening instructions](https://github.com/gem5/gem5/issues/442) +- [Implement AVX xsave/xstor to avoid workaround when checkpointing](https://github.com/gem5/gem5/issues/434) +- [Adding Vector Segmented Loads/Stores to RISC-V V 1.0 implementation](https://github.com/gem5/gem5/issues/382) +- [Integer overflow in AddrRange subset check](https://github.com/gem5/gem5/issues/240) +- [RISCV64 TLB refuses to access upper half of physical address space](https://github.com/gem5/gem5/issues/238) +- [Bug when trying to restore checkpoints in SPARC: “panic: panic condition !pte occurred: Tried to execute unmapped address 0.”](https://github.com/gem5/gem5/issues/197) +- [BaseCache::recvTimingResp can trigger an assertion error from getTarget() due to MSHR in senderState having no targets](https://github.com/gem5/gem5/issues/100) + # Version 23.0.1.0 This minor release incorporates documentation updates, bug fixes, and some minor improvements. diff --git a/SConsopts b/SConsopts deleted file mode 100644 index bb2de864b5..0000000000 --- a/SConsopts +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2013, 2015-2020 ARM Limited -# All rights reserved. -# -# The license below extends only to copyright in the software and shall -# not be construed as granting a license to any other intellectual -# property including but not limited to intellectual property relating -# to a hardware implementation of the functionality of the software -# licensed hereunder. You may use the software subject to the license -# terms below provided that you ensure that this notice is replicated -# unmodified and in its entirety in all distributions of the software, -# modified or unmodified, in source code or in binary form. -# -# Copyright (c) 2011 Advanced Micro Devices, Inc. -# Copyright (c) 2009 The Hewlett-Packard Development Company -# Copyright (c) 2004-2005 The Regents of The University of Michigan -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import os -import os.path - -from gem5_scons import warning - -Import('*') - -sticky_vars.AddVariables( - ('BATCH', 'Use batch pool for build and tests', False), - ('BATCH_CMD', 'Batch pool submission command name', 'qdo'), - ('M5_BUILD_CACHE', 'Cache built objects in this directory', False), - ('USE_EFENCE', 'Link with Electric Fence malloc debugger', False), - ) diff --git a/SConstruct b/SConstruct index 4fe2f64366..ef4d154312 100755 --- a/SConstruct +++ b/SConstruct @@ -44,15 +44,6 @@ # # SCons top-level build description (SConstruct) file. # -# While in this directory ('gem5'), just type 'scons' to build the default -# configuration (see below), or type 'scons build//' -# to build some other configuration (e.g., 'build/X86/gem5.opt' for -# the optimized X86 version). -# -# You can build gem5 in a different directory as long as there is a -# 'build/' somewhere along the target path. The build system -# expects that all configs under the same build directory are being -# built for the same host system. # # Examples: # @@ -77,10 +68,11 @@ # Global Python imports import atexit +import itertools import os import sys -from os import mkdir, remove, environ +from os import mkdir, remove, environ, listdir from os.path import abspath, dirname, expanduser from os.path import isdir, isfile from os.path import join, split @@ -115,8 +107,6 @@ AddOption('--no-colors', dest='use_colors', action='store_false', help="Don't add color to abbreviated scons output") AddOption('--with-cxx-config', action='store_true', help="Build with support for C++-based configuration") -AddOption('--default', - help='Override which build_opts file to use for defaults') AddOption('--ignore-style', action='store_true', help='Disable style checking hooks') AddOption('--linker', action='store', default=None, choices=linker_options, @@ -141,6 +131,8 @@ AddOption('--with-systemc-tests', action='store_true', help='Build systemc tests') AddOption('--install-hooks', action='store_true', help='Install revision control hooks non-interactively') +AddOption('--limit-ld-memory-usage', action='store_true', + help='Tell ld, the linker, to reduce memory usage.') AddOption('--gprof', action='store_true', help='Enable support for the gprof profiler') AddOption('--pprof', action='store_true', @@ -162,6 +154,7 @@ sys.path[1:1] = [ Dir('#build_tools').abspath ] # declared above. from gem5_scons import error, warning, summarize_warnings, parse_build_path from gem5_scons import TempFileSpawn, EnvDefaults, MakeAction, MakeActionTool +from gem5_scons import kconfig import gem5_scons from gem5_scons.builders import ConfigFile, AddLocalRPATH, SwitchingHeaders from gem5_scons.builders import Blob @@ -205,7 +198,71 @@ if not ('CC' in main and 'CXX' in main): error("No C++ compiler installed (package g++ on Ubuntu and RedHat)") # Find default configuration & binary. -Default(environ.get('M5_DEFAULT_BINARY', 'build/ARM/gem5.debug')) +default_target = environ.get('M5_DEFAULT_BINARY', None) +if default_target: + Default(default_target) + +# If no target is set, even a default, print help instead. +if not BUILD_TARGETS: + warning("No target specified, and no default.") + SetOption('help', True) + +buildopts_dir = Dir('#build_opts') +buildopts = list([f for f in os.listdir(buildopts_dir.abspath) if + isfile(os.path.join(buildopts_dir.abspath, f))]) +buildopts.sort() + +buildopt_list = '\n'.join(' ' * 10 + buildopt for buildopt in buildopts) + +Help(f""" +Targets: + To build gem5 using a predefined configuration, use a target with + a directory called "build" in the path, followed by a directory named + after a predefined configuration in "build_opts" directory, and then + the actual target, likely a gem5 binary. For example: + + scons build/ALL/gem5.opt + + The "build" component tells SCons that the next part names an initial + configuration, and the part after that is the actual target. + The predefined targets currently available are: + +{buildopt_list} + + The extension on the gem5 binary specifies what type of binary to + build. Options are: + + debug: A debug binary with optimizations turned off and debug info + turned on. + opt: An optimized binary with debugging still turned on. + fast: An optimized binary with debugging, asserts, and tracing + disabled. + + gem5 can also be built as a static or dynamic library. In that case, + the extension is determined by the operating system, so the binary type + is part of the target file name. For example: + + scons build/ARM/libgem5_opt.so + + In MacOS, the extension should change to "dylib" like this: + + scons build/ARM/libgem5_opt.dylib + + To build unit tests, you can use a target like this: + + scons build/RISCV/unittests.debug + + The unittests.debug part of the target is actual a directory which + holds the results for all the unit tests built with the "debug" + settings. When that's used as the target, SCons will build all the + files under that directory, which will run all the tests. + + To build and run an individual test, you can built it's binary + specifically and then run it manually: + + scons build/SPARC/base/bitunion.test.opt + build/SPARC/base/bitunion.test.opt +""", append=True) ######################################################################## @@ -215,52 +272,134 @@ Default(environ.get('M5_DEFAULT_BINARY', 'build/ARM/gem5.debug')) # ######################################################################## -# helper function: find last occurrence of element in list -def rfind(l, elt, offs = -1): - for i in range(len(l)+offs, 0, -1): - if l[i] == elt: - return i - raise ValueError("element not found") +kconfig_actions = ( + 'defconfig', + 'guiconfig', + 'listnewconfig', + 'menuconfig', + 'oldconfig', + 'olddefconfig', + 'savedefconfig', + 'setconfig', +) + +Help(""" +Kconfig: + In addition to the default configs, you can also create your own + configs, or edit one that already exists. To use one of the kconfig + tools with a particular directory, use a target which is the directory + to configure, and then the name of the tool. For example, to run + menuconfig on directory build_foo/bar, run: + + scons menuconfig build_foo/bar + + will set up a build directory in build_foo/bar if one doesn't already + exist, and open the menuconfig editor to view/set configuration + values. + +Kconfig tools: + defconfig: + Set up a config using values specified in a defconfig file, or if no + value is given, use the default. The second argument specifies the + defconfig file. A defconfig file in the build_opts directory can be + implicitly specified in the build path via `build//` + + scons defconfig build_foo/bar build_opts/MIPS + + + guiconfig: + Opens the guiconfig editor which will let you view and edit config + values, and view help text. guiconfig runs as a graphical application. + + scons guiconfig build_foo/bar + + + listnewconfig: + Lists config options which are new in the Kconfig and which are not + currently set in the existing config file. + + scons listnewconfig build_foo/bar + + + menuconfig: + Opens the menuconfig editor which will let you view and edit config + values, and view help text. menuconfig runs in text mode. + + scons menuconfig build_foo/bar + + + oldconfig: + Update an existing config by adding settings for new options. This is + the same as the olddefconfig tool, except it asks what values you want + for the new settings. + + scons oldconfig build_foo/bar + + + olddefconfig: + Update an existing config by adding settings for new options. This is + the same as the oldconfig tool, except it uses the default for any new + setting. + + scons olddefconfig build_foo/bar + + + savedefconfig: + Save a defconfig file which would give rise to the current config. + For instance, you could use menuconfig to set up a config how you want + it with the options you cared about, and then use savedefconfig to save + a minimal config file. These files would be suitable to use in the + defconfig directory. The second argument specifies the filename for + the new defconfig file. + + scons savedefconfig build_foo/bar new_def_config + + + setconfig: + Set values in an existing config directory as specified on the command + line. For example, to enable gem5's built in systemc kernel: + + scons setconfig build_foo/bar USE_SYSTEMC=y +""", append=True) # Take a list of paths (or SCons Nodes) and return a list with all # paths made absolute and ~-expanded. Paths will be interpreted # relative to the launch directory unless a different root is provided + +def makePathAbsolute(path, root=GetLaunchDir()): + return abspath(os.path.join(root, expanduser(str(path)))) def makePathListAbsolute(path_list, root=GetLaunchDir()): - return [abspath(os.path.join(root, expanduser(str(p)))) - for p in path_list] + return [makePathAbsolute(p, root) for p in path_list] -# Each target must have 'build' in the interior of the path; the -# directory below this will determine the build parameters. For -# example, for target 'foo/bar/build/X86/arch/x86/blah.do' we -# recognize that X86 specifies the configuration because it -# follow 'build' in the build path. +if BUILD_TARGETS and BUILD_TARGETS[0] in kconfig_actions: + # The build targets are really arguments for the kconfig action. + kconfig_args = BUILD_TARGETS[:] + BUILD_TARGETS[:] = [] -# The funky assignment to "[:]" is needed to replace the list contents -# in place rather than reassign the symbol to a new list, which -# doesn't work (obviously!). -BUILD_TARGETS[:] = makePathListAbsolute(BUILD_TARGETS) + kconfig_action = kconfig_args[0] + if len(kconfig_args) < 2: + error(f'Missing arguments for kconfig action {kconfig_action}') + dir_to_configure = makePathAbsolute(kconfig_args[1]) -# Generate a list of the unique build roots and configs that the -# collected targets reference. -variant_paths = set() -build_root = None -for t in BUILD_TARGETS: - this_build_root, variant = parse_build_path(t) + kconfig_args = kconfig_args[2:] - # Make sure all targets use the same build root. - if not build_root: - build_root = this_build_root - elif this_build_root != build_root: - error("build targets not under same build root\n %s\n %s" % - (build_root, this_build_root)) + variant_paths = {dir_to_configure} +else: + # Each target must have 'build' in the interior of the path; the + # directory below this will determine the build parameters. For + # example, for target 'foo/bar/build/X86/arch/x86/blah.do' we + # recognize that X86 specifies the configuration because it + # follow 'build' in the build path. - # Collect all the variants into a set. - variant_paths.add(os.path.join('/', build_root, variant)) + # The funky assignment to "[:]" is needed to replace the list contents + # in place rather than reassign the symbol to a new list, which + # doesn't work (obviously!). + BUILD_TARGETS[:] = makePathListAbsolute(BUILD_TARGETS) -# Make sure build_root exists (might not if this is the first build there) -if not isdir(build_root): - mkdir(build_root) -main['BUILDROOT'] = build_root + # Generate a list of the unique build directories that the collected + # targets reference. + variant_paths = set(map(parse_build_path, BUILD_TARGETS)) + kconfig_action = None ######################################################################## @@ -395,10 +534,14 @@ for variant_path in variant_paths: env = main.Clone() env['BUILDDIR'] = variant_path - gem5_build = os.path.join(build_root, variant_path, 'gem5.build') + gem5_build = os.path.join(variant_path, 'gem5.build') env['GEM5BUILD'] = gem5_build Execute(Mkdir(gem5_build)) + config_file = Dir(gem5_build).File('config') + kconfig_file = Dir(gem5_build).File('Kconfig') + gem5_kconfig_file = Dir('#src').File('Kconfig') + env.SConsignFile(os.path.join(gem5_build, 'sconsign')) # Set up default C++ compiler flags @@ -447,7 +590,13 @@ for variant_path in variant_paths: conf.CheckLinkFlag( '-Wl,--thread-count=%d' % GetOption('num_jobs')) - + with gem5_scons.Configure(env) as conf: + ld_optimize_memory_usage = GetOption('limit_ld_memory_usage') + if ld_optimize_memory_usage: + if conf.CheckLinkFlag('-Wl,--no-keep-memory'): + env.Append(LINKFLAGS=['-Wl,--no-keep-memory']) + else: + error("Unable to use --no-keep-memory with the linker") else: error('\n'.join(( "Don't know what compiler options to use for your compiler.", @@ -556,10 +705,14 @@ for variant_path in variant_paths: if sanitizers: sanitizers = ','.join(sanitizers) if env['GCC'] or env['CLANG']: + libsan = ( + ['-static-libubsan', '-static-libasan'] + if env['GCC'] + else ['-static-libsan'] + ) env.Append(CCFLAGS=['-fsanitize=%s' % sanitizers, '-fno-omit-frame-pointer'], - LINKFLAGS=['-fsanitize=%s' % sanitizers, - '-static-libasan']) + LINKFLAGS=['-fsanitize=%s' % sanitizers] + libsan) if main["BIN_TARGET_ARCH"] == "x86_64": # Sanitizers can enlarge binary size drammatically, north of @@ -626,7 +779,7 @@ for variant_path in variant_paths: LINKFLAGS=['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']) - env['HAVE_PKG_CONFIG'] = env.Detect('pkg-config') + env['HAVE_PKG_CONFIG'] = env.Detect('pkg-config') == 'pkg-config' with gem5_scons.Configure(env) as conf: # On Solaris you need to use libsocket for socket ops @@ -670,59 +823,13 @@ for variant_path in variant_paths: after_sconsopts_callbacks.append(cb) Export('AfterSConsopts') - # Sticky variables get saved in the variables file so they persist from - # one invocation to the next (unless overridden, in which case the new - # value becomes sticky). - sticky_vars = Variables(args=ARGUMENTS) - Export('sticky_vars') + extras_file = os.path.join(gem5_build, 'extras') + extras_var = Variables(extras_file, args=ARGUMENTS) - # EXTRAS is special since it affects what SConsopts need to be read. - sticky_vars.Add(('EXTRAS', 'Add extra directories to the compilation', '')) - - # Set env variables according to the build directory config. - sticky_vars.files = [] - # Variables for $BUILD_ROOT/$VARIANT_DIR are stored in - # $BUILD_ROOT/$VARIANT_DIR/gem5.build/variables - - gem5_build_vars = os.path.join(gem5_build, 'variables') - build_root_vars = os.path.join(build_root, 'variables', variant_dir) - current_vars_files = [gem5_build_vars, build_root_vars] - existing_vars_files = list(filter(isfile, current_vars_files)) - if existing_vars_files: - sticky_vars.files.extend(existing_vars_files) - if not GetOption('silent'): - print('Using saved variables file(s) %s' % - ', '.join(existing_vars_files)) - else: - # Variant specific variables file doesn't exist. - - # Get default build variables from source tree. Variables are - # normally determined by name of $VARIANT_DIR, but can be - # overridden by '--default=' arg on command line. - default = GetOption('default') - opts_dir = Dir('#build_opts').abspath - if default: - default_vars_files = [ - gem5_build_vars, - build_root_vars, - os.path.join(opts_dir, default) - ] - else: - default_vars_files = [os.path.join(opts_dir, variant_dir)] - existing_default_files = list(filter(isfile, default_vars_files)) - if existing_default_files: - default_vars_file = existing_default_files[0] - sticky_vars.files.append(default_vars_file) - print("Variables file(s) %s not found,\n using defaults in %s" % - (' or '.join(current_vars_files), default_vars_file)) - else: - error("Cannot find variables file(s) %s or default file(s) %s" % - (' or '.join(current_vars_files), - ' or '.join(default_vars_files))) - Exit(1) + extras_var.Add(('EXTRAS', 'Add extra directories to the compilation', '')) # Apply current settings for EXTRAS to env. - sticky_vars.Update(env) + extras_var.Update(env) # Parse EXTRAS variable to build list of all directories where we're # look for sources etc. This list is exported as extras_dir_list. @@ -733,6 +840,17 @@ for variant_path in variant_paths: Export('extras_dir_list') + # Generate a Kconfig that will source the main gem5 one, and any in any + # EXTRAS directories. + kconfig_base_py = Dir('#build_tools').File('kconfig_base.py') + kconfig_base_cmd_parts = [f'"{kconfig_base_py}" "{kconfig_file.abspath}"', + f'"{gem5_kconfig_file.abspath}"'] + for ed in extras_dir_list: + kconfig_base_cmd_parts.append(f'"{ed}"') + kconfig_base_cmd = ' '.join(kconfig_base_cmd_parts) + if env.Execute(kconfig_base_cmd) != 0: + error("Failed to build base Kconfig file") + # Variables which were determined with Configure. env['CONF'] = {} @@ -760,24 +878,48 @@ for variant_path in variant_paths: for cb in after_sconsopts_callbacks: cb() - # Update env for new variables added by the SConsopts. - sticky_vars.Update(env) + # Handle any requested kconfig action, then exit. + if kconfig_action: + if kconfig_action == 'defconfig': + if len(kconfig_args) != 1: + error('Usage: scons defconfig ') + defconfig_path = makePathAbsolute(kconfig_args[0]) + kconfig.defconfig(env, kconfig_file.abspath, + defconfig_path, config_file.abspath) + elif kconfig_action == 'guiconfig': + kconfig.guiconfig(env, kconfig_file.abspath, config_file.abspath, + variant_path) + elif kconfig_action == 'listnewconfig': + kconfig.listnewconfig(env, kconfig_file.abspath, + config_file.abspath) + elif kconfig_action == 'menuconfig': + kconfig.menuconfig(env, kconfig_file.abspath, config_file.abspath, + variant_path) + elif kconfig_action == 'oldconfig': + kconfig.oldconfig(env, kconfig_file.abspath, config_file.abspath) + elif kconfig_action == 'olddefconfig': + kconfig.olddefconfig(env, kconfig_file.abspath, + config_file.abspath) + elif kconfig_action == 'savedefconfig': + if len(kconfig_args) != 1: + error('Usage: scons defconfig ') + defconfig_path = makePathAbsolute(kconfig_args[0]) + kconfig.savedefconfig(env, kconfig_file.abspath, + config_file.abspath, defconfig_path) + elif kconfig_action == 'setconfig': + kconfig.setconfig(env, kconfig_file.abspath, config_file.abspath, + ARGUMENTS) + Exit(0) - Help(''' -Build variables for {dir}: -{help} -'''.format(dir=variant_dir, help=sticky_vars.GenerateHelpText(env)), - append=True) + # If no config exists yet, see if we know how to make one? + if not isfile(config_file.abspath): + buildopts_file = Dir('#build_opts').File(variant_dir) + if not isfile(buildopts_file.abspath): + error('No config found, and no implicit config recognized') + kconfig.defconfig(env, kconfig_file.abspath, buildopts_file.abspath, + config_file.abspath) - # If the old vars file exists, delete it to avoid confusion/stale values. - if isfile(build_root_vars): - warning(f'Deleting old variant variables file "{build_root_vars}"') - remove(build_root_vars) - # Save sticky variables back to the gem5.build variant variables file. - sticky_vars.Save(gem5_build_vars, env) - - # Pull all the sticky variables into the CONF dict. - env['CONF'].update({key: env[key] for key in sticky_vars.keys()}) + kconfig.update_env(env, kconfig_file.abspath, config_file.abspath) # Do this after we save setting back, or else we'll tack on an # extra 'qdo' every time we run scons. diff --git a/build_opts/ALL b/build_opts/ALL index 6e5ede2d50..b44c7a09f7 100644 --- a/build_opts/ALL +++ b/build_opts/ALL @@ -1,7 +1,9 @@ -USE_ARM_ISA = True -USE_MIPS_ISA = True -USE_POWER_ISA = True -USE_RISCV_ISA = True -USE_SPARC_ISA = True -USE_X86_ISA = True -PROTOCOL = 'MESI_Two_Level' +RUBY=y +RUBY_PROTOCOL_MESI_TWO_LEVEL=y +BUILD_ISA=y +USE_ARM_ISA=y +USE_MIPS_ISA=y +USE_POWER_ISA=y +USE_RISCV_ISA=y +USE_SPARC_ISA=y +USE_X86_ISA=y diff --git a/build_opts/ARM b/build_opts/ARM index 8c30c21e5a..b50f366703 100644 --- a/build_opts/ARM +++ b/build_opts/ARM @@ -1,2 +1,4 @@ -USE_ARM_ISA = True -PROTOCOL = 'CHI' +BUILD_ISA=y +USE_ARM_ISA=y +RUBY=y +RUBY_PROTOCOL_CHI=y diff --git a/build_opts/ARM_MESI_Three_Level b/build_opts/ARM_MESI_Three_Level index 3057bec0c4..4cda128fb1 100644 --- a/build_opts/ARM_MESI_Three_Level +++ b/build_opts/ARM_MESI_Three_Level @@ -1,5 +1,4 @@ -# Copyright (c) 2019 ARM Limited -# All rights reserved. - -USE_ARM_ISA = True -PROTOCOL = 'MESI_Three_Level' +BUILD_ISA=y +USE_ARM_ISA=y +RUBY=y +RUBY_PROTOCOL_MESI_THREE_LEVEL=y diff --git a/build_opts/ARM_MESI_Three_Level_HTM b/build_opts/ARM_MESI_Three_Level_HTM index 7f80c4eee2..ade6a0dfb6 100644 --- a/build_opts/ARM_MESI_Three_Level_HTM +++ b/build_opts/ARM_MESI_Three_Level_HTM @@ -1,5 +1,4 @@ -# Copyright (c) 2019 ARM Limited -# All rights reserved. - -USE_ARM_ISA = True -PROTOCOL = 'MESI_Three_Level_HTM' +BUILD_ISA=y +USE_ARM_ISA=y +RUBY=y +RUBY_PROTOCOL_MESI_THREE_LEVEL_HTM=y diff --git a/build_opts/ARM_MOESI_hammer b/build_opts/ARM_MOESI_hammer index 5322fd96f2..44038ff493 100644 --- a/build_opts/ARM_MOESI_hammer +++ b/build_opts/ARM_MOESI_hammer @@ -1,5 +1,4 @@ -# Copyright (c) 2019 ARM Limited -# All rights reserved. - -USE_ARM_ISA = True -PROTOCOL = 'MOESI_hammer' +BUILD_ISA=y +USE_ARM_ISA=y +RUBY=y +RUBY_PROTOCOL_MOESI_HAMMER=y diff --git a/build_opts/GCN3_X86 b/build_opts/GCN3_X86 index aca2f62878..fd471871b6 100644 --- a/build_opts/GCN3_X86 +++ b/build_opts/GCN3_X86 @@ -1,4 +1,6 @@ -PROTOCOL = 'GPU_VIPER' -USE_X86_ISA = True -TARGET_GPU_ISA = 'gcn3' -BUILD_GPU = True +RUBY=y +RUBY_PROTOCOL_GPU_VIPER=y +BUILD_ISA=y +USE_X86_ISA=y +GCN3_GPU_ISA=y +BUILD_GPU=y diff --git a/build_opts/Garnet_standalone b/build_opts/Garnet_standalone index 2351c5221d..5df3d6c74f 100644 --- a/build_opts/Garnet_standalone +++ b/build_opts/Garnet_standalone @@ -1,2 +1,2 @@ -USE_NULL_ISA = True -PROTOCOL = 'Garnet_standalone' +RUBY=y +RUBY_PROTOCOL_GARNET_STANDALONE=y diff --git a/build_opts/MIPS b/build_opts/MIPS index 382e10163a..e60d44ebc4 100644 --- a/build_opts/MIPS +++ b/build_opts/MIPS @@ -1,2 +1,4 @@ -USE_MIPS_ISA = True -PROTOCOL = 'MI_example' +RUBY=y +RUBY_PROTOCOL_MI_EXAMPLE=y +BUILD_ISA=y +USE_MIPS_ISA=y diff --git a/build_opts/NULL b/build_opts/NULL index 51e287a080..16f4686a5a 100644 --- a/build_opts/NULL +++ b/build_opts/NULL @@ -1,2 +1,2 @@ -USE_NULL_ISA = True -PROTOCOL='MI_example' +RUBY=y +RUBY_PROTOCOL_MI_EXAMPLE=y diff --git a/build_opts/NULL_MESI_Two_Level b/build_opts/NULL_MESI_Two_Level index bafb199592..57542472df 100644 --- a/build_opts/NULL_MESI_Two_Level +++ b/build_opts/NULL_MESI_Two_Level @@ -1,2 +1,2 @@ -USE_NULL_ISA = True -PROTOCOL = 'MESI_Two_Level' +RUBY=y +RUBY_PROTOCOL_MESI_TWO_LEVEL=y diff --git a/build_opts/NULL_MOESI_CMP_directory b/build_opts/NULL_MOESI_CMP_directory index 3346964a6b..3c5308f525 100644 --- a/build_opts/NULL_MOESI_CMP_directory +++ b/build_opts/NULL_MOESI_CMP_directory @@ -1,2 +1,2 @@ -USE_NULL_ISA = True -PROTOCOL='MOESI_CMP_directory' +RUBY=y +RUBY_PROTOCOL_MOESI_CMP_DIRECTORY=y diff --git a/build_opts/NULL_MOESI_CMP_token b/build_opts/NULL_MOESI_CMP_token index 4ea9e70536..2297bdd722 100644 --- a/build_opts/NULL_MOESI_CMP_token +++ b/build_opts/NULL_MOESI_CMP_token @@ -1,2 +1,2 @@ -USE_NULL_ISA = True -PROTOCOL='MOESI_CMP_token' +RUBY=y +RUBY_PROTOCOL_MOESI_CMP_TOKEN=y diff --git a/build_opts/NULL_MOESI_hammer b/build_opts/NULL_MOESI_hammer index e91b78dddb..fe63b9c54c 100644 --- a/build_opts/NULL_MOESI_hammer +++ b/build_opts/NULL_MOESI_hammer @@ -1,2 +1,2 @@ -USE_NULL_ISA = True -PROTOCOL='MOESI_hammer' +RUBY=y +RUBY_PROTOCOL_MOESI_HAMMER=y diff --git a/build_opts/POWER b/build_opts/POWER index 207356c0be..02f7e161d2 100644 --- a/build_opts/POWER +++ b/build_opts/POWER @@ -1,2 +1,4 @@ -USE_POWER_ISA = True -PROTOCOL = 'MI_example' +RUBY=y +RUBY_PROTOCOL_MI_EXAMPLE=y +BUILD_ISA=y +USE_POWER_ISA=y diff --git a/build_opts/RISCV b/build_opts/RISCV index 22097b0b3e..c0de30e4b5 100644 --- a/build_opts/RISCV +++ b/build_opts/RISCV @@ -1,2 +1,4 @@ -USE_RISCV_ISA = True -PROTOCOL = 'MI_example' +RUBY=y +RUBY_PROTOCOL_MI_EXAMPLE=y +BUILD_ISA=y +USE_RISCV_ISA=y diff --git a/build_opts/SPARC b/build_opts/SPARC index 22dec5f867..7f3b544d4a 100644 --- a/build_opts/SPARC +++ b/build_opts/SPARC @@ -1,2 +1,4 @@ -USE_SPARC_ISA = True -PROTOCOL = 'MI_example' +RUBY=y +RUBY_PROTOCOL_MI_EXAMPLE=y +BUILD_ISA=y +USE_SPARC_ISA=y diff --git a/build_opts/VEGA_X86 b/build_opts/VEGA_X86 index 437b048ce7..0e070529ad 100644 --- a/build_opts/VEGA_X86 +++ b/build_opts/VEGA_X86 @@ -1,4 +1,6 @@ -PROTOCOL = 'GPU_VIPER' -USE_X86_ISA = True -TARGET_GPU_ISA = 'vega' -BUILD_GPU = True +RUBY=y +RUBY_PROTOCOL_GPU_VIPER=y +BUILD_ISA=y +USE_X86_ISA=y +VEGA_GPU_ISA=y +BUILD_GPU=y diff --git a/build_opts/X86 b/build_opts/X86 index 259325b92e..6d6f4d523a 100644 --- a/build_opts/X86 +++ b/build_opts/X86 @@ -1,3 +1,5 @@ -USE_X86_ISA = True -PROTOCOL = 'MESI_Two_Level' -NUMBER_BITS_PER_SET = '128' +RUBY=y +NUMBER_BITS_PER_SET=128 +RUBY_PROTOCOL_MESI_TWO_LEVEL=y +BUILD_ISA=y +USE_X86_ISA=y diff --git a/build_opts/X86_MESI_Two_Level b/build_opts/X86_MESI_Two_Level index 259325b92e..6d6f4d523a 100644 --- a/build_opts/X86_MESI_Two_Level +++ b/build_opts/X86_MESI_Two_Level @@ -1,3 +1,5 @@ -USE_X86_ISA = True -PROTOCOL = 'MESI_Two_Level' -NUMBER_BITS_PER_SET = '128' +RUBY=y +NUMBER_BITS_PER_SET=128 +RUBY_PROTOCOL_MESI_TWO_LEVEL=y +BUILD_ISA=y +USE_X86_ISA=y diff --git a/build_opts/X86_MI_example b/build_opts/X86_MI_example index 71bc9a5f3a..4236851d4d 100644 --- a/build_opts/X86_MI_example +++ b/build_opts/X86_MI_example @@ -1,2 +1,4 @@ -USE_X86_ISA = True -PROTOCOL = 'MI_example' +RUBY=y +RUBY_PROTOCOL_MI_EXAMPLE=y +BUILD_ISA=y +USE_X86_ISA=y diff --git a/build_opts/X86_MOESI_AMD_Base b/build_opts/X86_MOESI_AMD_Base index f8f2ce7c8d..ffb7fb73c0 100644 --- a/build_opts/X86_MOESI_AMD_Base +++ b/build_opts/X86_MOESI_AMD_Base @@ -1,2 +1,4 @@ -PROTOCOL = 'MOESI_AMD_Base' -USE_X86_ISA = True +RUBY=y +RUBY_PROTOCOL_MOESI_AMD_BASE=y +BUILD_ISA=y +USE_X86_ISA=y diff --git a/build_tools/code_formatter.py b/build_tools/code_formatter.py index a2651c9dd0..cb6ce8e5a9 100644 --- a/build_tools/code_formatter.py +++ b/build_tools/code_formatter.py @@ -46,7 +46,7 @@ import os import re -class lookup(object): +class lookup: def __init__(self, formatter, frame, *args, **kwargs): self.frame = frame self.formatter = formatter @@ -106,7 +106,7 @@ class code_formatter_meta(type): """ def __init__(cls, name, bases, dct): - super(code_formatter_meta, cls).__init__(name, bases, dct) + super().__init__(name, bases, dct) if "pattern" in dct: pat = cls.pattern else: @@ -125,7 +125,7 @@ class code_formatter_meta(type): cls.pattern = re.compile(pat, re.VERBOSE | re.DOTALL | re.MULTILINE) -class code_formatter(object, metaclass=code_formatter_meta): +class code_formatter(metaclass=code_formatter_meta): delim = r"$" ident = r"[_A-z]\w*" pos = r"[0-9]+" @@ -272,7 +272,7 @@ class code_formatter(object, metaclass=code_formatter_meta): # check for a lone identifier if ident: indent = match.group("indent") # must be spaces - lone = "%s" % (l[ident],) + lone = f"{l[ident]}" def indent_lines(gen): for line in gen: @@ -284,7 +284,7 @@ class code_formatter(object, metaclass=code_formatter_meta): # check for an identifier, braced or not ident = match.group("ident") or match.group("b_ident") if ident is not None: - return "%s" % (l[ident],) + return f"{l[ident]}" # check for a positional parameter, braced or not pos = match.group("pos") or match.group("b_pos") @@ -295,13 +295,13 @@ class code_formatter(object, metaclass=code_formatter_meta): "Positional parameter #%d not found in pattern" % pos, code_formatter.pattern, ) - return "%s" % (args[int(pos)],) + return f"{args[int(pos)]}" # check for a double braced expression eval_expr = match.group("eval") if eval_expr is not None: result = eval(eval_expr, {}, l) - return "%s" % (result,) + return f"{result}" # check for an escaped delimiter if match.group("escaped") is not None: diff --git a/build_tools/cxx_config_cc.py b/build_tools/cxx_config_cc.py index 33d3bba864..1a4de0339d 100644 --- a/build_tools/cxx_config_cc.py +++ b/build_tools/cxx_config_cc.py @@ -3,6 +3,7 @@ # Copyright 2013 Mark D. Hill and David A. Wood # Copyright 2017-2020 ARM Limited # Copyright 2021 Google, Inc. +# Copyright 2023 COSEDA Technologies GmbH # # The license below extends only to copyright in the software and shall # not be construed as granting a license to any other intellectual @@ -42,7 +43,6 @@ import os.path import sys import importer - from code_formatter import code_formatter parser = argparse.ArgumentParser() @@ -58,8 +58,8 @@ importer.install() module = importlib.import_module(args.modpath) sim_object = getattr(module, sim_object_name) -from m5.params import isSimObjectClass import m5.params +from m5.params import isSimObjectClass code = code_formatter() @@ -104,7 +104,7 @@ for param in sim_object._params.values(): for port in sim_object._ports.values(): is_vector = isinstance(port, m5.params.VectorPort) - is_requestor = port.role == "GEM5 REQUESTOR" + is_requestor = port.is_source code( 'ports["%s"] = new PortDesc("%s", %s, %s);' diff --git a/build_tools/cxx_config_hh.py b/build_tools/cxx_config_hh.py index 55828e37b7..dfcb02c190 100644 --- a/build_tools/cxx_config_hh.py +++ b/build_tools/cxx_config_hh.py @@ -42,7 +42,6 @@ import os.path import sys import importer - from code_formatter import code_formatter parser = argparse.ArgumentParser() diff --git a/build_tools/enum_cc.py b/build_tools/enum_cc.py index 173143fc38..7cfd3990b9 100644 --- a/build_tools/enum_cc.py +++ b/build_tools/enum_cc.py @@ -42,7 +42,6 @@ import os.path import sys import importer - from code_formatter import code_formatter parser = argparse.ArgumentParser() @@ -118,7 +117,6 @@ code("} // namespace gem5") if use_python: - name = enum.__name__ enum_name = enum.__name__ if enum.enum_name is None else enum.enum_name wrapper_name = enum_name if enum.is_class else enum.wrapper_name diff --git a/build_tools/enum_hh.py b/build_tools/enum_hh.py index a5b9f42cba..dab47bfa19 100644 --- a/build_tools/enum_hh.py +++ b/build_tools/enum_hh.py @@ -42,7 +42,6 @@ import os.path import sys import importer - from code_formatter import code_formatter parser = argparse.ArgumentParser() @@ -66,7 +65,7 @@ code = code_formatter() wrapper_name = enum.wrapper_name wrapper = "struct" if enum.wrapper_is_struct else "namespace" name = enum.__name__ if enum.enum_name is None else enum.enum_name -idem_macro = "__ENUM__%s__%s__" % (wrapper_name, name) +idem_macro = f"__ENUM__{wrapper_name}__{name}__" code( """\ diff --git a/build_tools/grammar.py b/build_tools/grammar.py index 6ac638bcd0..582ff8710b 100644 --- a/build_tools/grammar.py +++ b/build_tools/grammar.py @@ -36,7 +36,7 @@ class ParseError(Exception): self.token = token -class Grammar(object): +class Grammar: def setupLexerFactory(self, **kwargs): if "module" in kwargs: raise AttributeError("module is an illegal attribute") @@ -92,7 +92,7 @@ class Grammar(object): return self.current_lexer.lineno raise AttributeError( - "'%s' object has no attribute '%s'" % (type(self), attr) + f"'{type(self)}' object has no attribute '{attr}'" ) def parse_string(self, data, source="", debug=None, tracking=0): @@ -118,7 +118,7 @@ class Grammar(object): def parse_file(self, f, **kwargs): if isinstance(f, str): source = f - f = open(f, "r") + f = open(f) elif isinstance(f, file): source = f.name else: @@ -137,7 +137,7 @@ class Grammar(object): t.value, ) else: - msg = "Syntax error at end of %s" % (self.current_source,) + msg = f"Syntax error at end of {self.current_source}" raise ParseError(msg, t) def t_error(self, t): diff --git a/build_tools/infopy.py b/build_tools/infopy.py index 8a4e013388..1662094573 100644 --- a/build_tools/infopy.py +++ b/build_tools/infopy.py @@ -56,7 +56,7 @@ for source in args.files: # `README.md = "..."` which is not valid as `md` is not a property of # `README`. src = os.path.basename(source).replace(".", "_") - with open(source, "r") as f: + with open(source) as f: data = "".join(f) code("${src} = ${{repr(data)}}") diff --git a/build_tools/kconfig_base.py b/build_tools/kconfig_base.py new file mode 100755 index 0000000000..13cdb7377a --- /dev/null +++ b/build_tools/kconfig_base.py @@ -0,0 +1,55 @@ +#! /usr/bin/env python3 +# +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import argparse + +from code_formatter import code_formatter + +parser = argparse.ArgumentParser() +parser.add_argument("output", help="path of generated base Kconfig file") +parser.add_argument("main", help="relative path to the main gem5 Kconfig file") +parser.add_argument("extras_dirs", nargs="*", help="EXTRAS paths") + +args = parser.parse_args() + +code = code_formatter() + +code( + f"""# Automatically generated base Kconfig file, DO NOT EDIT! + +source "{args.main}" +""" +) + +for extras_dir in args.extras_dirs: + code( + f""" +osource "{extras_dir}/Kconfig" +""" + ) + +code.write(args.output) diff --git a/build_tools/marshal.py b/build_tools/marshal.py index 58c78e1632..979c0eda6f 100644 --- a/build_tools/marshal.py +++ b/build_tools/marshal.py @@ -74,7 +74,7 @@ if "LC_CTYPE" in os.environ: _, cpp, python, modpath, abspath = sys.argv -with open(python, "r") as f: +with open(python) as f: src = f.read() compiled = compile(src, python, "exec") diff --git a/build_tools/sim_object_param_struct_cc.py b/build_tools/sim_object_param_struct_cc.py index 0384809456..5aca1b8711 100644 --- a/build_tools/sim_object_param_struct_cc.py +++ b/build_tools/sim_object_param_struct_cc.py @@ -42,7 +42,6 @@ import os.path import sys import importer - from code_formatter import code_formatter parser = argparse.ArgumentParser() @@ -88,7 +87,6 @@ ports = sim_object._ports.local # only include pybind if python is enabled in the build if use_python: - code( """#include "pybind11/pybind11.h" #include "pybind11/stl.h" diff --git a/build_tools/sim_object_param_struct_hh.py b/build_tools/sim_object_param_struct_hh.py index bf37da2a07..c82c25921c 100644 --- a/build_tools/sim_object_param_struct_hh.py +++ b/build_tools/sim_object_param_struct_hh.py @@ -42,7 +42,6 @@ import os.path import sys import importer - from code_formatter import code_formatter parser = argparse.ArgumentParser() @@ -81,7 +80,7 @@ except: warned_about_nested_templates = False -class CxxClass(object): +class CxxClass: def __init__(self, sig, template_params=[]): # Split the signature into its constituent parts. This could # potentially be done with regular expressions, but diff --git a/configs/common/Benchmarks.py b/configs/common/Benchmarks.py index c90e78ed61..9983a8ea84 100644 --- a/configs/common/Benchmarks.py +++ b/configs/common/Benchmarks.py @@ -24,8 +24,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from common.SysPaths import script, disk, binary from os import environ as env + +from common.SysPaths import ( + binary, + disk, + script, +) + from m5.defines import buildEnv diff --git a/configs/common/CacheConfig.py b/configs/common/CacheConfig.py index 7a191570e3..4f21a43924 100644 --- a/configs/common/CacheConfig.py +++ b/configs/common/CacheConfig.py @@ -40,13 +40,13 @@ # Configure the M5 cache hierarchy config in one place # +from common import ObjectList +from common.Caches import * + import m5 from m5.objects import * -from gem5.isas import ISA -from gem5.runtime import get_runtime_isa -from common.Caches import * -from common import ObjectList +from gem5.isas import ISA def _get_hwp(hwp_option): @@ -117,9 +117,6 @@ def config_cache(options, system): None, ) - if get_runtime_isa() in [ISA.X86, ISA.RISCV]: - walk_cache_class = PageTableWalkerCache - # Set the cache line size of the system system.cache_line_size = options.cacheline_size @@ -150,11 +147,13 @@ def config_cache(options, system): icache = icache_class(**_get_cache_opts("l1i", options)) dcache = dcache_class(**_get_cache_opts("l1d", options)) - # If we have a walker cache specified, instantiate two - # instances here - if walk_cache_class: - iwalkcache = walk_cache_class() - dwalkcache = walk_cache_class() + # If we are using ISA.X86 or ISA.RISCV, we set walker caches. + if ObjectList.cpu_list.get_isa(options.cpu_type) in [ + ISA.RISCV, + ISA.X86, + ]: + iwalkcache = PageTableWalkerCache() + dwalkcache = PageTableWalkerCache() else: iwalkcache = None dwalkcache = None @@ -192,7 +191,11 @@ def config_cache(options, system): # on these names. For simplicity, we would advise configuring # it to use this naming scheme; if this isn't possible, change # the names below. - if get_runtime_isa() in [ISA.X86, ISA.ARM, ISA.RISCV]: + if ObjectList.cpu_list.get_isa(options.cpu_type) in [ + ISA.X86, + ISA.ARM, + ISA.RISCV, + ]: system.cpu[i].addPrivateSplitL1Caches( ExternalCache("cpu%d.icache" % i), ExternalCache("cpu%d.dcache" % i), diff --git a/configs/common/Caches.py b/configs/common/Caches.py index e25d16ca1e..fed9ac7d19 100644 --- a/configs/common/Caches.py +++ b/configs/common/Caches.py @@ -39,8 +39,8 @@ from m5.defines import buildEnv from m5.objects import * + from gem5.isas import ISA -from gem5.runtime import get_runtime_isa # Base implementations of L1, L2, IO and TLB-walker caches. There are # used in the regressions and also as base components in the @@ -96,11 +96,4 @@ class PageTableWalkerCache(Cache): mshrs = 10 size = "1kB" tgts_per_mshr = 12 - - # the x86 table walker actually writes to the table-walker cache - if get_runtime_isa() in [ISA.X86, ISA.RISCV]: - is_read_only = False - else: - is_read_only = True - # Writeback clean lines as well - writeback_clean = True + is_read_only = False diff --git a/configs/common/CpuConfig.py b/configs/common/CpuConfig.py index 1672d43343..b4519dba27 100644 --- a/configs/common/CpuConfig.py +++ b/configs/common/CpuConfig.py @@ -33,8 +33,19 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5 import fatal import m5.objects +from m5 import fatal + +from gem5.isas import ISA + +isa_string_map = { + ISA.X86: "X86", + ISA.ARM: "Arm", + ISA.RISCV: "Riscv", + ISA.SPARC: "Sparc", + ISA.POWER: "Power", + ISA.MIPS: "Mips", +} def config_etrace(cpu_cls, cpu_list, options): diff --git a/configs/common/FSConfig.py b/configs/common/FSConfig.py index 5da951c93b..469101a82e 100644 --- a/configs/common/FSConfig.py +++ b/configs/common/FSConfig.py @@ -38,12 +38,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from common import ObjectList +from common.Benchmarks import * + import m5 import m5.defines from m5.objects import * from m5.util import * -from common.Benchmarks import * -from common import ObjectList # Populate to reflect supported os types per target ISA os_types = set() diff --git a/configs/common/FileSystemConfig.py b/configs/common/FileSystemConfig.py index 9c6647c861..91d427a259 100644 --- a/configs/common/FileSystemConfig.py +++ b/configs/common/FileSystemConfig.py @@ -36,18 +36,31 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import getpass +import operator +import os +import platform +from functools import reduce +from os import ( + access, + getpid, + listdir, + makedirs, + mkdir, + stat, +) +from os.path import isdir +from os.path import join as joinpath +from pwd import getpwuid +from shutil import ( + copyfile, + rmtree, +) + import m5 from m5.objects import * from m5.util.convert import * -from functools import reduce -import operator, os, platform, getpass -from os import mkdir, makedirs, getpid, listdir, stat, access -from pwd import getpwuid -from os.path import join as joinpath -from os.path import isdir -from shutil import rmtree, copyfile - def hex_mask(terms): dec_mask = reduce(operator.or_, [2**i for i in terms], 0) diff --git a/configs/common/GPUTLBConfig.py b/configs/common/GPUTLBConfig.py index e59cd00da4..5b34ddbfcd 100644 --- a/configs/common/GPUTLBConfig.py +++ b/configs/common/GPUTLBConfig.py @@ -36,7 +36,6 @@ from m5.objects import * def TLB_constructor(options, level, gpu_ctrl=None, full_system=False): - if full_system: constructor_call = ( "VegaGPUTLB(\ @@ -71,7 +70,6 @@ def TLB_constructor(options, level, gpu_ctrl=None, full_system=False): def Coalescer_constructor(options, level, full_system): - if full_system: constructor_call = ( "VegaTLBCoalescer(probesPerCycle = \ diff --git a/configs/common/GPUTLBOptions.py b/configs/common/GPUTLBOptions.py index 1a77a2c192..6f232e2d0d 100644 --- a/configs/common/GPUTLBOptions.py +++ b/configs/common/GPUTLBOptions.py @@ -29,7 +29,6 @@ def tlb_options(parser): - # =================================================================== # TLB Configuration # =================================================================== diff --git a/configs/common/HMC.py b/configs/common/HMC.py index f8321f356b..eef1e793fb 100644 --- a/configs/common/HMC.py +++ b/configs/common/HMC.py @@ -430,7 +430,6 @@ def add_options(parser): # configure HMC host controller def config_hmc_host_ctrl(opt, system): - # create HMC host controller system.hmc_host = SubSystem() @@ -533,7 +532,6 @@ def config_hmc_host_ctrl(opt, system): # Create an HMC device def config_hmc_dev(opt, system, hmc_host): - # create HMC device system.hmc_dev = SubSystem() diff --git a/configs/common/MemConfig.py b/configs/common/MemConfig.py index baa0d233af..717ffa5fae 100644 --- a/configs/common/MemConfig.py +++ b/configs/common/MemConfig.py @@ -33,14 +33,17 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from common import ( + HMC, + ObjectList, +) + import m5.objects -from common import ObjectList -from common import HMC def create_mem_intf(intf, r, i, intlv_bits, intlv_size, xor_low_bit): """ - Helper function for creating a single memoy controller from the given + Helper function for creating a single memory controller from the given options. This function is invoked multiple times in config_mem function to create an array of controllers. """ @@ -174,6 +177,7 @@ def config_mem(options, system): nbr_mem_ctrls = opt_mem_channels import math + from m5.util import fatal intlv_bits = int(math.log(nbr_mem_ctrls, 2)) diff --git a/configs/common/ObjectList.py b/configs/common/ObjectList.py index 4b862db9e8..d36044e068 100644 --- a/configs/common/ObjectList.py +++ b/configs/common/ObjectList.py @@ -34,15 +34,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from gem5.runtime import get_supported_isas -import m5.objects -import m5.internal.params import inspect import sys from textwrap import TextWrapper +import m5.internal.params +import m5.objects -class ObjectList(object): +from gem5.isas import ISA +from gem5.runtime import get_supported_isas + + +class ObjectList: """Creates a list of objects that are sub-classes of a given class.""" def _is_obj_class(self, cls): @@ -86,7 +89,7 @@ class ObjectList(object): print(line) if self._aliases: - print("\Aliases:") + print(r"\Aliases:") for alias, target in list(self._aliases.items()): print(f"\t{alias} => {target}") @@ -127,14 +130,14 @@ class CPUList(ObjectList): # We can't use the normal inspect.isclass because the ParamFactory # and ProxyFactory classes have a tendency to confuse it. try: - return super(CPUList, self)._is_obj_class(cls) and not issubclass( + return super()._is_obj_class(cls) and not issubclass( cls, m5.objects.CheckerCPU ) except (TypeError, AttributeError): return False def _add_objects(self): - super(CPUList, self)._add_objects() + super()._add_objects() from importlib import import_module @@ -157,6 +160,27 @@ class CPUList(ObjectList): ): self._sub_classes[name] = cls + def get_isa(self, name: str) -> ISA: + """For a given CPU (string representation) determine the ISA of the + CPU.""" + + cls = self.get(name) + + if hasattr(m5.objects, "X86CPU") and issubclass( + cls, m5.objects.X86CPU + ): + return ISA.X86 + elif hasattr(m5.objects, "ArmCPU") and issubclass( + cls, m5.objects.ArmCPU + ): + return ISA.ARM + elif hasattr(m5.objects, "RiscvCPU") and issubclass( + cls, m5.objects.RiscvCPU + ): + return ISA.RISCV + else: + raise ValueError("Unable to determine CPU ISA.") + class EnumList(ObjectList): """Creates a list of possible values for a given enum class.""" @@ -164,7 +188,7 @@ class EnumList(ObjectList): def _add_objects(self): """Add all enum values to the ObjectList""" self._sub_classes = {} - for (key, value) in list(self.base_cls.__members__.items()): + for key, value in list(self.base_cls.__members__.items()): # All Enums have a value Num_NAME at the end which we # do not want to include if not key.startswith("Num_"): @@ -204,3 +228,4 @@ def _subclass_tester(name): is_kvm_cpu = _subclass_tester("BaseKvmCPU") is_noncaching_cpu = _subclass_tester("NonCachingSimpleCPU") +is_o3_cpu = _subclass_tester("BaseO3CPU") diff --git a/configs/common/Options.py b/configs/common/Options.py index 8344d9fd44..97335f13b9 100644 --- a/configs/common/Options.py +++ b/configs/common/Options.py @@ -37,13 +37,20 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import argparse +from typing import Optional + +from common import ( + CpuConfig, + ObjectList, +) +from common.Benchmarks import * import m5 from m5.defines import buildEnv from m5.objects import * -from common.Benchmarks import * -from common import ObjectList +from gem5.isas import ISA +from gem5.runtime import get_supported_isas vio_9p_help = """\ Enable the Virtio 9P device and set the path to share. The default 9p path is @@ -237,9 +244,13 @@ def addNoISAOptions(parser): # Add common options that assume a non-NULL ISA. -def addCommonOptions(parser): +def addCommonOptions(parser, default_isa: Optional[ISA] = None): # start by adding the base options that do not assume an ISA addNoISAOptions(parser) + if default_isa is None: + isa = list(get_supported_isas())[0] + else: + isa = default_isa # system options parser.add_argument( @@ -250,7 +261,7 @@ def addCommonOptions(parser): ) parser.add_argument( "--cpu-type", - default="AtomicSimpleCPU", + default=CpuConfig.isa_string_map[isa] + "AtomicSimpleCPU", choices=ObjectList.cpu_list.get_names(), help="type of cpu to run with", ) @@ -581,7 +592,7 @@ def addCommonOptions(parser): parser.add_argument( "--restore-with-cpu", action="store", - default="AtomicSimpleCPU", + default=CpuConfig.isa_string_map[isa] + "AtomicSimpleCPU", choices=ObjectList.cpu_list.get_names(), help="cpu type for restoring from a checkpoint", ) @@ -784,12 +795,20 @@ def addFSOptions(parser): "files in the gem5 output directory", ) - if buildEnv["USE_ARM_ISA"]: + if buildEnv["USE_ARM_ISA"] or buildEnv["USE_RISCV_ISA"]: parser.add_argument( "--bare-metal", action="store_true", help="Provide the raw system without the linux specific bits", ) + parser.add_argument( + "--dtb-filename", + action="store", + type=str, + help="Specifies device tree blob file to use with device-tree-" + "enabled kernels", + ) + if buildEnv["USE_ARM_ISA"]: parser.add_argument( "--list-machine-types", action=ListPlatform, @@ -802,13 +821,6 @@ def addFSOptions(parser): choices=ObjectList.platform_list.get_names(), default="VExpress_GEM5_V1", ) - parser.add_argument( - "--dtb-filename", - action="store", - type=str, - help="Specifies device tree blob file to use with device-tree-" - "enabled kernels", - ) parser.add_argument( "--enable-context-switch-stats-dump", action="store_true", diff --git a/configs/common/SimpleOpts.py b/configs/common/SimpleOpts.py index 96c73f57b8..7faf092bcd 100644 --- a/configs/common/SimpleOpts.py +++ b/configs/common/SimpleOpts.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2015 Jason Power # All rights reserved. # @@ -35,12 +34,12 @@ from each class instead of only from the configuration script. # Module-level variable to track if we've called the parse_args function yet called_parse_args = False -# For fatal -import m5 - # import the argument parser from argparse import ArgumentParser +# For fatal +import m5 + # add the args we want to be able to control from the command line parser = ArgumentParser() diff --git a/configs/common/Simulation.py b/configs/common/Simulation.py index 4377b65e64..3e332d76b4 100644 --- a/configs/common/Simulation.py +++ b/configs/common/Simulation.py @@ -41,8 +41,10 @@ import sys from os import getcwd from os.path import join as joinpath -from common import CpuConfig -from common import ObjectList +from common import ( + CpuConfig, + ObjectList, +) import m5 from m5.defines import buildEnv @@ -79,7 +81,10 @@ def setCPUClass(options): TmpClass, test_mem_mode = getCPUClass(options.restore_with_cpu) elif options.fast_forward: CPUClass = TmpClass - TmpClass = AtomicSimpleCPU + CPUISA = ObjectList.cpu_list.get_isa(options.cpu_type) + TmpClass = getCPUClass( + CpuConfig.isa_string_map[CPUISA] + "AtomicSimpleCPU" + ) test_mem_mode = "atomic" # Ruby only supports atomic accesses in noncaching mode @@ -128,9 +133,12 @@ def findCptDir(options, cptdir, testsys): the appropriate directory. """ - from os.path import isdir, exists - from os import listdir import re + from os import listdir + from os.path import ( + exists, + isdir, + ) if not isdir(cptdir): fatal("checkpoint dir %s does not exist!", cptdir) @@ -153,8 +161,8 @@ def findCptDir(options, cptdir, testsys): # Assumes that the checkpoint dir names are formatted as follows: dirs = listdir(cptdir) expr = re.compile( - "cpt\.simpoint_(\d+)_inst_(\d+)" - + "_weight_([\d\.e\-]+)_interval_(\d+)_warmup_(\d+)" + r"cpt\.simpoint_(\d+)_inst_(\d+)" + + r"_weight_([\d\.e\-]+)_interval_(\d+)_warmup_(\d+)" ) cpts = [] for dir in dirs: @@ -190,7 +198,7 @@ def findCptDir(options, cptdir, testsys): else: dirs = listdir(cptdir) - expr = re.compile("cpt\.([0-9]+)") + expr = re.compile(r"cpt\.([0-9]+)") cpts = [] for dir in dirs: match = expr.match(dir) @@ -325,7 +333,7 @@ def parseSimpointAnalysisFile(options, testsys): line = simpoint_file.readline() if not line: break - m = re.match("(\d+)\s+(\d+)", line) + m = re.match(r"(\d+)\s+(\d+)", line) if m: interval = int(m.group(1)) else: @@ -334,7 +342,7 @@ def parseSimpointAnalysisFile(options, testsys): line = weight_file.readline() if not line: fatal("not enough lines in simpoint weight file!") - m = re.match("([0-9\.e\-]+)\s+(\d+)", line) + m = re.match(r"([0-9\.e\-]+)\s+(\d+)", line) if m: weight = float(m.group(1)) else: @@ -771,7 +779,6 @@ def run(options, root, testsys, cpu_class): if ( options.take_checkpoints or options.take_simpoint_checkpoints ) and options.checkpoint_restore: - if m5.options.outdir: cptdir = m5.options.outdir else: diff --git a/configs/common/SysPaths.py b/configs/common/SysPaths.py index 60375c30c5..f8bd6883fb 100644 --- a/configs/common/SysPaths.py +++ b/configs/common/SysPaths.py @@ -24,13 +24,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import os, sys +import os +import sys config_path = os.path.dirname(os.path.abspath(__file__)) config_root = os.path.dirname(config_path) -class PathSearchFunc(object): +class PathSearchFunc: _sys_paths = None environment_variable = "M5_PATH" @@ -58,7 +59,7 @@ class PathSearchFunc(object): paths = list(filter(os.path.isdir, paths)) if not paths: - raise IOError( + raise OSError( "Can't find system files directory, " "check your {} environment variable".format( self.environment_variable @@ -72,7 +73,7 @@ class PathSearchFunc(object): try: return next(p for p in paths if os.path.exists(p)) except StopIteration: - raise IOError( + raise OSError( f"Can't find file '{filepath}' on {self.environment_variable}." ) diff --git a/configs/common/cores/arm/HPI.py b/configs/common/cores/arm/HPI.py index d3d46054f1..8fe396abfa 100644 --- a/configs/common/cores/arm/HPI.py +++ b/configs/common/cores/arm/HPI.py @@ -44,6 +44,7 @@ at: http://www.arm.com/ResearchEnablement/SystemModeling from m5.objects import * + # Simple function to allow a string of [01x_] to be converted into a # mask and value for use with MinorFUTiming def make_implicant(implicant_string): @@ -1679,7 +1680,14 @@ class HPI_MMU(ArmMMU): dtb = ArmTLB(entry_type="data", size=256) +class HPI_BTB(SimpleBTB): + numEntries = 128 + tagBits = 18 + + class HPI_BP(TournamentBP): + btb = HPI_BTB() + ras = ReturnAddrStack(numEntries=8) localPredictorSize = 64 localCtrBits = 2 localHistoryTableSize = 64 @@ -1687,9 +1695,6 @@ class HPI_BP(TournamentBP): globalCtrBits = 2 choicePredictorSize = 1024 choiceCtrBits = 2 - BTBEntries = 128 - BTBTagSize = 18 - RASSize = 8 instShiftAmt = 2 diff --git a/tests/configs/memcheck.py b/configs/common/cores/arm/O3_ARM_Etrace.py similarity index 68% rename from tests/configs/memcheck.py rename to configs/common/cores/arm/O3_ARM_Etrace.py index 25a48f9f9d..b925a86e9d 100644 --- a/tests/configs/memcheck.py +++ b/configs/common/cores/arm/O3_ARM_Etrace.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016 ARM Limited +# Copyright (c) 2012, 2017-2018, 2023 Arm Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -10,9 +10,6 @@ # unmodified and in its entirety in all distributions of the software, # modified or unmodified, in source code or in binary form. # -# Copyright (c) 2015 Jason Lowe-Power -# All rights reserved. -# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: redistributions of source code must retain the above copyright @@ -36,25 +33,28 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import m5 from m5.objects import * -# the traffic generator is only available if we have protobuf support, -# so potentially skip this test -require_sim_object("TrafficGen") - -# A wrapper around configs/example/memcheck.py - -# For some reason, this is implicitly needed by run.py -root = None +from .O3_ARM_v7a import O3_ARM_v7a_3 -def run_test(root): - # Called from tests/run.py +# O3_ARM_v7a_3 adapted to generate elastic traces +class O3_ARM_v7a_3_Etrace(O3_ARM_v7a_3): + # Make the number of entries in the ROB, LQ and SQ very + # large so that there are no stalls due to resource + # limitation as such stalls will get captured in the trace + # as compute delay. For replay, ROB, LQ and SQ sizes are + # modelled in the Trace CPU. + numROBEntries = 512 + LQEntries = 128 + SQEntries = 128 - import sys - - argv = [sys.argv[0], "-m %d" % maxtick] - - # Execute the script we are wrapping - run_config("configs/example/memcheck.py", argv=argv) + def attach_probe_listener(self, inst_trace_file, data_trace_file): + # Attach the elastic trace probe listener. Set the protobuf trace + # file names. Set the dependency window size equal to the cpu it + # is attached to. + self.traceListener = m5.objects.ElasticTrace( + instFetchTraceFile=inst_trace_file, + dataDepTraceFile=data_trace_file, + depWindowSize=3 * self.numROBEntries, + ) diff --git a/configs/common/cores/arm/O3_ARM_v7a.py b/configs/common/cores/arm/O3_ARM_v7a.py index 6a1734235a..5413839747 100644 --- a/configs/common/cores/arm/O3_ARM_v7a.py +++ b/configs/common/cores/arm/O3_ARM_v7a.py @@ -26,6 +26,7 @@ from m5.objects import * + # Simple ALU Instructions have a latency of 1 class O3_ARM_v7a_Simple_Int(FUDesc): opList = [OpDesc(opClass="IntAlu", opLat=1)] @@ -107,15 +108,19 @@ class O3_ARM_v7a_FUP(FUPool): ] +class O3_ARM_v7a_BTB(SimpleBTB): + numEntries = 2048 + tagBits = 18 + + # Bi-Mode Branch Predictor class O3_ARM_v7a_BP(BiModeBP): + btb = O3_ARM_v7a_BTB() + ras = ReturnAddrStack(numEntries=16) globalPredictorSize = 8192 globalCtrBits = 2 choicePredictorSize = 8192 choiceCtrBits = 2 - BTBEntries = 2048 - BTBTagSize = 18 - RASSize = 16 instShiftAmt = 2 @@ -202,9 +207,8 @@ class O3_ARM_v7aL2(Cache): size = "1MB" assoc = 16 write_buffers = 8 - prefetch_on_access = True clusivity = "mostly_excl" # Simple stride prefetcher - prefetcher = StridePrefetcher(degree=8, latency=1) + prefetcher = StridePrefetcher(degree=8, latency=1, prefetch_on_access=True) tags = BaseSetAssoc() replacement_policy = RandomRP() diff --git a/configs/common/cores/arm/__init__.py b/configs/common/cores/arm/__init__.py index 135b75f802..6c1c7e869f 100644 --- a/configs/common/cores/arm/__init__.py +++ b/configs/common/cores/arm/__init__.py @@ -33,8 +33,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from pkgutil import iter_modules from importlib import import_module +from pkgutil import iter_modules _cpu_modules = [name for _, name, ispkg in iter_modules(__path__) if not ispkg] diff --git a/configs/common/cores/arm/ex5_LITTLE.py b/configs/common/cores/arm/ex5_LITTLE.py index 982792d2d2..372e5c97b3 100644 --- a/configs/common/cores/arm/ex5_LITTLE.py +++ b/configs/common/cores/arm/ex5_LITTLE.py @@ -31,6 +31,7 @@ from m5.objects import * # ex5 LITTLE core (based on the ARM Cortex-A7) # ----------------------------------------------------------------------- + # Simple ALU Instructions have a latency of 3 class ex5_LITTLE_Simple_Int(MinorDefaultIntFU): opList = [OpDesc(opClass="IntAlu", opLat=4)] @@ -146,9 +147,8 @@ class L2(Cache): size = "512kB" assoc = 8 write_buffers = 16 - prefetch_on_access = True clusivity = "mostly_excl" # Simple stride prefetcher - prefetcher = StridePrefetcher(degree=1, latency=1) + prefetcher = StridePrefetcher(degree=1, latency=1, prefetch_on_access=True) tags = BaseSetAssoc() replacement_policy = RandomRP() diff --git a/configs/common/cores/arm/ex5_big.py b/configs/common/cores/arm/ex5_big.py index 0d4d4903cf..53677ce3b3 100644 --- a/configs/common/cores/arm/ex5_big.py +++ b/configs/common/cores/arm/ex5_big.py @@ -31,6 +31,7 @@ from m5.objects import * # ex5 big core (based on the ARM Cortex-A15) # ----------------------------------------------------------------------- + # Simple ALU Instructions have a latency of 1 class ex5_big_Simple_Int(FUDesc): opList = [OpDesc(opClass="IntAlu", opLat=1)] @@ -104,15 +105,19 @@ class ex5_big_FUP(FUPool): ] +class ex5_big_BTB(SimpleBTB): + numEntries = 4096 + tagBits = 18 + + # Bi-Mode Branch Predictor class ex5_big_BP(BiModeBP): + btb = ex5_big_BTB() + ras = ReturnAddrStack(numEntries=48) globalPredictorSize = 4096 globalCtrBits = 2 choicePredictorSize = 1024 choiceCtrBits = 3 - BTBEntries = 4096 - BTBTagSize = 18 - RASSize = 48 instShiftAmt = 2 @@ -195,9 +200,8 @@ class L2(Cache): size = "2MB" assoc = 16 write_buffers = 8 - prefetch_on_access = True clusivity = "mostly_excl" # Simple stride prefetcher - prefetcher = StridePrefetcher(degree=8, latency=1) + prefetcher = StridePrefetcher(degree=8, latency=1, prefetch_on_access=True) tags = BaseSetAssoc() replacement_policy = RandomRP() diff --git a/configs/common/cpu2000.py b/configs/common/cpu2000.py index 06f927cbcf..40dab5c820 100644 --- a/configs/common/cpu2000.py +++ b/configs/common/cpu2000.py @@ -26,8 +26,15 @@ import os import sys -from os.path import basename, exists, join as joinpath, normpath -from os.path import isdir, isfile, islink +from os.path import ( + basename, + exists, + isdir, + isfile, + islink, +) +from os.path import join as joinpath +from os.path import normpath spec_dist = os.environ.get("M5_CPU2000", "/dist/m5/cpu2000") @@ -71,7 +78,7 @@ def copyfiles(srcdir, dstdir): os.symlink(".", outlink) -class Benchmark(object): +class Benchmark: def __init__(self, isa, os, input_set): if not hasattr(self.__class__, "name"): self.name = self.__class__.__name__ @@ -877,7 +884,7 @@ class vortex(Benchmark): else: raise AttributeError(f"unknown ISA {isa}") - super(vortex, self).__init__(isa, os, input_set) + super().__init__(isa, os, input_set) def test(self, isa, os): self.args = [f"{self.endian}.raw"] diff --git a/configs/deprecated/example/fs.py b/configs/deprecated/example/fs.py index c50e3ac4cc..7426c47c7e 100644 --- a/configs/deprecated/example/fs.py +++ b/configs/deprecated/example/fs.py @@ -45,25 +45,30 @@ import sys import m5 from m5.defines import buildEnv from m5.objects import * -from m5.util import addToPath, fatal, warn +from m5.util import ( + addToPath, + fatal, + warn, +) from m5.util.fdthelper import * + from gem5.isas import ISA -from gem5.runtime import get_runtime_isa addToPath("../../") -from ruby import Ruby - +from common import ( + CacheConfig, + CpuConfig, + MemConfig, + ObjectList, + Options, + Simulation, +) +from common.Benchmarks import * +from common.Caches import * from common.FSConfig import * from common.SysPaths import * -from common.Benchmarks import * -from common import Simulation -from common import CacheConfig -from common import CpuConfig -from common import MemConfig -from common import ObjectList -from common.Caches import * -from common import Options +from ruby import Ruby def cmd_line_template(): @@ -80,9 +85,8 @@ def cmd_line_template(): return None -def build_test_system(np): +def build_test_system(np, isa: ISA): cmdline = cmd_line_template() - isa = get_runtime_isa() if isa == ISA.MIPS: test_sys = makeLinuxMipsSystem(test_mem_mode, bm[0], cmdline=cmdline) elif isa == ISA.SPARC: @@ -164,7 +168,7 @@ def build_test_system(np): # assuming that there is just one such port. test_sys.iobus.mem_side_ports = test_sys.ruby._io_port.in_ports - for (i, cpu) in enumerate(test_sys.cpu): + for i, cpu in enumerate(test_sys.cpu): # # Tie the cpu ports to the correct ruby system ports # @@ -378,7 +382,8 @@ else: np = args.num_cpus -test_sys = build_test_system(np) +isa = ObjectList.cpu_list.get_isa(args.cpu_type) +test_sys = build_test_system(np, isa) if len(bm) == 2: drive_sys = build_drive_system(np) diff --git a/configs/deprecated/example/se.py b/configs/deprecated/example/se.py index 8d6735903f..afdb82489d 100644 --- a/configs/deprecated/example/se.py +++ b/configs/deprecated/example/se.py @@ -41,30 +41,35 @@ # "m5 test.py" import argparse -import sys import os +import sys import m5 from m5.defines import buildEnv from m5.objects import * from m5.params import NULL -from m5.util import addToPath, fatal, warn +from m5.util import ( + addToPath, + fatal, + warn, +) + from gem5.isas import ISA -from gem5.runtime import get_runtime_isa addToPath("../../") -from ruby import Ruby - -from common import Options -from common import Simulation -from common import CacheConfig -from common import CpuConfig -from common import ObjectList -from common import MemConfig -from common.FileSystemConfig import config_filesystem +from common import ( + CacheConfig, + CpuConfig, + MemConfig, + ObjectList, + Options, + Simulation, +) from common.Caches import * from common.cpu2000 import * +from common.FileSystemConfig import config_filesystem +from ruby import Ruby def get_processes(args): @@ -94,7 +99,7 @@ def get_processes(args): process.gid = os.getgid() if args.env: - with open(args.env, "r") as f: + with open(args.env) as f: process.env = [line.rstrip() for line in f] if len(pargs) > idx: @@ -113,7 +118,8 @@ def get_processes(args): idx += 1 if args.smt: - assert args.cpu_type == "DerivO3CPU" + cpu_type = ObjectList.cpu_list.get(args.cpu_type) + assert ObjectList.is_o3_cpu(cpu_type), "SMT requires an O3CPU" return multiprocesses, idx else: return multiprocesses, 1 @@ -144,7 +150,7 @@ if args.bench: for app in apps: try: - if get_runtime_isa() == ISA.ARM: + if ObjectList.cpu_list.get_isa(args.cpu_type) == ISA.ARM: exec( "workload = %s('arm_%s', 'linux', '%s')" % (app, args.arm_iset, args.spec_input) @@ -159,7 +165,7 @@ if args.bench: multiprocesses.append(workload.makeProcess()) except: print( - f"Unable to find workload for {get_runtime_isa().name()}: {app}", + f"Unable to find workload for ISA: {app}", file=sys.stderr, ) sys.exit(1) diff --git a/configs/dist/sw.py b/configs/dist/sw.py index 726735773e..701e986707 100644 --- a/configs/dist/sw.py +++ b/configs/dist/sw.py @@ -62,7 +62,7 @@ def build_switch(args): for i in range(args.dist_size) ] - for (i, link) in enumerate(switch.portlink): + for i, link in enumerate(switch.portlink): link.int0 = switch.interface[i] return switch diff --git a/configs/dram/lat_mem_rd.py b/configs/dram/lat_mem_rd.py index 74a94997bb..b632e72b76 100644 --- a/configs/dram/lat_mem_rd.py +++ b/configs/dram/lat_mem_rd.py @@ -33,18 +33,20 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import gzip import argparse +import gzip import os import m5 from m5.objects import * -from m5.util import addToPath from m5.stats import periodicStatDump +from m5.util import addToPath addToPath("../") -from common import ObjectList -from common import MemConfig +from common import ( + MemConfig, + ObjectList, +) addToPath("../../util") import protolib @@ -150,6 +152,7 @@ cfg_file = open(cfg_file_name, "w") burst_size = 64 system.cache_line_size = burst_size + # lazy version to check if an integer is a power of two def is_pow2(num): return num != 0 and ((num & (num - 1)) == 0) @@ -177,13 +180,14 @@ iterations = 2 # do not pile up in the system, adjust if needed itt = 150 * 1000 + # for every data point, we create a trace containing a random address # sequence, so that we can play back the same sequence for warming and # the actual measurement def create_trace(filename, max_addr, burst_size, itt): try: proto_out = gzip.open(filename, "wb") - except IOError: + except OSError: print("Failed to open ", filename, " for writing") exit(-1) @@ -276,6 +280,7 @@ system.tgen.port = system.monitor.cpu_side_port # basic to explore some of the options from common.Caches import * + # a starting point for an L3 cache class L3Cache(Cache): assoc = 16 diff --git a/configs/dram/low_power_sweep.py b/configs/dram/low_power_sweep.py index 7f8591b692..5ef91d0618 100644 --- a/configs/dram/low_power_sweep.py +++ b/configs/dram/low_power_sweep.py @@ -37,13 +37,15 @@ import argparse import m5 from m5.objects import * -from m5.util import addToPath from m5.stats import periodicStatDump +from m5.util import addToPath addToPath("../") -from common import ObjectList -from common import MemConfig +from common import ( + MemConfig, + ObjectList, +) # This script aims at triggering low power state transitions in the DRAM # controller. The traffic generator is used in DRAM mode and traffic diff --git a/configs/dram/sweep.py b/configs/dram/sweep.py index ca7b70d4ed..95190a8ab8 100644 --- a/configs/dram/sweep.py +++ b/configs/dram/sweep.py @@ -33,18 +33,20 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import math import argparse +import math import m5 from m5.objects import * -from m5.util import addToPath from m5.stats import periodicStatDump +from m5.util import addToPath addToPath("../") -from common import ObjectList -from common import MemConfig +from common import ( + MemConfig, + ObjectList, +) # this script is helpful to sweep the efficiency of a specific memory # controller configuration, by varying the number of banks accessed, diff --git a/configs/example/apu_se.py b/configs/example/apu_se.py index 287135fd62..f9daf8a88b 100644 --- a/configs/example/apu_se.py +++ b/configs/example/apu_se.py @@ -27,28 +27,32 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -import argparse, os, re, getpass -import math +import argparse +import getpass import glob import inspect +import math +import os +import re import m5 from m5.objects import * from m5.util import addToPath + from gem5.isas import ISA -from gem5.runtime import get_runtime_isa +from gem5.runtime import get_supported_isas addToPath("../") -from ruby import Ruby - -from common import Options -from common import Simulation -from common import GPUTLBOptions, GPUTLBConfig - import hsaTopology -from common import FileSystemConfig - +from common import ( + FileSystemConfig, + GPUTLBConfig, + GPUTLBOptions, + Options, + Simulation, +) +from ruby import Ruby # Adding script options parser = argparse.ArgumentParser() @@ -394,8 +398,8 @@ if buildEnv["PROTOCOL"] == "None": fatal("GPU model requires ruby") # Currently the gpu model requires only timing or detailed CPU -if not (args.cpu_type == "TimingSimpleCPU" or args.cpu_type == "DerivO3CPU"): - fatal("GPU model requires TimingSimpleCPU or DerivO3CPU") +if not (args.cpu_type == "X86TimingSimpleCPU" or args.cpu_type == "X86O3CPU"): + fatal("GPU model requires X86TimingSimpleCPU or X86O3CPU.") # This file can support multiple compute units assert args.num_compute_units >= 1 @@ -567,7 +571,7 @@ cp_list = [] cpu_list = [] CpuClass, mem_mode = Simulation.getCPUClass(args.cpu_type) -if CpuClass == AtomicSimpleCPU: +if CpuClass == X86AtomicSimpleCPU or CpuClass == AtomicSimpleCPU: fatal("AtomicSimpleCPU is not supported") if mem_mode != "timing": fatal("Only the timing memory mode is supported") @@ -673,6 +677,7 @@ gpu_driver.device = gpu_cmd_proc shader.dispatcher = dispatcher shader.gpu_cmd_proc = gpu_cmd_proc + # Create and assign the workload Check for rel_path in elements of # base_list using test, returning the first full path that satisfies test def find_path(base_list, rel_path, test): @@ -698,7 +703,7 @@ if os.path.isdir(executable): executable = find_file(benchmark_path, args.cmd) if args.env: - with open(args.env, "r") as f: + with open(args.env) as f: env = [line.rstrip() for line in f] else: env = [ @@ -756,7 +761,7 @@ if fast_forward: ] # Other CPU strings cause bad addresses in ROCm. Revert back to M5 Simulator. -for (i, cpu) in enumerate(cpu_list): +for i, cpu in enumerate(cpu_list): for j in range(len(cpu)): cpu.isa[j].vendor_string = "M5 Simulator" @@ -781,7 +786,7 @@ system.clk_domain = SrcClockDomain( if fast_forward: have_kvm_support = "BaseKvmCPU" in globals() - if have_kvm_support and get_runtime_isa() == ISA.X86: + if have_kvm_support and get_supported_isas().contains(ISA.X86): system.vm = KvmVM() system.m5ops_base = 0xFFFF0000 for i in range(len(host_cpu.workload)): @@ -820,18 +825,15 @@ for i in range(args.num_cpus): system.cpu[i].dcache_port = ruby_port.in_ports ruby_port.mem_request_port = system.piobus.cpu_side_ports - if get_runtime_isa() == ISA.X86: - system.cpu[i].interrupts[0].pio = system.piobus.mem_side_ports - system.cpu[i].interrupts[ - 0 - ].int_requestor = system.piobus.cpu_side_ports - system.cpu[i].interrupts[ - 0 - ].int_responder = system.piobus.mem_side_ports - if fast_forward: - system.cpu[i].mmu.connectWalkerPorts( - ruby_port.in_ports, ruby_port.in_ports - ) + + # X86 ISA is implied from cpu type check above + system.cpu[i].interrupts[0].pio = system.piobus.mem_side_ports + system.cpu[i].interrupts[0].int_requestor = system.piobus.cpu_side_ports + system.cpu[i].interrupts[0].int_responder = system.piobus.mem_side_ports + if fast_forward: + system.cpu[i].mmu.connectWalkerPorts( + ruby_port.in_ports, ruby_port.in_ports + ) # attach CU ports to Ruby # Because of the peculiarities of the CP core, you may have 1 CPU but 2 diff --git a/configs/example/arm/baremetal.py b/configs/example/arm/baremetal.py index 08af3ef435..a986a2c44d 100644 --- a/configs/example/arm/baremetal.py +++ b/configs/example/arm/baremetal.py @@ -39,24 +39,29 @@ Research Starter Kit on System Modeling. More information can be found at: http://www.arm.com/ResearchEnablement/SystemModeling """ +import argparse import os + import m5 -from m5.util import addToPath from m5.objects import * from m5.options import * +from m5.util import addToPath + from gem5.simulate.exit_event import ExitEvent -import argparse m5.util.addToPath("../..") -from common import SysPaths -from common import MemConfig -from common import ObjectList -from common.cores.arm import HPI -from common.cores.arm import O3_ARM_v7a - import devices import workloads +from common import ( + MemConfig, + ObjectList, + SysPaths, +) +from common.cores.arm import ( + HPI, + O3_ARM_v7a, +) # Pre-defined CPU configurations. Each tuple must be ordered as : (cpu_class, # l1_icache_class, l1_dcache_class, walk_cache_class, l2_Cache_class). Any of @@ -171,9 +176,10 @@ def create(args): system.workload = workload_class(object_file, system) if args.with_pmu: - enabled_pmu_events = set( - (*args.pmu_dump_stats_on, *args.pmu_reset_stats_on) - ) + enabled_pmu_events = { + *args.pmu_dump_stats_on, + *args.pmu_reset_stats_on, + } exit_sim_on_control = bool( enabled_pmu_events & set(pmu_control_events.keys()) ) diff --git a/configs/example/arm/devices.py b/configs/example/arm/devices.py index 6c6474ca2b..dc62525204 100644 --- a/configs/example/arm/devices.py +++ b/configs/example/arm/devices.py @@ -39,8 +39,8 @@ import m5 from m5.objects import * m5.util.addToPath("../../") -from common.Caches import * from common import ObjectList +from common.Caches import * have_kvm = "ArmV8KvmCPU" in ObjectList.cpu_list.get_names() have_fastmodel = "FastModelCortexA76" in ObjectList.cpu_list.get_names() @@ -338,56 +338,15 @@ class FastmodelCluster(CpuCluster): pass -class BaseSimpleSystem(ArmSystem): - cache_line_size = 64 - - def __init__(self, mem_size, platform, **kwargs): - super(BaseSimpleSystem, self).__init__(**kwargs) - - self.voltage_domain = VoltageDomain(voltage="1.0V") - self.clk_domain = SrcClockDomain( - clock="1GHz", voltage_domain=Parent.voltage_domain - ) - - if platform is None: - self.realview = VExpress_GEM5_V1() - else: - self.realview = platform - - if hasattr(self.realview.gic, "cpu_addr"): - self.gic_cpu_addr = self.realview.gic.cpu_addr - - self.terminal = Terminal() - self.vncserver = VncServer() - - self.iobus = IOXBar() - # Device DMA -> MEM - self.mem_ranges = self.getMemRanges(int(Addr(mem_size))) +class ClusterSystem: + """ + Base class providing cpu clusters generation/handling methods to + SE/FS systems + """ + def __init__(self, **kwargs): self._clusters = [] - def getMemRanges(self, mem_size): - """ - Define system memory ranges. This depends on the physical - memory map provided by the realview platform and by the memory - size provided by the user (mem_size argument). - The method is iterating over all platform ranges until they cover - the entire user's memory requirements. - """ - mem_ranges = [] - for mem_range in self.realview._mem_regions: - size_in_range = min(mem_size, mem_range.size()) - - mem_ranges.append( - AddrRange(start=mem_range.start, size=size_in_range) - ) - - mem_size -= size_in_range - if mem_size == 0: - return mem_ranges - - raise ValueError("memory size too big for platform capabilities") - def numCpuClusters(self): return len(self._clusters) @@ -423,13 +382,87 @@ class BaseSimpleSystem(ArmSystem): cluster.connectMemSide(cluster_mem_bus) +class SimpleSeSystem(System, ClusterSystem): + """ + Example system class for syscall emulation mode + """ + + # Use a fixed cache line size of 64 bytes + cache_line_size = 64 + + def __init__(self, **kwargs): + System.__init__(self, **kwargs) + ClusterSystem.__init__(self, **kwargs) + # Create a voltage and clock domain for system components + self.voltage_domain = VoltageDomain(voltage="3.3V") + self.clk_domain = SrcClockDomain( + clock="1GHz", voltage_domain=self.voltage_domain + ) + + # Create the off-chip memory bus. + self.membus = SystemXBar() + + def connect(self): + self.system_port = self.membus.cpu_side_ports + + +class BaseSimpleSystem(ArmSystem, ClusterSystem): + cache_line_size = 64 + + def __init__(self, mem_size, platform, **kwargs): + ArmSystem.__init__(self, **kwargs) + ClusterSystem.__init__(self, **kwargs) + + self.voltage_domain = VoltageDomain(voltage="1.0V") + self.clk_domain = SrcClockDomain( + clock="1GHz", voltage_domain=Parent.voltage_domain + ) + + if platform is None: + self.realview = VExpress_GEM5_V1() + else: + self.realview = platform + + if hasattr(self.realview.gic, "cpu_addr"): + self.gic_cpu_addr = self.realview.gic.cpu_addr + + self.terminal = Terminal() + self.vncserver = VncServer() + + self.iobus = IOXBar() + # Device DMA -> MEM + self.mem_ranges = self.getMemRanges(int(Addr(mem_size))) + + def getMemRanges(self, mem_size): + """ + Define system memory ranges. This depends on the physical + memory map provided by the realview platform and by the memory + size provided by the user (mem_size argument). + The method is iterating over all platform ranges until they cover + the entire user's memory requirements. + """ + mem_ranges = [] + for mem_range in self.realview._mem_regions: + size_in_range = min(mem_size, mem_range.size()) + + mem_ranges.append( + AddrRange(start=mem_range.start, size=size_in_range) + ) + + mem_size -= size_in_range + if mem_size == 0: + return mem_ranges + + raise ValueError("memory size too big for platform capabilities") + + class SimpleSystem(BaseSimpleSystem): """ Meant to be used with the classic memory model """ def __init__(self, caches, mem_size, platform=None, **kwargs): - super(SimpleSystem, self).__init__(mem_size, platform, **kwargs) + super().__init__(mem_size, platform, **kwargs) self.membus = MemBus() # CPUs->PIO @@ -468,7 +501,7 @@ class ArmRubySystem(BaseSimpleSystem): """ def __init__(self, mem_size, platform=None, **kwargs): - super(ArmRubySystem, self).__init__(mem_size, platform, **kwargs) + super().__init__(mem_size, platform, **kwargs) self._dma_ports = [] self._mem_ports = [] diff --git a/configs/example/arm/dist_bigLITTLE.py b/configs/example/arm/dist_bigLITTLE.py index 2884a5efd5..f5a816c12a 100644 --- a/configs/example/arm/dist_bigLITTLE.py +++ b/configs/example/arm/dist_bigLITTLE.py @@ -39,11 +39,11 @@ import argparse import os +import fs_bigLITTLE as bL + import m5 from m5.objects import * -import fs_bigLITTLE as bL - m5.util.addToPath("../../dist") import sw diff --git a/configs/example/arm/etrace_se.py b/configs/example/arm/etrace_se.py new file mode 100644 index 0000000000..1b06ba3fdf --- /dev/null +++ b/configs/example/arm/etrace_se.py @@ -0,0 +1,191 @@ +# Copyright (c) 2016-2017, 2022-2023 Arm Limited +# All rights reserved. +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +import argparse +import os +import shlex + +import m5 +from m5.objects import * +from m5.util import addToPath + +m5.util.addToPath("../..") + +import devices +from common import ObjectList + + +def get_processes(cmd): + """Interprets commands to run and returns a list of processes""" + + cwd = os.getcwd() + multiprocesses = [] + for idx, c in enumerate(cmd): + argv = shlex.split(c) + + process = Process(pid=100 + idx, cwd=cwd, cmd=argv, executable=argv[0]) + process.gid = os.getgid() + + print("info: %d. command and arguments: %s" % (idx + 1, process.cmd)) + multiprocesses.append(process) + + return multiprocesses + + +def create(args): + """Create and configure the system object.""" + + system = devices.SimpleSeSystem( + mem_mode="timing", + ) + + # Add CPUs to the system. A cluster of CPUs typically have + # private L1 caches and a shared L2 cache. + system.cpu_cluster = devices.ArmCpuCluster( + system, + args.num_cores, + args.cpu_freq, + "1.2V", + ObjectList.cpu_list.get("O3_ARM_v7a_3_Etrace"), + devices.L1I, + devices.L1D, + devices.L2, + ) + + # Attach the elastic trace probe listener to every CPU in the cluster + for cpu in system.cpu_cluster: + cpu.attach_probe_listener(args.inst_trace_file, args.data_trace_file) + + # As elastic trace generation is enabled, make sure the memory system is + # minimal so that compute delays do not include memory access latencies. + # Configure the compulsory L1 caches for the O3CPU, do not configure + # any more caches. + system.addCaches(True, last_cache_level=1) + + # For elastic trace, over-riding Simple Memory latency to 1ns." + system.memory = SimpleMemory( + range=AddrRange(start=0, size=args.mem_size), + latency="1ns", + port=system.membus.mem_side_ports, + ) + + # Parse the command line and get a list of Processes instances + # that we can pass to gem5. + processes = get_processes(args.commands_to_run) + if len(processes) != args.num_cores: + print( + "Error: Cannot map %d command(s) onto %d CPU(s)" + % (len(processes), args.num_cores) + ) + sys.exit(1) + + system.workload = SEWorkload.init_compatible(processes[0].executable) + + # Assign one workload to each CPU + for cpu, workload in zip(system.cpu_cluster.cpus, processes): + cpu.workload = workload + + return system + + +def main(): + parser = argparse.ArgumentParser(epilog=__doc__) + + parser.add_argument( + "commands_to_run", + metavar="command(s)", + nargs="+", + help="Command(s) to run", + ) + parser.add_argument( + "--inst-trace-file", + action="store", + type=str, + help="""Instruction fetch trace file input to + Elastic Trace probe in a capture simulation and + Trace CPU in a replay simulation""", + default="fetchtrace.proto.gz", + ) + parser.add_argument( + "--data-trace-file", + action="store", + type=str, + help="""Data dependency trace file input to + Elastic Trace probe in a capture simulation and + Trace CPU in a replay simulation""", + default="deptrace.proto.gz", + ) + parser.add_argument("--cpu-freq", type=str, default="4GHz") + parser.add_argument( + "--num-cores", type=int, default=1, help="Number of CPU cores" + ) + parser.add_argument( + "--mem-size", + action="store", + type=str, + default="2GB", + help="Specify the physical memory size", + ) + + args = parser.parse_args() + + # Create a single root node for gem5's object hierarchy. There can + # only exist one root node in the simulator at any given + # time. Tell gem5 that we want to use syscall emulation mode + # instead of full system mode. + root = Root(full_system=False) + + # Populate the root node with a system. A system corresponds to a + # single node with shared memory. + root.system = create(args) + + # Instantiate the C++ object hierarchy. After this point, + # SimObjects can't be instantiated anymore. + m5.instantiate() + + # Start the simulator. This gives control to the C++ world and + # starts the simulator. The returned event tells the simulation + # script why the simulator exited. + event = m5.simulate() + + # Print the reason for the simulation exit. Some exit codes are + # requests for service (e.g., checkpoints) from the simulation + # script. We'll just ignore them here and exit. + print(f"{event.getCause()} ({event.getCode()}) @ {m5.curTick()}") + + +if __name__ == "__m5_main__": + main() diff --git a/configs/example/arm/fs_bigLITTLE.py b/configs/example/arm/fs_bigLITTLE.py index 401eb0c9e7..17e9e6dd28 100644 --- a/configs/example/arm/fs_bigLITTLE.py +++ b/configs/example/arm/fs_bigLITTLE.py @@ -39,21 +39,29 @@ import argparse import os import sys + import m5 import m5.util from m5.objects import * m5.util.addToPath("../../") -from common import FSConfig -from common import SysPaths -from common import ObjectList -from common import Options -from common.cores.arm import ex5_big, ex5_LITTLE - import devices -from devices import AtomicCluster, KvmCluster, FastmodelCluster - +from common import ( + FSConfig, + ObjectList, + Options, + SysPaths, +) +from common.cores.arm import ( + ex5_big, + ex5_LITTLE, +) +from devices import ( + AtomicCluster, + FastmodelCluster, + KvmCluster, +) default_disk = "aarch64-ubuntu-trusty-headless.img" @@ -410,7 +418,8 @@ def build(options): system.generateDtb(system.workload.dtb_filename) if devices.have_fastmodel and issubclass(big_model, FastmodelCluster): - from m5 import arm_fast_model as fm, systemc as sc + from m5 import arm_fast_model as fm + from m5 import systemc as sc # setup FastModels for simulation fm.setup_simulation("cortexa76") diff --git a/configs/example/arm/fs_power.py b/configs/example/arm/fs_power.py index 671cf63f2f..83e43eef66 100644 --- a/configs/example/arm/fs_power.py +++ b/configs/example/arm/fs_power.py @@ -39,15 +39,18 @@ import argparse import os -import m5 -from m5.objects import MathExprPowerModel, PowerModel - import fs_bigLITTLE as bL +import m5 +from m5.objects import ( + MathExprPowerModel, + PowerModel, +) + class CpuPowerOn(MathExprPowerModel): def __init__(self, cpu_path, **kwargs): - super(CpuPowerOn, self).__init__(**kwargs) + super().__init__(**kwargs) # 2A per IPC, 3pA per cache miss # and then convert to Watt self.dyn = ( @@ -64,7 +67,7 @@ class CpuPowerOff(MathExprPowerModel): class CpuPowerModel(PowerModel): def __init__(self, cpu_path, **kwargs): - super(CpuPowerModel, self).__init__(**kwargs) + super().__init__(**kwargs) self.pm = [ CpuPowerOn(cpu_path), # ON CpuPowerOff(), # CLK_GATED @@ -75,7 +78,7 @@ class CpuPowerModel(PowerModel): class L2PowerOn(MathExprPowerModel): def __init__(self, l2_path, **kwargs): - super(L2PowerOn, self).__init__(**kwargs) + super().__init__(**kwargs) # Example to report l2 Cache overallAccesses # The estimated power is converted to Watt and will vary based # on the size of the cache @@ -90,7 +93,7 @@ class L2PowerOff(MathExprPowerModel): class L2PowerModel(PowerModel): def __init__(self, l2_path, **kwargs): - super(L2PowerModel, self).__init__(**kwargs) + super().__init__(**kwargs) # Choose a power model for every power state self.pm = [ L2PowerOn(l2_path), # ON diff --git a/configs/example/arm/ruby_fs.py b/configs/example/arm/ruby_fs.py index 67a8a6e0b3..a0979c1ced 100644 --- a/configs/example/arm/ruby_fs.py +++ b/configs/example/arm/ruby_fs.py @@ -33,24 +33,28 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import argparse import os + import m5 -from m5.util import addToPath from m5.objects import * from m5.options import * -import argparse +from m5.util import addToPath m5.util.addToPath("../..") -from common import MemConfig -from common import ObjectList -from common import Options -from common import SysPaths -from common.cores.arm import O3_ARM_v7a, HPI -from ruby import Ruby - import devices - +from common import ( + MemConfig, + ObjectList, + Options, + SysPaths, +) +from common.cores.arm import ( + HPI, + O3_ARM_v7a, +) +from ruby import Ruby default_kernel = "vmlinux.arm64" default_disk = "linaro-minimal-aarch64.img" diff --git a/configs/example/arm/starter_fs.py b/configs/example/arm/starter_fs.py index 07280bd204..44f72a6291 100644 --- a/configs/example/arm/starter_fs.py +++ b/configs/example/arm/starter_fs.py @@ -38,22 +38,26 @@ Research Starter Kit on System Modeling. More information can be found at: http://www.arm.com/ResearchEnablement/SystemModeling """ +import argparse import os + import m5 -from m5.util import addToPath from m5.objects import * from m5.options import * -import argparse +from m5.util import addToPath m5.util.addToPath("../..") -from common import SysPaths -from common import ObjectList -from common import MemConfig -from common.cores.arm import O3_ARM_v7a, HPI - import devices - +from common import ( + MemConfig, + ObjectList, + SysPaths, +) +from common.cores.arm import ( + HPI, + O3_ARM_v7a, +) default_kernel = "vmlinux.arm64" default_disk = "linaro-minimal-aarch64.img" diff --git a/configs/example/arm/starter_se.py b/configs/example/arm/starter_se.py index f21f399675..33cf7b2f40 100644 --- a/configs/example/arm/starter_se.py +++ b/configs/example/arm/starter_se.py @@ -38,21 +38,22 @@ Research Starter Kit on System Modeling. More information can be found at: http://www.arm.com/ResearchEnablement/SystemModeling """ -import os -import m5 -from m5.util import addToPath -from m5.objects import * import argparse +import os import shlex +import m5 +from m5.objects import * +from m5.util import addToPath + m5.util.addToPath("../..") -from common import ObjectList -from common import MemConfig -from common.cores.arm import HPI - import devices - +from common import ( + MemConfig, + ObjectList, +) +from common.cores.arm import HPI # Pre-defined CPU configurations. Each tuple must be ordered as : (cpu_class, # l1_icache_class, l1_dcache_class, walk_cache_class, l2_Cache_class). Any of @@ -64,72 +65,6 @@ cpu_types = { } -class SimpleSeSystem(System): - """ - Example system class for syscall emulation mode - """ - - # Use a fixed cache line size of 64 bytes - cache_line_size = 64 - - def __init__(self, args, **kwargs): - super(SimpleSeSystem, self).__init__(**kwargs) - - # Setup book keeping to be able to use CpuClusters from the - # devices module. - self._clusters = [] - self._num_cpus = 0 - - # Create a voltage and clock domain for system components - self.voltage_domain = VoltageDomain(voltage="3.3V") - self.clk_domain = SrcClockDomain( - clock="1GHz", voltage_domain=self.voltage_domain - ) - - # Create the off-chip memory bus. - self.membus = SystemXBar() - - # Wire up the system port that gem5 uses to load the kernel - # and to perform debug accesses. - self.system_port = self.membus.cpu_side_ports - - # Add CPUs to the system. A cluster of CPUs typically have - # private L1 caches and a shared L2 cache. - self.cpu_cluster = devices.ArmCpuCluster( - self, - args.num_cores, - args.cpu_freq, - "1.2V", - *cpu_types[args.cpu], - tarmac_gen=args.tarmac_gen, - tarmac_dest=args.tarmac_dest, - ) - - # Create a cache hierarchy (unless we are simulating a - # functional CPU in atomic memory mode) for the CPU cluster - # and connect it to the shared memory bus. - if self.cpu_cluster.memory_mode() == "timing": - self.cpu_cluster.addL1() - self.cpu_cluster.addL2(self.cpu_cluster.clk_domain) - self.cpu_cluster.connectMemSide(self.membus) - - # Tell gem5 about the memory mode used by the CPUs we are - # simulating. - self.mem_mode = self.cpu_cluster.memory_mode() - - def numCpuClusters(self): - return len(self._clusters) - - def addCpuCluster(self, cpu_cluster): - assert cpu_cluster not in self._clusters - assert len(cpu_cluster) > 0 - self._clusters.append(cpu_cluster) - self._num_cpus += len(cpu_cluster) - - def numCpus(self): - return self._num_cpus - - def get_processes(cmd): """Interprets commands to run and returns a list of processes""" @@ -150,7 +85,31 @@ def get_processes(cmd): def create(args): """Create and configure the system object.""" - system = SimpleSeSystem(args) + cpu_class = cpu_types[args.cpu][0] + mem_mode = cpu_class.memory_mode() + # Only simulate caches when using a timing CPU (e.g., the HPI model) + want_caches = True if mem_mode == "timing" else False + + system = devices.SimpleSeSystem( + mem_mode=mem_mode, + ) + + # Add CPUs to the system. A cluster of CPUs typically have + # private L1 caches and a shared L2 cache. + system.cpu_cluster = devices.ArmCpuCluster( + system, + args.num_cores, + args.cpu_freq, + "1.2V", + *cpu_types[args.cpu], + tarmac_gen=args.tarmac_gen, + tarmac_dest=args.tarmac_dest, + ) + + # Create a cache hierarchy for the cluster. We are assuming that + # clusters have core-private L1 caches and an L2 that's shared + # within the cluster. + system.addCaches(want_caches, last_cache_level=2) # Tell components about the expected physical memory ranges. This # is, for example, used by the MemConfig helper to determine where @@ -160,6 +119,9 @@ def create(args): # Configure the off-chip memory system. MemConfig.config_mem(args, system) + # Wire up the system's memory system + system.connect() + # Parse the command line and get a list of Processes instances # that we can pass to gem5. processes = get_processes(args.commands_to_run) diff --git a/configs/example/arm/workloads.py b/configs/example/arm/workloads.py index 5c70dabfc2..d84657712e 100644 --- a/configs/example/arm/workloads.py +++ b/configs/example/arm/workloads.py @@ -35,13 +35,17 @@ # import inspect + +from common.ObjectList import ObjectList +from common.SysPaths import ( + binary, + disk, +) + import m5 from m5.objects import * from m5.options import * -from common.ObjectList import ObjectList -from common.SysPaths import binary, disk - class ArmBaremetal(ArmFsWorkload): """Baremetal workload""" @@ -49,7 +53,7 @@ class ArmBaremetal(ArmFsWorkload): dtb_addr = 0 def __init__(self, obj, system, **kwargs): - super(ArmBaremetal, self).__init__(**kwargs) + super().__init__(**kwargs) self.object_file = obj @@ -76,7 +80,7 @@ class ArmTrustedFirmware(ArmFsWorkload): dtb_addr = 0 def __init__(self, obj, system, **kwargs): - super(ArmTrustedFirmware, self).__init__(**kwargs) + super().__init__(**kwargs) self.extras = [binary("bl1.bin"), binary("fip.bin")] self.extras_addrs = [ diff --git a/configs/example/dramsys.py b/configs/example/dramsys.py index 934ff17b57..191e1937f7 100755 --- a/configs/example/dramsys.py +++ b/configs/example/dramsys.py @@ -25,7 +25,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import m5 - from m5.objects import * traffic_gen = PyTrafficGen() @@ -37,9 +36,8 @@ system.mem_mode = "timing" system.cpu = traffic_gen dramsys = DRAMSys( - configuration="ext/dramsys/DRAMSys/DRAMSys/" - "library/resources/simulations/ddr4-example.json", - resource_directory="ext/dramsys/DRAMSys/DRAMSys/library/resources", + configuration="ext/dramsys/DRAMSys/configs/ddr4-example.json", + resource_directory="ext/dramsys/DRAMSys/configs", ) system.target = dramsys diff --git a/configs/example/etrace_replay.py b/configs/example/etrace_replay.py index ddbf01acf5..7df1333f2b 100644 --- a/configs/example/etrace_replay.py +++ b/configs/example/etrace_replay.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015 ARM Limited +# Copyright (c) 2015, 2023 Arm Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -37,16 +37,53 @@ import argparse -from m5.util import addToPath, fatal +from m5.util import ( + addToPath, + fatal, +) addToPath("../") -from common import Options -from common import Simulation -from common import CacheConfig -from common import MemConfig +from common import ( + MemConfig, + Options, + Simulation, +) from common.Caches import * + +def config_cache(args, system): + """ + Configure the cache hierarchy. Only two configurations are natively + supported as an example: L1(I/D) only or L1 + L2. + """ + from common.CacheConfig import _get_cache_opts + + system.l1i = L1_ICache(**_get_cache_opts("l1i", args)) + system.l1d = L1_DCache(**_get_cache_opts("l1d", args)) + + system.cpu.dcache_port = system.l1d.cpu_side + system.cpu.icache_port = system.l1i.cpu_side + + if args.l2cache: + # Provide a clock for the L2 and the L1-to-L2 bus here as they + # are not connected using addTwoLevelCacheHierarchy. Use the + # same clock as the CPUs. + system.l2 = L2Cache( + clk_domain=system.cpu_clk_domain, **_get_cache_opts("l2", args) + ) + + system.tol2bus = L2XBar(clk_domain=system.cpu_clk_domain) + system.l2.cpu_side = system.tol2bus.mem_side_ports + system.l2.mem_side = system.membus.cpu_side_ports + + system.l1i.mem_side = system.tol2bus.cpu_side_ports + system.l1d.mem_side = system.tol2bus.cpu_side_ports + else: + system.l1i.mem_side = system.membus.cpu_side_ports + system.l1d.mem_side = system.membus.cpu_side_ports + + parser = argparse.ArgumentParser() Options.addCommonOptions(parser) @@ -59,29 +96,18 @@ if "--ruby" in sys.argv: args = parser.parse_args() -numThreads = 1 - -if args.cpu_type != "TraceCPU": - fatal( - "This is a script for elastic trace replay simulation, use " - "--cpu-type=TraceCPU\n" - ) - if args.num_cpus > 1: fatal("This script does not support multi-processor trace replay.\n") -# In this case FutureClass will be None as there is not fast forwarding or -# switching -(CPUClass, test_mem_mode, FutureClass) = Simulation.setCPUClass(args) -CPUClass.numThreads = numThreads - system = System( - cpu=CPUClass(cpu_id=0), - mem_mode=test_mem_mode, + mem_mode=TraceCPU.memory_mode(), mem_ranges=[AddrRange(args.mem_size)], cache_line_size=args.cacheline_size, ) +# Generate the TraceCPU +system.cpu = TraceCPU() + # Create a top-level voltage domain system.voltage_domain = VoltageDomain(voltage=args.sys_voltage) @@ -105,11 +131,6 @@ system.cpu_clk_domain = SrcClockDomain( for cpu in system.cpu: cpu.clk_domain = system.cpu_clk_domain -# BaseCPU no longer has default values for the BaseCPU.isa -# createThreads() is needed to fill in the cpu.isa -for cpu in system.cpu: - cpu.createThreads() - # Assign input trace files to the Trace CPU system.cpu.instTraceFile = args.inst_trace_file system.cpu.dataTraceFile = args.data_trace_file @@ -118,8 +139,11 @@ system.cpu.dataTraceFile = args.data_trace_file MemClass = Simulation.setMemClass(args) system.membus = SystemXBar() system.system_port = system.membus.cpu_side_ports -CacheConfig.config_cache(args, system) + +# Configure the classic cache hierarchy +config_cache(args, system) + MemConfig.config_mem(args, system) root = Root(full_system=False, system=system) -Simulation.run(args, root, system, FutureClass) +Simulation.run(args, root, system, None) diff --git a/configs/example/garnet_synth_traffic.py b/configs/example/garnet_synth_traffic.py index 1da82e11b8..8bbacd7a8e 100644 --- a/configs/example/garnet_synth_traffic.py +++ b/configs/example/garnet_synth_traffic.py @@ -26,11 +26,14 @@ # # Author: Tushar Krishna +import argparse +import os +import sys + import m5 -from m5.objects import * from m5.defines import buildEnv +from m5.objects import * from m5.util import addToPath -import os, argparse, sys addToPath("../") diff --git a/configs/example/gem5_library/arm-hello.py b/configs/example/gem5_library/arm-hello.py index b4180f11eb..39583463e7 100644 --- a/configs/example/gem5_library/arm-hello.py +++ b/configs/example/gem5_library/arm-hello.py @@ -41,15 +41,15 @@ scons build/ARM/gem5.opt ``` """ -from gem5.isas import ISA -from gem5.utils.requires import requires -from gem5.resources.resource import Resource -from gem5.components.memory import SingleChannelDDR3_1600 -from gem5.components.processors.cpu_types import CPUTypes from gem5.components.boards.simple_board import SimpleBoard from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires # This check ensures the gem5 binary is compiled to the ARM ISA target. If not, # an exception will be thrown. @@ -84,7 +84,7 @@ board.set_se_binary_workload( # Any resource specified in this file will be automatically retrieved. # At the time of writing, this file is a WIP and does not contain all # resources. Jira ticket: https://gem5.atlassian.net/browse/GEM5-1096 - Resource("arm-hello64-static") + obtain_resource("arm-hello64-static") ) # Lastly we run the simulation. diff --git a/configs/example/gem5_library/arm-ubuntu-run.py b/configs/example/gem5_library/arm-ubuntu-run.py index 7f976f06db..734fb9ee1b 100644 --- a/configs/example/gem5_library/arm-ubuntu-run.py +++ b/configs/example/gem5_library/arm-ubuntu-run.py @@ -40,18 +40,20 @@ scons build/ARM/gem5.opt -j """ -from gem5.isas import ISA -from m5.objects import ArmDefaultRelease -from gem5.utils.requires import requires -from gem5.resources.workload import Workload -from gem5.simulate.simulator import Simulator -from m5.objects import VExpress_GEM5_Foundation +from m5.objects import ( + ArmDefaultRelease, + VExpress_GEM5_Foundation, +) + from gem5.coherence_protocol import CoherenceProtocol from gem5.components.boards.arm_board import ArmBoard from gem5.components.memory import DualChannelDDR4_2400 from gem5.components.processors.cpu_types import CPUTypes from gem5.components.processors.simple_processor import SimpleProcessor - +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires # This runs a check to ensure the gem5 binary is compiled for ARM and the # protocol is CHI. @@ -100,7 +102,7 @@ board = ArmBoard( # Here we set a full system workload. The "arm64-ubuntu-20.04-boot" boots # Ubuntu 20.04. -board.set_workload(Workload("arm64-ubuntu-20.04-boot")) +board.set_workload(obtain_resource("arm64-ubuntu-20.04-boot")) # We define the system with the aforementioned system defined. diff --git a/configs/example/gem5_library/caches/octopi-cache-example.py b/configs/example/gem5_library/caches/octopi-cache-example.py index 1b39a8bee5..fa19773167 100644 --- a/configs/example/gem5_library/caches/octopi-cache-example.py +++ b/configs/example/gem5_library/caches/octopi-cache-example.py @@ -38,20 +38,23 @@ scons build/ARM_MESI_Three_Level/gem5.opt -j `nproc` """ -from m5.objects import ArmDefaultRelease, VExpress_GEM5_Foundation +from m5.objects import ( + ArmDefaultRelease, + VExpress_GEM5_Foundation, +) -from gem5.utils.requires import requires +from gem5.coherence_protocol import CoherenceProtocol from gem5.components.boards.arm_board import ArmBoard -from gem5.components.memory import DualChannelDDR4_2400 -from gem5.components.processors.simple_processor import SimpleProcessor -from gem5.components.processors.cpu_types import CPUTypes from gem5.components.cachehierarchies.ruby.caches.mesi_three_level.octopi import ( OctopiCache, ) +from gem5.components.memory import DualChannelDDR4_2400 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor from gem5.isas import ISA -from gem5.coherence_protocol import CoherenceProtocol +from gem5.resources.resource import obtain_resource from gem5.simulate.simulator import Simulator -from gem5.resources.workload import Workload +from gem5.utils.requires import requires num_ccds = 1 # CCDs num_cores_per_ccd = 8 # 8 cores/CCD @@ -94,7 +97,7 @@ board = ArmBoard( platform=platform, ) -board.set_workload(Workload("arm64-ubuntu-20.04-boot")) +board.set_workload(obtain_resource("arm64-ubuntu-20.04-boot")) simulator = Simulator(board=board) simulator.run() diff --git a/configs/example/gem5_library/checkpoints/riscv-hello-restore-checkpoint.py b/configs/example/gem5_library/checkpoints/riscv-hello-restore-checkpoint.py index 60a7dd0f59..aa78de5647 100644 --- a/configs/example/gem5_library/checkpoints/riscv-hello-restore-checkpoint.py +++ b/configs/example/gem5_library/checkpoints/riscv-hello-restore-checkpoint.py @@ -46,15 +46,15 @@ scons build/RISCV/gem5.opt ``` """ -from gem5.isas import ISA -from gem5.utils.requires import requires -from gem5.resources.resource import Resource -from gem5.components.memory import SingleChannelDDR3_1600 -from gem5.components.processors.cpu_types import CPUTypes from gem5.components.boards.simple_board import SimpleBoard from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires # This check ensures the gem5 binary is compiled to the RISCV ISA target. # If not, an exception will be thrown. @@ -89,8 +89,8 @@ board = SimpleBoard( # configs/example/gem5_library/checkpoints/riscv-hello-save-checkpoint.py board.set_se_binary_workload( # the workload should be the same as the save-checkpoint script - Resource("riscv-hello"), - checkpoint=Resource("riscv-hello-example-checkpoint-v23"), + obtain_resource("riscv-hello"), + checkpoint=obtain_resource("riscv-hello-example-checkpoint"), ) simulator = Simulator( diff --git a/configs/example/gem5_library/checkpoints/riscv-hello-save-checkpoint.py b/configs/example/gem5_library/checkpoints/riscv-hello-save-checkpoint.py index 439d2054fa..b024a3a44a 100644 --- a/configs/example/gem5_library/checkpoints/riscv-hello-save-checkpoint.py +++ b/configs/example/gem5_library/checkpoints/riscv-hello-save-checkpoint.py @@ -44,15 +44,16 @@ scons build/RISCV/gem5.opt """ import argparse -from gem5.isas import ISA -from gem5.utils.requires import requires -from gem5.resources.resource import Resource -from gem5.components.memory import SingleChannelDDR3_1600 -from gem5.components.processors.cpu_types import CPUTypes + from gem5.components.boards.simple_board import SimpleBoard from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires parser = argparse.ArgumentParser() @@ -101,7 +102,7 @@ board.set_se_binary_workload( # Any resource specified in this file will be automatically retrieved. # At the time of writing, this file is a WIP and does not contain all # resources. Jira ticket: https://gem5.atlassian.net/browse/GEM5-1096 - Resource("riscv-hello") + obtain_resource("riscv-hello") ) # Lastly we run the simulation. diff --git a/configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py b/configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py index b5eb7e9912..4649f4a07e 100644 --- a/configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py +++ b/configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py @@ -48,22 +48,23 @@ scons build/X86/gem5.opt """ import argparse +from pathlib import Path +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory.single_channel import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import ( + SimpointResource, + obtain_resource, +) +from gem5.resources.workload import Workload from gem5.simulate.exit_event import ExitEvent +from gem5.simulate.exit_event_generators import save_checkpoint_generator from gem5.simulate.simulator import Simulator from gem5.utils.requires import requires -from gem5.components.boards.simple_board import SimpleBoard -from gem5.components.memory.single_channel import SingleChannelDDR3_1600 -from gem5.components.processors.simple_processor import SimpleProcessor -from gem5.components.processors.cpu_types import CPUTypes -from gem5.isas import ISA -from gem5.resources.workload import Workload -from gem5.resources.resource import obtain_resource, SimpointResource -from pathlib import Path -from gem5.components.cachehierarchies.classic.no_cache import NoCache -from gem5.simulate.exit_event_generators import ( - save_checkpoint_generator, -) requires(isa_required=ISA.X86) @@ -128,7 +129,6 @@ board.set_se_simpoint_workload( ) dir = Path(args.checkpoint_path) -dir.mkdir(exist_ok=True) simulator = Simulator( board=board, diff --git a/configs/example/gem5_library/checkpoints/simpoints-se-restore.py b/configs/example/gem5_library/checkpoints/simpoints-se-restore.py index d063c143a7..284289be6f 100644 --- a/configs/example/gem5_library/checkpoints/simpoints-se-restore.py +++ b/configs/example/gem5_library/checkpoints/simpoints-se-restore.py @@ -52,23 +52,29 @@ scons build/X86/gem5.opt """ -from gem5.simulate.exit_event import ExitEvent -from gem5.simulate.simulator import Simulator -from gem5.utils.requires import requires +from pathlib import Path + +from m5.stats import ( + dump, + reset, +) + +from gem5.components.boards.simple_board import SimpleBoard from gem5.components.cachehierarchies.classic.private_l1_private_l2_cache_hierarchy import ( PrivateL1PrivateL2CacheHierarchy, ) -from gem5.components.boards.simple_board import SimpleBoard from gem5.components.memory import DualChannelDDR4_2400 -from gem5.components.processors.simple_processor import SimpleProcessor from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor from gem5.isas import ISA -from gem5.resources.resource import SimpointResource, obtain_resource +from gem5.resources.resource import ( + SimpointResource, + obtain_resource, +) from gem5.resources.workload import Workload -from gem5.resources.resource import SimpointResource - -from pathlib import Path -from m5.stats import reset, dump +from gem5.simulate.exit_event import ExitEvent +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires requires(isa_required=ISA.X86) diff --git a/configs/example/gem5_library/dramsys/arm-hello-dramsys.py b/configs/example/gem5_library/dramsys/arm-hello-dramsys.py index ae0f51ad40..2561f98fae 100644 --- a/configs/example/gem5_library/dramsys/arm-hello-dramsys.py +++ b/configs/example/gem5_library/dramsys/arm-hello-dramsys.py @@ -33,17 +33,17 @@ DRRAMSys simulator. Please consult 'ext/dramsys/README' on how to compile correctly. If this is not done correctly this script will run with error. """ -from gem5.isas import ISA -from gem5.utils.requires import requires -from gem5.resources.resource import Resource -from gem5.components.memory import DRAMSysDDR3_1600 -from gem5.components.processors.cpu_types import CPUTypes from gem5.components.boards.simple_board import SimpleBoard from gem5.components.cachehierarchies.classic.private_l1_cache_hierarchy import ( PrivateL1CacheHierarchy, ) +from gem5.components.memory import DRAMSysDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires # This check ensures the gem5 binary is compiled to the ARM ISA target. If not, # an exception will be thrown. @@ -78,7 +78,7 @@ board.set_se_binary_workload( # Any resource specified in this file will be automatically retrieved. # At the time of writing, this file is a WIP and does not contain all # resources. Jira ticket: https://gem5.atlassian.net/browse/GEM5-1096 - Resource("arm-hello64-static") + obtain_resource("arm-hello64-static") ) # Lastly we run the simulation. diff --git a/configs/example/gem5_library/dramsys/dramsys-traffic.py b/configs/example/gem5_library/dramsys/dramsys-traffic.py index ee9ad7228d..cc10dd0657 100644 --- a/configs/example/gem5_library/dramsys/dramsys-traffic.py +++ b/configs/example/gem5_library/dramsys/dramsys-traffic.py @@ -31,16 +31,14 @@ DRAMSys simulator. DRRAMSys simulator. Please consult 'ext/dramsys/README' on how to compile correctly. If this is not done correctly this script will run with error. """ -import m5 -from gem5.components.memory import DRAMSysMem + from gem5.components.boards.test_board import TestBoard +from gem5.components.memory.dramsys import DRAMSysMem from gem5.components.processors.linear_generator import LinearGenerator -from m5.objects import Root +from gem5.simulate.simulator import Simulator memory = DRAMSysMem( - configuration="ext/dramsys/DRAMSys/DRAMSys/" - "library/resources/simulations/ddr4-example.json", - resource_directory="ext/dramsys/DRAMSys/DRAMSys/library/resources", + configuration="ext/dramsys/DRAMSys/configs/ddr4-example.json", recordable=True, size="4GB", ) @@ -51,12 +49,16 @@ generator = LinearGenerator( num_cores=1, max_addr=memory.get_size(), ) + board = TestBoard( clk_freq="3GHz", generator=generator, memory=memory, cache_hierarchy=None ) -root = Root(full_system=False, system=board) -board._pre_instantiate() -m5.instantiate() -generator.start_traffic() -exit_event = m5.simulate() +simulator = Simulator(board=board) +simulator.run() + +print( + "Exiting @ tick {} because {}.".format( + simulator.get_current_tick(), simulator.get_last_exit_event_cause() + ) +) diff --git a/configs/example/gem5_library/looppoints/create-looppoint-checkpoints.py b/configs/example/gem5_library/looppoints/create-looppoint-checkpoints.py index abb15fb7f8..b31353c681 100644 --- a/configs/example/gem5_library/looppoints/create-looppoint-checkpoints.py +++ b/configs/example/gem5_library/looppoints/create-looppoint-checkpoints.py @@ -47,22 +47,22 @@ scons build/X86/gem5.opt ``` """ -from gem5.simulate.exit_event import ExitEvent -from gem5.simulate.simulator import Simulator -from gem5.utils.requires import requires -from gem5.components.cachehierarchies.classic.no_cache import NoCache -from gem5.components.boards.simple_board import SimpleBoard -from gem5.components.memory.single_channel import SingleChannelDDR3_1600 -from gem5.components.processors.simple_processor import SimpleProcessor -from gem5.components.processors.cpu_types import CPUTypes -from gem5.isas import ISA -from gem5.resources.workload import Workload +import argparse from pathlib import Path + +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory.single_channel import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource +from gem5.simulate.exit_event import ExitEvent from gem5.simulate.exit_event_generators import ( looppoint_save_checkpoint_generator, ) - -import argparse +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires requires(isa_required=ISA.X86) @@ -110,7 +110,9 @@ board = SimpleBoard( cache_hierarchy=cache_hierarchy, ) -board.set_workload(Workload("x86-matrix-multiply-omp-100-8-looppoint-csv")) +board.set_workload( + obtain_resource("x86-matrix-multiply-omp-100-8-looppoint-csv") +) dir = Path(args.checkpoint_path) dir.mkdir(exist_ok=True) diff --git a/configs/example/gem5_library/looppoints/restore-looppoint-checkpoint.py b/configs/example/gem5_library/looppoints/restore-looppoint-checkpoint.py index 21353a34a1..781b2f7281 100644 --- a/configs/example/gem5_library/looppoints/restore-looppoint-checkpoint.py +++ b/configs/example/gem5_library/looppoints/restore-looppoint-checkpoint.py @@ -42,20 +42,23 @@ Usage """ import argparse -from gem5.simulate.exit_event import ExitEvent -from gem5.simulate.simulator import Simulator -from gem5.utils.requires import requires +from m5.stats import ( + dump, + reset, +) + +from gem5.components.boards.simple_board import SimpleBoard from gem5.components.cachehierarchies.classic.private_l1_private_l2_cache_hierarchy import ( PrivateL1PrivateL2CacheHierarchy, ) -from gem5.components.boards.simple_board import SimpleBoard from gem5.components.memory import DualChannelDDR4_2400 -from gem5.components.processors.simple_processor import SimpleProcessor from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor from gem5.isas import ISA from gem5.resources.resource import obtain_resource -from gem5.resources.workload import Workload -from m5.stats import reset, dump +from gem5.simulate.exit_event import ExitEvent +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires requires(isa_required=ISA.X86) @@ -113,11 +116,12 @@ board = SimpleBoard( ) board.set_workload( - Workload( + obtain_resource( f"x86-matrix-multiply-omp-100-8-looppoint-region-{args.checkpoint_region}" ) ) + # This generator will dump the stats and exit the simulation loop when the # simulation region reaches its end. In the case there is a warmup interval, # the simulation stats are reset after the warmup is complete. diff --git a/configs/example/gem5_library/memory_traffic.py b/configs/example/gem5_library/memory_traffic.py index d6772d1e77..4c653114fb 100644 --- a/configs/example/gem5_library/memory_traffic.py +++ b/configs/example/gem5_library/memory_traffic.py @@ -36,14 +36,12 @@ and this channel is driven with 32GiB/s of traffic for 1ms. import argparse from m5.objects import MemorySize -from gem5.components.boards.test_board import TestBoard +from gem5.components.boards.test_board import TestBoard +from gem5.components.memory.dram_interfaces.hbm import HBM_2000_4H_1x64 +from gem5.components.memory.hbm import HighBandwidthMemory from gem5.components.processors.linear_generator import LinearGenerator from gem5.components.processors.random_generator import RandomGenerator - -from gem5.components.memory.hbm import HighBandwidthMemory -from gem5.components.memory.dram_interfaces.hbm import HBM_2000_4H_1x64 - from gem5.simulate.simulator import Simulator diff --git a/configs/example/gem5_library/power-hello.py b/configs/example/gem5_library/power-hello.py index cf31778945..8a73b6a201 100644 --- a/configs/example/gem5_library/power-hello.py +++ b/configs/example/gem5_library/power-hello.py @@ -41,15 +41,15 @@ scons build/POWER/gem5.opt ``` """ -from gem5.isas import ISA -from gem5.utils.requires import requires -from gem5.resources.resource import Resource -from gem5.components.memory import SingleChannelDDR4_2400 -from gem5.components.processors.cpu_types import CPUTypes from gem5.components.boards.simple_board import SimpleBoard from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR4_2400 +from gem5.components.processors.cpu_types import CPUTypes from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires # This check ensures the gem5 binary is compiled to the POWER ISA target. # If not, an exception will be thrown. @@ -75,7 +75,7 @@ board = SimpleBoard( cache_hierarchy=cache_hierarchy, ) -board.set_se_binary_workload(Resource("power-hello")) +board.set_se_binary_workload(obtain_resource("power-hello")) # Lastly we run the simulation. simulator = Simulator(board=board) diff --git a/configs/example/gem5_library/riscv-fs.py b/configs/example/gem5_library/riscv-fs.py index e4dce027e3..914d9a7023 100644 --- a/configs/example/gem5_library/riscv-fs.py +++ b/configs/example/gem5_library/riscv-fs.py @@ -40,16 +40,16 @@ Characteristics """ from gem5.components.boards.riscv_board import RiscvBoard -from gem5.components.memory import SingleChannelDDR3_1600 -from gem5.components.processors.simple_processor import SimpleProcessor from gem5.components.cachehierarchies.classic.private_l1_private_l2_cache_hierarchy import ( PrivateL1PrivateL2CacheHierarchy, ) +from gem5.components.memory import SingleChannelDDR3_1600 from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor from gem5.isas import ISA -from gem5.utils.requires import requires -from gem5.resources.resource import Resource +from gem5.resources.resource import obtain_resource from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires # Run a check to ensure the right version of gem5 is being used. requires(isa_required=ISA.RISCV) @@ -79,8 +79,8 @@ board = RiscvBoard( # Set the Full System workload. board.set_kernel_disk_workload( - kernel=Resource("riscv-bootloader-vmlinux-5.10"), - disk_image=Resource("riscv-disk-img"), + kernel=obtain_resource("riscv-bootloader-vmlinux-5.10"), + disk_image=obtain_resource("riscv-disk-img"), ) simulator = Simulator(board=board) diff --git a/configs/example/gem5_library/riscv-ubuntu-run.py b/configs/example/gem5_library/riscv-ubuntu-run.py index 87b98cc5ba..1d31b055de 100644 --- a/configs/example/gem5_library/riscv-ubuntu-run.py +++ b/configs/example/gem5_library/riscv-ubuntu-run.py @@ -43,14 +43,14 @@ scons build/RISCV/gem5.opt import m5 from m5.objects import Root -from gem5.utils.requires import requires from gem5.components.boards.riscv_board import RiscvBoard from gem5.components.memory import DualChannelDDR4_2400 -from gem5.components.processors.simple_processor import SimpleProcessor from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor from gem5.isas import ISA +from gem5.resources.resource import obtain_resource from gem5.simulate.simulator import Simulator -from gem5.resources.workload import Workload +from gem5.utils.requires import requires # This runs a check to ensure the gem5 binary is compiled for RISCV. @@ -88,7 +88,7 @@ board = RiscvBoard( # Ubuntu 20.04. Once the system successfully boots it encounters an `m5_exit` # instruction which stops the simulation. When the simulation has ended you may # inspect `m5out/system.pc.com_1.device` to see the stdout. -board.set_workload(Workload("riscv-ubuntu-20.04-boot")) +board.set_workload(obtain_resource("riscv-ubuntu-20.04-boot")) simulator = Simulator(board=board) simulator.run() diff --git a/configs/example/gem5_library/riscvmatched-fs.py b/configs/example/gem5_library/riscvmatched-fs.py index 3e84b8c1ea..ad045cac3d 100644 --- a/configs/example/gem5_library/riscvmatched-fs.py +++ b/configs/example/gem5_library/riscvmatched-fs.py @@ -38,14 +38,14 @@ scons build/RISCV/gem5.opt ``` """ -from gem5.prebuilt.riscvmatched.riscvmatched_board import RISCVMatchedBoard -from gem5.utils.requires import requires -from gem5.isas import ISA -from gem5.simulate.simulator import Simulator -from gem5.resources.workload import Workload - import argparse +from gem5.isas import ISA +from gem5.prebuilt.riscvmatched.riscvmatched_board import RISCVMatchedBoard +from gem5.resources.resource import obtain_resource +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires + requires(isa_required=ISA.RISCV) parser = argparse.ArgumentParser( @@ -76,7 +76,7 @@ board = RISCVMatchedBoard( # In the case where the `-i` flag is passed, we add the kernel argument # `init=/root/exit.sh`. This means the simulation will exit after the Linux # Kernel has booted. -workload = Workload("riscv-ubuntu-20.04-boot") +workload = obtain_resource("riscv-ubuntu-20.04-boot") kernel_args = board.get_default_kernel_args() if args.to_init: kernel_args.append("init=/root/exit.sh") diff --git a/configs/example/gem5_library/riscvmatched-hello.py b/configs/example/gem5_library/riscvmatched-hello.py index e7b4cf7128..3ea13b4851 100644 --- a/configs/example/gem5_library/riscvmatched-hello.py +++ b/configs/example/gem5_library/riscvmatched-hello.py @@ -37,10 +37,10 @@ scons build/RISCV/gem5.opt ``` """ -from gem5.resources.resource import Resource -from gem5.simulate.simulator import Simulator -from gem5.prebuilt.riscvmatched.riscvmatched_board import RISCVMatchedBoard from gem5.isas import ISA +from gem5.prebuilt.riscvmatched.riscvmatched_board import RISCVMatchedBoard +from gem5.resources.resource import obtain_resource +from gem5.simulate.simulator import Simulator from gem5.utils.requires import requires requires(isa_required=ISA.RISCV) @@ -49,7 +49,7 @@ requires(isa_required=ISA.RISCV) board = RISCVMatchedBoard() # set the hello world riscv binary as the board workload -board.set_se_binary_workload(Resource("riscv-hello")) +board.set_se_binary_workload(obtain_resource("riscv-hello")) # run the simulation with the RISCV Matched board simulator = Simulator(board=board, full_system=False) diff --git a/configs/example/gem5_library/riscvmatched-microbenchmark-suite.py b/configs/example/gem5_library/riscvmatched-microbenchmark-suite.py new file mode 100644 index 0000000000..2024bdddf0 --- /dev/null +++ b/configs/example/gem5_library/riscvmatched-microbenchmark-suite.py @@ -0,0 +1,80 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This script shows how to use a suite. In this example, we will use the +RISCVMatchedBoard and the RISCV Vertical Microbenchmark Suite, +and show the different functionalities of the suite. + +The print statements in the script are for illustrative purposes only, +and are not required to run the script. +""" + +from gem5.isas import ISA +from gem5.prebuilt.riscvmatched.riscvmatched_board import RISCVMatchedBoard +from gem5.resources.resource import obtain_resource +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires + +requires(isa_required=ISA.RISCV) + +# instantiate the riscv matched board with default parameters +board = RISCVMatchedBoard() + +# obtain the RISC-V Vertical Microbenchmarks +microbenchmarks = obtain_resource("riscv-vertical-microbenchmarks") + +# list all the microbenchmarks present in the suite +print("Microbenchmarks present in the suite:") +print("====================================") +for workload in microbenchmarks: + print(f"Workload ID: {workload.get_id()}") + print(f"Workload Version: {workload.get_resource_version()}") + print(f"WorkloadResource Object: {workload}") + print("====================================") + +# list all the WorkloadResource objects present in the suite +for resource in microbenchmarks: + print(f"WorkloadResource Object: {resource}") + +# list all the available input groups in the suite +print("Input groups present in the suite:") +print(microbenchmarks.get_input_groups()) + +# for this example, we will filter the suite +# to run the Workload "riscv-cca-run" +# it has the input group 'cca', which is used as the filter +board.set_workload(list(microbenchmarks.with_input_group("cca"))[0]) + +# run the simulation with the RISCV Matched board +simulator = Simulator(board=board, full_system=False) +simulator.run() +print( + "Exiting @ tick {} because {}.".format( + simulator.get_current_tick(), + simulator.get_last_exit_event_cause(), + ) +) diff --git a/configs/example/gem5_library/x86-gapbs-benchmarks.py b/configs/example/gem5_library/x86-gapbs-benchmarks.py index b85ce6e7e8..4ef6f52b9c 100644 --- a/configs/example/gem5_library/x86-gapbs-benchmarks.py +++ b/configs/example/gem5_library/x86-gapbs-benchmarks.py @@ -48,24 +48,24 @@ scons build/X86/gem5.opt """ import argparse -import time import sys +import time import m5 from m5.objects import Root -from gem5.utils.requires import requires +from gem5.coherence_protocol import CoherenceProtocol from gem5.components.boards.x86_board import X86Board from gem5.components.memory import DualChannelDDR4_2400 +from gem5.components.processors.cpu_types import CPUTypes from gem5.components.processors.simple_switchable_processor import ( SimpleSwitchableProcessor, ) -from gem5.components.processors.cpu_types import CPUTypes from gem5.isas import ISA -from gem5.coherence_protocol import CoherenceProtocol -from gem5.resources.resource import Resource -from gem5.simulate.simulator import Simulator +from gem5.resources.resource import obtain_resource from gem5.simulate.exit_event import ExitEvent +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires requires( isa_required=ISA.X86, @@ -73,36 +73,12 @@ requires( kvm_required=True, ) -# Following are the list of benchmark programs for gapbs - -benchmark_choices = ["cc", "bc", "tc", "pr", "bfs"] - -synthetic_choices = ["0", "1"] - -size_choices = [ - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "10", - "11", - "12", - "13", - "14", - "15", - "16", - "USA-road-d.NY.gr", -] - parser = argparse.ArgumentParser( description="An example configuration script to run the gapbs benchmarks." ) +gapbs_suite = obtain_resource("gapbs-benchmark-suite") + # The only positional argument accepted is the benchmark name in this script. parser.add_argument( @@ -110,28 +86,12 @@ parser.add_argument( type=str, required=True, help="Input the benchmark program to execute.", - choices=benchmark_choices, -) - -parser.add_argument( - "--synthetic", - type=str, - required=True, - help="Synthetic Graph:: 1: synthetic graph is True; 0: real graph", - choices=synthetic_choices, -) - -parser.add_argument( - "--size", - type=str, - required=True, - help="Graph Size:: If synthetic is True, then specify a size [1 .. 15]. \ - Otherwise, specify a graph name [USA-road-d.NY.gr]", - choices=size_choices, + choices=[workload.get_id() for workload in gapbs_suite], ) args = parser.parse_args() + # Setting up all the fixed system parameters here # Caches: MESI Two Level Cache Hierarchy @@ -185,30 +145,7 @@ board = X86Board( # committed instructions till ROI ends (marked by `workend`). We then finish # executing the rest of the benchmark. -# GAPBS benchmarks can be run using a synthetic graph - -if args.synthetic == "1": - if args.size == "USA-road-d.NY.gr": - print( - "fatal: cannot use a real graph with --synthetic 1", - file=sys.stderr, - ) - exit(-1) - - command = f"./{args.benchmark} -g {args.size}\n" -else: - command = f"./{args.benchmark} -sf ../{args.size}" - -board.set_kernel_disk_workload( - # The x86 linux kernel will be automatically downloaded to the - # `~/.cache/gem5` directory if not already present. - # gapbs benchamarks was tested with kernel version 4.19.83 - kernel=Resource("x86-linux-kernel-4.19.83"), - # The x86-gapbs image will be automatically downloaded to the - # `~/.cache/gem5` directory if not already present. - disk_image=Resource("x86-gapbs"), - readfile_contents=command, -) +board.set_workload(obtain_resource(args.benchmark)) def handle_workbegin(): diff --git a/configs/example/gem5_library/x86-npb-benchmarks.py b/configs/example/gem5_library/x86-npb-benchmarks.py index cffba5a294..6e6d501c37 100644 --- a/configs/example/gem5_library/x86-npb-benchmarks.py +++ b/configs/example/gem5_library/x86-npb-benchmarks.py @@ -50,22 +50,23 @@ import time import m5 from m5.objects import Root +from m5.stats.gem5stats import get_simstat +from m5.util import warn -from gem5.utils.requires import requires +from gem5.coherence_protocol import CoherenceProtocol from gem5.components.boards.x86_board import X86Board from gem5.components.memory import DualChannelDDR4_2400 +from gem5.components.processors.cpu_types import CPUTypes from gem5.components.processors.simple_switchable_processor import ( SimpleSwitchableProcessor, ) -from gem5.components.processors.cpu_types import CPUTypes from gem5.isas import ISA -from gem5.coherence_protocol import CoherenceProtocol -from gem5.resources.resource import Resource -from gem5.simulate.simulator import Simulator -from gem5.simulate.simulator import ExitEvent - -from m5.stats.gem5stats import get_simstat -from m5.util import warn +from gem5.resources.resource import obtain_resource +from gem5.simulate.simulator import ( + ExitEvent, + Simulator, +) +from gem5.utils.requires import requires requires( isa_required=ISA.X86, @@ -75,8 +76,6 @@ requires( # Following are the list of benchmark programs for npb. -benchmark_choices = ["bt", "cg", "ep", "ft", "is", "lu", "mg", "sp"] - # We are restricting classes of NPB to A, B and C as the other classes (D and # F) require main memory size of more than 3 GB. The X86Board is currently # limited to 3 GB of memory. This limitation is explained later in line 136. @@ -85,12 +84,11 @@ benchmark_choices = ["bt", "cg", "ep", "ft", "is", "lu", "mg", "sp"] # works with class D in the current configuration. More information on the # memory footprint for NPB is available at https://arxiv.org/abs/2010.13216 -size_choices = ["A", "B", "C"] - parser = argparse.ArgumentParser( description="An example configuration script to run the npb benchmarks." ) +npb_suite = obtain_resource("npb-benchmark-suite") # The only positional argument accepted is the benchmark name in this script. parser.add_argument( @@ -98,15 +96,7 @@ parser.add_argument( type=str, required=True, help="Input the benchmark program to execute.", - choices=benchmark_choices, -) - -parser.add_argument( - "--size", - type=str, - required=True, - help="Input the class of the program to simulate.", - choices=size_choices, + choices=[workload.get_id() for workload in npb_suite], ) parser.add_argument( @@ -118,11 +108,12 @@ parser.add_argument( args = parser.parse_args() + # The simulation may fail in the case of `mg` with class C as it uses 3.3 GB -# of memory (more information is availabe at https://arxiv.org/abs/2010.13216). +# of memory (more information is available at https://arxiv.org/abs/2010.13216). # We warn the user here. -if args.benchmark == "mg" and args.size == "C": +if args.benchmark == "npb-mg-c": warn( "mg.C uses 3.3 GB of memory. Currently we are simulating 3 GB\ of main memory in the system." @@ -130,7 +121,7 @@ if args.benchmark == "mg" and args.size == "C": # The simulation will fail in the case of `ft` with class C. We warn the user # here. -elif args.benchmark == "ft" and args.size == "C": +elif args.benchmark == "npb-ft-c": warn( "There is not enough memory for ft.C. Currently we are\ simulating 3 GB of main memory in the system." @@ -193,23 +184,8 @@ board = X86Board( # Also, we sleep the system for some time so that the output is printed # properly. +board.set_workload(obtain_resource(args.benchmark)) -command = ( - f"/home/gem5/NPB3.3-OMP/bin/{args.benchmark}.{args.size}.x;" - + "sleep 5;" - + "m5 exit;" -) - -board.set_kernel_disk_workload( - # The x86 linux kernel will be automatically downloaded to the - # `~/.cache/gem5` directory if not already present. - # npb benchamarks was tested with kernel version 4.19.83 - kernel=Resource("x86-linux-kernel-4.19.83"), - # The x86-npb image will be automatically downloaded to the - # `~/.cache/gem5` directory if not already present. - disk_image=Resource("x86-npb"), - readfile_contents=command, -) # The first exit_event ends with a `workbegin` cause. This means that the # system started successfully and the execution on the program started. @@ -236,6 +212,7 @@ def handle_workbegin(): # The next exit_event is to simulate the ROI. It should be exited with a cause # marked by `workend`. + # We exepect that ROI ends with `workend` or `simulate() limit reached`. def handle_workend(): print("Dump stats at the end of the ROI!") diff --git a/configs/example/gem5_library/x86-parsec-benchmarks.py b/configs/example/gem5_library/x86-parsec-benchmarks.py index aaffec8edc..71cfd4a9ef 100644 --- a/configs/example/gem5_library/x86-parsec-benchmarks.py +++ b/configs/example/gem5_library/x86-parsec-benchmarks.py @@ -50,18 +50,18 @@ import time import m5 from m5.objects import Root -from gem5.utils.requires import requires +from gem5.coherence_protocol import CoherenceProtocol from gem5.components.boards.x86_board import X86Board from gem5.components.memory import DualChannelDDR4_2400 +from gem5.components.processors.cpu_types import CPUTypes from gem5.components.processors.simple_switchable_processor import ( SimpleSwitchableProcessor, ) -from gem5.components.processors.cpu_types import CPUTypes from gem5.isas import ISA -from gem5.coherence_protocol import CoherenceProtocol -from gem5.resources.resource import Resource -from gem5.simulate.simulator import Simulator +from gem5.resources.resource import obtain_resource from gem5.simulate.exit_event import ExitEvent +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires # We check for the required gem5 build. @@ -175,7 +175,7 @@ board = X86Board( command = ( - "cd /home/gem5/parsec-benchmark;".format(args.benchmark) + f"cd /home/gem5/parsec-benchmark;" + "source env.sh;" + f"parsecmgmt -a run -p {args.benchmark} -c gcc-hooks -i {args.size} -n 2;" + "sleep 5;" @@ -185,13 +185,14 @@ board.set_kernel_disk_workload( # The x86 linux kernel will be automatically downloaded to the # `~/.cache/gem5` directory if not already present. # PARSEC benchamarks were tested with kernel version 4.19.83 - kernel=Resource("x86-linux-kernel-4.19.83"), + kernel=obtain_resource("x86-linux-kernel-4.19.83"), # The x86-parsec image will be automatically downloaded to the # `~/.cache/gem5` directory if not already present. - disk_image=Resource("x86-parsec"), + disk_image=obtain_resource("x86-parsec"), readfile_contents=command, ) + # functions to handle different exit events during the simuation def handle_workbegin(): print("Done booting Linux") @@ -235,7 +236,7 @@ print("Done with the simulation") print() print("Performance statistics:") -print("Simulated time in ROI: " + ((str(simulator.get_roi_ticks()[0])))) +print("Simulated time in ROI: " + (str(simulator.get_roi_ticks()[0]))) print( "Ran a total of", simulator.get_current_tick() / 1e12, "simulated seconds" ) diff --git a/configs/example/gem5_library/x86-spec-cpu2006-benchmarks.py b/configs/example/gem5_library/x86-spec-cpu2006-benchmarks.py index 10d5da0adb..18bac9671d 100644 --- a/configs/example/gem5_library/x86-spec-cpu2006-benchmarks.py +++ b/configs/example/gem5_library/x86-spec-cpu2006-benchmarks.py @@ -49,29 +49,33 @@ scons build/X86/gem5.opt """ import argparse -import time -import os import json +import os +import time import m5 from m5.objects import Root +from m5.stats.gem5stats import get_simstat +from m5.util import ( + fatal, + warn, +) -from gem5.utils.requires import requires +from gem5.coherence_protocol import CoherenceProtocol from gem5.components.boards.x86_board import X86Board from gem5.components.memory import DualChannelDDR4_2400 +from gem5.components.processors.cpu_types import CPUTypes from gem5.components.processors.simple_switchable_processor import ( SimpleSwitchableProcessor, ) -from gem5.components.processors.cpu_types import CPUTypes from gem5.isas import ISA -from gem5.coherence_protocol import CoherenceProtocol -from gem5.resources.resource import Resource, CustomDiskImageResource -from gem5.simulate.simulator import Simulator +from gem5.resources.resource import ( + DiskImageResource, + Resource, +) from gem5.simulate.exit_event import ExitEvent - -from m5.stats.gem5stats import get_simstat -from m5.util import warn -from m5.util import fatal +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires # We check for the required gem5 build. @@ -261,9 +265,7 @@ board.set_kernel_disk_workload( # 5.4.49 kernel=Resource("x86-linux-kernel-4.19.83"), # The location of the x86 SPEC CPU 2017 image - disk_image=CustomDiskImageResource( - args.image, root_partition=args.partition - ), + disk_image=DiskImageResource(args.image, root_partition=args.partition), readfile_contents=command, ) diff --git a/configs/example/gem5_library/x86-spec-cpu2017-benchmarks.py b/configs/example/gem5_library/x86-spec-cpu2017-benchmarks.py index cb5f5d19e3..96c4a37c9e 100644 --- a/configs/example/gem5_library/x86-spec-cpu2017-benchmarks.py +++ b/configs/example/gem5_library/x86-spec-cpu2017-benchmarks.py @@ -47,29 +47,33 @@ scons build/X86/gem5.opt """ import argparse -import time -import os import json +import os +import time import m5 from m5.objects import Root +from m5.stats.gem5stats import get_simstat +from m5.util import ( + fatal, + warn, +) -from gem5.utils.requires import requires +from gem5.coherence_protocol import CoherenceProtocol from gem5.components.boards.x86_board import X86Board from gem5.components.memory import DualChannelDDR4_2400 +from gem5.components.processors.cpu_types import CPUTypes from gem5.components.processors.simple_switchable_processor import ( SimpleSwitchableProcessor, ) -from gem5.components.processors.cpu_types import CPUTypes from gem5.isas import ISA -from gem5.coherence_protocol import CoherenceProtocol -from gem5.resources.resource import Resource, CustomDiskImageResource -from gem5.simulate.simulator import Simulator +from gem5.resources.resource import ( + DiskImageResource, + obtain_resource, +) from gem5.simulate.exit_event import ExitEvent - -from m5.stats.gem5stats import get_simstat -from m5.util import warn -from m5.util import fatal +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires # We check for the required gem5 build. @@ -268,18 +272,16 @@ except FileExistsError: command = f"{args.benchmark} {args.size} {output_dir}" -# For enabling CustomResource, we pass an additional parameter to mount the +# For enabling DiskImageResource, we pass an additional parameter to mount the # correct partition. board.set_kernel_disk_workload( # The x86 linux kernel will be automatically downloaded to the # `~/.cache/gem5` directory if not already present. # SPEC CPU2017 benchamarks were tested with kernel version 4.19.83 - kernel=Resource("x86-linux-kernel-4.19.83"), + kernel=obtain_resource("x86-linux-kernel-4.19.83"), # The location of the x86 SPEC CPU 2017 image - disk_image=CustomDiskImageResource( - args.image, root_partition=args.partition - ), + disk_image=DiskImageResource(args.image, root_partition=args.partition), readfile_contents=command, ) diff --git a/configs/example/gem5_library/x86-ubuntu-run-with-kvm-no-perf.py b/configs/example/gem5_library/x86-ubuntu-run-with-kvm-no-perf.py index 1c65357921..632b409b16 100644 --- a/configs/example/gem5_library/x86-ubuntu-run-with-kvm-no-perf.py +++ b/configs/example/gem5_library/x86-ubuntu-run-with-kvm-no-perf.py @@ -38,21 +38,21 @@ scons build/X86/gem5.opt -j`nproc` ``` """ -from gem5.utils.requires import requires +from gem5.coherence_protocol import CoherenceProtocol from gem5.components.boards.x86_board import X86Board from gem5.components.cachehierarchies.ruby.mesi_two_level_cache_hierarchy import ( MESITwoLevelCacheHierarchy, ) from gem5.components.memory.single_channel import SingleChannelDDR4_2400 +from gem5.components.processors.cpu_types import CPUTypes from gem5.components.processors.simple_switchable_processor import ( SimpleSwitchableProcessor, ) -from gem5.components.processors.cpu_types import CPUTypes from gem5.isas import ISA -from gem5.coherence_protocol import CoherenceProtocol -from gem5.simulate.simulator import Simulator +from gem5.resources.resource import obtain_resource from gem5.simulate.exit_event import ExitEvent -from gem5.resources.workload import Workload +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires # This simulation requires using KVM with gem5 compiled for X86 simulation # and with MESI_Two_Level cache coherence protocol. @@ -121,7 +121,7 @@ command = ( + "m5 exit;" ) -workload = Workload("x86-ubuntu-18.04-boot") +workload = obtain_resource("x86-ubuntu-18.04-boot") workload.set_parameter("readfile_contents", command) board.set_workload(workload) diff --git a/configs/example/gem5_library/x86-ubuntu-run-with-kvm.py b/configs/example/gem5_library/x86-ubuntu-run-with-kvm.py index f55ec60f21..ec361dcd6e 100644 --- a/configs/example/gem5_library/x86-ubuntu-run-with-kvm.py +++ b/configs/example/gem5_library/x86-ubuntu-run-with-kvm.py @@ -40,18 +40,18 @@ scons build/X86/gem5.opt ``` """ -from gem5.utils.requires import requires +from gem5.coherence_protocol import CoherenceProtocol from gem5.components.boards.x86_board import X86Board from gem5.components.memory.single_channel import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes from gem5.components.processors.simple_switchable_processor import ( SimpleSwitchableProcessor, ) -from gem5.components.processors.cpu_types import CPUTypes from gem5.isas import ISA -from gem5.coherence_protocol import CoherenceProtocol -from gem5.simulate.simulator import Simulator +from gem5.resources.resource import obtain_resource from gem5.simulate.exit_event import ExitEvent -from gem5.resources.workload import Workload +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires # This runs a check to ensure the gem5 binary is compiled to X86 and to the # MESI Two Level coherence protocol. @@ -117,7 +117,7 @@ command = ( + "m5 exit;" ) -workload = Workload("x86-ubuntu-18.04-boot") +workload = obtain_resource("x86-ubuntu-18.04-boot") workload.set_parameter("readfile_contents", command) board.set_workload(workload) diff --git a/configs/example/gem5_library/x86-ubuntu-run.py b/configs/example/gem5_library/x86-ubuntu-run.py index 50b52e6e3c..3b7b754b90 100644 --- a/configs/example/gem5_library/x86-ubuntu-run.py +++ b/configs/example/gem5_library/x86-ubuntu-run.py @@ -45,10 +45,9 @@ scons build/X86/gem5.opt """ from gem5.prebuilt.demo.x86_demo_board import X86DemoBoard -from gem5.resources.workload import Workload +from gem5.resources.resource import obtain_resource from gem5.simulate.simulator import Simulator - # Here we setup the board. The prebuilt X86DemoBoard allows for Full-System X86 # simulation. board = X86DemoBoard() @@ -56,7 +55,7 @@ board = X86DemoBoard() # We then set the workload. Here we use the "x86-ubuntu-18.04-boot" workload. # This boots Ubuntu 18.04 with Linux 5.4.49. If the required resources are not # found locally, they will be downloaded. -board.set_workload(Workload("x86-ubuntu-18.04-boot")) +board.set_workload(obtain_resource("x86-ubuntu-18.04-boot")) simulator = Simulator(board=board) simulator.run() diff --git a/configs/example/gpufs/DisjointNetwork.py b/configs/example/gpufs/DisjointNetwork.py index 1fbd0dcb15..393ac81385 100644 --- a/configs/example/gpufs/DisjointNetwork.py +++ b/configs/example/gpufs/DisjointNetwork.py @@ -27,17 +27,17 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * -from m5.util import fatal - from importlib import * from network import Network +from m5.objects import * +from m5.util import fatal + class DisjointSimple(SimpleNetwork): def __init__(self, ruby_system): - super(DisjointSimple, self).__init__() + super().__init__() self.netifs = [] self.routers = [] @@ -46,7 +46,6 @@ class DisjointSimple(SimpleNetwork): self.ruby_system = ruby_system def connectCPU(self, opts, controllers): - # Setup parameters for makeTopology call for CPU network topo_module = import_module(f"topologies.{opts.cpu_topology}") topo_class = getattr(topo_module, opts.cpu_topology) @@ -56,7 +55,6 @@ class DisjointSimple(SimpleNetwork): self.initSimple(opts, self.int_links, self.ext_links) def connectGPU(self, opts, controllers): - # Setup parameters for makeTopology call for GPU network topo_module = import_module(f"topologies.{opts.gpu_topology}") topo_class = getattr(topo_module, opts.gpu_topology) @@ -66,7 +64,6 @@ class DisjointSimple(SimpleNetwork): self.initSimple(opts, self.int_links, self.ext_links) def initSimple(self, opts, int_links, ext_links): - # Attach links to network self.int_links = int_links self.ext_links = ext_links @@ -76,13 +73,12 @@ class DisjointSimple(SimpleNetwork): class DisjointGarnet(GarnetNetwork): def __init__(self, ruby_system): - super(DisjointGarnet, self).__init__() + super().__init__() self.netifs = [] self.ruby_system = ruby_system def connectCPU(self, opts, controllers): - # Setup parameters for makeTopology call for CPU network topo_module = import_module(f"topologies.{opts.cpu_topology}") topo_class = getattr(topo_module, opts.cpu_topology) @@ -94,7 +90,6 @@ class DisjointGarnet(GarnetNetwork): Network.init_network(opts, self, GarnetNetworkInterface) def connectGPU(self, opts, controllers): - # Setup parameters for makeTopology call topo_module = import_module(f"topologies.{opts.gpu_topology}") topo_class = getattr(topo_module, opts.gpu_topology) diff --git a/configs/example/gpufs/Disjoint_VIPER.py b/configs/example/gpufs/Disjoint_VIPER.py index 14b47d8cf0..28f0768c2a 100644 --- a/configs/example/gpufs/Disjoint_VIPER.py +++ b/configs/example/gpufs/Disjoint_VIPER.py @@ -27,18 +27,17 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +from example.gpufs.DisjointNetwork import * +from ruby import Ruby +from ruby.GPU_VIPER import * + from m5.defines import buildEnv from m5.objects import * from m5.util import fatal -from example.gpufs.DisjointNetwork import * -from ruby.GPU_VIPER import * -from ruby import Ruby - class DummySystem: def __init__(self, mem_ranges): - self.mem_ctrls = [] self.mem_ranges = mem_ranges @@ -48,10 +47,9 @@ class Disjoint_VIPER(RubySystem): if buildEnv["PROTOCOL"] != "GPU_VIPER": fatal("This ruby config only supports the GPU_VIPER protocol") - super(Disjoint_VIPER, self).__init__() + super().__init__() def create(self, options, system, piobus, dma_devices): - # Disjoint network topology if "garnet" in options.network: self.network_cpu = DisjointGarnet(self) diff --git a/configs/example/gpufs/hip_cookbook.py b/configs/example/gpufs/hip_cookbook.py index 6a7bb428db..0b2c084a4f 100644 --- a/configs/example/gpufs/hip_cookbook.py +++ b/configs/example/gpufs/hip_cookbook.py @@ -27,18 +27,21 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -import m5 -import runfs -import tempfile import argparse -import sys import os +import sys +import tempfile +import runfs from amd import AmdGPUOptions -from common import Options -from common import GPUTLBOptions +from common import ( + GPUTLBOptions, + Options, +) from ruby import Ruby +import m5 + cookbook_runscript = """\ export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH export HSA_ENABLE_INTERRUPT=0 diff --git a/configs/example/gpufs/hip_rodinia.py b/configs/example/gpufs/hip_rodinia.py index b8a7858fcd..d93bf957fb 100644 --- a/configs/example/gpufs/hip_rodinia.py +++ b/configs/example/gpufs/hip_rodinia.py @@ -27,19 +27,22 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -import m5 -import runfs -import base64 -import tempfile import argparse -import sys +import base64 import os +import sys +import tempfile +import runfs from amd import AmdGPUOptions -from common import Options -from common import GPUTLBOptions +from common import ( + GPUTLBOptions, + Options, +) from ruby import Ruby +import m5 + rodinia_runscript = """\ export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH export HSA_ENABLE_INTERRUPT=0 diff --git a/configs/example/gpufs/hip_samples.py b/configs/example/gpufs/hip_samples.py index 9f83c2550e..86fdd0e61d 100644 --- a/configs/example/gpufs/hip_samples.py +++ b/configs/example/gpufs/hip_samples.py @@ -27,18 +27,21 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -import m5 -import runfs -import tempfile import argparse -import sys import os +import sys +import tempfile +import runfs from amd import AmdGPUOptions -from common import Options -from common import GPUTLBOptions +from common import ( + GPUTLBOptions, + Options, +) from ruby import Ruby +import m5 + samples_runscript = """\ export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH export HSA_ENABLE_INTERRUPT=0 diff --git a/configs/example/gpufs/runfs.py b/configs/example/gpufs/runfs.py index f8ef70d5a2..9dcc1187f3 100644 --- a/configs/example/gpufs/runfs.py +++ b/configs/example/gpufs/runfs.py @@ -29,8 +29,8 @@ # System includes import argparse -import math import hashlib +import math # gem5 related import m5 @@ -39,13 +39,15 @@ from m5.util import addToPath # gem5 options and objects addToPath("../../") -from ruby import Ruby -from common import Simulation -from common import ObjectList -from common import Options -from common import GPUTLBOptions -from common import GPUTLBConfig from amd import AmdGPUOptions +from common import ( + GPUTLBConfig, + GPUTLBOptions, + ObjectList, + Options, + Simulation, +) +from ruby import Ruby # GPU FS related from system.system import makeGpuFSSystem @@ -137,6 +139,44 @@ def addRunFSOptions(parser): "MI200 (gfx90a)", ) + parser.add_argument( + "--debug-at-gpu-kernel", + type=int, + default=-1, + help="Turn on debug flags starting with this kernel", + ) + + parser.add_argument( + "--exit-at-gpu-kernel", + type=int, + default=-1, + help="Exit simulation after running this many kernels", + ) + + parser.add_argument( + "--root-partition", + type=str, + default="/dev/sda1", + help="Root partition of disk image", + ) + + parser.add_argument( + "--disable-avx", + action="store_true", + default=False, + help="Disables AVX. AVX is used in some ROCm libraries but " + "does not have checkpointing support yet. If simulation either " + "creates a checkpoint or restores from one, then AVX needs to " + "be disabled for correct functionality ", + ) + + parser.add_argument( + "--no-kvm-perf", + default=False, + action="store_true", + help="Disable KVM perf counters (use this with LSF / ETX)", + ) + def runGpuFSSystem(args): """ @@ -148,7 +188,8 @@ def runGpuFSSystem(args): # GPUFS is primarily designed to use the X86 KVM CPU. This model needs to # use multiple event queues when more than one CPU is simulated. Force it # on if that is the case. - args.host_parallel = True if args.num_cpus > 1 else False + if ObjectList.is_kvm_cpu(ObjectList.cpu_list.get(args.cpu_type)): + args.host_parallel = True if args.num_cpus > 1 else False # These are used by the protocols. They should not be set by the user. n_cu = args.num_compute_units @@ -157,10 +198,15 @@ def runGpuFSSystem(args): math.ceil(float(n_cu) / args.cu_per_scalar_cache) ) - # Verify MMIO trace is valid - mmio_md5 = hashlib.md5(open(args.gpu_mmio_trace, "rb").read()).hexdigest() - if mmio_md5 != "c4ff3326ae8a036e329b8b595c83bd6d": - m5.util.panic("MMIO file does not match gem5 resources") + # Verify MMIO trace is valid. This is only needed for Vega10 simulations. + # The md5sum refers to the md5sum of the Vega10 MMIO hardware trace in + # the gem5-resources repository. By checking it here, we avoid potential + # errors that would cause the driver not to load and simulations to fail. + if args.gpu_device == "Vega10": + mmio_file = open(args.gpu_mmio_trace, "rb") + mmio_md5 = hashlib.md5(mmio_file.read()).hexdigest() + if mmio_md5 != "c4ff3326ae8a036e329b8b595c83bd6d": + m5.util.panic("MMIO file does not match gem5 resources") system = makeGpuFSSystem(args) @@ -184,6 +230,9 @@ def runGpuFSSystem(args): print("Running the simulation") sim_ticks = args.abs_max_tick + kernels_launched = 0 + if args.debug_at_gpu_kernel != -1: + m5.trace.disable() exit_event = m5.simulate(sim_ticks) @@ -199,11 +248,21 @@ def runGpuFSSystem(args): assert args.checkpoint_dir is not None m5.checkpoint(args.checkpoint_dir) break + elif "GPU Kernel Completed" in exit_event.getCause(): + kernels_launched += 1 else: print( f"Unknown exit event: {exit_event.getCause()}. Continuing..." ) + if kernels_launched == args.debug_at_gpu_kernel: + m5.trace.enable() + if kernels_launched == args.exit_at_gpu_kernel: + print(f"Exiting @ GPU kernel {kernels_launched}") + break + + exit_event = m5.simulate(sim_ticks - m5.curTick()) + print( "Exiting @ tick %i because %s" % (m5.curTick(), exit_event.getCause()) ) diff --git a/configs/example/gpufs/system/amdgpu.py b/configs/example/gpufs/system/amdgpu.py index 9697e50a04..4bca52c77e 100644 --- a/configs/example/gpufs/system/amdgpu.py +++ b/configs/example/gpufs/system/amdgpu.py @@ -184,4 +184,27 @@ def connectGPU(system, args): elif args.gpu_device == "Vega10": system.pc.south_bridge.gpu.DeviceID = 0x6863 else: - panic("Unknown GPU device: {}".format(args.gpu_device)) + panic(f"Unknown GPU device: {args.gpu_device}") + + # Use the gem5 default of 0x280 OR'd with 0x10 which tells Linux there is + # a PCI capabilities list to travse. + system.pc.south_bridge.gpu.Status = 0x0290 + + # The PCI capabilities are like a linked list. The list has a memory + # offset and a capability type ID read by the OS. Make the first + # capability at 0x80 and set the PXCAP (PCI express) capability to + # that address. Mark the type ID as PCI express. + # We leave the next ID of PXCAP blank to end the list. + system.pc.south_bridge.gpu.PXCAPBaseOffset = 0x80 + system.pc.south_bridge.gpu.CapabilityPtr = 0x80 + system.pc.south_bridge.gpu.PXCAPCapId = 0x10 + + # Set bits 7 and 8 in the second PCIe device capabilities register which + # reports support for PCIe atomics for 32 and 64 bits respectively. + # Bit 9 for 128-bit compare and swap is not set because the amdgpu driver + # does not check this. + system.pc.south_bridge.gpu.PXCAPDevCap2 = 0x00000180 + + # Set bit 6 to enable atomic requestor, meaning this device can request + # atomics from other PCI devices. + system.pc.south_bridge.gpu.PXCAPDevCtrl2 = 0x00000040 diff --git a/configs/example/gpufs/system/system.py b/configs/example/gpufs/system/system.py index 471892945e..2803e10fb4 100644 --- a/configs/example/gpufs/system/system.py +++ b/configs/example/gpufs/system/system.py @@ -27,18 +27,18 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +from common import ( + GPUTLBConfig, + Simulation, +) +from common.Benchmarks import * +from common.FSConfig import * +from example.gpufs.Disjoint_VIPER import * +from ruby import Ruby from system.amdgpu import * from m5.util import panic -from common.Benchmarks import * -from common.FSConfig import * -from common import GPUTLBConfig -from common import Simulation -from ruby import Ruby - -from example.gpufs.Disjoint_VIPER import * - def makeGpuFSSystem(args): # Boot options are standard gem5 options plus: @@ -50,7 +50,7 @@ def makeGpuFSSystem(args): "earlyprintk=ttyS0", "console=ttyS0,9600", "lpj=7999923", - "root=/dev/sda1", + f"root={args.root_partition}", "drm_kms_helper.fbdev_emulation=0", "modprobe.blacklist=amdgpu", "modprobe.blacklist=psmouse", @@ -231,7 +231,43 @@ def makeGpuFSSystem(args): clock=args.ruby_clock, voltage_domain=system.voltage_domain ) - for (i, cpu) in enumerate(system.cpu): + # If we are using KVM cpu, enable AVX. AVX is used in some ROCm libraries + # such as rocBLAS which is used in higher level libraries like PyTorch. + use_avx = False + if ObjectList.is_kvm_cpu(TestCPUClass) and not args.disable_avx: + # AVX also requires CR4.osxsave to be 1. These must be set together + # of KVM will error out. + system.workload.enable_osxsave = 1 + use_avx = True + + # These values are taken from a real CPU and are further explained here: + # https://sandpile.org/x86/cpuid.htm#level_0000_000Dh + avx_extended_state = [ + 0x00000007, + 0x00000340, + 0x00000000, + 0x00000340, + 0x0000000F, + 0x00000340, + 0x00000000, + 0x00000000, + 0x00000100, + 0x00000240, + 0x00000000, + 0x00000040, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + ] + + # This modifies the default value for ECX only (4th in this array). + # See: https://sandpile.org/x86/cpuid.htm#level_0000_0001h + # Enables AVX, OSXSAVE, XSAVE, POPCNT, SSE4.2, SSE4.1, CMPXCHG16B, + # and FMA. + avx_cpu_features = [0x00020F51, 0x00000805, 0xEFDBFBFF, 0x1C983209] + + for i, cpu in enumerate(system.cpu): # Break once we reach the shader "CPU" if i == args.num_cpus: break @@ -247,6 +283,9 @@ def makeGpuFSSystem(args): for j in range(len(system.cpu[i].isa)): system.cpu[i].isa[j].vendor_string = "AuthenticAMD" + if use_avx: + system.cpu[i].isa[j].ExtendedState = avx_extended_state + system.cpu[i].isa[j].FamilyModelStepping = avx_cpu_features if args.host_parallel: # To get the KVM CPUs to run on different host CPUs, specify a @@ -257,6 +296,12 @@ def makeGpuFSSystem(args): obj.eventq_index = 0 cpu.eventq_index = i + 1 + # Disable KVM Perf counters if specified. This is useful for machines + # with more restrictive KVM paranoid levels. + if args.no_kvm_perf and ObjectList.is_kvm_cpu(TestCPUClass): + for i, cpu in enumerate(system.cpu[:-1]): + cpu.usePerf = False + gpu_port_idx = ( len(system.ruby._cpu_ports) - args.num_compute_units diff --git a/configs/example/gpufs/vega10.py b/configs/example/gpufs/vega10.py index 9eff5a2974..ae74efd39b 100644 --- a/configs/example/gpufs/vega10.py +++ b/configs/example/gpufs/vega10.py @@ -27,19 +27,21 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -import m5 -import runfs -import base64 -import tempfile import argparse -import sys +import base64 import os +import sys +import tempfile +import runfs from amd import AmdGPUOptions -from common import Options -from common import GPUTLBOptions +from common import ( + GPUTLBOptions, + Options, +) from ruby import Ruby +import m5 demo_runscript_without_checkpoint = """\ export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH diff --git a/configs/example/hmc_hello.py b/configs/example/hmc_hello.py index bb1711b977..d796bcad99 100644 --- a/configs/example/hmc_hello.py +++ b/configs/example/hmc_hello.py @@ -30,22 +30,30 @@ # # Author: Éder F. Zulian -import sys import argparse +import sys import m5 from m5.objects import * from m5.util import * -from gem5.runtime import get_runtime_isa addToPath("../") -from common import MemConfig -from common import HMC - +from common import ( + HMC, + MemConfig, + ObjectList, +) pd = "Simple 'hello world' example using HMC as main memory" parser = argparse.ArgumentParser(description=pd) +parser.add_argument( + "--cpu-type", + type=str, + default="X86TimingSimpleCPU", + choices=ObjectList.cpu_list.get_names(), + help="CPU model to use", +) HMC.add_options(parser) options = parser.parse_args() # create the system we are going to simulate @@ -56,8 +64,8 @@ system.mem_mode = "timing" clk = "1GHz" vd = VoltageDomain(voltage="1V") system.clk_domain = SrcClockDomain(clock=clk, voltage_domain=vd) -# create a simple CPU -system.cpu = TimingSimpleCPU() +# create a CPU +system.cpu = ObjectList.cpu_list.get(options.cpu_type)() # config memory system MemConfig.config_mem(options, system) # hook the CPU ports up to the membus @@ -68,10 +76,15 @@ system.cpu.createInterruptController() # connect special port in the system to the membus. This port is a # functional-only port to allow the system to read and write memory. system.system_port = system.membus.cpu_side_ports -# get ISA for the binary to run. -isa = get_runtime_isa() # run 'hello' and use the compiled ISA to find the binary -binary = "tests/test-progs/hello/bin/" + isa.name.lower() + "/linux/hello" + + +binary = ( + "tests/test-progs/hello/bin/" + + ObjectList.cpu_list.get_isa(options.cpu_type).name.lower() + + "/linux/hello" +) + # create a process for a simple "Hello World" application process = Process() # cmd is a list which begins with the executable (like argv) diff --git a/configs/example/hmctest.py b/configs/example/hmctest.py index eca3c28465..8218da87cf 100644 --- a/configs/example/hmctest.py +++ b/configs/example/hmctest.py @@ -1,6 +1,6 @@ -import sys import argparse import subprocess +import sys from pprint import pprint import m5 @@ -9,8 +9,10 @@ from m5.util import * addToPath("../") -from common import MemConfig -from common import HMC +from common import ( + HMC, + MemConfig, +) def add_options(parser): diff --git a/configs/example/hsaTopology.py b/configs/example/hsaTopology.py index 909b9ef519..2dcbdeca01 100644 --- a/configs/example/hsaTopology.py +++ b/configs/example/hsaTopology.py @@ -27,14 +27,26 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -import m5 - import operator -from os import mkdir, makedirs, getpid, listdir, fsync -from os.path import join as joinpath +from os import ( + fsync, + getpid, + listdir, + makedirs, + mkdir, +) from os.path import isdir -from shutil import rmtree, copyfile -from m5.util.convert import toFrequency, toMemorySize +from os.path import join as joinpath +from shutil import ( + copyfile, + rmtree, +) + +import m5 +from m5.util.convert import ( + toFrequency, + toMemorySize, +) def file_append(path, contents): diff --git a/configs/example/lupv/run_lupv.py b/configs/example/lupv/run_lupv.py index d92ea3fa3f..4be6b924a5 100644 --- a/configs/example/lupv/run_lupv.py +++ b/configs/example/lupv/run_lupv.py @@ -33,18 +33,18 @@ Characteristics * Automatically generates the DTB file """ +import argparse + import m5 from m5.objects import Root from gem5.components.boards.experimental.lupv_board import LupvBoard from gem5.components.memory.single_channel import SingleChannelDDR3_1600 -from gem5.components.processors.simple_processor import SimpleProcessor from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor from gem5.isas import ISA +from gem5.resources.resource import obtain_resource from gem5.utils.requires import requires -from gem5.resources.resource import Resource, CustomResource - -import argparse # Run a check to ensure the right version of gem5 is being used. requires(isa_required=ISA.RISCV) @@ -98,8 +98,8 @@ board = LupvBoard( # Set the Full System workload. board.set_kernel_disk_workload( - kernel=Resource("riscv-lupio-linux-kernel"), - disk_image=Resource("riscv-lupio-busybox-img"), + kernel=obtain_resource("riscv-lupio-linux-kernel"), + disk_image=obtain_resource("riscv-lupio-busybox-img"), ) diff --git a/configs/example/memcheck.py b/configs/example/memcheck.py index aee2ef74d0..f26eabcacd 100644 --- a/configs/example/memcheck.py +++ b/configs/example/memcheck.py @@ -260,6 +260,7 @@ system.memchecker = MemChecker() # For each level, track the next subsys index to use next_subsys_index = [0] * (len(cachespec) + 1) + # Recursive function to create a sub-tree of the cache and tester # hierarchy def make_cache_level(ncaches, prototypes, level, next_cache): diff --git a/configs/example/memtest.py b/configs/example/memtest.py index 0cbbab5b4f..96ee11c107 100644 --- a/configs/example/memtest.py +++ b/configs/example/memtest.py @@ -145,6 +145,7 @@ parser.add_argument( args = parser.parse_args() + # Get the total number of testers def numtesters(cachespec, testerspec): # Determine the tester multiplier for each level as the @@ -278,6 +279,7 @@ system.clk_domain = SrcClockDomain( # For each level, track the next subsys index to use next_subsys_index = [0] * (len(cachespec) + 1) + # Recursive function to create a sub-tree of the cache and tester # hierarchy def make_cache_level(ncaches, prototypes, level, next_cache): diff --git a/configs/example/noc_config/2x4.py b/configs/example/noc_config/2x4.py index 3dd2403799..b6c1e80fc5 100644 --- a/configs/example/noc_config/2x4.py +++ b/configs/example/noc_config/2x4.py @@ -35,6 +35,7 @@ from ruby import CHI_config + # CustomMesh parameters for a 2x4 mesh. Routers will have the following layout: # # 0 --- 1 --- 2 --- 3 diff --git a/configs/example/read_config.py b/configs/example/read_config.py index 40c20ef501..27e23b69ee 100644 --- a/configs/example/read_config.py +++ b/configs/example/read_config.py @@ -140,7 +140,7 @@ for name, parser in list(param_parsers.items()): setattr(m5.params.__dict__[name], "parse_ini", classmethod(parser)) -class PortConnection(object): +class PortConnection: """This class is similar to m5.params.PortRef but with just enough information for ConfigManager""" @@ -151,7 +151,7 @@ class PortConnection(object): @classmethod def from_string(cls, str): - m = re.match("(.*)\.([^.\[]+)(\[(\d+)\])?", str) + m = re.match(r"(.*)\.([^.\[]+)(\[(\d+)\])?", str) object_name, port_name, whole_index, index = m.groups() if index is not None: index = int(index) @@ -178,7 +178,7 @@ def to_list(v): return [v] -class ConfigManager(object): +class ConfigManager: """Manager for parsing a Root configuration from a config file""" def __init__(self, config): @@ -296,7 +296,7 @@ class ConfigManager(object): def parse_port_name(self, port): """Parse the name of a port""" - m = re.match("(.*)\.([^.\[]+)(\[(\d+)\])?", port) + m = re.match(r"(.*)\.([^.\[]+)(\[(\d+)\])?", port) peer, peer_port, whole_index, index = m.groups() if index is not None: index = int(index) @@ -366,7 +366,6 @@ class ConfigManager(object): if port_has_correct_index(from_port) and port_has_correct_index( to_port ): - connections_to_make.append((from_port, to_port)) increment_port_index(from_port) @@ -416,7 +415,7 @@ class ConfigManager(object): self.bind_ports(connections) -class ConfigFile(object): +class ConfigFile: def get_flags(self): return set() @@ -445,7 +444,7 @@ class ConfigFile(object): pass def get_port_peers(self, object_name, port_name): - """Get the list of connected port names (in the string form + r"""Get the list of connected port names (in the string form object.port(\[index\])?) of the port object_name.port_name""" pass @@ -508,7 +507,7 @@ class ConfigJsonFile(ConfigFile): self.find_all_objects(elem) def load(self, config_file): - root = json.load(open(config_file, "r")) + root = json.load(open(config_file)) self.object_dicts = {} self.find_all_objects(root) diff --git a/configs/example/riscv/fs_linux.py b/configs/example/riscv/fs_linux.py index 949c7e2623..c0643c883d 100644 --- a/configs/example/riscv/fs_linux.py +++ b/configs/example/riscv/fs_linux.py @@ -45,23 +45,33 @@ from os import path import m5 from m5.defines import buildEnv from m5.objects import * -from m5.util import addToPath, fatal, warn +from m5.util import ( + addToPath, + fatal, + warn, +) from m5.util.fdthelper import * +from gem5.utils.requires import requires + addToPath("../../") -from ruby import Ruby - +from common import ( + CacheConfig, + CpuConfig, + MemConfig, + ObjectList, + Options, + Simulation, +) +from common.Benchmarks import * +from common.Caches import * from common.FSConfig import * from common.SysPaths import * -from common.Benchmarks import * -from common import Simulation -from common import CacheConfig -from common import CpuConfig -from common import MemConfig -from common import ObjectList -from common.Caches import * -from common import Options +from ruby import Ruby + +# Run a check to ensure the RISC-V ISA is complied into gem5. +requires(isa_required=ISA.RISCV) # ------------------------- Usage Instructions ------------------------- # # Common system confirguration options (cpu types, num cpus, checkpointing @@ -130,20 +140,8 @@ def generateDtb(system): # ----------------------------- Add Options ---------------------------- # parser = argparse.ArgumentParser() -Options.addCommonOptions(parser) +Options.addCommonOptions(parser, ISA.RISCV) Options.addFSOptions(parser) -parser.add_argument( - "--bare-metal", - action="store_true", - help="Provide the raw system without the linux specific bits", -) -parser.add_argument( - "--dtb-filename", - action="store", - type=str, - help="Specifies device tree blob file to use with device-tree-" - "enabled kernels", -) parser.add_argument( "--virtio-rng", action="store_true", help="Enable VirtIORng device" ) @@ -153,6 +151,7 @@ args = parser.parse_args() # CPU and Memory (CPUClass, mem_mode, FutureClass) = Simulation.setCPUClass(args) +assert issubclass(CPUClass, RiscvCPU) MemClass = Simulation.setMemClass(args) np = args.num_cpus diff --git a/configs/example/ruby_direct_test.py b/configs/example/ruby_direct_test.py index edbeed4cc7..25139b7253 100644 --- a/configs/example/ruby_direct_test.py +++ b/configs/example/ruby_direct_test.py @@ -25,11 +25,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import argparse +import os +import sys + import m5 -from m5.objects import * from m5.defines import buildEnv +from m5.objects import * from m5.util import addToPath -import os, argparse, sys addToPath("../") diff --git a/configs/example/ruby_gpu_random_test.py b/configs/example/ruby_gpu_random_test.py index 25d5a51892..17915c6a6d 100644 --- a/configs/example/ruby_gpu_random_test.py +++ b/configs/example/ruby_gpu_random_test.py @@ -27,11 +27,14 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +import argparse +import os +import sys + import m5 -from m5.objects import * from m5.defines import buildEnv +from m5.objects import * from m5.util import addToPath -import os, argparse, sys addToPath("../") diff --git a/configs/example/ruby_mem_test.py b/configs/example/ruby_mem_test.py index c90950107e..6a9e8c313c 100644 --- a/configs/example/ruby_mem_test.py +++ b/configs/example/ruby_mem_test.py @@ -25,11 +25,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import argparse +import os +import sys + import m5 -from m5.objects import * from m5.defines import buildEnv +from m5.objects import * from m5.util import addToPath -import os, argparse, sys addToPath("../") @@ -62,6 +65,12 @@ parser.add_argument( default=0, help="percentage of accesses that should be functional", ) +parser.add_argument( + "--atomic", + type=int, + default=0, + help="percentage of accesses that should be atomic", +) parser.add_argument( "--suppress-func-errors", action="store_true", @@ -105,6 +114,7 @@ cpus = [ max_loads=args.maxloads, percent_functional=args.functional, percent_uncacheable=0, + percent_atomic=args.atomic, progress_interval=args.progress, suppress_func_errors=args.suppress_func_errors, ) @@ -133,7 +143,7 @@ else: dmas = [] dma_ports = [] -for (i, dma) in enumerate(dmas): +for i, dma in enumerate(dmas): dma_ports.append(dma.test) Ruby.create_system(args, False, system, dma_ports=dma_ports) @@ -155,7 +165,7 @@ system.ruby.randomization = True assert len(cpus) == len(system.ruby._cpu_ports) -for (i, cpu) in enumerate(cpus): +for i, cpu in enumerate(cpus): # # Tie the cpu memtester ports to the correct system ports # diff --git a/configs/example/ruby_random_test.py b/configs/example/ruby_random_test.py index 816864be91..ed1b971a9b 100644 --- a/configs/example/ruby_random_test.py +++ b/configs/example/ruby_random_test.py @@ -25,11 +25,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import argparse +import os +import sys + import m5 -from m5.objects import * from m5.defines import buildEnv +from m5.objects import * from m5.util import addToPath -import os, argparse, sys addToPath("../") diff --git a/configs/example/sc_main.py b/configs/example/sc_main.py index 4a1482d87d..141ac173ac 100755 --- a/configs/example/sc_main.py +++ b/configs/example/sc_main.py @@ -26,7 +26,10 @@ import sys import m5 -from m5.objects import SystemC_Kernel, Root +from m5.objects import ( + Root, + SystemC_Kernel, +) # pylint:disable=unused-variable diff --git a/configs/example/sst/arm_fs.py b/configs/example/sst/arm_fs.py index bee4be1118..b8fcb68e37 100644 --- a/configs/example/sst/arm_fs.py +++ b/configs/example/sst/arm_fs.py @@ -48,7 +48,7 @@ from common import SysPaths class ArmSstSystem(ArmSystem): def __init__(self, cpu_clock_rate, **kwargs): - super(ArmSstSystem, self).__init__(**kwargs) + super().__init__(**kwargs) self.voltage_domain = VoltageDomain(voltage="1.0V") self.clk_domain = SrcClockDomain( @@ -185,3 +185,4 @@ for cpu in system.cpu: cpu.createInterruptController() root = Root(full_system=True, system=system) +m5.instantiate() diff --git a/configs/example/sst/riscv_fs.py b/configs/example/sst/riscv_fs.py index fc8f8618c4..74cb179114 100644 --- a/configs/example/sst/riscv_fs.py +++ b/configs/example/sst/riscv_fs.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021 The Regents of the University of California +# Copyright (c) 2021-2023 The Regents of the University of California # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -24,14 +24,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import m5 -from m5.objects import * +import argparse from os import path -# For downloading the disk image -from gem5.resources.resource import Resource +import m5 +from m5.objects import * -import argparse +# For downloading the disk image +from gem5.resources.resource import obtain_resource def generateMemNode(state, mem_range): @@ -104,16 +104,9 @@ def createHiFivePlatform(system): system.platform.pci_host.pio = system.membus.mem_side_ports - system.platform.rtc = RiscvRTC(frequency=Frequency("100MHz")) + system.platform.rtc = RiscvRTC(frequency=Frequency("10MHz")) system.platform.clint.int_pin = system.platform.rtc.int_pin - system.pma_checker = PMAChecker( - uncacheable=[ - *system.platform._on_chip_ranges(), - *system.platform._off_chip_ranges(), - ] - ) - system.iobus = IOXBar() system.bridge = Bridge(delay="50ns") system.bridge.mem_side_port = system.iobus.cpu_side_ports @@ -122,6 +115,15 @@ def createHiFivePlatform(system): system.platform.setNumCores(1) + for cpu in system.cpu: + # pma_checker has to be added for each of the system cpus. + cpu.mmu.pma_checker = PMAChecker( + uncacheable=[ + *system.platform._on_chip_ranges(), + *system.platform._off_chip_ranges(), + ] + ) + system.platform.attachOnChipIO(system.membus) system.platform.attachOffChipIO(system.iobus) @@ -139,7 +141,7 @@ cpu_clock_rate = args.cpu_clock_rate memory_size = args.memory_size # Try downloading the Resource -bbl_resource = Resource("riscv-boot-exit-nodisk") +bbl_resource = obtain_resource("riscv-boot-exit-nodisk") bbl_path = bbl_resource.get_local_path() system = System() @@ -173,3 +175,4 @@ for cpu in system.cpu: cpu.createInterruptController() root = Root(full_system=True, system=system) +m5.instantiate() diff --git a/configs/learning_gem5/part1/caches.py b/configs/learning_gem5/part1/caches.py index 3f7d26ed21..04c7d6797c 100644 --- a/configs/learning_gem5/part1/caches.py +++ b/configs/learning_gem5/part1/caches.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2015 Jason Power # All rights reserved. # @@ -55,7 +54,7 @@ class L1Cache(Cache): tgts_per_mshr = 20 def __init__(self, options=None): - super(L1Cache, self).__init__() + super().__init__() pass def connectBus(self, bus): @@ -79,7 +78,7 @@ class L1ICache(L1Cache): ) def __init__(self, opts=None): - super(L1ICache, self).__init__(opts) + super().__init__(opts) if not opts or not opts.l1i_size: return self.size = opts.l1i_size @@ -100,7 +99,7 @@ class L1DCache(L1Cache): ) def __init__(self, opts=None): - super(L1DCache, self).__init__(opts) + super().__init__(opts) if not opts or not opts.l1d_size: return self.size = opts.l1d_size @@ -125,7 +124,7 @@ class L2Cache(Cache): SimpleOpts.add_option("--l2_size", help=f"L2 cache size. Default: {size}") def __init__(self, opts=None): - super(L2Cache, self).__init__() + super().__init__() if not opts or not opts.l2_size: return self.size = opts.l2_size diff --git a/configs/learning_gem5/part1/simple-arm.py b/configs/learning_gem5/part1/simple-arm.py index 62f7645c5a..fac53a78af 100644 --- a/configs/learning_gem5/part1/simple-arm.py +++ b/configs/learning_gem5/part1/simple-arm.py @@ -73,6 +73,6 @@ system.cpu.createThreads() root = Root(full_system=False, system=system) m5.instantiate() -print("Beginning simulation!") +print(f"Beginning simulation!") exit_event = m5.simulate() -print("Exiting @ tick %i because %s" % (m5.curTick(), exit_event.getCause())) +print(f"Exiting @ tick {m5.curTick()} because {exit_event.getCause()}") diff --git a/configs/learning_gem5/part1/simple-riscv.py b/configs/learning_gem5/part1/simple-riscv.py index f05ca4ab50..6e296d5fc0 100644 --- a/configs/learning_gem5/part1/simple-riscv.py +++ b/configs/learning_gem5/part1/simple-riscv.py @@ -73,6 +73,6 @@ system.cpu.createThreads() root = Root(full_system=False, system=system) m5.instantiate() -print("Beginning simulation!") +print(f"Beginning simulation!") exit_event = m5.simulate() -print("Exiting @ tick %i because %s" % (m5.curTick(), exit_event.getCause())) +print(f"Exiting @ tick {m5.curTick()} because {exit_event.getCause()}") diff --git a/configs/learning_gem5/part1/simple.py b/configs/learning_gem5/part1/simple.py index e36cd78c8e..3792fddc4d 100644 --- a/configs/learning_gem5/part1/simple.py +++ b/configs/learning_gem5/part1/simple.py @@ -113,6 +113,6 @@ root = Root(full_system=False, system=system) # instantiate all of the objects we've created above m5.instantiate() -print("Beginning simulation!") +print(f"Beginning simulation!") exit_event = m5.simulate() -print("Exiting @ tick %i because %s" % (m5.curTick(), exit_event.getCause())) +print(f"Exiting @ tick {m5.curTick()} because {exit_event.getCause()}") diff --git a/configs/learning_gem5/part1/two_level.py b/configs/learning_gem5/part1/two_level.py index 8aa7dd7e83..ab3111c799 100644 --- a/configs/learning_gem5/part1/two_level.py +++ b/configs/learning_gem5/part1/two_level.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2015 Jason Power # All rights reserved. # @@ -43,7 +42,6 @@ import m5 # import all of the SimObjects from m5.objects import * -from gem5.runtime import get_runtime_isa # Add the common scripts to our path m5.util.addToPath("../../") @@ -140,6 +138,6 @@ root = Root(full_system=False, system=system) # instantiate all of the objects we've created above m5.instantiate() -print("Beginning simulation!") +print(f"Beginning simulation!") exit_event = m5.simulate() -print("Exiting @ tick %i because %s" % (m5.curTick(), exit_event.getCause())) +print(f"Exiting @ tick {m5.curTick()} because {exit_event.getCause()}") diff --git a/configs/learning_gem5/part2/hello_goodbye.py b/configs/learning_gem5/part2/hello_goodbye.py index e4b70ba3ae..4a4171c990 100644 --- a/configs/learning_gem5/part2/hello_goodbye.py +++ b/configs/learning_gem5/part2/hello_goodbye.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2017 Jason Lowe-Power # All rights reserved. # @@ -50,6 +49,6 @@ root.hello.goodbye_object = GoodbyeObject(buffer_size="100B") # instantiate all of the objects we've created above m5.instantiate() -print("Beginning simulation!") +print(f"Beginning simulation!") exit_event = m5.simulate() -print("Exiting @ tick %i because %s" % (m5.curTick(), exit_event.getCause())) +print(f"Exiting @ tick {m5.curTick()} because {exit_event.getCause()}") diff --git a/configs/learning_gem5/part2/run_simple.py b/configs/learning_gem5/part2/run_simple.py index be5f6ee7b8..ce7ea277e8 100644 --- a/configs/learning_gem5/part2/run_simple.py +++ b/configs/learning_gem5/part2/run_simple.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2017 Jason Lowe-Power # All rights reserved. # @@ -48,6 +47,6 @@ root.hello = SimpleObject() # instantiate all of the objects we've created above m5.instantiate() -print("Beginning simulation!") +print(f"Beginning simulation!") exit_event = m5.simulate() -print("Exiting @ tick %i because %s" % (m5.curTick(), exit_event.getCause())) +print(f"Exiting @ tick {m5.curTick()} because {exit_event.getCause()}") diff --git a/configs/learning_gem5/part2/simple_cache.py b/configs/learning_gem5/part2/simple_cache.py index 4228956126..2ba138c879 100644 --- a/configs/learning_gem5/part2/simple_cache.py +++ b/configs/learning_gem5/part2/simple_cache.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2017 Jason Lowe-Power # All rights reserved. # @@ -103,6 +102,6 @@ root = Root(full_system=False, system=system) # instantiate all of the objects we've created above m5.instantiate() -print("Beginning simulation!") +print(f"Beginning simulation!") exit_event = m5.simulate() -print("Exiting @ tick %i because %s" % (m5.curTick(), exit_event.getCause())) +print(f"Exiting @ tick {m5.curTick()} because {exit_event.getCause()}") diff --git a/configs/learning_gem5/part2/simple_memobj.py b/configs/learning_gem5/part2/simple_memobj.py index 20f4362b81..c845241ced 100644 --- a/configs/learning_gem5/part2/simple_memobj.py +++ b/configs/learning_gem5/part2/simple_memobj.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2017 Jason Lowe-Power # All rights reserved. # @@ -101,6 +100,6 @@ root = Root(full_system=False, system=system) # instantiate all of the objects we've created above m5.instantiate() -print("Beginning simulation!") +print(f"Beginning simulation!") exit_event = m5.simulate() -print("Exiting @ tick %i because %s" % (m5.curTick(), exit_event.getCause())) +print(f"Exiting @ tick {m5.curTick()} because {exit_event.getCause()}") diff --git a/configs/learning_gem5/part3/msi_caches.py b/configs/learning_gem5/part3/msi_caches.py index 13b2a11b1a..0945dc2bbd 100644 --- a/configs/learning_gem5/part3/msi_caches.py +++ b/configs/learning_gem5/part3/msi_caches.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2017 Jason Power # All rights reserved. # @@ -38,9 +37,11 @@ IMPORTANT: If you modify this file, it's likely that the Learning gem5 book import math from m5.defines import buildEnv -from m5.util import fatal, panic - from m5.objects import * +from m5.util import ( + fatal, + panic, +) class MyCacheSystem(RubySystem): @@ -48,7 +49,7 @@ class MyCacheSystem(RubySystem): if buildEnv["PROTOCOL"] != "MSI": fatal("This system assumes MSI from learning gem5!") - super(MyCacheSystem, self).__init__() + super().__init__() def setup(self, system, cpus, mem_ctrls): """Set up the Ruby cache subsystem. Note: This can't be done in the @@ -110,7 +111,6 @@ class MyCacheSystem(RubySystem): class L1Cache(L1Cache_Controller): - _version = 0 @classmethod @@ -122,7 +122,7 @@ class L1Cache(L1Cache_Controller): """CPUs are needed to grab the clock domain and system is needed for the cache block size. """ - super(L1Cache, self).__init__() + super().__init__() self.version = self.versionCount() # This is the cache memory object that stores the cache data and tags @@ -174,7 +174,6 @@ class L1Cache(L1Cache_Controller): class DirController(Directory_Controller): - _version = 0 @classmethod @@ -186,7 +185,7 @@ class DirController(Directory_Controller): """ranges are the memory ranges assigned to this controller.""" if len(mem_ctrls) > 1: panic("This cache system can only be connected to one mem ctrl") - super(DirController, self).__init__() + super().__init__() self.version = self.versionCount() self.addr_ranges = ranges self.ruby_system = ruby_system @@ -218,7 +217,7 @@ class MyNetwork(SimpleNetwork): """A simple point-to-point network. This doesn't not use garnet.""" def __init__(self, ruby_system): - super(MyNetwork, self).__init__() + super().__init__() self.netifs = [] self.ruby_system = ruby_system diff --git a/configs/learning_gem5/part3/ruby_caches_MI_example.py b/configs/learning_gem5/part3/ruby_caches_MI_example.py index 8c25a9b2d9..eecaded31f 100644 --- a/configs/learning_gem5/part3/ruby_caches_MI_example.py +++ b/configs/learning_gem5/part3/ruby_caches_MI_example.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2015 Jason Power # All rights reserved. # @@ -40,9 +39,11 @@ IMPORTANT: If you modify this file, it's likely that the Learning gem5 book import math from m5.defines import buildEnv -from m5.util import fatal, panic - from m5.objects import * +from m5.util import ( + fatal, + panic, +) class MyCacheSystem(RubySystem): @@ -50,7 +51,7 @@ class MyCacheSystem(RubySystem): if buildEnv["PROTOCOL"] != "MI_example": fatal("This system assumes MI_example!") - super(MyCacheSystem, self).__init__() + super().__init__() def setup(self, system, cpus, mem_ctrls): """Set up the Ruby cache subsystem. Note: This can't be done in the @@ -108,7 +109,6 @@ class MyCacheSystem(RubySystem): class L1Cache(L1Cache_Controller): - _version = 0 @classmethod @@ -120,7 +120,7 @@ class L1Cache(L1Cache_Controller): """CPUs are needed to grab the clock domain and system is needed for the cache block size. """ - super(L1Cache, self).__init__() + super().__init__() self.version = self.versionCount() # This is the cache memory object that stores the cache data and tags @@ -163,7 +163,6 @@ class L1Cache(L1Cache_Controller): class DirController(Directory_Controller): - _version = 0 @classmethod @@ -175,7 +174,7 @@ class DirController(Directory_Controller): """ranges are the memory ranges assigned to this controller.""" if len(mem_ctrls) > 1: panic("This cache system can only be connected to one mem ctrl") - super(DirController, self).__init__() + super().__init__() self.version = self.versionCount() self.addr_ranges = ranges self.ruby_system = ruby_system @@ -204,7 +203,7 @@ class MyNetwork(SimpleNetwork): """A simple point-to-point network. This doesn't not use garnet.""" def __init__(self, ruby_system): - super(MyNetwork, self).__init__() + super().__init__() self.netifs = [] self.ruby_system = ruby_system diff --git a/configs/learning_gem5/part3/ruby_test.py b/configs/learning_gem5/part3/ruby_test.py index e46f07bb0a..67ac697bb3 100644 --- a/configs/learning_gem5/part3/ruby_test.py +++ b/configs/learning_gem5/part3/ruby_test.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2015 Jason Power # All rights reserved. # @@ -34,14 +33,14 @@ IMPORTANT: If you modify this file, it's likely that the Learning gem5 book """ +from test_caches import TestCacheSystem + # import the m5 (gem5) library created when gem5 is built import m5 # import all of the SimObjects from m5.objects import * -from test_caches import TestCacheSystem - # create the system we are going to simulate system = System() diff --git a/configs/learning_gem5/part3/simple_ruby.py b/configs/learning_gem5/part3/simple_ruby.py index f3f84353e8..6e22beb4dd 100644 --- a/configs/learning_gem5/part3/simple_ruby.py +++ b/configs/learning_gem5/part3/simple_ruby.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2015 Jason Power # All rights reserved. # diff --git a/configs/learning_gem5/part3/test_caches.py b/configs/learning_gem5/part3/test_caches.py index 7b0ce52dad..4e8e8febda 100644 --- a/configs/learning_gem5/part3/test_caches.py +++ b/configs/learning_gem5/part3/test_caches.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2017 Jason Power # All rights reserved. # @@ -35,12 +34,15 @@ IMPORTANT: If you modify this file, it's likely that the Learning gem5 book """ +from msi_caches import ( + DirController, + L1Cache, + MyNetwork, +) + from m5.defines import buildEnv -from m5.util import fatal - from m5.objects import * - -from msi_caches import L1Cache, DirController, MyNetwork +from m5.util import fatal class TestCacheSystem(RubySystem): @@ -48,7 +50,7 @@ class TestCacheSystem(RubySystem): if buildEnv["PROTOCOL"] != "MSI": fatal("This system assumes MSI from learning gem5!") - super(TestCacheSystem, self).__init__() + super().__init__() def setup(self, system, tester, mem_ctrls): """Set up the Ruby cache subsystem. Note: This can't be done in the diff --git a/configs/network/Network.py b/configs/network/Network.py index a5334741c0..2b4b6af244 100644 --- a/configs/network/Network.py +++ b/configs/network/Network.py @@ -25,15 +25,20 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import math + import m5 -from m5.objects import * from m5.defines import buildEnv -from m5.util import addToPath, fatal, warn +from m5.objects import * +from m5.util import ( + addToPath, + fatal, + warn, +) def define_options(parser): - # By default, ruby uses the simple timing cpu - parser.set_defaults(cpu_type="TimingSimpleCPU") + # By default, ruby uses the simple timing cpu and the X86 ISA + parser.set_defaults(cpu_type="X86TimingSimpleCPU") parser.add_argument( "--topology", @@ -121,7 +126,6 @@ def define_options(parser): def create_network(options, ruby): - # Allow legacy users to use garnet through garnet2.0 option # until next gem5 release. if options.network == "garnet2.0": @@ -162,7 +166,6 @@ def create_network(options, ruby): def init_network(options, network, InterfaceClass): - if options.network == "garnet": network.num_rows = options.mesh_rows network.vcs_per_vnet = options.vcs_per_vnet diff --git a/configs/nvm/sweep.py b/configs/nvm/sweep.py index b569cb35b5..d5d23ad76a 100644 --- a/configs/nvm/sweep.py +++ b/configs/nvm/sweep.py @@ -33,18 +33,20 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import math import argparse +import math import m5 from m5.objects import * -from m5.util import addToPath from m5.stats import periodicStatDump +from m5.util import addToPath addToPath("../") -from common import ObjectList -from common import MemConfig +from common import ( + MemConfig, + ObjectList, +) # this script is helpful to sweep the efficiency of a specific memory # controller configuration, by varying the number of banks accessed, diff --git a/configs/nvm/sweep_hybrid.py b/configs/nvm/sweep_hybrid.py index d1e2994268..669f847eb1 100644 --- a/configs/nvm/sweep_hybrid.py +++ b/configs/nvm/sweep_hybrid.py @@ -33,18 +33,20 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import math import argparse +import math import m5 from m5.objects import * -from m5.util import addToPath from m5.stats import periodicStatDump +from m5.util import addToPath addToPath("../") -from common import ObjectList -from common import MemConfig +from common import ( + MemConfig, + ObjectList, +) # this script is helpful to sweep the efficiency of a specific memory # controller configuration, by varying the number of banks accessed, diff --git a/configs/ruby/AMD_Base_Constructor.py b/configs/ruby/AMD_Base_Constructor.py index 030b45cbb6..ff4246a7e0 100644 --- a/configs/ruby/AMD_Base_Constructor.py +++ b/configs/ruby/AMD_Base_Constructor.py @@ -28,16 +28,22 @@ # POSSIBILITY OF SUCH DAMAGE. import math + import m5 -from m5.objects import * from m5.defines import buildEnv -from m5.util import addToPath, convert +from m5.objects import * +from m5.util import ( + addToPath, + convert, +) + from .CntrlBase import * addToPath("../") from topologies.Cluster import Cluster + # # Note: the L1 Cache latency is only used by the sequencer on fast path hits # @@ -115,7 +121,6 @@ def construct(options, system, ruby_system): cpuCluster = None cpuCluster = Cluster(name="CPU Cluster", extBW=8, intBW=8) # 16 GB/s for i in range((options.num_cpus + 1) // 2): - cp_cntrl = CPCntrl() cp_cntrl.create(options, ruby_system, system) diff --git a/configs/ruby/CHI.py b/configs/ruby/CHI.py index 96537e558a..cb8fe64664 100644 --- a/configs/ruby/CHI.py +++ b/configs/ruby/CHI.py @@ -34,8 +34,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import m5 -from m5.objects import * from m5.defines import buildEnv +from m5.objects import * + from .Ruby import create_topology @@ -53,8 +54,8 @@ def define_options(parser): def read_config_file(file): """Read file as a module and return it""" - import types import importlib.machinery + import types loader = importlib.machinery.SourceFileLoader("chi_configs", file) chi_configs = types.ModuleType(loader.name) @@ -65,7 +66,6 @@ def read_config_file(file): def create_system( options, full_system, system, dma_ports, bootmem, ruby_system, cpus ): - if buildEnv["PROTOCOL"] != "CHI": m5.panic("This script requires the CHI build") diff --git a/configs/ruby/CHI_config.py b/configs/ruby/CHI_config.py index 4f2580c373..80ca38ad5a 100644 --- a/configs/ruby/CHI_config.py +++ b/configs/ruby/CHI_config.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021,2022 ARM Limited +# Copyright (c) 2021-2023 ARM Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -46,6 +46,7 @@ node to router binding. See configs/example/noc_config/2x4.py for an example. """ import math + import m5 from m5.objects import * @@ -116,7 +117,7 @@ class CHI_Node(SubSystem): router_list = None def __init__(self, ruby_system): - super(CHI_Node, self).__init__() + super().__init__() self._ruby_system = ruby_system self._network = ruby_system.network @@ -201,7 +202,7 @@ class CHI_Cache_Controller(Cache_Controller): """ def __init__(self, ruby_system): - super(CHI_Cache_Controller, self).__init__( + super().__init__( version=Versions.getVersion(Cache_Controller), ruby_system=ruby_system, mandatoryQueue=MessageBuffer(), @@ -228,10 +229,11 @@ class CHI_L1Controller(CHI_Cache_Controller): """ def __init__(self, ruby_system, sequencer, cache, prefetcher): - super(CHI_L1Controller, self).__init__(ruby_system) + super().__init__(ruby_system) self.sequencer = sequencer self.cache = cache - self.use_prefetcher = False + self.prefetcher = prefetcher + self.use_prefetcher = prefetcher != NULL self.send_evictions = True self.is_HN = False self.enable_DMT = False @@ -244,6 +246,7 @@ class CHI_L1Controller(CHI_Cache_Controller): self.alloc_on_readunique = True self.alloc_on_readonce = True self.alloc_on_writeback = True + self.alloc_on_atomic = False self.dealloc_on_unique = False self.dealloc_on_shared = False self.dealloc_backinv_unique = True @@ -264,10 +267,11 @@ class CHI_L2Controller(CHI_Cache_Controller): """ def __init__(self, ruby_system, cache, prefetcher): - super(CHI_L2Controller, self).__init__(ruby_system) + super().__init__(ruby_system) self.sequencer = NULL self.cache = cache - self.use_prefetcher = False + self.prefetcher = prefetcher + self.use_prefetcher = prefetcher != NULL self.allow_SD = True self.is_HN = False self.enable_DMT = False @@ -280,6 +284,7 @@ class CHI_L2Controller(CHI_Cache_Controller): self.alloc_on_readunique = True self.alloc_on_readonce = True self.alloc_on_writeback = True + self.alloc_on_atomic = False self.dealloc_on_unique = False self.dealloc_on_shared = False self.dealloc_backinv_unique = True @@ -299,10 +304,11 @@ class CHI_HNFController(CHI_Cache_Controller): """ def __init__(self, ruby_system, cache, prefetcher, addr_ranges): - super(CHI_HNFController, self).__init__(ruby_system) + super().__init__(ruby_system) self.sequencer = NULL self.cache = cache - self.use_prefetcher = False + self.prefetcher = prefetcher + self.use_prefetcher = prefetcher != NULL self.addr_ranges = addr_ranges self.allow_SD = True self.is_HN = True @@ -316,6 +322,7 @@ class CHI_HNFController(CHI_Cache_Controller): self.alloc_on_readunique = False self.alloc_on_readonce = True self.alloc_on_writeback = True + self.alloc_on_atomic = True self.dealloc_on_unique = True self.dealloc_on_shared = False self.dealloc_backinv_unique = False @@ -337,7 +344,7 @@ class CHI_MNController(MiscNode_Controller): def __init__( self, ruby_system, addr_range, l1d_caches, early_nonsync_comp ): - super(CHI_MNController, self).__init__( + super().__init__( version=Versions.getVersion(MiscNode_Controller), ruby_system=ruby_system, mandatoryQueue=MessageBuffer(), @@ -368,7 +375,7 @@ class CHI_DMAController(CHI_Cache_Controller): """ def __init__(self, ruby_system, sequencer): - super(CHI_DMAController, self).__init__(ruby_system) + super().__init__(ruby_system) self.sequencer = sequencer class DummyCache(RubyCache): @@ -377,6 +384,7 @@ class CHI_DMAController(CHI_Cache_Controller): size = "128" assoc = 1 + self.prefetcher = NULL self.use_prefetcher = False self.cache = DummyCache() self.sequencer.dcache = NULL @@ -392,6 +400,7 @@ class CHI_DMAController(CHI_Cache_Controller): self.alloc_on_readunique = False self.alloc_on_readonce = False self.alloc_on_writeback = False + self.alloc_on_atomic = False self.dealloc_on_unique = False self.dealloc_on_shared = False self.dealloc_backinv_unique = False @@ -459,7 +468,7 @@ class CHI_RNF(CHI_Node): l1Iprefetcher_type=None, l1Dprefetcher_type=None, ): - super(CHI_RNF, self).__init__(ruby_system) + super().__init__(ruby_system) self._block_size_bits = int(math.log(cache_line_size, 2)) @@ -495,11 +504,16 @@ class CHI_RNF(CHI_Node): start_index_bit=self._block_size_bits, is_icache=False ) - # Placeholders for future prefetcher support - if l1Iprefetcher_type != None or l1Dprefetcher_type != None: - m5.fatal("Prefetching not supported yet") - l1i_pf = NULL - l1d_pf = NULL + # prefetcher wrappers + if l1Iprefetcher_type != None: + l1i_pf = l1Iprefetcher_type() + else: + l1i_pf = NULL + + if l1Dprefetcher_type != None: + l1d_pf = l1Dprefetcher_type() + else: + l1d_pf = NULL # cache controllers cpu.l1i = CHI_L1Controller( @@ -544,9 +558,11 @@ class CHI_RNF(CHI_Node): l2_cache = cache_type( start_index_bit=self._block_size_bits, is_icache=False ) + if pf_type != None: - m5.fatal("Prefetching not supported yet") - l2_pf = NULL + l2_pf = pf_type() + else: + l2_pf = NULL cpu.l2 = CHI_L2Controller(self._ruby_system, l2_cache, l2_pf) @@ -602,7 +618,7 @@ class CHI_HNF(CHI_Node): # The CHI controller can be a child of this object or another if # 'parent' if specified def __init__(self, hnf_idx, ruby_system, llcache_type, parent): - super(CHI_HNF, self).__init__(ruby_system) + super().__init__(ruby_system) addr_ranges, intlvHighBit = self.getAddrRanges(hnf_idx) # All ranges should have the same interleaving @@ -640,7 +656,7 @@ class CHI_MN(CHI_Node): # The CHI controller can be a child of this object or another if # 'parent' if specified def __init__(self, ruby_system, l1d_caches, early_nonsync_comp=False): - super(CHI_MN, self).__init__(ruby_system) + super().__init__(ruby_system) # MiscNode has internal address range starting at 0 addr_range = AddrRange(0, size="1kB") @@ -671,7 +687,7 @@ class CHI_SNF_Base(CHI_Node): # The CHI controller can be a child of this object or another if # 'parent' if specified def __init__(self, ruby_system, parent): - super(CHI_SNF_Base, self).__init__(ruby_system) + super().__init__(ruby_system) self._cntrl = Memory_Controller( version=Versions.getVersion(Memory_Controller), @@ -718,7 +734,7 @@ class CHI_SNF_BootMem(CHI_SNF_Base): """ def __init__(self, ruby_system, parent, bootmem): - super(CHI_SNF_BootMem, self).__init__(ruby_system, parent) + super().__init__(ruby_system, parent) self._cntrl.memory_out_port = bootmem.port self._cntrl.addr_ranges = self.getMemRange(bootmem) @@ -729,7 +745,7 @@ class CHI_SNF_MainMem(CHI_SNF_Base): """ def __init__(self, ruby_system, parent, mem_ctrl=None): - super(CHI_SNF_MainMem, self).__init__(ruby_system, parent) + super().__init__(ruby_system, parent) if mem_ctrl: self._cntrl.memory_out_port = mem_ctrl.port self._cntrl.addr_ranges = self.getMemRange(mem_ctrl) @@ -744,7 +760,7 @@ class CHI_RNI_Base(CHI_Node): # The CHI controller can be a child of this object or another if # 'parent' if specified def __init__(self, ruby_system, parent): - super(CHI_RNI_Base, self).__init__(ruby_system) + super().__init__(ruby_system) self._sequencer = RubySequencer( version=Versions.getSeqId(), @@ -773,7 +789,7 @@ class CHI_RNI_DMA(CHI_RNI_Base): """ def __init__(self, ruby_system, dma_port, parent): - super(CHI_RNI_DMA, self).__init__(ruby_system, parent) + super().__init__(ruby_system, parent) assert dma_port != None self._sequencer.in_ports = dma_port @@ -784,5 +800,5 @@ class CHI_RNI_IO(CHI_RNI_Base): """ def __init__(self, ruby_system, parent): - super(CHI_RNI_IO, self).__init__(ruby_system, parent) + super().__init__(ruby_system, parent) ruby_system._io_port = self._sequencer diff --git a/configs/ruby/GPU_VIPER.py b/configs/ruby/GPU_VIPER.py index ee8d570498..0818b7f0eb 100644 --- a/configs/ruby/GPU_VIPER.py +++ b/configs/ruby/GPU_VIPER.py @@ -28,15 +28,22 @@ # POSSIBILITY OF SUCH DAMAGE. import math + +from common import ( + FileSystemConfig, + MemConfig, + ObjectList, +) + import m5 -from m5.objects import * from m5.defines import buildEnv +from m5.objects import * from m5.util import addToPath -from .Ruby import create_topology -from .Ruby import send_evicts -from common import ObjectList -from common import MemConfig -from common import FileSystemConfig + +from .Ruby import ( + create_topology, + send_evicts, +) addToPath("../") @@ -122,7 +129,7 @@ class CPCntrl(CorePair_Controller, CntrlBase): self.sequencer1.is_cpu_sequencer = True self.issue_latency = options.cpu_to_dir_latency - self.send_evictions = send_evicts(options) + self.send_evictions = True if options.cpu_type == "X86O3CPU" else False self.ruby_system = ruby_system @@ -154,6 +161,8 @@ class TCPCntrl(TCP_Controller, CntrlBase): dataAccessLatency=options.TCP_latency, ) self.L1cache.resourceStalls = options.no_resource_stalls + self.L1cache.dataArrayBanks = options.tcp_num_banks + self.L1cache.tagArrayBanks = options.tcp_num_banks self.L1cache.create(options) self.issue_latency = 1 # TCP_Controller inherits this from RubyController @@ -273,6 +282,8 @@ class TCC(RubyCache): def create(self, options): self.assoc = options.tcc_assoc + self.atomicLatency = options.atomic_alu_latency + self.atomicALUs = options.tcc_num_atomic_alus if hasattr(options, "bw_scalor") and options.bw_scalor > 0: s = options.num_compute_units tcc_size = s * 128 @@ -298,7 +309,10 @@ class TCC(RubyCache): class TCCCntrl(TCC_Controller, CntrlBase): def create(self, options, ruby_system, system): self.version = self.versionCount() - self.L2cache = TCC() + self.L2cache = TCC( + tagAccessLatency=options.tcc_tag_access_latency, + dataAccessLatency=options.tcc_data_access_latency, + ) self.L2cache.create(options) self.L2cache.resourceStalls = options.no_tcc_resource_stalls @@ -331,6 +345,7 @@ class L3Cache(RubyCache): self.replacement_policy = TreePLRURP() +# unused in GPU_VIPER; see git blame for discussion class L3Cntrl(L3Cache_Controller, CntrlBase): def create(self, options, ruby_system, system): self.version = self.versionCount() @@ -489,10 +504,45 @@ def define_options(parser): help="Size of the mandatory queue in the GPU scalar " "cache controller", ) + parser.add_argument( + "--glc-atomic-latency", type=int, default=1, help="GLC Atomic Latency" + ) + parser.add_argument( + "--atomic-alu-latency", type=int, default=0, help="Atomic ALU Latency" + ) + parser.add_argument( + "--tcc-num-atomic-alus", + type=int, + default=64, + help="Number of atomic ALUs in the TCC", + ) + parser.add_argument( + "--tcp-num-banks", + type=int, + default="16", + help="Num of banks in L1 cache", + ) + parser.add_argument( + "--tcc-num-banks", + type=int, + default="16", + help="Num of banks in L2 cache", + ) + parser.add_argument( + "--tcc-tag-access-latency", + type=int, + default="2", + help="Tag access latency in L2 cache", + ) + parser.add_argument( + "--tcc-data-access-latency", + type=int, + default="8", + help="Data access latency in L2 cache", + ) def construct_dirs(options, system, ruby_system, network): - dir_cntrl_nodes = [] # For an odd number of CPUs, still create the right number of controllers @@ -524,6 +574,7 @@ def construct_dirs(options, system, ruby_system, network): dir_cntrl.create(options, dir_ranges, ruby_system, system) dir_cntrl.number_of_TBEs = options.num_tbes dir_cntrl.useL3OnWT = options.use_L3_on_WT + dir_cntrl.L2isWB = options.WB_L2 # the number_of_TBEs is inclusive of TBEs below # Connect the Directory controller to the ruby network @@ -560,7 +611,6 @@ def construct_dirs(options, system, ruby_system, network): def construct_gpudirs(options, system, ruby_system, network): - dir_cntrl_nodes = [] mem_ctrls = [] @@ -588,6 +638,7 @@ def construct_gpudirs(options, system, ruby_system, network): dir_cntrl.create(options, [addr_range], ruby_system, system) dir_cntrl.number_of_TBEs = options.num_tbes dir_cntrl.useL3OnWT = False + dir_cntrl.L2isWB = options.WB_L2 # Connect the Directory controller to the ruby network dir_cntrl.requestFromCores = MessageBuffer(ordered=True) @@ -649,12 +700,10 @@ def construct_gpudirs(options, system, ruby_system, network): def construct_corepairs(options, system, ruby_system, network): - cpu_sequencers = [] cp_cntrl_nodes = [] for i in range((options.num_cpus + 1) // 2): - cp_cntrl = CPCntrl() cp_cntrl.create(options, ruby_system, system) @@ -689,7 +738,6 @@ def construct_corepairs(options, system, ruby_system, network): def construct_tcps(options, system, ruby_system, network): - tcp_sequencers = [] tcp_cntrl_nodes = [] @@ -697,7 +745,6 @@ def construct_tcps(options, system, ruby_system, network): TCC_bits = int(math.log(options.num_tccs, 2)) for i in range(options.num_compute_units): - tcp_cntrl = TCPCntrl( TCC_select_num_bits=TCC_bits, issue_latency=1, number_of_TBEs=2560 ) @@ -737,7 +784,6 @@ def construct_tcps(options, system, ruby_system, network): def construct_sqcs(options, system, ruby_system, network): - sqc_sequencers = [] sqc_cntrl_nodes = [] @@ -745,7 +791,6 @@ def construct_sqcs(options, system, ruby_system, network): TCC_bits = int(math.log(options.num_tccs, 2)) for i in range(options.num_sqc): - sqc_cntrl = SQCCntrl(TCC_select_num_bits=TCC_bits) sqc_cntrl.create(options, ruby_system, system) @@ -772,7 +817,6 @@ def construct_sqcs(options, system, ruby_system, network): def construct_scalars(options, system, ruby_system, network): - scalar_sequencers = [] scalar_cntrl_nodes = [] @@ -805,7 +849,6 @@ def construct_scalars(options, system, ruby_system, network): def construct_cmdprocs(options, system, ruby_system, network): - cmdproc_sequencers = [] cmdproc_cntrl_nodes = [] @@ -813,7 +856,6 @@ def construct_cmdprocs(options, system, ruby_system, network): TCC_bits = int(math.log(options.num_tccs, 2)) for i in range(options.num_cp): - tcp_ID = options.num_compute_units + i sqc_ID = options.num_sqc + i @@ -866,15 +908,14 @@ def construct_cmdprocs(options, system, ruby_system, network): def construct_tccs(options, system, ruby_system, network): - tcc_cntrl_nodes = [] for i in range(options.num_tccs): - tcc_cntrl = TCCCntrl(l2_response_latency=options.TCC_latency) tcc_cntrl.create(options, ruby_system, system) tcc_cntrl.l2_request_latency = options.gpu_to_dir_latency tcc_cntrl.l2_response_latency = options.TCC_latency + tcc_cntrl.glc_atomic_latency = options.glc_atomic_latency tcc_cntrl_nodes.append(tcc_cntrl) tcc_cntrl.WB = options.WB_L2 tcc_cntrl.number_of_TBEs = 2560 * options.num_compute_units diff --git a/configs/ruby/Garnet_standalone.py b/configs/ruby/Garnet_standalone.py index ba5216eb24..8329295574 100644 --- a/configs/ruby/Garnet_standalone.py +++ b/configs/ruby/Garnet_standalone.py @@ -26,10 +26,15 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import m5 -from m5.objects import * from m5.defines import buildEnv +from m5.objects import * from m5.util import addToPath -from .Ruby import create_topology, create_directories + +from .Ruby import ( + create_directories, + create_topology, +) + # # Declare caches used by the protocol diff --git a/configs/ruby/MESI_Three_Level.py b/configs/ruby/MESI_Three_Level.py index 70f9c82723..e0de4e0636 100644 --- a/configs/ruby/MESI_Three_Level.py +++ b/configs/ruby/MESI_Three_Level.py @@ -28,13 +28,20 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import math -import m5 -from m5.objects import * -from m5.defines import buildEnv -from .Ruby import create_topology, create_directories -from .Ruby import send_evicts + from common import FileSystemConfig +import m5 +from m5.defines import buildEnv +from m5.objects import * + +from .Ruby import ( + create_directories, + create_topology, + send_evicts, +) + + # # Declare caches used by the protocol # @@ -77,7 +84,6 @@ def define_options(parser): def create_system( options, full_system, system, dma_ports, bootmem, ruby_system, cpus ): - if buildEnv["PROTOCOL"] != "MESI_Three_Level": fatal( "This script requires the MESI_Three_Level protocol to be\ diff --git a/configs/ruby/MESI_Three_Level_HTM.py b/configs/ruby/MESI_Three_Level_HTM.py index 883db9800e..e6c4e81f91 100644 --- a/configs/ruby/MESI_Three_Level_HTM.py +++ b/configs/ruby/MESI_Three_Level_HTM.py @@ -28,13 +28,20 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import math -import m5 -from m5.objects import * -from m5.defines import buildEnv -from .Ruby import create_topology, create_directories -from .Ruby import send_evicts + from common import FileSystemConfig +import m5 +from m5.defines import buildEnv +from m5.objects import * + +from .Ruby import ( + create_directories, + create_topology, + send_evicts, +) + + # # Declare caches used by the protocol # @@ -77,7 +84,6 @@ def define_options(parser): def create_system( options, full_system, system, dma_ports, bootmem, ruby_system, cpus ): - if buildEnv["PROTOCOL"] != "MESI_Three_Level_HTM": fatal( "This script requires the MESI_Three_Level protocol to be\ diff --git a/configs/ruby/MESI_Two_Level.py b/configs/ruby/MESI_Two_Level.py index 80a823bc52..500afbc199 100644 --- a/configs/ruby/MESI_Two_Level.py +++ b/configs/ruby/MESI_Two_Level.py @@ -26,11 +26,17 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import math + import m5 -from m5.objects import * from m5.defines import buildEnv -from .Ruby import create_topology, create_directories -from .Ruby import send_evicts +from m5.objects import * + +from .Ruby import ( + create_directories, + create_topology, + send_evicts, +) + # # Declare caches used by the protocol @@ -50,7 +56,6 @@ def define_options(parser): def create_system( options, full_system, system, dma_ports, bootmem, ruby_system, cpus ): - if buildEnv["PROTOCOL"] != "MESI_Two_Level": fatal("This script requires the MESI_Two_Level protocol to be built.") diff --git a/configs/ruby/MI_example.py b/configs/ruby/MI_example.py index 0ccfd75506..ba0d446840 100644 --- a/configs/ruby/MI_example.py +++ b/configs/ruby/MI_example.py @@ -26,11 +26,17 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import math + import m5 -from m5.objects import * from m5.defines import buildEnv -from .Ruby import create_topology, create_directories -from .Ruby import send_evicts +from m5.objects import * + +from .Ruby import ( + create_directories, + create_topology, + send_evicts, +) + # # Declare caches used by the protocol @@ -46,7 +52,6 @@ def define_options(parser): def create_system( options, full_system, system, dma_ports, bootmem, ruby_system, cpus ): - if buildEnv["PROTOCOL"] != "MI_example": panic("This script requires the MI_example protocol to be built.") diff --git a/configs/ruby/MOESI_AMD_Base.py b/configs/ruby/MOESI_AMD_Base.py index 30c7678f08..aeab96a85f 100644 --- a/configs/ruby/MOESI_AMD_Base.py +++ b/configs/ruby/MOESI_AMD_Base.py @@ -28,14 +28,19 @@ # POSSIBILITY OF SUCH DAMAGE. import math -import m5 -from m5.objects import * -from m5.defines import buildEnv -from m5.util import addToPath -from .Ruby import create_topology -from .Ruby import send_evicts + from common import FileSystemConfig +import m5 +from m5.defines import buildEnv +from m5.objects import * +from m5.util import addToPath + +from .Ruby import ( + create_topology, + send_evicts, +) + addToPath("../") from topologies.Cluster import Cluster @@ -327,7 +332,6 @@ def create_system( # For an odd number of CPUs, still create the right number of controllers cpuCluster = Cluster(extBW=512, intBW=512) # 1 TB/s for i in range((options.num_cpus + 1) // 2): - cp_cntrl = CPCntrl() cp_cntrl.create(options, ruby_system, system) diff --git a/configs/ruby/MOESI_CMP_directory.py b/configs/ruby/MOESI_CMP_directory.py index ead03c1693..700967f98a 100644 --- a/configs/ruby/MOESI_CMP_directory.py +++ b/configs/ruby/MOESI_CMP_directory.py @@ -38,11 +38,17 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import math + import m5 -from m5.objects import * from m5.defines import buildEnv -from .Ruby import create_topology, create_directories -from .Ruby import send_evicts +from m5.objects import * + +from .Ruby import ( + create_directories, + create_topology, + send_evicts, +) + # # Declare caches used by the protocol @@ -64,7 +70,6 @@ def define_options(parser): def create_system( options, full_system, system, dma_ports, bootmem, ruby_system, cpus ): - if buildEnv["PROTOCOL"] != "MOESI_CMP_directory": panic( "This script requires the MOESI_CMP_directory protocol to be built." diff --git a/configs/ruby/MOESI_CMP_token.py b/configs/ruby/MOESI_CMP_token.py index a610db5076..b6ee7a3875 100644 --- a/configs/ruby/MOESI_CMP_token.py +++ b/configs/ruby/MOESI_CMP_token.py @@ -26,11 +26,17 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import math + import m5 -from m5.objects import * from m5.defines import buildEnv -from .Ruby import create_topology, create_directories -from .Ruby import send_evicts +from m5.objects import * + +from .Ruby import ( + create_directories, + create_topology, + send_evicts, +) + # # Declare caches used by the protocol @@ -71,7 +77,6 @@ def define_options(parser): def create_system( options, full_system, system, dma_ports, bootmem, ruby_system, cpus ): - if buildEnv["PROTOCOL"] != "MOESI_CMP_token": panic("This script requires the MOESI_CMP_token protocol to be built.") diff --git a/configs/ruby/MOESI_hammer.py b/configs/ruby/MOESI_hammer.py index 65ec11a1ad..f0e1c3730f 100644 --- a/configs/ruby/MOESI_hammer.py +++ b/configs/ruby/MOESI_hammer.py @@ -26,13 +26,20 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import math -import m5 -from m5.objects import * -from m5.defines import buildEnv -from .Ruby import create_topology, create_directories -from .Ruby import send_evicts + from common import FileSystemConfig +import m5 +from m5.defines import buildEnv +from m5.objects import * + +from .Ruby import ( + create_directories, + create_topology, + send_evicts, +) + + # # Declare caches used by the protocol # @@ -70,7 +77,6 @@ def define_options(parser): def create_system( options, full_system, system, dma_ports, bootmem, ruby_system, cpus ): - if buildEnv["PROTOCOL"] != "MOESI_hammer": panic("This script requires the MOESI_hammer protocol to be built.") diff --git a/configs/ruby/Ruby.py b/configs/ruby/Ruby.py index d3c2efbb3f..e427a39de8 100644 --- a/configs/ruby/Ruby.py +++ b/configs/ruby/Ruby.py @@ -38,26 +38,32 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import math + import m5 -from m5.objects import * from m5.defines import buildEnv -from m5.util import addToPath, fatal +from m5.objects import * +from m5.util import ( + addToPath, + fatal, +) + from gem5.isas import ISA -from gem5.runtime import get_runtime_isa +from gem5.runtime import get_supported_isas addToPath("../") -from common import ObjectList -from common import MemConfig -from common import FileSystemConfig - -from topologies import * +from common import ( + FileSystemConfig, + MemConfig, + ObjectList, +) from network import Network +from topologies import * def define_options(parser): - # By default, ruby uses the simple timing cpu - parser.set_defaults(cpu_type="TimingSimpleCPU") + # By default, ruby uses the simple timing cpu and the X86 ISA + parser.set_defaults(cpu_type="X86TimingSimpleCPU") parser.add_argument( "--ruby-clock", @@ -221,7 +227,6 @@ def create_system( bootmem=None, cpus=None, ): - system.ruby = RubySystem() ruby = system.ruby @@ -326,9 +331,12 @@ def send_evicts(options): # 1. The O3 model must keep the LSQ coherent with the caches # 2. The x86 mwait instruction is built on top of coherence invalidations # 3. The local exclusive monitor in ARM systems - if options.cpu_type == "DerivO3CPU" or get_runtime_isa() in ( - ISA.X86, - ISA.ARM, - ): + if get_supported_isas() == {ISA.NULL}: + return False + + if ( + hasattr(m5.objects, "DerivO3CPU") + and isinstance(options.cpu_type, DerivO3CPU) + ) or ObjectList.cpu_list.get_isa(options.cpu_type) in [ISA.X86, ISA.ARM]: return True return False diff --git a/configs/splash2/cluster.py b/configs/splash2/cluster.py index 4c09eee8f8..c43067e29c 100644 --- a/configs/splash2/cluster.py +++ b/configs/splash2/cluster.py @@ -28,8 +28,8 @@ # # "m5 test.py" -import os import argparse +import os import sys import m5 @@ -64,6 +64,7 @@ parser.add_argument("-b", "--benchmark", help="Splash 2 benchmark to run") args = parser.parse_args() + # -------------------- # Define Splash2 Benchmarks # ==================== diff --git a/configs/splash2/run.py b/configs/splash2/run.py index 08c11e0f5a..e53983d4b1 100644 --- a/configs/splash2/run.py +++ b/configs/splash2/run.py @@ -27,8 +27,8 @@ # Splash2 Run Script # -import os import argparse +import os import sys import m5 @@ -66,6 +66,7 @@ if not args.numcpus: print("Specify the number of cpus with -n") sys.exit(1) + # -------------------- # Define Splash2 Benchmarks # ==================== diff --git a/configs/topologies/BaseTopology.py b/configs/topologies/BaseTopology.py index cdcca3f7eb..2e5132927c 100644 --- a/configs/topologies/BaseTopology.py +++ b/configs/topologies/BaseTopology.py @@ -27,7 +27,7 @@ import m5 -class BaseTopology(object): +class BaseTopology: description = "BaseTopology" def __init__(self): diff --git a/configs/topologies/Crossbar.py b/configs/topologies/Crossbar.py index e0d220a0fd..f6d4e545ec 100644 --- a/configs/topologies/Crossbar.py +++ b/configs/topologies/Crossbar.py @@ -24,17 +24,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.objects import * - from topologies.BaseTopology import SimpleTopology +from m5.objects import * +from m5.params import * + class Crossbar(SimpleTopology): description = "Crossbar" def makeTopology(self, options, network, IntLink, ExtLink, Router): - # default values for link latency and router latency. # Can be over-ridden on a per link/router basis link_latency = options.link_latency # used by simple and garnet diff --git a/configs/topologies/CrossbarGarnet.py b/configs/topologies/CrossbarGarnet.py index 603e1dfd35..74d69e846f 100644 --- a/configs/topologies/CrossbarGarnet.py +++ b/configs/topologies/CrossbarGarnet.py @@ -24,11 +24,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.objects import * - from topologies.BaseTopology import SimpleTopology +from m5.objects import * +from m5.params import * + class CrossbarGarnet(SimpleTopology): description = "CrossbarGarnet" diff --git a/configs/topologies/CustomMesh.py b/configs/topologies/CustomMesh.py index 21fa2dcc13..f440e00887 100644 --- a/configs/topologies/CustomMesh.py +++ b/configs/topologies/CustomMesh.py @@ -36,11 +36,10 @@ import math -from m5.util import fatal -from m5.params import * -from m5.objects import * - from m5.defines import buildEnv +from m5.objects import * +from m5.params import * +from m5.util import fatal if buildEnv["PROTOCOL"] == "CHI": import ruby.CHI_config as CHI @@ -67,7 +66,6 @@ class CustomMesh(SimpleTopology): cross_links, cross_link_latency, ): - # East->West, West->East, North->South, South->North # XY routing weights link_weights = [1, 1, 2, 2] diff --git a/configs/topologies/MeshDirCorners_XY.py b/configs/topologies/MeshDirCorners_XY.py index 6faf340c5b..afab634bde 100644 --- a/configs/topologies/MeshDirCorners_XY.py +++ b/configs/topologies/MeshDirCorners_XY.py @@ -24,13 +24,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.objects import * - from common import FileSystemConfig - from topologies.BaseTopology import SimpleTopology +from m5.objects import * +from m5.params import * + # Creates a Mesh topology with 4 directories, one at each corner. # One L1 (and L2, depending on the protocol) are connected to each router. # XY routing is enforced (using link weights) to guarantee deadlock freedom. @@ -91,7 +90,7 @@ class MeshDirCorners_XY(SimpleTopology): # Connect each cache controller to the appropriate router ext_links = [] - for (i, n) in enumerate(cache_nodes): + for i, n in enumerate(cache_nodes): cntrl_level, router_id = divmod(i, num_routers) assert cntrl_level < caches_per_router ext_links.append( @@ -161,7 +160,7 @@ class MeshDirCorners_XY(SimpleTopology): link_count += 1 # Connect the dma nodes to router 0. These should only be DMA nodes. - for (i, node) in enumerate(dma_nodes): + for i, node in enumerate(dma_nodes): assert node.type == "DMA_Controller" ext_links.append( ExtLink( diff --git a/configs/topologies/Mesh_XY.py b/configs/topologies/Mesh_XY.py index 94cb770750..6a01793b12 100644 --- a/configs/topologies/Mesh_XY.py +++ b/configs/topologies/Mesh_XY.py @@ -25,13 +25,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.objects import * - from common import FileSystemConfig - from topologies.BaseTopology import SimpleTopology +from m5.objects import * +from m5.params import * + # Creates a generic Mesh assuming an equal number of cache # and directory controllers. # XY routing is enforced (using link weights) @@ -87,7 +86,7 @@ class Mesh_XY(SimpleTopology): # Connect each node to the appropriate router ext_links = [] - for (i, n) in enumerate(network_nodes): + for i, n in enumerate(network_nodes): cntrl_level, router_id = divmod(i, num_routers) assert cntrl_level < cntrls_per_router ext_links.append( @@ -102,7 +101,7 @@ class Mesh_XY(SimpleTopology): # Connect the remainding nodes to router 0. These should only be # DMA nodes. - for (i, node) in enumerate(remainder_nodes): + for i, node in enumerate(remainder_nodes): assert node.type == "DMA_Controller" assert i < remainder ext_links.append( diff --git a/configs/topologies/Mesh_westfirst.py b/configs/topologies/Mesh_westfirst.py index 663c31e2cf..a9ed5c145e 100644 --- a/configs/topologies/Mesh_westfirst.py +++ b/configs/topologies/Mesh_westfirst.py @@ -25,11 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.objects import * - from topologies.BaseTopology import SimpleTopology +from m5.objects import * +from m5.params import * + # Creates a generic Mesh assuming an equal number of cache # and directory controllers. # West-first routing is enforced (using link weights) @@ -90,7 +90,7 @@ class Mesh_westfirst(SimpleTopology): # Connect each node to the appropriate router ext_links = [] - for (i, n) in enumerate(network_nodes): + for i, n in enumerate(network_nodes): cntrl_level, router_id = divmod(i, num_routers) assert cntrl_level < cntrls_per_router ext_links.append( @@ -105,7 +105,7 @@ class Mesh_westfirst(SimpleTopology): # Connect the remainding nodes to router 0. These should only be # DMA nodes. - for (i, node) in enumerate(remainder_nodes): + for i, node in enumerate(remainder_nodes): assert node.type == "DMA_Controller" assert i < remainder ext_links.append( diff --git a/configs/topologies/Pt2Pt.py b/configs/topologies/Pt2Pt.py index 8d85b31f1e..1268751370 100644 --- a/configs/topologies/Pt2Pt.py +++ b/configs/topologies/Pt2Pt.py @@ -25,11 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.objects import * - from topologies.BaseTopology import SimpleTopology +from m5.objects import * +from m5.params import * + class Pt2Pt(SimpleTopology): description = "Pt2Pt" diff --git a/ext/drampower/src/CmdScheduler.h b/ext/drampower/src/CmdScheduler.h index 58efd279b1..1497304f54 100644 --- a/ext/drampower/src/CmdScheduler.h +++ b/ext/drampower/src/CmdScheduler.h @@ -84,8 +84,7 @@ class cmdScheduler { std::string name; physicalAddr PhysicalAddr; // sorting the commands according to their scheduling time. - struct commandItemSorter : public std::binary_function{ + struct commandItemSorter { bool operator()(const commandItem& lhs, const commandItem& rhs) const { diff --git a/ext/dramsys/.gitignore b/ext/dramsys/.gitignore new file mode 100644 index 0000000000..0193b6e8f3 --- /dev/null +++ b/ext/dramsys/.gitignore @@ -0,0 +1 @@ +DRAMSys diff --git a/ext/dramsys/CMakeLists.txt b/ext/dramsys/CMakeLists.txt new file mode 100644 index 0000000000..ada9369124 --- /dev/null +++ b/ext/dramsys/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (c) 2023 Fraunhofer IESE +# All rights reserved +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +cmake_minimum_required(VERSION 3.22.0) +project(DRAMSys) + +set(BUILD_SHARED_LIBS OFF) + +add_library(systemc INTERFACE) + +target_include_directories(systemc INTERFACE "${SCONS_SOURCE_DIR}/src/systemc/ext/systemc_home/include") + +add_library(SystemC::systemc ALIAS systemc) + +add_subdirectory(DRAMSys) diff --git a/ext/dramsys/README b/ext/dramsys/README index 477da52895..f7cac523b0 100644 --- a/ext/dramsys/README +++ b/ext/dramsys/README @@ -1,10 +1,16 @@ -Follow these steps to get DRAMSys as part of gem5 +Follow these steps to build DRAMSys as part of gem5 1. Go to ext/dramsys (this directory) -2. Clone DRAMSys: 'git clone --recursive git@github.com:tukl-msd/DRAMSys.git DRAMSys' -3. Change directory to DRAMSys: 'cd DRAMSys' -4. Checkout the correct commit: 'git checkout -b gem5 09f6dcbb91351e6ee7cadfc7bc8b29d97625db8f' +2. Clone DRAMSys: 'git clone https://github.com/tukl-msd/DRAMSys --branch v5.0 --depth 1 DRAMSys' + +The latest verified working version is v5.0, but later versions might work too. +gem5 will automatically pick up DRAMSys as an external module when it is rebuilt. If you wish to run a simulation using the gem5 processor cores, make sure to enable the storage mode in DRAMSys. This is done by setting the value of the "StoreMode" key to "Store" in the base configuration file. Those configuration file can be found in 'DRAMSys/library/resources/configs/simulator'. + +Currently, DRAMSys is only supported in conjunction with a cache. Running DRAMSys in Release mode without caches will silently fail! + +Note: DRAMSys requires cmake version >= 3.24. This is not available via apt on Ubuntu 20.04 or 22.04. +See `util/dockerfiles/ubuntu-20.04_all-dependencies/Dockerfile` for an example of how to install cmake 3.24. diff --git a/ext/dramsys/SConscript b/ext/dramsys/SConscript index d6ea27e0d1..bfe4ee5ec1 100644 --- a/ext/dramsys/SConscript +++ b/ext/dramsys/SConscript @@ -25,72 +25,68 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import os +import subprocess -Import('env') +from shutil import which -build_root = Dir('../..').abspath -src_root = Dir('DRAMSys/DRAMSys/library').srcnode().abspath +from gem5_scons import warning + +Import("env") + +build_root = Dir("../..").abspath +build_current = Dir(".").abspath +src_root = Dir(".").srcnode().abspath +scons_root = Dir("#").abspath # See if we got a cloned DRAMSys repo as a subdirectory and set the # HAVE_DRAMSys flag accordingly -if not os.path.exists(Dir('.').srcnode().abspath + '/DRAMSys'): - env['HAVE_DRAMSYS'] = False +if not os.path.exists(Dir(".").srcnode().abspath + "/DRAMSys"): + env["HAVE_DRAMSYS"] = False Return() -env['HAVE_DRAMSYS'] = True +# DRAMSys requires CMake to build but this is is not a dependency for +# gem5 outside of this DRAMSys integration. Therefore, we do not fail the +# entire gem5 build if CMake is not found. Instead we just skip the building of +# DRAMSys and print a warning. +if which("cmake") is None: + warning("The DRAMSys repo is present but CMake cannot be found. " + "DRAMSys will not be built.") + env["HAVE_DRAMSYS"] = False + Return() -dramsys_files = [] -dramsys_configuration_files = [] +env["HAVE_DRAMSYS"] = True -dramsys_files.extend(Glob("%s/*.cpp" % f"{src_root}/src/controller")) -for root, dirs, files in os.walk(f"{src_root}/src/controller", topdown=False): - for dir in dirs: - dramsys_files.extend(Glob("%s/*.cpp" % os.path.join(root, dir))) +subprocess.run( + [ + "cmake", + f"-S{src_root}", + f"-B{build_current}", + "-DCMAKE_BUILD_TYPE=Release", + f"-DSCONS_SOURCE_DIR:STRING={scons_root}", + "-DDRAMSYS_BUILD_CLI=OFF" + ], + check=True +) -dramsys_files.extend(Glob("%s/*.cpp" % f"{src_root}/src/simulation")) -for root, dirs, files in os.walk(f"{src_root}/src/simulation", topdown=False): - for dir in dirs: - dramsys_files.extend(Glob("%s/*.cpp" % os.path.join(root, dir))) +subprocess.run( + ["cmake", "--build", build_current], + check=True +) -dramsys_files.extend(Glob("%s/*.cpp" % f"{src_root}/src/configuration")) -for root, dirs, files in os.walk(f"{src_root}/src/configuration", topdown=False): - for dir in dirs: - dramsys_files.extend(Glob("%s/*.cpp" % os.path.join(root, dir))) +env.Append(LIBS="DRAMSys_libdramsys") +env.Append(LIBPATH=Dir("./DRAMSys/src/libdramsys").abspath) -dramsys_files.extend(Glob("%s/*.cpp" % f"{src_root}/src/error")) -dramsys_files.extend(Glob(f"{src_root}/src/error/ECC/Bit.cpp")) -dramsys_files.extend(Glob(f"{src_root}/src/error/ECC/ECC.cpp")) -dramsys_files.extend(Glob(f"{src_root}/src/error/ECC/Word.cpp")) +env.Append(LIBS="DRAMSys_Configuration") +env.Append(LIBPATH=Dir("./DRAMSys/src/configuration").abspath) -dramsys_files.extend(Glob("%s/*.cpp" % f"{src_root}/src/common")) -dramsys_files.extend(Glob("%s/*.cpp" % f"{src_root}/src/common/configuration")) -dramsys_files.extend(Glob("%s/*.cpp" % f"{src_root}/src/common/configuration/memspec")) -dramsys_files.extend(Glob("%s/*.c" % f"{src_root}/src/common/third_party/sqlite-amalgamation")) +env.Append(LIBS="sqlite3") +env.Append(LIBPATH=Dir("./DRAMSys/lib/sqlite3").abspath) -env.Prepend(CPPPATH=[ - src_root + "/src", - src_root + "/src/common/configuration", - src_root + "/src/common/third_party/nlohmann/include", -]) +env.Append(CPPPATH=src_root + "/DRAMSys/src/libdramsys") +env.Append(CPPPATH=src_root + "/DRAMSys/src/configuration") +env.Append(CPPPATH=src_root + "/DRAMSys/src/util") +env.Append(CPPPATH=src_root + "/DRAMSys/lib/nlohmann_json/include") -env.Prepend(CPPDEFINES=[("DRAMSysResourceDirectory", '\\"' + os.getcwd() + '/resources' + '\\"')]) env.Prepend(CPPDEFINES=[("SYSTEMC_VERSION", 20191203)]) - -dramsys = env.Clone() - -if '-Werror' in dramsys['CCFLAGS']: - dramsys['CCFLAGS'].remove('-Werror') - -dramsys.Prepend(CPPPATH=[ - src_root + "/src/common/third_party/sqlite-amalgamation", - build_root + "/systemc/ext" -]) - -dramsys.Prepend(CPPDEFINES=[("SQLITE_ENABLE_RTREE", "1")]) - -dramsys_configuration = env.Clone() - -dramsys.Library('dramsys', dramsys_files) - -env.Append(LIBS=['dramsys', 'dl']) -env.Append(LIBPATH=[Dir('.')]) +env.Prepend(CPPDEFINES=[("DRAMSYS_RESOURCE_DIR", + '\\"' + os.getcwd() + '/DRAMSys/configs' + '\\"')]) diff --git a/ext/gdbremote/signals.hh b/ext/gdbremote/signals.hh index 11835e6f5a..07c0064d39 100644 --- a/ext/gdbremote/signals.hh +++ b/ext/gdbremote/signals.hh @@ -168,12 +168,12 @@ namespace gem5{ INFO = 142, //information request unknown = 143, //unknown signal - EXC_BAD_ACCESS = 145, //could not access memory - EXC_BAD_INSTRUCTION = 146, //illegal instruction/operand - EXC_ARITHMETIC = 147, //arithmetic exception - EXC_EMULATION = 148, //emulation instruction - EXC_SOFTWARE = 149, //software generated exception - EXC_BREAKPOINT = 150, //breakpoint + GEM5_EXC_BAD_ACCESS = 145, //could not access memory + GEM5_EXC_BAD_INSTRUCTION = 146, //illegal instruction/operand + GEM5_EXC_ARITHMETIC = 147, //arithmetic exception + GEM5_EXC_EMULATION = 148, //emulation instruction + GEM5_EXC_SOFTWARE = 149, //software generated exception + GEM5_EXC_BREAKPOINT = 150, //breakpoint LIBRT = 151, //librt internal signal }; diff --git a/ext/magic_enum/SConscript b/ext/magic_enum/SConscript new file mode 100644 index 0000000000..9ba39bcaa9 --- /dev/null +++ b/ext/magic_enum/SConscript @@ -0,0 +1,37 @@ +# Copyright (c) 2022 Arteris, Inc. and its applicable licensors and affiliates. +# All rights reserved. This license is licensed under the Gem5 license. +# +# The license below extends only to copyright in the software and shall not be +# construed as granting a license to any other intellectual property including +# but not limited to intellectual property relating to a hardware +# implementation of the functionality of the software licensed hereunder. You +# may use the software subject to the license terms below provided that you +# ensure that this notice is replicated unmodified and in its entirety in all +# distributions of the software, modified or unmodified, in source code or in +# binary form. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer; redistributions in binary +# form must reproduce the above copyright notice, this list of conditions and +# the following disclaimer in the documentation and/or other materials provided +# with the distribution; neither the name of the copyright holders nor the +# names of its contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +Import('env') + +env.Append(CPPDEFINES=['MAGIC_ENUM_RANGE_MAX=0x100']) \ No newline at end of file diff --git a/ext/magic_enum/magic_enum.hh b/ext/magic_enum/magic_enum.hh new file mode 100644 index 0000000000..1763cc314e --- /dev/null +++ b/ext/magic_enum/magic_enum.hh @@ -0,0 +1,1462 @@ +// __ __ _ ______ _____ +// | \/ | (_) | ____| / ____|_ _ +// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_ +// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _| +// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_| +// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____| +// __/ | https://github.com/Neargye/magic_enum +// |___/ version 0.9.2 +// +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2019 - 2023 Daniil Goncharov . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef NEARGYE_MAGIC_ENUM_HPP +#define NEARGYE_MAGIC_ENUM_HPP + +#define MAGIC_ENUM_VERSION_MAJOR 0 +#define MAGIC_ENUM_VERSION_MINOR 9 +#define MAGIC_ENUM_VERSION_PATCH 2 + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(MAGIC_ENUM_CONFIG_FILE) +#include MAGIC_ENUM_CONFIG_FILE +#endif + +#if !defined(MAGIC_ENUM_USING_ALIAS_OPTIONAL) +#include +#endif +#if !defined(MAGIC_ENUM_USING_ALIAS_STRING) +#include +#endif +#if !defined(MAGIC_ENUM_USING_ALIAS_STRING_VIEW) +#include +#endif + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunknown-warning-option" +# pragma clang diagnostic ignored "-Wenum-constexpr-conversion" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // May be used uninitialized 'return {};'. +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 26495) // Variable 'static_str::chars_' is uninitialized. +# pragma warning(disable : 28020) // Arithmetic overflow: Using operator '-' on a 4 byte value and then casting the result to a 8 byte value. +# pragma warning(disable : 26451) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call. +# pragma warning(disable : 4514) // Unreferenced inline function has been removed. +#endif + +// Checks magic_enum compiler compatibility. +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1910 || defined(__RESHARPER__) +# undef MAGIC_ENUM_SUPPORTED +# define MAGIC_ENUM_SUPPORTED 1 +#endif + +// Checks magic_enum compiler aliases compatibility. +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1920 +# undef MAGIC_ENUM_SUPPORTED_ALIASES +# define MAGIC_ENUM_SUPPORTED_ALIASES 1 +#endif + +// Enum value must be greater or equals than MAGIC_ENUM_RANGE_MIN. By default MAGIC_ENUM_RANGE_MIN = -128. +// If need another min range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN. +#if !defined(MAGIC_ENUM_RANGE_MIN) +# define MAGIC_ENUM_RANGE_MIN -128 +#endif + +// Enum value must be less or equals than MAGIC_ENUM_RANGE_MAX. By default MAGIC_ENUM_RANGE_MAX = 128. +// If need another max range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MAX. +#if !defined(MAGIC_ENUM_RANGE_MAX) +# define MAGIC_ENUM_RANGE_MAX 0x100 +#endif + +// Improve ReSharper C++ intellisense performance with builtins, avoiding unnecessary template instantiations. +#if defined(__RESHARPER__) +# undef MAGIC_ENUM_GET_ENUM_NAME_BUILTIN +# undef MAGIC_ENUM_GET_TYPE_NAME_BUILTIN +# if __RESHARPER__ >= 20230100 +# define MAGIC_ENUM_GET_ENUM_NAME_BUILTIN(V) __rscpp_enumerator_name(V) +# define MAGIC_ENUM_GET_TYPE_NAME_BUILTIN(T) __rscpp_type_name() +# else +# define MAGIC_ENUM_GET_ENUM_NAME_BUILTIN(V) nullptr +# define MAGIC_ENUM_GET_TYPE_NAME_BUILTIN(T) nullptr +# endif +#endif + +namespace magic_enum { + +// If need another optional type, define the macro MAGIC_ENUM_USING_ALIAS_OPTIONAL. +#if defined(MAGIC_ENUM_USING_ALIAS_OPTIONAL) +MAGIC_ENUM_USING_ALIAS_OPTIONAL +#else +using std::optional; +#endif + +// If need another string_view type, define the macro MAGIC_ENUM_USING_ALIAS_STRING_VIEW. +#if defined(MAGIC_ENUM_USING_ALIAS_STRING_VIEW) +MAGIC_ENUM_USING_ALIAS_STRING_VIEW +#else +using std::string_view; +#endif + +// If need another string type, define the macro MAGIC_ENUM_USING_ALIAS_STRING. +#if defined(MAGIC_ENUM_USING_ALIAS_STRING) +MAGIC_ENUM_USING_ALIAS_STRING +#else +using std::string; +#endif + +using char_type = string_view::value_type; +static_assert(std::is_same_v, "magic_enum::customize requires same string_view::value_type and string::value_type"); +static_assert([] { + if constexpr (std::is_same_v) { + constexpr const char c[] = "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789|"; + constexpr const wchar_t wc[] = L"abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789|"; + static_assert(std::size(c) == std::size(wc), "magic_enum::customize identifier characters are multichars in wchar_t."); + + for (std::size_t i = 0; i < std::size(c); ++i) { + if (c[i] != wc[i]) { + return false; + } + } + } + return true; +} (), "magic_enum::customize wchar_t is not compatible with ASCII."); + +namespace customize { + +// Enum value must be in range [MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX]. By default MAGIC_ENUM_RANGE_MIN = -128, MAGIC_ENUM_RANGE_MAX = 128. +// If need another range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN and MAGIC_ENUM_RANGE_MAX. +// If need another range for specific enum type, add specialization enum_range for necessary enum type. +template +struct enum_range { + static_assert(std::is_enum_v, "magic_enum::customize::enum_range requires enum type."); + static constexpr int min = MAGIC_ENUM_RANGE_MIN; + static constexpr int max = MAGIC_ENUM_RANGE_MAX; + static_assert(max > min, "magic_enum::customize::enum_range requires max > min."); +}; + +static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN."); +static_assert((MAGIC_ENUM_RANGE_MAX - MAGIC_ENUM_RANGE_MIN) < (std::numeric_limits::max)(), "MAGIC_ENUM_RANGE must be less than UINT16_MAX."); + +namespace detail { + +enum class customize_tag { + default_tag, + invalid_tag, + custom_tag +}; + +} // namespace magic_enum::customize::detail + +class customize_t : public std::pair { + public: + constexpr customize_t(string_view srt) : std::pair{detail::customize_tag::custom_tag, srt} {} + constexpr customize_t(const char_type* srt) : customize_t{string_view{srt}} {} + constexpr customize_t(detail::customize_tag tag) : std::pair{tag, string_view{}} { + assert(tag != detail::customize_tag::custom_tag); + } +}; + +// Default customize. +inline constexpr auto default_tag = customize_t{detail::customize_tag::default_tag}; +// Invalid customize. +inline constexpr auto invalid_tag = customize_t{detail::customize_tag::invalid_tag}; + +// If need custom names for enum, add specialization enum_name for necessary enum type. +template +constexpr customize_t enum_name(E) noexcept { + return default_tag; +} + +// If need custom type name for enum, add specialization enum_type_name for necessary enum type. +template +constexpr customize_t enum_type_name() noexcept { + return default_tag; +} + +} // namespace magic_enum::customize + +namespace detail { + +template +struct supported +#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED || defined(MAGIC_ENUM_NO_CHECK_SUPPORT) + : std::true_type {}; +#else + : std::false_type {}; +#endif + +template , std::enable_if_t, int> = 0> +using enum_constant = std::integral_constant; + +template +inline constexpr bool always_false_v = false; + +template +struct has_is_flags : std::false_type {}; + +template +struct has_is_flags::is_flags)>> : std::bool_constant::is_flags)>>> {}; + +template +struct range_min : std::integral_constant {}; + +template +struct range_min::min)>> : std::integral_constant::min), customize::enum_range::min> {}; + +template +struct range_max : std::integral_constant {}; + +template +struct range_max::max)>> : std::integral_constant::max), customize::enum_range::max> {}; + +struct str_view { + const char* str_ = nullptr; + std::size_t size_ = 0; +}; + +template +class static_str { + public: + constexpr explicit static_str(str_view str) noexcept : static_str{str.str_, std::make_integer_sequence{}} { + assert(str.size_ == N); + } + + constexpr explicit static_str(string_view str) noexcept : static_str{str.data(), std::make_integer_sequence{}} { + assert(str.size() == N); + } + + constexpr const char_type* data() const noexcept { return chars_; } + + constexpr std::uint16_t size() const noexcept { return N; } + + constexpr operator string_view() const noexcept { return {data(), size()}; } + + private: + template + constexpr static_str(const char* str, std::integer_sequence) noexcept : chars_{static_cast(str[I])..., static_cast('\0')} {} + + template + constexpr static_str(string_view str, std::integer_sequence) noexcept : chars_{str[I]..., static_cast('\0')} {} + + char_type chars_[static_cast(N) + 1]; +}; + +template <> +class static_str<0> { + public: + constexpr explicit static_str() = default; + + constexpr explicit static_str(str_view) noexcept {} + + constexpr explicit static_str(string_view) noexcept {} + + constexpr const char_type* data() const noexcept { return nullptr; } + + constexpr std::uint16_t size() const noexcept { return 0; } + + constexpr operator string_view() const noexcept { return {}; } +}; + +template > +class case_insensitive { + static constexpr char_type to_lower(char_type c) noexcept { + return (c >= static_cast('A') && c <= static_cast('Z')) ? static_cast(c + (static_cast('a') - static_cast('A'))) : c; + } + + public: + template + constexpr auto operator()(L lhs,R rhs) const noexcept -> std::enable_if_t, char_type> && std::is_same_v, char_type>, bool> { + return Op{}(to_lower(lhs), to_lower(rhs)); + } +}; + +constexpr std::size_t find(string_view str, char_type c) noexcept { +#if defined(__clang__) && __clang_major__ < 9 && defined(__GLIBCXX__) || defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__) +// https://stackoverflow.com/questions/56484834/constexpr-stdstring-viewfind-last-of-doesnt-work-on-clang-8-with-libstdc +// https://developercommunity.visualstudio.com/content/problem/360432/vs20178-regression-c-failed-in-test.html + constexpr bool workaround = true; +#else + constexpr bool workaround = false; +#endif + + if constexpr (workaround) { + for (std::size_t i = 0; i < str.size(); ++i) { + if (str[i] == c) { + return i; + } + } + + return string_view::npos; + } else { + return str.find(c); + } +} + +template +constexpr bool is_default_predicate() noexcept { + return std::is_same_v, std::equal_to> || + std::is_same_v, std::equal_to<>>; +} + +template +constexpr bool is_nothrow_invocable() { + return is_default_predicate() || + std::is_nothrow_invocable_r_v; +} + +template +constexpr bool cmp_equal(string_view lhs, string_view rhs, [[maybe_unused]] BinaryPredicate&& p) noexcept(is_nothrow_invocable()) { +#if defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__) + // https://developercommunity.visualstudio.com/content/problem/360432/vs20178-regression-c-failed-in-test.html + // https://developercommunity.visualstudio.com/content/problem/232218/c-constexpr-string-view.html + constexpr bool workaround = true; +#else + constexpr bool workaround = false; +#endif + + if constexpr (!is_default_predicate() || workaround) { + if (lhs.size() != rhs.size()) { + return false; + } + + const auto size = lhs.size(); + for (std::size_t i = 0; i < size; ++i) { + if (!p(lhs[i], rhs[i])) { + return false; + } + } + + return true; + } else { + return lhs == rhs; + } +} + +template +constexpr bool cmp_less(L lhs, R rhs) noexcept { + static_assert(std::is_integral_v && std::is_integral_v, "magic_enum::detail::cmp_less requires integral type."); + + if constexpr (std::is_signed_v == std::is_signed_v) { + // If same signedness (both signed or both unsigned). + return lhs < rhs; + } else if constexpr (std::is_same_v) { // bool special case + return static_cast(lhs) < rhs; + } else if constexpr (std::is_same_v) { // bool special case + return lhs < static_cast(rhs); + } else if constexpr (std::is_signed_v) { + // If 'right' is negative, then result is 'false', otherwise cast & compare. + return rhs > 0 && lhs < static_cast>(rhs); + } else { + // If 'left' is negative, then result is 'true', otherwise cast & compare. + return lhs < 0 || static_cast>(lhs) < rhs; + } +} + +template +constexpr I log2(I value) noexcept { + static_assert(std::is_integral_v, "magic_enum::detail::log2 requires integral type."); + + if constexpr (std::is_same_v) { // bool special case + return assert(false), value; + } else { + auto ret = I{0}; + for (; value > I{1}; value >>= I{1}, ++ret) {} + + return ret; + } +} + +#if defined(__cpp_lib_array_constexpr) && __cpp_lib_array_constexpr >= 201603L +# define MAGIC_ENUM_ARRAY_CONSTEXPR 1 +#else +template +constexpr std::array, N> to_array(T (&a)[N], std::index_sequence) noexcept { + return {{a[I]...}}; +} +#endif + +template +inline constexpr bool is_enum_v = std::is_enum_v && std::is_same_v>; + +template +constexpr auto n() noexcept { + static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); + + if constexpr (supported::value) { +#if defined(MAGIC_ENUM_GET_TYPE_NAME_BUILTIN) + constexpr auto name_ptr = MAGIC_ENUM_GET_TYPE_NAME_BUILTIN(E); + constexpr auto name = name_ptr ? str_view{name_ptr, std::char_traits::length(name_ptr)} : str_view{}; +#elif defined(__clang__) + auto name = str_view{__PRETTY_FUNCTION__ + 34, sizeof(__PRETTY_FUNCTION__) - 36}; +#elif defined(__GNUC__) + auto name = str_view{__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1}; + if (name.str_[name.size_ - 1] == ']') { + name.size_ -= 50; + name.str_ += 49; + } else { + name.size_ -= 40; + name.str_ += 37; + } +#elif defined(_MSC_VER) + auto name = str_view{__FUNCSIG__ + 40, sizeof(__FUNCSIG__) - 57}; +#else + auto name = str_view{}; +#endif + return name; + } else { + return str_view{}; // Unsupported compiler or Invalid customize. + } +} + +template +constexpr auto type_name() noexcept { + [[maybe_unused]] constexpr auto custom = customize::enum_type_name(); + static_assert(std::is_same_v, customize::customize_t>, "magic_enum::customize requires customize_t type."); + if constexpr (custom.first == customize::detail::customize_tag::custom_tag) { + constexpr auto name = custom.second; + static_assert(!name.empty(), "magic_enum::customize requires not empty string."); + return static_str{name}; + } else if constexpr (custom.first == customize::detail::customize_tag::invalid_tag) { + return static_str<0>{}; + } else if constexpr (custom.first == customize::detail::customize_tag::default_tag) { + constexpr auto name = n(); + return static_str{name}; + } else { + static_assert(always_false_v, "magic_enum::customize invalid."); + } +} + +template +inline constexpr auto type_name_v = type_name(); + +template +constexpr auto n() noexcept { + static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); + + if constexpr (supported::value) { +#if defined(MAGIC_ENUM_GET_ENUM_NAME_BUILTIN) + constexpr auto name_ptr = MAGIC_ENUM_GET_ENUM_NAME_BUILTIN(V); + auto name = name_ptr ? str_view{name_ptr, std::char_traits::length(name_ptr)} : str_view{}; +#elif defined(__clang__) + auto name = str_view{__PRETTY_FUNCTION__ + 34, sizeof(__PRETTY_FUNCTION__) - 36}; + if (name.size_ > 22 && name.str_[0] == '(' && name.str_[1] == 'a' && name.str_[10] == ' ' && name.str_[22] == ':') { + name.size_ -= 23; + name.str_ += 23; + } + if (name.str_[0] == '(' || name.str_[0] == '-' || (name.str_[0] >= '0' && name.str_[0] <= '9')) { + name = str_view{}; + } +#elif defined(__GNUC__) + auto name = str_view{__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1}; + if (name.str_[name.size_ - 1] == ']') { + name.size_ -= 55; + name.str_ += 54; + } else { + name.size_ -= 40; + name.str_ += 37; + } + if (name.str_[0] == '(') { + name = str_view{}; + } +#elif defined(_MSC_VER) + str_view name; + if ((__FUNCSIG__[5] == '_' && __FUNCSIG__[35] != '(') || (__FUNCSIG__[5] == 'c' && __FUNCSIG__[41] != '(')) { + name = str_view{__FUNCSIG__ + 35, sizeof(__FUNCSIG__) - 52}; + } +#else + auto name = str_view{}; +#endif + std::size_t p = 0; + for (std::size_t i = name.size_; i > 0; --i) { + if (name.str_[i] == ':') { + p = i + 1; + break; + } + } + if (p > 0) { + name.size_ -= p; + name.str_ += p; + } + return name; + } else { + return str_view{}; // Unsupported compiler or Invalid customize. + } +} + +#if defined(_MSC_VER) && !defined(__clang__) && _MSC_VER < 1920 +# define MAGIC_ENUM_VS_2017_WORKAROUND 1 +#endif + +#if defined(MAGIC_ENUM_VS_2017_WORKAROUND) +template +constexpr auto n() noexcept { + static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); + + str_view name = str_view{__FUNCSIG__, sizeof(__FUNCSIG__) - 17}; + std::size_t p = 0; + for (std::size_t i = name.size_; i > 0; --i) { + if (name.str_[i] == ',' || name.str_[i] == ':') { + p = i + 1; + break; + } + } + if (p > 0) { + name.size_ -= p; + name.str_ += p; + } + if (name.str_[0] == '(' || name.str_[0] == '-' || (name.str_[0] >= '0' && name.str_[0] <= '9')) { + name = str_view{}; + } + return name; +} +#endif + +template +constexpr auto enum_name() noexcept { + [[maybe_unused]] constexpr auto custom = customize::enum_name(V); + static_assert(std::is_same_v, customize::customize_t>, "magic_enum::customize requires customize_t type."); + if constexpr (custom.first == customize::detail::customize_tag::custom_tag) { + constexpr auto name = custom.second; + static_assert(!name.empty(), "magic_enum::customize requires not empty string."); + return static_str{name}; + } else if constexpr (custom.first == customize::detail::customize_tag::invalid_tag) { + return static_str<0>{}; + } else if constexpr (custom.first == customize::detail::customize_tag::default_tag) { +#if defined(MAGIC_ENUM_VS_2017_WORKAROUND) + constexpr auto name = n(); +#else + constexpr auto name = n(); +#endif + return static_str{name}; + } else { + static_assert(always_false_v, "magic_enum::customize invalid."); + } +} + +template +inline constexpr auto enum_name_v = enum_name(); + +template +constexpr bool is_valid() noexcept { +#if defined(__clang__) && __clang_major__ >= 16 + // https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307 + constexpr E v = __builtin_bit_cast(E, V); +#else + constexpr E v = static_cast(V); +#endif + [[maybe_unused]] constexpr auto custom = customize::enum_name(v); + static_assert(std::is_same_v, customize::customize_t>, "magic_enum::customize requires customize_t type."); + if constexpr (custom.first == customize::detail::customize_tag::custom_tag) { + constexpr auto name = custom.second; + static_assert(!name.empty(), "magic_enum::customize requires not empty string."); + return name.size() != 0; + } else if constexpr (custom.first == customize::detail::customize_tag::default_tag) { +#if defined(MAGIC_ENUM_VS_2017_WORKAROUND) + return n().size_ != 0; +#else + return n().size_ != 0; +#endif + } else { + return false; + } +} + +enum class enum_subtype { + common, + flags +}; + +template > +constexpr U ualue(std::size_t i) noexcept { + if constexpr (std::is_same_v) { // bool special case + static_assert(O == 0, "magic_enum::detail::ualue requires valid offset."); + + return static_cast(i); + } else if constexpr (S == enum_subtype::flags) { + return static_cast(U{1} << static_cast(static_cast(i) + O)); + } else { + return static_cast(static_cast(i) + O); + } +} + +template > +constexpr E value(std::size_t i) noexcept { + return static_cast(ualue(i)); +} + +template > +constexpr int reflected_min() noexcept { + if constexpr (S == enum_subtype::flags) { + return 0; + } else { + constexpr auto lhs = range_min::value; + constexpr auto rhs = (std::numeric_limits::min)(); + + if constexpr (cmp_less(rhs, lhs)) { + return lhs; + } else { + return rhs; + } + } +} + +template > +constexpr int reflected_max() noexcept { + if constexpr (S == enum_subtype::flags) { + return std::numeric_limits::digits - 1; + } else { + constexpr auto lhs = range_max::value; + constexpr auto rhs = (std::numeric_limits::max)(); + + if constexpr (cmp_less(lhs, rhs)) { + return lhs; + } else { + return rhs; + } + } +} + +#define MAGIC_ENUM_FOR_EACH_256(T) \ + T( 0)T( 1)T( 2)T( 3)T( 4)T( 5)T( 6)T( 7)T( 8)T( 9)T( 10)T( 11)T( 12)T( 13)T( 14)T( 15)T( 16)T( 17)T( 18)T( 19)T( 20)T( 21)T( 22)T( 23)T( 24)T( 25)T( 26)T( 27)T( 28)T( 29)T( 30)T( 31) \ + T( 32)T( 33)T( 34)T( 35)T( 36)T( 37)T( 38)T( 39)T( 40)T( 41)T( 42)T( 43)T( 44)T( 45)T( 46)T( 47)T( 48)T( 49)T( 50)T( 51)T( 52)T( 53)T( 54)T( 55)T( 56)T( 57)T( 58)T( 59)T( 60)T( 61)T( 62)T( 63) \ + T( 64)T( 65)T( 66)T( 67)T( 68)T( 69)T( 70)T( 71)T( 72)T( 73)T( 74)T( 75)T( 76)T( 77)T( 78)T( 79)T( 80)T( 81)T( 82)T( 83)T( 84)T( 85)T( 86)T( 87)T( 88)T( 89)T( 90)T( 91)T( 92)T( 93)T( 94)T( 95) \ + T( 96)T( 97)T( 98)T( 99)T(100)T(101)T(102)T(103)T(104)T(105)T(106)T(107)T(108)T(109)T(110)T(111)T(112)T(113)T(114)T(115)T(116)T(117)T(118)T(119)T(120)T(121)T(122)T(123)T(124)T(125)T(126)T(127) \ + T(128)T(129)T(130)T(131)T(132)T(133)T(134)T(135)T(136)T(137)T(138)T(139)T(140)T(141)T(142)T(143)T(144)T(145)T(146)T(147)T(148)T(149)T(150)T(151)T(152)T(153)T(154)T(155)T(156)T(157)T(158)T(159) \ + T(160)T(161)T(162)T(163)T(164)T(165)T(166)T(167)T(168)T(169)T(170)T(171)T(172)T(173)T(174)T(175)T(176)T(177)T(178)T(179)T(180)T(181)T(182)T(183)T(184)T(185)T(186)T(187)T(188)T(189)T(190)T(191) \ + T(192)T(193)T(194)T(195)T(196)T(197)T(198)T(199)T(200)T(201)T(202)T(203)T(204)T(205)T(206)T(207)T(208)T(209)T(210)T(211)T(212)T(213)T(214)T(215)T(216)T(217)T(218)T(219)T(220)T(221)T(222)T(223) \ + T(224)T(225)T(226)T(227)T(228)T(229)T(230)T(231)T(232)T(233)T(234)T(235)T(236)T(237)T(238)T(239)T(240)T(241)T(242)T(243)T(244)T(245)T(246)T(247)T(248)T(249)T(250)T(251)T(252)T(253)T(254)T(255) + +template +constexpr void valid_count(bool* valid, std::size_t& count) noexcept { +#define MAGIC_ENUM_V(O) \ + if constexpr ((I + O) < Size) { \ + if constexpr (is_valid(I + O)>()) { \ + valid[I + O] = true; \ + ++count; \ + } \ + } + + MAGIC_ENUM_FOR_EACH_256(MAGIC_ENUM_V); + + if constexpr ((I + 256) < Size) { + valid_count(valid, count); + } +#undef MAGIC_ENUM_V +} + +template +struct valid_count_t { + std::size_t count = 0; + bool valid[N] = {}; +}; + +template +constexpr auto valid_count() noexcept { + valid_count_t vc; + valid_count(vc.valid, vc.count); + return vc; +} + +template +constexpr auto values() noexcept { + constexpr auto vc = valid_count(); + + if constexpr (vc.count > 0) { +#if defined(MAGIC_ENUM_ARRAY_CONSTEXPR) + std::array values = {}; +#else + E values[vc.count] = {}; +#endif + for (std::size_t i = 0, v = 0; v < vc.count; ++i) { + if (vc.valid[i]) { + values[v++] = value(i); + } + } +#if defined(MAGIC_ENUM_ARRAY_CONSTEXPR) + return values; +#else + return to_array(values, std::make_index_sequence{}); +#endif + } else { + return std::array{}; + } +} + +template > +constexpr auto values() noexcept { + constexpr auto min = reflected_min(); + constexpr auto max = reflected_max(); + constexpr auto range_size = max - min + 1; + static_assert(range_size > 0, "magic_enum::enum_range requires valid size."); + static_assert(range_size < (std::numeric_limits::max)(), "magic_enum::enum_range requires valid size."); + + return values(); +} + +template > +constexpr enum_subtype subtype(std::true_type) noexcept { + if constexpr (std::is_same_v) { // bool special case + return enum_subtype::common; + } else if constexpr (has_is_flags::value) { + return customize::enum_range::is_flags ? enum_subtype::flags : enum_subtype::common; + } else { +#if defined(MAGIC_ENUM_AUTO_IS_FLAGS) + constexpr auto flags_values = values(); + constexpr auto default_values = values(); + if (flags_values.size() == 0 || default_values.size() > flags_values.size()) { + return enum_subtype::common; + } + for (std::size_t i = 0; i < default_values.size(); ++i) { + const auto v = static_cast(default_values[i]); + if (v != 0 && (v & (v - 1)) != 0) { + return enum_subtype::common; + } + } + return enum_subtype::flags; +#else + return enum_subtype::common; +#endif + } +} + +template +constexpr enum_subtype subtype(std::false_type) noexcept { + // For non-enum type return default common subtype. + return enum_subtype::common; +} + +template > +inline constexpr auto subtype_v = subtype(std::is_enum{}); + +template +inline constexpr auto values_v = values(); + +template > +using values_t = decltype((values_v)); + +template +inline constexpr auto count_v = values_v.size(); + +template > +inline constexpr auto min_v = (count_v > 0) ? static_cast(values_v.front()) : U{0}; + +template > +inline constexpr auto max_v = (count_v > 0) ? static_cast(values_v.back()) : U{0}; + +template +constexpr auto names(std::index_sequence) noexcept { + return std::array{{enum_name_v[I]>...}}; +} + +template +inline constexpr auto names_v = names(std::make_index_sequence>{}); + +template > +using names_t = decltype((names_v)); + +template +constexpr auto entries(std::index_sequence) noexcept { + return std::array, sizeof...(I)>{{{values_v[I], enum_name_v[I]>}...}}; +} + +template +inline constexpr auto entries_v = entries(std::make_index_sequence>{}); + +template > +using entries_t = decltype((entries_v)); + +template > +constexpr bool is_sparse() noexcept { + if constexpr (count_v == 0) { + return false; + } else if constexpr (std::is_same_v) { // bool special case + return false; + } else { + constexpr auto max = (S == enum_subtype::flags) ? log2(max_v) : max_v; + constexpr auto min = (S == enum_subtype::flags) ? log2(min_v) : min_v; + constexpr auto range_size = max - min + 1; + + return range_size != count_v; + } +} + +template > +inline constexpr bool is_sparse_v = is_sparse(); + +template > +constexpr U values_ors() noexcept { + static_assert(S == enum_subtype::flags, "magic_enum::detail::values_ors requires valid subtype."); + + auto ors = U{0}; + for (std::size_t i = 0; i < count_v; ++i) { + ors |= static_cast(values_v[i]); + } + + return ors; +} + +template +struct enable_if_enum {}; + +template +struct enable_if_enum { + using type = R; + static_assert(supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); +}; + +template , typename D = std::decay_t> +using enable_if_t = typename enable_if_enum && std::is_invocable_r_v, R>::type; + +template >, int> = 0> +using enum_concept = T; + +template > +struct is_scoped_enum : std::false_type {}; + +template +struct is_scoped_enum : std::bool_constant>> {}; + +template > +struct is_unscoped_enum : std::false_type {}; + +template +struct is_unscoped_enum : std::bool_constant>> {}; + +template >> +struct underlying_type {}; + +template +struct underlying_type : std::underlying_type> {}; + +#if defined(MAGIC_ENUM_ENABLE_HASH) + +template +inline constexpr bool has_hash = true; + +template +struct constexpr_hash_t; + +template +struct constexpr_hash_t>> { + constexpr auto operator()(Value value) const noexcept { + using U = typename underlying_type::type; + if constexpr (std::is_same_v) { // bool special case + return static_cast(value); + } else { + return static_cast(value); + } + } + using secondary_hash = constexpr_hash_t; +}; + +template +struct constexpr_hash_t>> { + static constexpr std::uint32_t crc_table[256] { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, + 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, + 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, + 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, + 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, + 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, + 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, + 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, + 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, + 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, + 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, + 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, + 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, + 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, + 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, + 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, + 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, + 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, + 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, + 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, + 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, + 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, + 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, + 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, + 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, + 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL + }; + constexpr std::uint32_t operator()(string_view value) const noexcept { + auto crc = static_cast(0xffffffffL); + for (const auto c : value) { + crc = (crc >> 8) ^ crc_table[(crc ^ static_cast(c)) & 0xff]; + } + return crc ^ 0xffffffffL; + } + + struct secondary_hash { + constexpr std::uint32_t operator()(string_view value) const noexcept { + auto acc = static_cast(2166136261ULL); + for (const auto c : value) { + acc = ((acc ^ static_cast(c)) * static_cast(16777619ULL)) & (std::numeric_limits::max)(); + } + return static_cast(acc); + } + }; +}; + +template +inline constexpr Hash hash_v{}; + +template +constexpr auto calculate_cases(std::size_t Page) noexcept { + constexpr std::array values = *GlobValues; + constexpr std::size_t size = values.size(); + + using switch_t = std::invoke_result_t; + static_assert(std::is_integral_v && !std::is_same_v); + const std::size_t values_to = (std::min)(static_cast(256), size - Page); + + std::array result{}; + auto fill = result.begin(); + { + auto first = values.begin() + static_cast(Page); + auto last = values.begin() + static_cast(Page + values_to); + while (first != last) { + *fill++ = hash_v(*first++); + } + } + + // dead cases, try to avoid case collisions + for (switch_t last_value = result[values_to - 1]; fill != result.end() && last_value != (std::numeric_limits::max)(); *fill++ = ++last_value) { + } + + { + auto it = result.begin(); + auto last_value = (std::numeric_limits::min)(); + for (; fill != result.end(); *fill++ = last_value++) { + while (last_value == *it) { + ++last_value, ++it; + } + } + } + + return result; +} + +template +constexpr R invoke_r(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_r_v) { + if constexpr (std::is_void_v) { + std::forward(f)(std::forward(args)...); + } else { + return static_cast(std::forward(f)(std::forward(args)...)); + } +} + +enum class case_call_t { + index, + value +}; + +template +inline constexpr auto default_result_type_lambda = []() noexcept(std::is_nothrow_default_constructible_v) { return T{}; }; + +template <> +inline constexpr auto default_result_type_lambda = []() noexcept {}; + +template +constexpr bool has_duplicate() noexcept { + using value_t = std::decay_t; + using hash_value_t = std::invoke_result_t; + std::arraysize()> hashes{}; + std::size_t size = 0; + for (auto elem : *Arr) { + hashes[size] = hash_v(elem); + for (auto i = size++; i > 0; --i) { + if (hashes[i] < hashes[i - 1]) { + auto tmp = hashes[i]; + hashes[i] = hashes[i - 1]; + hashes[i - 1] = tmp; + } else if (hashes[i] == hashes[i - 1]) { + return false; + } else { + break; + } + } + } + return true; +} + +#define MAGIC_ENUM_CASE(val) \ + case cases[val]: \ + if constexpr ((val) + Page < size) { \ + if (!pred(values[val + Page], searched)) { \ + break; \ + } \ + if constexpr (CallValue == case_call_t::index) { \ + if constexpr (std::is_invocable_r_v>) { \ + return detail::invoke_r(std::forward(lambda), std::integral_constant{}); \ + } else if constexpr (std::is_invocable_v>) { \ + assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \ + } \ + } else if constexpr (CallValue == case_call_t::value) { \ + if constexpr (std::is_invocable_r_v>) { \ + return detail::invoke_r(std::forward(lambda), enum_constant{}); \ + } else if constexpr (std::is_invocable_r_v>) { \ + assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \ + } \ + } \ + break; \ + } else [[fallthrough]]; + +template ::value_type>, + typename BinaryPredicate = std::equal_to<>, + typename Lambda, + typename ResultGetterType> +constexpr decltype(auto) constexpr_switch( + Lambda&& lambda, + typename std::decay_t::value_type searched, + ResultGetterType&& def, + BinaryPredicate&& pred = {}) { + using result_t = std::invoke_result_t; + using hash_t = std::conditional_t(), Hash, typename Hash::secondary_hash>; + static_assert(has_duplicate(), "magic_enum::detail::constexpr_switch duplicated hash found, please report it: https://github.com/Neargye/magic_enum/issues."); + constexpr std::array values = *GlobValues; + constexpr std::size_t size = values.size(); + constexpr std::array cases = calculate_cases(Page); + + switch (hash_v(searched)) { + MAGIC_ENUM_FOR_EACH_256(MAGIC_ENUM_CASE) + default: + if constexpr (size > 256 + Page) { + return constexpr_switch(std::forward(lambda), searched, std::forward(def)); + } + break; + } + return def(); +} + +#undef MAGIC_ENUM_CASE + +#else +template +inline constexpr bool has_hash = false; +#endif + +template +constexpr auto for_each(F&& f, std::index_sequence) { + constexpr bool has_void_return = (std::is_void_v[I]>>> || ...); + constexpr bool all_same_return = (std::is_same_v[0]>>, std::invoke_result_t[I]>>> && ...); + + if constexpr (has_void_return) { + (f(enum_constant[I]>{}), ...); + } else if constexpr (all_same_return) { + return std::array{f(enum_constant[I]>{})...}; + } else { + return std::tuple{f(enum_constant[I]>{})...}; + } +} + +template +constexpr bool all_invocable(std::index_sequence) { + if constexpr (count_v == 0) { + return false; + } else { + return (std::is_invocable_v[I]>> && ...); + } +} + +} // namespace magic_enum::detail + +// Checks is magic_enum supported compiler. +inline constexpr bool is_magic_enum_supported = detail::supported::value; + +template +using Enum = detail::enum_concept; + +// Checks whether T is an Unscoped enumeration type. +// Provides the member constant value which is equal to true, if T is an [Unscoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration) type. Otherwise, value is equal to false. +template +struct is_unscoped_enum : detail::is_unscoped_enum {}; + +template +inline constexpr bool is_unscoped_enum_v = is_unscoped_enum::value; + +// Checks whether T is an Scoped enumeration type. +// Provides the member constant value which is equal to true, if T is an [Scoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations) type. Otherwise, value is equal to false. +template +struct is_scoped_enum : detail::is_scoped_enum {}; + +template +inline constexpr bool is_scoped_enum_v = is_scoped_enum::value; + +// If T is a complete enumeration type, provides a member typedef type that names the underlying type of T. +// Otherwise, if T is not an enumeration type, there is no member type. Otherwise (T is an incomplete enumeration type), the program is ill-formed. +template +struct underlying_type : detail::underlying_type {}; + +template +using underlying_type_t = typename underlying_type::type; + +template +using enum_constant = detail::enum_constant; + +// Returns type name of enum. +template +[[nodiscard]] constexpr auto enum_type_name() noexcept -> detail::enable_if_t { + constexpr string_view name = detail::type_name_v>; + static_assert(!name.empty(), "magic_enum::enum_type_name enum type does not have a name."); + + return name; +} + +// Returns number of enum values. +template > +[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_t { + return detail::count_v, S>; +} + +// Returns enum value at specified index. +// No bounds checking is performed: the behavior is undefined if index >= number of enum values. +template > +[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_t> { + using D = std::decay_t; + + if constexpr (detail::is_sparse_v) { + return assert((index < detail::count_v)), detail::values_v[index]; + } else { + constexpr auto min = (S == detail::enum_subtype::flags) ? detail::log2(detail::min_v) : detail::min_v; + + return assert((index < detail::count_v)), detail::value(index); + } +} + +// Returns enum value at specified index. +template > +[[nodiscard]] constexpr auto enum_value() noexcept -> detail::enable_if_t> { + using D = std::decay_t; + static_assert(I < detail::count_v, "magic_enum::enum_value out of range."); + + return enum_value(I); +} + +// Returns std::array with enum values, sorted by enum value. +template > +[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_t> { + return detail::values_v, S>; +} + +// Returns integer value from enum value. +template +[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_t> { + return static_cast>(value); +} + +// Returns underlying value from enum value. +template +[[nodiscard]] constexpr auto enum_underlying(E value) noexcept -> detail::enable_if_t> { + return static_cast>(value); +} + +// Obtains index in enum values from enum value. +// Returns optional with index. +template > +[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t> { + using D = std::decay_t; + using U = underlying_type_t; + + if constexpr (detail::count_v == 0) { + static_cast(value); + return {}; // Empty enum. + } else if constexpr (detail::is_sparse_v || (S == detail::enum_subtype::flags)) { +#if defined(MAGIC_ENUM_ENABLE_HASH) + return detail::constexpr_switch<&detail::values_v, detail::case_call_t::index>( + [](std::size_t i) { return optional{i}; }, + value, + detail::default_result_type_lambda>); +#else + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (enum_value(i) == value) { + return i; + } + } + return {}; // Invalid value or out of range. +#endif + } else { + const auto v = static_cast(value); + if (v >= detail::min_v && v <= detail::max_v) { + return static_cast(v - detail::min_v); + } + return {}; // Invalid value or out of range. + } +} + +// Obtains index in enum values from enum value. +// Returns optional with index. +template +[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t> { + using D = std::decay_t; + + return enum_index(value); +} + +// Obtains index in enum values from static storage enum variable. +template >> +[[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_t { + constexpr auto index = enum_index, S>(V); + static_assert(index, "magic_enum::enum_index enum value does not have a index."); + + return *index; +} + +// Returns name from static storage enum variable. +// This version is much lighter on the compile times and is not restricted to the enum_range limitation. +template +[[nodiscard]] constexpr auto enum_name() noexcept -> detail::enable_if_t { + constexpr string_view name = detail::enum_name_v, V>; + static_assert(!name.empty(), "magic_enum::enum_name enum value does not have a name."); + + return name; +} + +// Returns name from enum value. +// If enum value does not have name or value out of range, returns empty string. +template > +[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_t { + using D = std::decay_t; + + if (const auto i = enum_index(value)) { + return detail::names_v[*i]; + } + return {}; +} + +// Returns name from enum value. +// If enum value does not have name or value out of range, returns empty string. +template +[[nodiscard]] constexpr auto enum_name(E value) -> detail::enable_if_t { + using D = std::decay_t; + + return enum_name(value); +} + +// Returns std::array with names, sorted by enum value. +template > +[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_t> { + return detail::names_v, S>; +} + +// Returns std::array with pairs (value, name), sorted by enum value. +template > +[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_t> { + return detail::entries_v, S>; +} + +// Allows you to write magic_enum::enum_cast("bar", magic_enum::case_insensitive); +inline constexpr auto case_insensitive = detail::case_insensitive<>{}; + +// Obtains enum value from integer value. +// Returns optional with enum value. +template > +[[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_t>> { + using D = std::decay_t; + + if constexpr (detail::count_v == 0) { + static_cast(value); + return {}; // Empty enum. + } else { + if constexpr (detail::is_sparse_v || (S == detail::enum_subtype::flags)) { +#if defined(MAGIC_ENUM_ENABLE_HASH) + return detail::constexpr_switch<&detail::values_v, detail::case_call_t::value>( + [](D v) { return optional{v}; }, + static_cast(value), + detail::default_result_type_lambda>); +#else + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (value == static_cast>(enum_value(i))) { + return static_cast(value); + } + } + return {}; // Invalid value or out of range. +#endif + } else { + if (value >= detail::min_v && value <= detail::max_v) { + return static_cast(value); + } + return {}; // Invalid value or out of range. + } + } +} + +// Obtains enum value from name. +// Returns optional with enum value. +template , typename BinaryPredicate = std::equal_to<>> +[[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable()) -> detail::enable_if_t>, BinaryPredicate> { + using D = std::decay_t; + + if constexpr (detail::count_v == 0) { + static_cast(value); + return {}; // Empty enum. + } else { + if constexpr (detail::is_default_predicate() && detail::has_hash) { +#if defined(MAGIC_ENUM_ENABLE_HASH) + return detail::constexpr_switch<&detail::names_v, detail::case_call_t::index>( + [](std::size_t i) { return optional{detail::values_v[i]}; }, + value, + detail::default_result_type_lambda>, + [&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); }); +#else + static_assert(detail::always_false_v, "magic_enum::enum_cast invalid."); +#endif + } else { + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (detail::cmp_equal(value, detail::names_v[i], p)) { + return enum_value(i); + } + } + return {}; // Invalid value or out of range. + } + } +} + +// Checks whether enum contains value with such value. +template > +[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_t { + using D = std::decay_t; + using U = underlying_type_t; + + return static_cast(enum_cast(static_cast(value))); +} + +// Checks whether enum contains value with such value. +template +[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_t { + using D = std::decay_t; + using U = underlying_type_t; + + return static_cast(enum_cast(static_cast(value))); +} + +// Checks whether enum contains value with such integer value. +template > +[[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_t { + using D = std::decay_t; + + return static_cast(enum_cast(value)); +} + +// Checks whether enum contains enumerator with such name. +template , typename BinaryPredicate = std::equal_to<>> +[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable()) -> detail::enable_if_t { + using D = std::decay_t; + + return static_cast(enum_cast(value, std::move(p))); +} + +template , typename F, detail::enable_if_t = 0> +constexpr auto enum_for_each(F&& f) { + using D = std::decay_t; + static_assert(std::is_enum_v, "magic_enum::enum_for_each requires enum type."); + constexpr auto sep = std::make_index_sequence>{}; + + if constexpr (detail::all_invocable(sep)) { + return detail::for_each(std::forward(f), sep); + } else { + static_assert(detail::always_false_v, "magic_enum::enum_for_each requires invocable of all enum value."); + } +} + +template +inline constexpr auto as_flags = AsFlags ? detail::enum_subtype::flags : detail::enum_subtype::common; + +template +inline constexpr auto as_common = AsFlags ? detail::enum_subtype::common : detail::enum_subtype::flags; + +namespace bitwise_operators { + +template = 0> +constexpr E operator~(E rhs) noexcept { + return static_cast(~static_cast>(rhs)); +} + +template = 0> +constexpr E operator|(E lhs, E rhs) noexcept { + return static_cast(static_cast>(lhs) | static_cast>(rhs)); +} + +template = 0> +constexpr E operator&(E lhs, E rhs) noexcept { + return static_cast(static_cast>(lhs) & static_cast>(rhs)); +} + +template = 0> +constexpr E operator^(E lhs, E rhs) noexcept { + return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); +} + +template = 0> +constexpr E& operator|=(E& lhs, E rhs) noexcept { + return lhs = (lhs | rhs); +} + +template = 0> +constexpr E& operator&=(E& lhs, E rhs) noexcept { + return lhs = (lhs & rhs); +} + +template = 0> +constexpr E& operator^=(E& lhs, E rhs) noexcept { + return lhs = (lhs ^ rhs); +} + +} // namespace magic_enum::bitwise_operators + +} // namespace magic_enum + +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif + +#undef MAGIC_ENUM_GET_ENUM_NAME_BUILTIN +#undef MAGIC_ENUM_GET_TYPE_NAME_BUILTIN +#undef MAGIC_ENUM_VS_2017_WORKAROUND +#undef MAGIC_ENUM_ARRAY_CONSTEXPR +#undef MAGIC_ENUM_FOR_EACH_256 + +#endif // NEARGYE_MAGIC_ENUM_HPP diff --git a/ext/sst/INSTALL.md b/ext/sst/INSTALL.md index 91f92eb7ff..ba61996b32 100644 --- a/ext/sst/INSTALL.md +++ b/ext/sst/INSTALL.md @@ -1,8 +1,8 @@ # Installing SST -The links to download SST source code are available here -[http://sst-simulator.org/SSTPages/SSTMainDownloads/]. -This guide is using the most recent SST version (11.0.0) as of September 2021. +The links to download SST source code are available at +. +This guide is using the most recent SST version (13.0.0) as of September 2023. The following guide assumes `$SST_CORE_HOME` as the location where SST will be installed. @@ -11,14 +11,14 @@ installed. ### Downloading the SST-Core Source Code ```sh -wget https://github.com/sstsimulator/sst-core/releases/download/v11.1.0_Final/sstcore-11.1.0.tar.gz -tar xf sstcore-11.1.0.tar.gz +wget https://github.com/sstsimulator/sst-core/releases/download/v13.0.0_Final/sstcore-13.0.0.tar.gz +tar xzf sstcore-13.0.0.tar.gz ``` ### Installing SST-Core ```sh -cd sstcore-11.1.0 +cd sstcore-13.0.0 ./configure --prefix=$SST_CORE_HOME --with-python=/usr/bin/python3-config \ --disable-mpi # optional, used when MPI is not available. make all -j$(nproc) @@ -36,14 +36,14 @@ export PATH=$SST_CORE_HOME/bin:$PATH ### Downloading the SST-Elements Source Code ```sh -wget https://github.com/sstsimulator/sst-elements/releases/download/v11.1.0_Final/sstelements-11.1.0.tar.gz -tar xf sstelements-11.1.0.tar.gz +wget https://github.com/sstsimulator/sst-elements/releases/download/v13.0.0_Final/sstelements-13.0.0.tar.gz +tar xzf sstelements-13.0.0.tar.gz ``` ### Installing SST-Elements ```sh -cd sst-elements-library-11.1.0 +cd sst-elements-library-13.0.0 ./configure --prefix=$SST_CORE_HOME --with-python=/usr/bin/python3-config \ --with-sst-core=$SST_CORE_HOME make all -j$(nproc) @@ -58,24 +58,36 @@ echo "export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$SST_CORE_HOME/lib/pkgconfig/" >> ### Building gem5 library -At the root of gem5 folder, - +At the root of the gem5 folder, you need to compile gem5 as a library. This +varies dependent on which OS you are using. If you're using Linux, then +execute the following: ```sh scons build/RISCV/libgem5_opt.so -j $(nproc) --without-tcmalloc --duplicate-sources ``` +In case you're using Mac, then type the following: +```sh +scons build/RISCV/libgem5_opt.dylib -j $(nproc) --without-tcmalloc --duplicate-sources +``` -**Note:** `--without-tcmalloc` is required to avoid a conflict with SST's malloc. -`--duplicate-sources` is required as the compilation of SST depends on sources to be present in the "build" directory. +**Note:** +* `--without-tcmalloc` is required to avoid a conflict with SST's malloc. +* `--duplicate-sources` is required as the compilation of SST depends on sources to be present in the "build" directory. +* The Mac version was tested on a Macbook Air with M2 processor. ### Compiling the SST integration -At the root of gem5 folder, - +Go to the SST directory in the gem5 repo. ```sh cd ext/sst -make ``` - +Depending on your OS, you need to copy the correct `Makefile.xxx` file to +`Makefile`. +```sh +cp Makefile.xxx Makefile # linux or mac +make -j4 +``` +The make file is hardcoded to RISC-V. IN the case you wish to compile to ARM, +edit the Makefile or pass `ARCH=RISCV` to `ARCH=ARM` while compiling. ### Running an example simulation See `README.md` diff --git a/ext/sst/Makefile.linux b/ext/sst/Makefile.linux new file mode 100644 index 0000000000..f44ecd46d9 --- /dev/null +++ b/ext/sst/Makefile.linux @@ -0,0 +1,21 @@ +SST_VERSION=SST-13.0.0 # Name of the .pc file in lib/pkgconfig where SST is installed +GEM5_LIB=gem5_opt +ARCH=RISCV +OFLAG=3 + +LDFLAGS=-shared -fno-common ${shell pkg-config ${SST_VERSION} --libs} -L../../build/${ARCH}/ -Wl,-rpath ../../build/${ARCH} +CXXFLAGS=-std=c++17 -g -O${OFLAG} -fPIC ${shell pkg-config ${SST_VERSION} --cflags} ${shell python3-config --includes} -I../../build/${ARCH}/ -I../../ext/pybind11/include/ -I../../build/softfloat/ -I../../ext +CPPFLAGS+=-MMD -MP +SRC=$(wildcard *.cc) + +.PHONY: clean all + +all: libgem5.so + +libgem5.so: $(SRC:%.cc=%.o) + ${CXX} ${CPPFLAGS} ${LDFLAGS} $? -o $@ -l${GEM5_LIB} + +-include $(SRC:%.cc=%.d) + +clean: + ${RM} *.[do] libgem5.so diff --git a/ext/sst/Makefile.mac b/ext/sst/Makefile.mac new file mode 100644 index 0000000000..4a67570a44 --- /dev/null +++ b/ext/sst/Makefile.mac @@ -0,0 +1,21 @@ +SST_VERSION=SST-13.0.0 # Name of the .pc file in lib/pkgconfig where SST is installed +GEM5_LIB=gem5_opt +ARCH=RISCV +OFLAG=3 + +LDFLAGS=-shared -fno-common ${shell pkg-config ${SST_VERSION} --libs} -L../../build/${ARCH}/ -Wl,-rpath ../../build/${ARCH} +CXXFLAGS=-std=c++17 -g -O${OFLAG} -fPIC ${shell pkg-config ${SST_VERSION} --cflags} ${shell python3-config --includes} -I../../build/${ARCH}/ -I../../ext/pybind11/include/ -I../../build/softfloat/ -I../../ext +CPPFLAGS+=-MMD -MP +SRC=$(wildcard *.cc) + +.PHONY: clean all + +all: libgem5.dylib + +libgem5.dylib: $(SRC:%.cc=%.o) + ${CXX} ${CPPFLAGS} ${LDFLAGS} $? -o $@ -l${GEM5_LIB} + +-include $(SRC:%.cc=%.d) + +clean: + ${RM} *.[do] libgem5.dylib diff --git a/ext/sst/gem5.cc b/ext/sst/gem5.cc index 7af0eed7b7..3ea6127ecd 100644 --- a/ext/sst/gem5.cc +++ b/ext/sst/gem5.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2021 The Regents of the University of California +// Copyright (c) 2021-2023 The Regents of the University of California // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -70,7 +70,6 @@ #include #include -#include #include #include #include @@ -169,16 +168,29 @@ gem5Component::gem5Component(SST::ComponentId_t id, SST::Params& params): registerAsPrimaryComponent(); primaryComponentDoNotEndSim(); - systemPort = \ - loadUserSubComponent("system_port",0); - cachePort = \ - loadUserSubComponent("cache_port", 0); - - systemPort->setTimeConverter(timeConverter); - systemPort->setOutputStream(&(output)); - cachePort->setTimeConverter(timeConverter); - cachePort->setOutputStream(&(output)); - + // We need to add another parameter when invoking gem5 scripts from SST to + // keep a track of all the OutgoingBridges. This will allow to add or + // remove OutgoingBridges from gem5 configs without the need to recompile + // the ext/sst source everytime. + std::string ports = params.find("ports", ""); + if (ports.empty()) { + output.fatal( + CALL_INFO, -1, "Component %s must have a 'ports' parameter.\n", + getName().c_str() + ); + } + // Split the port names using the util method defined. + splitPortNames(ports); + for (int i = 0 ; i < sstPortCount ; i++) { + std::cout << sstPortNames[i] << std::endl; + sstPorts.push_back( + loadUserSubComponent(sstPortNames[i], 0) + ); + // If the name defined in the `ports` is incorrect, then the program + // will crash when calling `setTimeConverter`. + sstPorts[i]->setTimeConverter(timeConverter); + sstPorts[i]->setOutputStream(&(output)); + } } gem5Component::~gem5Component() @@ -192,13 +204,7 @@ gem5Component::init(unsigned phase) if (phase == 0) { initPython(args.size(), &args[0]); - - const std::vector m5_instantiate_commands = { - "import m5", - "m5.instantiate()" - }; - execPythonCommands(m5_instantiate_commands); - + // m5.instantiate() was moved to the gem5 script. // calling SimObject.startup() const std::vector simobject_setup_commands = { "import atexit", @@ -216,8 +222,9 @@ gem5Component::init(unsigned phase) // find the corresponding SimObject for each SSTResponderSubComponent gem5::Root* gem5_root = gem5::Root::root(); - systemPort->findCorrespondingSimObject(gem5_root); - cachePort->findCorrespondingSimObject(gem5_root); + for (auto &port : sstPorts) { + port->findCorrespondingSimObject(gem5_root); + } // initialize the gem5 event queue if (!(threadInitialized)) { @@ -230,17 +237,18 @@ gem5Component::init(unsigned phase) } } - - systemPort->init(phase); - cachePort->init(phase); + for (auto &port : sstPorts) { + port->init(phase); + } } void gem5Component::setup() { output.verbose(CALL_INFO, 1, 0, "Component is being setup.\n"); - systemPort->setup(); - cachePort->setup(); + for (auto &port : sstPorts) { + port->setup(); + } } void @@ -427,3 +435,16 @@ gem5Component::splitCommandArgs(std::string &cmd, std::vector &args) for (auto part: parsed_args) args.push_back(strdup(part.c_str())); } + +void +gem5Component::splitPortNames(std::string port_names) +{ + std::vector parsed_args = tokenizeString( + port_names, {'\\', ' ', '\'', '\"'} + ); + sstPortCount = 0; + for (auto part: parsed_args) { + sstPortNames.push_back(strdup(part.c_str())); + sstPortCount++; + } +} diff --git a/ext/sst/gem5.hh b/ext/sst/gem5.hh index 447c68c3b2..f9f00beabd 100644 --- a/ext/sst/gem5.hh +++ b/ext/sst/gem5.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2021 The Regents of the University of California +// Copyright (c) 2021-2023 The Regents of the University of California // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -79,9 +79,7 @@ #include #include -#include #include -#include #include @@ -108,15 +106,20 @@ class gem5Component: public SST::Component private: SST::Output output; - SSTResponderSubComponent* systemPort; - SSTResponderSubComponent* cachePort; uint64_t clocksProcessed; SST::TimeConverter* timeConverter; gem5::GlobalSimLoopExitEvent *simulateLimitEvent; std::vector args; + // We need a list of incoming port names so that we don't need to recompile + // everytime when we add a new OutgoingBridge from python. + std::vector sstPorts; + std::vector sstPortNames; + int sstPortCount; + void initPython(int argc, char **argv); void splitCommandArgs(std::string &cmd, std::vector &args); + void splitPortNames(std::string port_names); bool threadInitialized; @@ -139,6 +142,7 @@ class gem5Component: public SST::Component ) SST_ELI_DOCUMENT_SUBCOMPONENT_SLOTS( + // These are the generally expected ports. {"system_port", "Connection to gem5 system_port", "gem5.gem5Bridge"}, {"cache_port", "Connection to gem5 CPU", "gem5.gem5Bridge"} ) diff --git a/ext/sst/sst/arm_example.py b/ext/sst/sst/arm_example.py index cdee3ca40a..4bc111cb86 100644 --- a/ext/sst/sst/arm_example.py +++ b/ext/sst/sst/arm_example.py @@ -10,7 +10,7 @@ # unmodified and in its entirety in all distributions of the software, # modified or unmodified, in source code or in binary form. # -# Copyright (c) 2021 The Regents of the University of California +# Copyright (c) 2021-2023 The Regents of the University of California # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -46,9 +46,10 @@ cache_link_latency = "1ps" kernel = "vmlinux_exit.arm64" cpu_clock_rate = "3GHz" -# gem5 will send requests to physical addresses of range [0x80000000, inf) to memory -# currently, we do not subtract 0x80000000 from the request's address to get the "real" address -# so, the mem_size would always be 2GiB larger than the desired memory size +# gem5 will send requests to physical addresses of range [0x80000000, inf) to +# memory currently, we do not subtract 0x80000000 from the request's address to +# get the "real" address so, the mem_size would always be 2GiB larger than the +# desired memory size memory_size_gem5 = "4GiB" memory_size_sst = "16GiB" addr_range_end = UnitAlgebra(memory_size_sst).getRoundedValue() @@ -69,9 +70,22 @@ gem5_command = f" ../../configs/example/sst/arm_fs.py \ --cpu-clock-rate {cpu_clock_rate} \ --memory-size {memory_size_gem5}" +# We keep a track of all the memory ports that we have. +sst_ports = { + "system_port" : "system.system_outgoing_bridge", + "cache_port" : "system.memory_outgoing_bridge" +} + +# We need a list of ports. +port_list = [] +for port in sst_ports: + port_list.append(port) + cpu_params = { "frequency": cpu_clock_rate, "cmd": gem5_command, + "ports" : " ".join(port_list), + "debug_flags" : "" } gem5_node = sst.Component("gem5_node", "gem5.gem5Component") @@ -79,16 +93,16 @@ gem5_node.addParams(cpu_params) cache_bus = sst.Component("cache_bus", "memHierarchy.Bus") cache_bus.addParams( { "bus_frequency" : cpu_clock_rate } ) - -system_port = gem5_node.setSubComponent("system_port", "gem5.gem5Bridge", 0) # for initialization +# for initialization +system_port = gem5_node.setSubComponent("system_port", "gem5.gem5Bridge", 0) system_port.addParams({ - "response_receiver_name": "system.system_outgoing_bridge", + "response_receiver_name": sst_ports["system_port"], "mem_size": memory_size_sst }) - -cache_port = gem5_node.setSubComponent("cache_port", "gem5.gem5Bridge", 0) # SST -> gem5 +# SST -> gem5 +cache_port = gem5_node.setSubComponent("cache_port", "gem5.gem5Bridge", 0) cache_port.addParams({ - "response_receiver_name": "system.memory_outgoing_bridge", + "response_receiver_name": sst_ports["cache_port"], "mem_size": memory_size_sst }) @@ -98,11 +112,12 @@ l1_cache.addParams(l1_params) # Memory memctrl = sst.Component("memory", "memHierarchy.MemController") +# `addr_range_end` should be changed accordingly to memory_size_sst memctrl.addParams({ "debug" : "0", "clock" : "1GHz", "request_width" : "64", - "addr_range_end" : addr_range_end, # should be changed accordingly to memory_size_sst + "addr_range_end" : addr_range_end, }) memory = memctrl.setSubComponent("backend", "memHierarchy.simpleMem") memory.addParams({ diff --git a/ext/sst/sst/example.py b/ext/sst/sst/example.py index 76cf8ad24e..1c35bc3f83 100644 --- a/ext/sst/sst/example.py +++ b/ext/sst/sst/example.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021 The Regents of the University of California +# Copyright (c) 2021-2023 The Regents of the University of California # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -34,9 +34,10 @@ cache_link_latency = "1ps" bbl = "riscv-boot-exit-nodisk" cpu_clock_rate = "3GHz" -# gem5 will send requests to physical addresses of range [0x80000000, inf) to memory -# currently, we do not subtract 0x80000000 from the request's address to get the "real" address -# so, the mem_size would always be 2GiB larger than the desired memory size +# gem5 will send requests to physical addresses of range [0x80000000, inf) to +# memory currently, we do not subtract 0x80000000 from the request's address to +# get the "real" address so, the mem_size would always be 2GiB larger than the +# desired memory size memory_size_gem5 = "4GiB" memory_size_sst = "6GiB" addr_range_end = UnitAlgebra(memory_size_sst).getRoundedValue() @@ -52,10 +53,24 @@ l1_params = { "L1" : "1", } +# We keep a track of all the memory ports that we have. +sst_ports = { + "system_port" : "system.system_outgoing_bridge", + "cache_port" : "system.memory_outgoing_bridge" +} + +# We need a list of ports. +port_list = [] +for port in sst_ports: + port_list.append(port) + cpu_params = { "frequency": cpu_clock_rate, - "cmd": " ../../configs/example/sst/riscv_fs.py --cpu-clock-rate {} --memory-size {}".format(cpu_clock_rate, memory_size_gem5), - "debug_flags": "" + "cmd": " ../../configs/example/sst/riscv_fs.py" + + f" --cpu-clock-rate {cpu_clock_rate}" + + f" --memory-size {memory_size_gem5}", + "debug_flags": "", + "ports" : " ".join(port_list) } gem5_node = sst.Component("gem5_node", "gem5.gem5Component") @@ -64,11 +79,14 @@ gem5_node.addParams(cpu_params) cache_bus = sst.Component("cache_bus", "memHierarchy.Bus") cache_bus.addParams( { "bus_frequency" : cpu_clock_rate } ) -system_port = gem5_node.setSubComponent("system_port", "gem5.gem5Bridge", 0) # for initialization -system_port.addParams({ "response_receiver_name": "system.system_outgoing_bridge"}) # tell the SubComponent the name of the corresponding SimObject +# for initialization +system_port = gem5_node.setSubComponent(port_list[0], "gem5.gem5Bridge", 0) +# tell the SubComponent the name of the corresponding SimObject +system_port.addParams({ "response_receiver_name": sst_ports["system_port"]}) -cache_port = gem5_node.setSubComponent("cache_port", "gem5.gem5Bridge", 0) # SST -> gem5 -cache_port.addParams({ "response_receiver_name": "system.memory_outgoing_bridge"}) +# SST -> gem5 +cache_port = gem5_node.setSubComponent(port_list[1], "gem5.gem5Bridge", 0) +cache_port.addParams({ "response_receiver_name": sst_ports["cache_port"]}) # L1 cache l1_cache = sst.Component("l1_cache", "memHierarchy.Cache") @@ -76,11 +94,12 @@ l1_cache.addParams(l1_params) # Memory memctrl = sst.Component("memory", "memHierarchy.MemController") +# `addr_range_end` should be changed accordingly to memory_size_sst memctrl.addParams({ "debug" : "0", "clock" : "1GHz", "request_width" : "64", - "addr_range_end" : addr_range_end, # should be changed accordingly to memory_size_sst + "addr_range_end" : addr_range_end, }) memory = memctrl.setSubComponent("backend", "memHierarchy.simpleMem") memory.addParams({ diff --git a/ext/sst/sst_responder.hh b/ext/sst/sst_responder.hh index a89d311064..5f483be845 100644 --- a/ext/sst/sst_responder.hh +++ b/ext/sst/sst_responder.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2021 The Regents of the University of California +// Copyright (c) 2021-2023 The Regents of the University of California // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -35,9 +35,8 @@ #include #include -#include #include -#include +#include #include #include diff --git a/ext/sst/sst_responder_subcomponent.cc b/ext/sst/sst_responder_subcomponent.cc index 366f99aecf..8cd2c04628 100644 --- a/ext/sst/sst_responder_subcomponent.cc +++ b/ext/sst/sst_responder_subcomponent.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2021 The Regents of the University of California +// Copyright (c) 2021-2023 The Regents of the University of California // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -64,13 +64,12 @@ SSTResponderSubComponent::setTimeConverter(SST::TimeConverter* tc) // SHARE_PORTS means the interface can use our port as if it were its own // INSERT_STATS means the interface will inherit our statistic // configuration (e.g., if ours are enabled, the interface’s will be too) - memoryInterface = \ - loadAnonymousSubComponent( - "memHierarchy.memInterface", "memory", 0, - SST::ComponentInfo::SHARE_PORTS | SST::ComponentInfo::INSERT_STATS, - interface_params, timeConverter, - new SST::Interfaces::SimpleMem::Handler( - this, &SSTResponderSubComponent::portEventHandler) + memoryInterface = loadAnonymousSubComponent( + "memHierarchy.standardInterface", "memory", 0, + SST::ComponentInfo::SHARE_PORTS | SST::ComponentInfo::INSERT_STATS, + interface_params, timeConverter, + new SST::Interfaces::StandardMem::Handler( + this, &SSTResponderSubComponent::portEventHandler) ); assert(memoryInterface != NULL); } @@ -91,9 +90,9 @@ SSTResponderSubComponent::setResponseReceiver( bool SSTResponderSubComponent::handleTimingReq( - SST::Interfaces::SimpleMem::Request* request) + SST::Interfaces::StandardMem::Request* request) { - memoryInterface->sendRequest(request); + memoryInterface->send(request); return true; } @@ -104,12 +103,10 @@ SSTResponderSubComponent::init(unsigned phase) for (auto p: responseReceiver->getInitData()) { gem5::Addr addr = p.first; std::vector data = p.second; - SST::Interfaces::SimpleMem::Request* request = \ - new SST::Interfaces::SimpleMem::Request( - SST::Interfaces::SimpleMem::Request::Command::Write, addr, - data.size(), data - ); - memoryInterface->sendInitData(request); + SST::Interfaces::StandardMem::Request* request = \ + new SST::Interfaces::StandardMem::Write( + addr, data.size(), data); + memoryInterface->sendUntimedData(request); } } memoryInterface->init(phase); @@ -132,20 +129,24 @@ SSTResponderSubComponent::findCorrespondingSimObject(gem5::Root* gem5_root) void SSTResponderSubComponent::handleSwapReqResponse( - SST::Interfaces::SimpleMem::Request* request) + SST::Interfaces::StandardMem::Request* request) { // get the data, then, // 1. send a response to gem5 with the original data // 2. send a write to memory with atomic op applied - SST::Interfaces::SimpleMem::Request::id_t request_id = request->id; + SST::Interfaces::StandardMem::Request::id_t request_id = request->getID(); TPacketMap::iterator it = sstRequestIdToPacketMap.find(request_id); assert(it != sstRequestIdToPacketMap.end()); - std::vector data = request->data; + std::vector data = \ + dynamic_cast(request)->data; // step 1 gem5::PacketPtr pkt = it->second; - pkt->setData(request->data.data()); + pkt->setData( + dynamic_cast( + request)->data.data() + ); pkt->makeAtomicResponse(); pkt->headerDelay = pkt->payloadDelay = 0; if (blocked() || !responseReceiver->sendTimingResp(pkt)) @@ -153,27 +154,29 @@ SSTResponderSubComponent::handleSwapReqResponse( // step 2 (*(pkt->getAtomicOp()))(data.data()); // apply the atomic op - SST::Interfaces::SimpleMem::Request::Command cmd = \ - SST::Interfaces::SimpleMem::Request::Command::Write; - SST::Interfaces::SimpleMem::Addr addr = request->addr; + // This is a Write. Need to use the Write visitor class. But the original + // request is a read response. Therefore, we need to find the address and + // the data size and then call Write. + SST::Interfaces::StandardMem::Addr addr = \ + dynamic_cast(request)->pAddr; auto data_size = data.size(); - SST::Interfaces::SimpleMem::Request* write_request = \ - new SST::Interfaces::SimpleMem::Request( - cmd, addr, data_size, data - ); - write_request->setMemFlags( - SST::Interfaces::SimpleMem::Request::Flags::F_LOCKED); - memoryInterface->sendRequest(write_request); + // Create the Write request here. + SST::Interfaces::StandardMem::Request* write_request = \ + new SST::Interfaces::StandardMem::Write(addr, data_size, data); + // F_LOCKED flag in SimpleMem was changed to ReadLock and WriteUnlock + // visitor classes. This has to be addressed in the future. The boot test + // works without using ReadLock and WriteUnlock classes. + memoryInterface->send(write_request); delete request; } void SSTResponderSubComponent::portEventHandler( - SST::Interfaces::SimpleMem::Request* request) + SST::Interfaces::StandardMem::Request* request) { // Expect to handle an SST response - SST::Interfaces::SimpleMem::Request::id_t request_id = request->id; + SST::Interfaces::StandardMem::Request::id_t request_id = request->getID(); TPacketMap::iterator it = sstRequestIdToPacketMap.find(request_id); @@ -193,19 +196,27 @@ SSTResponderSubComponent::portEventHandler( Translator::inplaceSSTRequestToGem5PacketPtr(pkt, request); - if (blocked() || !(responseReceiver->sendTimingResp(pkt))) + if (blocked() || !(responseReceiver->sendTimingResp(pkt))) { responseQueue.push(pkt); - } else { // we can handle unexpected invalidates, but nothing else. - SST::Interfaces::SimpleMem::Request::Command cmd = request->cmd; - if (cmd == SST::Interfaces::SimpleMem::Request::Command::WriteResp) + } + } else { + // we can handle unexpected invalidates, but nothing else. + if (SST::Interfaces::StandardMem::Read* test = + dynamic_cast(request)) { return; - assert(cmd == SST::Interfaces::SimpleMem::Request::Command::Inv); - - // make Req/Pkt for Snoop/no response needed + } + else if (SST::Interfaces::StandardMem::WriteResp* test = + dynamic_cast( + request)) { + return; + } + // for Snoop/no response needed // presently no consideration for masterId, packet type, flags... gem5::RequestPtr req = std::make_shared( - request->addr, request->size, 0, 0 - ); + dynamic_cast( + request)->pAddr, + dynamic_cast( + request)->size, 0, 0); gem5::PacketPtr pkt = new gem5::Packet( req, gem5::MemCmd::InvalidateReq); diff --git a/ext/sst/sst_responder_subcomponent.hh b/ext/sst/sst_responder_subcomponent.hh index 51bc4f9318..ed9f09d6b8 100644 --- a/ext/sst/sst_responder_subcomponent.hh +++ b/ext/sst/sst_responder_subcomponent.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2021 The Regents of the University of California +// Copyright (c) 2021-2023 The Regents of the University of California // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -36,10 +36,8 @@ #include #include - -#include #include -#include +#include #include #include @@ -59,12 +57,12 @@ class SSTResponderSubComponent: public SST::SubComponent gem5::OutgoingRequestBridge* responseReceiver; gem5::SSTResponderInterface* sstResponder; - SST::Interfaces::SimpleMem* memoryInterface; + SST::Interfaces::StandardMem* memoryInterface; SST::TimeConverter* timeConverter; SST::Output* output; std::queue responseQueue; - std::vector initRequests; + std::vector initRequests; std::string gem5SimObjectName; std::string memSize; @@ -78,7 +76,7 @@ class SSTResponderSubComponent: public SST::SubComponent void setOutputStream(SST::Output* output_); void setResponseReceiver(gem5::OutgoingRequestBridge* gem5_bridge); - void portEventHandler(SST::Interfaces::SimpleMem::Request* request); + void portEventHandler(SST::Interfaces::StandardMem::Request* request); bool blocked(); void setup(); @@ -86,18 +84,18 @@ class SSTResponderSubComponent: public SST::SubComponent // return true if the SimObject could be found bool findCorrespondingSimObject(gem5::Root* gem5_root); - bool handleTimingReq(SST::Interfaces::SimpleMem::Request* request); + bool handleTimingReq(SST::Interfaces::StandardMem::Request* request); void handleRecvRespRetry(); void handleRecvFunctional(gem5::PacketPtr pkt); - void handleSwapReqResponse(SST::Interfaces::SimpleMem::Request* request); + void handleSwapReqResponse(SST::Interfaces::StandardMem::Request* request); TPacketMap sstRequestIdToPacketMap; public: // register the component to SST SST_ELI_REGISTER_SUBCOMPONENT_API(SSTResponderSubComponent); - SST_ELI_REGISTER_SUBCOMPONENT_DERIVED( + SST_ELI_REGISTER_SUBCOMPONENT( SSTResponderSubComponent, - "gem5", // SST will look for libgem5.so + "gem5", // SST will look for libgem5.so or libgem5.dylib "gem5Bridge", SST_ELI_ELEMENT_VERSION(1, 0, 0), "Initialize gem5 and link SST's ports to gem5's ports", @@ -106,7 +104,7 @@ class SSTResponderSubComponent: public SST::SubComponent SST_ELI_DOCUMENT_SUBCOMPONENT_SLOTS( {"memory", "Interface to the memory subsystem", \ - "SST::Interfaces::SimpleMem"} + "SST::Interfaces::StandardMem"} ) SST_ELI_DOCUMENT_PORTS( diff --git a/ext/sst/translator.hh b/ext/sst/translator.hh index 2d8c8b782a..bf6a168d9a 100644 --- a/ext/sst/translator.hh +++ b/ext/sst/translator.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2021 The Regents of the University of California +// Copyright (c) 2021-2023 The Regents of the University of California // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -27,87 +27,143 @@ #ifndef __TRANSLATOR_H__ #define __TRANSLATOR_H__ -#include +#include #include -#include #include #include #include -typedef std::unordered_map TPacketMap; namespace Translator { -inline SST::Interfaces::SimpleMem::Request* +inline SST::Interfaces::StandardMem::Request* gem5RequestToSSTRequest(gem5::PacketPtr pkt, TPacketMap& sst_request_id_to_packet_map) { - SST::Interfaces::SimpleMem::Request::Command cmd; + // Listing all the different SST Memory commands. + enum sst_standard_mem_commands + { + Read, + ReadResp, + Write, + WriteResp, + FlushAddr, + FlushResp, + ReadLock, + WriteUnlock, + LoadLink, + StoreConditional, + MoveData, + CustomReq, + CustomResp, + InvNotify + + }; + // SST's standard memory class has visitor classes for all the different + // types of memory commands. Request class now does not have a command + // variable. Instead for different types of request, we now need to + // dynamically cast the class object. I'm using an extra variable to map + // the type of command for SST. + int sst_command_type = -1; + // StandardMem only has one cache flush class with an option to flush or + // flush and invalidate an address. By default, this is set to true so that + // it corresponds to ge,::MemCmd::InvalidateReq + bool flush_addr_flag = true; switch ((gem5::MemCmd::Command)pkt->cmd.toInt()) { case gem5::MemCmd::HardPFReq: case gem5::MemCmd::SoftPFReq: case gem5::MemCmd::SoftPFExReq: case gem5::MemCmd::LoadLockedReq: case gem5::MemCmd::ReadExReq: + case gem5::MemCmd::ReadCleanReq: + case gem5::MemCmd::ReadSharedReq: case gem5::MemCmd::ReadReq: case gem5::MemCmd::SwapReq: - cmd = SST::Interfaces::SimpleMem::Request::Command::Read; + sst_command_type = Read; break; case gem5::MemCmd::StoreCondReq: + case gem5::MemCmd::WritebackDirty: + case gem5::MemCmd::WritebackClean: case gem5::MemCmd::WriteReq: - cmd = SST::Interfaces::SimpleMem::Request::Command::Write; + sst_command_type = Write; break; case gem5::MemCmd::CleanInvalidReq: case gem5::MemCmd::InvalidateReq: - cmd = SST::Interfaces::SimpleMem::Request::Command::FlushLineInv; + sst_command_type = FlushAddr; break; case gem5::MemCmd::CleanSharedReq: - cmd = SST::Interfaces::SimpleMem::Request::Command::FlushLine; + sst_command_type = FlushAddr; + flush_addr_flag = false; break; default: panic("Unable to convert gem5 packet: %s\n", pkt->cmd.toString()); } - SST::Interfaces::SimpleMem::Addr addr = pkt->getAddr(); - - uint8_t* data_ptr = pkt->getPtr(); + SST::Interfaces::StandardMem::Addr addr = pkt->getAddr(); auto data_size = pkt->getSize(); - std::vector data = std::vector( - data_ptr, data_ptr + data_size - ); + std::vector data; + // Need to make sure that the command type is a Write to retrive the data + // data_ptr. + if (sst_command_type == Write) { + uint8_t* data_ptr = pkt->getPtr(); + data = std::vector(data_ptr, data_ptr + data_size); - SST::Interfaces::SimpleMem::Request* request = \ - new SST::Interfaces::SimpleMem::Request( - cmd, addr, data_size, data - ); + } + // Now convert a sst StandardMem request. + SST::Interfaces::StandardMem::Request* request = nullptr; + // find the corresponding memory command type. + switch(sst_command_type) { + case Read: + request = new SST::Interfaces::StandardMem::Read(addr, data_size); + break; + case Write: + request = + new SST::Interfaces::StandardMem::Write(addr, data_size, data); + break; + case FlushAddr: { + // StandardMem::FlushAddr has a invoking variable called `depth` + // which defines the number of cache levels to invalidate. Ideally + // this has to be input from the SST config, however in + // implementation I'm hardcoding this value to 2. + int cache_depth = 2; + request = + new SST::Interfaces::StandardMem::FlushAddr( + addr, data_size, flush_addr_flag, cache_depth); + break; + } + default: + panic("Unable to translate command %d to Request class!", + sst_command_type); + } if ((gem5::MemCmd::Command)pkt->cmd.toInt() == gem5::MemCmd::LoadLockedReq || (gem5::MemCmd::Command)pkt->cmd.toInt() == gem5::MemCmd::SwapReq || pkt->req->isLockedRMW()) { - request->setMemFlags( - SST::Interfaces::SimpleMem::Request::Flags::F_LOCKED); - } else if ((gem5::MemCmd::Command)pkt->cmd.toInt() == \ + // F_LOCKED is deprecated. Therefore I'm skipping this flag for the + // StandardMem request. + } else if ((gem5::MemCmd::Command)pkt->cmd.toInt() == gem5::MemCmd::StoreCondReq) { - request->setMemFlags( - SST::Interfaces::SimpleMem::Request::Flags::F_LLSC); + // F_LLSC is deprecated. Therefore I'm skipping this flag for the + // StandardMem request. } if (pkt->req->isUncacheable()) { - request->setFlags( - SST::Interfaces::SimpleMem::Request::Flags::F_NONCACHEABLE); + request->setFlag( + SST::Interfaces::StandardMem::Request::Flag::F_NONCACHEABLE); } if (pkt->needsResponse()) - sst_request_id_to_packet_map[request->id] = pkt; + sst_request_id_to_packet_map[request->getID()] = pkt; return request; } inline void inplaceSSTRequestToGem5PacketPtr(gem5::PacketPtr pkt, - SST::Interfaces::SimpleMem::Request* request) + SST::Interfaces::StandardMem::Request* request) { pkt->makeResponse(); @@ -116,8 +172,18 @@ inplaceSSTRequestToGem5PacketPtr(gem5::PacketPtr pkt, // SC interprets ExtraData == 1 as the store was successful pkt->req->setExtraData(1); } - - pkt->setData(request->data.data()); + // If there is data in the request, send it back. Only ReadResp requests + // have data associated with it. Other packets does not need to be casted. + if (!pkt->isWrite()) { + // Need to verify whether the packet is a ReadResp, otherwise the + // program will try to incorrectly cast the request object. + if (SST::Interfaces::StandardMem::ReadResp* test = + dynamic_cast(request)) { + pkt->setData(dynamic_cast( + request)->data.data() + ); + } + } // Clear out bus delay notifications pkt->headerDelay = pkt->payloadDelay = 0; diff --git a/ext/testlib/__init__.py b/ext/testlib/__init__.py index 898205d9ab..d2dad35eff 100644 --- a/ext/testlib/__init__.py +++ b/ext/testlib/__init__.py @@ -27,18 +27,18 @@ # Authors: Sean Wilson -from .state import * -from .runner import * -from .test_util import * -from .suite import * -from .loader import * -from .fixture import * -from .configuration import * -from .main import main +# TODO Remove this as an export, users should getcwd from os +from os import getcwd -#TODO Remove this awkward bootstrap -#FIXME +# TODO Remove this awkward bootstrap +# FIXME from gem5 import * -#TODO Remove this as an export, users should getcwd from os -from os import getcwd +from .configuration import * +from .fixture import * +from .loader import * +from .main import main +from .runner import * +from .state import * +from .suite import * +from .test_util import * diff --git a/ext/testlib/configuration.py b/ext/testlib/configuration.py index fd47e3b33a..60c0c17654 100644 --- a/ext/testlib/configuration.py +++ b/ext/testlib/configuration.py @@ -38,7 +38,7 @@ # # Authors: Sean Wilson -''' +""" Global configuration module which exposes two types of configuration variables: @@ -76,40 +76,49 @@ found an AttributeError will be raised. common string names used across the test framework. :code:`_defaults.build_dir = None` Once this module has been imported constants should not be modified and their base attributes are frozen. -''' +""" import abc import argparse import copy import os import re - from pickle import HIGHEST_PROTOCOL as highest_pickle_protocol -from testlib.helper import absdirpath, AttrDict, FrozenAttrDict +from testlib.helper import ( + AttrDict, + FrozenAttrDict, + absdirpath, +) + class UninitialzedAttributeException(Exception): - ''' + """ Signals that an attribute in the config file was not initialized. - ''' + """ + pass + class UninitializedConfigException(Exception): - ''' + """ Signals that the config was not initialized before trying to access an attribute. - ''' + """ + pass -class TagRegex(object): + +class TagRegex: def __init__(self, include, regex): self.include = include self.regex = re.compile(regex) def __str__(self): - type_ = 'Include' if self.include else 'Remove' - return '%10s: %s' % (type_, self.regex.pattern) + type_ = "Include" if self.include else "Remove" + return "%10s: %s" % (type_, self.regex.pattern) -class _Config(object): + +class _Config: _initialized = False __shared_dict = {} @@ -131,14 +140,14 @@ class _Config(object): self._initialized = True def _add_post_processor(self, attr, post_processor): - ''' + """ :param attr: Attribute to pass to and recieve from the :func:`post_processor`. :param post_processor: A callback functions called in a chain to perform additional setup for a config argument. Should return a tuple containing the new value for the config attr. - ''' + """ if attr not in self._post_processors: self._post_processors[attr] = [] self._post_processors[attr].append(post_processor) @@ -153,7 +162,7 @@ class _Config(object): for attr in dir(args): # Ignore non-argument attributes. - if not attr.startswith('_'): + if not attr.startswith("_"): self._config_file_args[attr] = getattr(args, attr) self._config.update(self._config_file_args) @@ -166,100 +175,104 @@ class _Config(object): newval = newval[0] self._set(attr, newval) - def _lookup_val(self, attr): - ''' + """ Get the attribute from the config or fallback to defaults. :returns: If the value is not stored return None. Otherwise a tuple containing the value. - ''' + """ if attr in self._config: return (self._config[attr],) elif hasattr(self._defaults, attr): return (getattr(self._defaults, attr),) def __getattr__(self, attr): - if attr in dir(super(_Config, self)): - return getattr(super(_Config, self), attr) + if attr in dir(super()): + return getattr(super(), attr) elif not self._initialized: raise UninitializedConfigException( - 'Cannot directly access elements from the config before it is' - ' initialized') + "Cannot directly access elements from the config before it is" + " initialized" + ) else: val = self._lookup_val(attr) if val is not None: return val[0] else: raise UninitialzedAttributeException( - '%s was not initialzed in the config.' % attr) + "%s was not initialzed in the config." % attr + ) def get_tags(self): - d = {typ: set(self.__getattr__(typ)) - for typ in self.constants.supported_tags} + d = { + typ: set(self.__getattr__(typ)) + for typ in self.constants.supported_tags + } if any(map(lambda vals: bool(vals), d.values())): return d else: return {} + def define_defaults(defaults): - ''' + """ Defaults are provided by the config if the attribute is not found in the config or commandline. For instance, if we are using the list command fixtures might not be able to count on the build_dir being provided since we aren't going to build anything. - ''' - defaults.base_dir = os.path.abspath(os.path.join(absdirpath(__file__), - os.pardir, - os.pardir)) - defaults.result_path = os.path.join(os.getcwd(), 'testing-results') - defaults.resource_url = 'http://dist.gem5.org/dist/develop' - defaults.resource_path = os.path.abspath(os.path.join(defaults.base_dir, - 'tests', - 'gem5', - 'resources')) + """ + defaults.base_dir = os.path.abspath( + os.path.join(absdirpath(__file__), os.pardir, os.pardir) + ) + defaults.result_path = os.path.join(os.getcwd(), "testing-results") + defaults.resource_url = "http://dist.gem5.org/dist/develop" + defaults.resource_path = os.path.abspath( + os.path.join(defaults.base_dir, "tests", "gem5", "resources") + ) + def define_constants(constants): - ''' + """ 'constants' are values not directly exposed by the config, but are attached to the object for centralized access. These should be used for setting common string names used across the test framework. A simple typo in a string can take a lot of debugging to uncover the issue, attribute errors are easier to notice and most autocompletion systems detect them. - ''' - constants.system_out_name = 'system-out' - constants.system_err_name = 'system-err' + """ + constants.system_out_name = "system-out" + constants.system_err_name = "system-err" - constants.isa_tag_type = 'isa' - constants.x86_tag = 'X86' - constants.gcn3_x86_tag = 'GCN3_X86' - constants.vega_x86_tag = 'VEGA_X86' - constants.sparc_tag = 'SPARC' - constants.riscv_tag = 'RISCV' - constants.arm_tag = 'ARM' - constants.mips_tag = 'MIPS' - constants.power_tag = 'POWER' - constants.null_tag = 'NULL' - constants.all_compiled_tag = 'ALL' + constants.isa_tag_type = "isa" + constants.x86_tag = "X86" + constants.gcn3_x86_tag = "GCN3_X86" + constants.vega_x86_tag = "VEGA_X86" + constants.sparc_tag = "SPARC" + constants.riscv_tag = "RISCV" + constants.arm_tag = "ARM" + constants.mips_tag = "MIPS" + constants.power_tag = "POWER" + constants.null_tag = "NULL" + constants.all_compiled_tag = "ALL" - constants.variant_tag_type = 'variant' - constants.opt_tag = 'opt' - constants.debug_tag = 'debug' - constants.fast_tag = 'fast' + constants.variant_tag_type = "variant" + constants.opt_tag = "opt" + constants.debug_tag = "debug" + constants.fast_tag = "fast" - constants.length_tag_type = 'length' - constants.quick_tag = 'quick' - constants.long_tag = 'long' - constants.very_long_tag = 'very-long' + constants.length_tag_type = "length" + constants.quick_tag = "quick" + constants.long_tag = "long" + constants.very_long_tag = "very-long" - constants.host_isa_tag_type = 'host' - constants.host_x86_64_tag = 'x86_64' - constants.host_arm_tag = 'aarch64' + constants.host_isa_tag_type = "host" + constants.host_x86_64_tag = "x86_64" + constants.host_arm_tag = "aarch64" - constants.kvm_tag = 'kvm' + constants.kvm_tag = "kvm" constants.supported_tags = { - constants.isa_tag_type : ( + constants.isa_tag_type: ( constants.x86_tag, constants.gcn3_x86_tag, constants.vega_x86_tag, @@ -270,7 +283,7 @@ def define_constants(constants): constants.power_tag, constants.null_tag, constants.all_compiled_tag, - ), + ), constants.variant_tag_type: ( constants.opt_tag, constants.debug_tag, @@ -290,41 +303,43 @@ def define_constants(constants): # Binding target ISA with host ISA. This is useful for the # case where host ISA and target ISA need to coincide constants.target_host = { - constants.arm_tag : (constants.host_arm_tag,), - constants.x86_tag : (constants.host_x86_64_tag,), - constants.gcn3_x86_tag : (constants.host_x86_64_tag,), - constants.vega_x86_tag : (constants.host_x86_64_tag,), - constants.sparc_tag : (constants.host_x86_64_tag,), - constants.riscv_tag : (constants.host_x86_64_tag,), - constants.mips_tag : (constants.host_x86_64_tag,), - constants.power_tag : (constants.host_x86_64_tag,), - constants.null_tag : (None,), + constants.arm_tag: (constants.host_arm_tag,), + constants.x86_tag: (constants.host_x86_64_tag,), + constants.gcn3_x86_tag: (constants.host_x86_64_tag,), + constants.vega_x86_tag: (constants.host_x86_64_tag,), + constants.sparc_tag: (constants.host_x86_64_tag,), + constants.riscv_tag: (constants.host_x86_64_tag,), + constants.mips_tag: (constants.host_x86_64_tag,), + constants.power_tag: (constants.host_x86_64_tag,), + constants.null_tag: (None,), constants.all_compiled_tag: (None,), } - constants.supported_isas = constants.supported_tags['isa'] - constants.supported_variants = constants.supported_tags['variant'] - constants.supported_lengths = constants.supported_tags['length'] - constants.supported_hosts = constants.supported_tags['host'] + constants.supported_isas = constants.supported_tags["isa"] + constants.supported_variants = constants.supported_tags["variant"] + constants.supported_lengths = constants.supported_tags["length"] + constants.supported_hosts = constants.supported_tags["host"] - constants.tempdir_fixture_name = 'tempdir' - constants.gem5_simulation_stderr = 'simerr' - constants.gem5_simulation_stdout = 'simout' - constants.gem5_simulation_stats = 'stats.txt' - constants.gem5_simulation_config_ini = 'config.ini' - constants.gem5_simulation_config_json = 'config.json' - constants.gem5_returncode_fixture_name = 'gem5-returncode' - constants.gem5_binary_fixture_name = 'gem5' - constants.xml_filename = 'results.xml' - constants.pickle_filename = 'results.pickle' + constants.tempdir_fixture_name = "tempdir" + constants.gem5_simulation_stderr = "simerr.txt" + constants.gem5_simulation_stdout = "simout.txt" + constants.gem5_simulation_stats = "stats.txt" + constants.gem5_simulation_config_ini = "config.ini" + constants.gem5_simulation_config_json = "config.json" + constants.gem5_returncode_fixture_name = "gem5-returncode" + constants.gem5_binary_fixture_name = "gem5" + constants.xml_filename = "results.xml" + constants.pickle_filename = "results.pickle" constants.pickle_protocol = highest_pickle_protocol # The root directory which all test names will be based off of. - constants.testing_base = absdirpath(os.path.join(absdirpath(__file__), - os.pardir)) + constants.testing_base = absdirpath( + os.path.join(absdirpath(__file__), os.pardir) + ) + def define_post_processors(config): - ''' + """ post_processors are used to do final configuration of variables. This is useful if there is a dynamically set default, or some function that needs to be applied after parsing in order to set a configration value. @@ -333,17 +348,17 @@ def define_post_processors(config): containing the already set config value or ``None`` if the config value has not been set to anything. They must return the modified value in the same format. - ''' + """ def set_default_build_dir(build_dir): - ''' + """ Post-processor to set the default build_dir based on the base_dir. .. seealso :func:`~_Config._add_post_processor` - ''' + """ if not build_dir or build_dir[0] is None: - base_dir = config._lookup_val('base_dir')[0] - build_dir = (os.path.join(base_dir, 'build'),) + base_dir = config._lookup_val("base_dir")[0] + build_dir = (os.path.join(base_dir, "build"),) return build_dir def fix_verbosity_hack(verbose): @@ -381,6 +396,7 @@ def define_post_processors(config): if not host[0]: try: import platform + host_machine = platform.machine() if host_machine not in constants.supported_hosts: raise ValueError("Invalid host machine") @@ -398,87 +414,98 @@ def define_post_processors(config): positional_tags = positional_tags[0] for flag, regex in positional_tags: - if flag == 'exclude_tags': + if flag == "exclude_tags": tag_regex = TagRegex(False, regex) - elif flag == 'include_tags': + elif flag == "include_tags": tag_regex = TagRegex(True, regex) else: - raise ValueError('Unsupported flag.') + raise ValueError("Unsupported flag.") new_positional_tags_list.append(tag_regex) return (new_positional_tags_list,) - config._add_post_processor('build_dir', set_default_build_dir) - config._add_post_processor('verbose', fix_verbosity_hack) - config._add_post_processor('isa', default_isa) - config._add_post_processor('variant', default_variant) - config._add_post_processor('length', default_length) - config._add_post_processor('host', default_host) - config._add_post_processor('threads', threads_as_int) - config._add_post_processor('test_threads', test_threads_as_int) - config._add_post_processor(StorePositionalTagsAction.position_kword, - compile_tag_regex) -class Argument(object): - ''' + config._add_post_processor("build_dir", set_default_build_dir) + config._add_post_processor("verbose", fix_verbosity_hack) + config._add_post_processor("isa", default_isa) + config._add_post_processor("variant", default_variant) + config._add_post_processor("length", default_length) + config._add_post_processor("host", default_host) + config._add_post_processor("threads", threads_as_int) + config._add_post_processor("test_threads", test_threads_as_int) + config._add_post_processor( + StorePositionalTagsAction.position_kword, compile_tag_regex + ) + + +class Argument: + """ Class represents a cli argument/flag for a argparse parser. :attr name: The long name of this object that will be stored in the arg output by the final parser. - ''' + """ + def __init__(self, *flags, **kwargs): self.flags = flags self.kwargs = kwargs if len(flags) == 0: raise ValueError("Need at least one argument.") - elif 'dest' in kwargs: - self.name = kwargs['dest'] - elif len(flags) > 1 or flags[0].startswith('-'): + elif "dest" in kwargs: + self.name = kwargs["dest"] + elif len(flags) > 1 or flags[0].startswith("-"): for flag in flags: - if not flag.startswith('-'): - raise ValueError("invalid option string %s: must start" - "with a character '-'" % flag) + if not flag.startswith("-"): + raise ValueError( + "invalid option string %s: must start" + "with a character '-'" % flag + ) - if flag.startswith('--'): - if not hasattr(self, 'name'): - self.name = flag.lstrip('-') + if flag.startswith("--"): + if not hasattr(self, "name"): + self.name = flag.lstrip("-") - if not hasattr(self, 'name'): - self.name = flags[0].lstrip('-') - self.name = self.name.replace('-', '_') + if not hasattr(self, "name"): + self.name = flags[0].lstrip("-") + self.name = self.name.replace("-", "_") def add_to(self, parser): - '''Add this argument to the given parser.''' + """Add this argument to the given parser.""" parser.add_argument(*self.flags, **self.kwargs) def copy(self): - '''Copy this argument so you might modify any of its kwargs.''' + """Copy this argument so you might modify any of its kwargs.""" return copy.deepcopy(self) class _StickyInt: - ''' + """ A class that is used to cheat the verbosity count incrementer by pretending to be an int. This makes the int stay on the heap and eat other real numbers when they are added to it. We use this so we can allow the verbose flag to be provided before or after the subcommand. This likely has no utility outside of this use case. - ''' + """ + def __init__(self, val=0): self.val = val self.type = int + def __add__(self, other): self.val += other return self + common_args = NotImplemented + class StorePositionAction(argparse.Action): - '''Base class for classes wishing to create namespaces where + """Base class for classes wishing to create namespaces where arguments are stored in the order provided via the command line. - ''' - position_kword = 'positional' + """ + + position_kword = "positional" def __call__(self, parser, namespace, values, option_string=None): if not self.position_kword in namespace: @@ -487,120 +514,134 @@ class StorePositionAction(argparse.Action): previous.append((self.dest, values)) setattr(namespace, self.position_kword, previous) + class StorePositionalTagsAction(StorePositionAction): - position_kword = 'tag_filters' + position_kword = "tag_filters" + def define_common_args(config): - ''' + """ Common args are arguments which are likely to be simular between different subcommands, so they are available to all by placing their definitions here. - ''' + """ global common_args - parse_comma_separated_string = lambda st: st.split(',') + parse_comma_separated_string = lambda st: st.split(",") # A list of common arguments/flags used across cli parsers. common_args = [ Argument( - 'directories', - nargs='*', + "directories", + nargs="*", default=[os.getcwd()], - help='Space separated list of directories to start searching ' - 'for tests in'), + help="Space separated list of directories to start searching " + "for tests in", + ), Argument( - '--exclude-tags', + "--exclude-tags", action=StorePositionalTagsAction, - help='A tag comparison used to select tests.'), + help="A tag comparison used to select tests.", + ), Argument( - '--include-tags', + "--include-tags", action=StorePositionalTagsAction, - help='A tag comparison used to select tests.'), + help="A tag comparison used to select tests.", + ), Argument( - '--isa', - action='extend', + "--isa", + action="extend", default=[], type=parse_comma_separated_string, help="Only tests that are valid with one of these ISAs. " - "Comma separated."), + "Comma separated.", + ), Argument( - '--variant', - action='extend', + "--variant", + action="extend", default=[], type=parse_comma_separated_string, help="Only tests that are valid with one of these binary variants" - "(e.g., opt, debug). Comma separated."), + "(e.g., opt, debug). Comma separated.", + ), Argument( - '--length', - action='extend', + "--length", + action="extend", default=[], type=parse_comma_separated_string, - help="Only tests that are one of these lengths. Comma separated."), + help="Only tests that are one of these lengths. Comma separated.", + ), Argument( - '--host', - action='append', + "--host", + action="append", default=[], - help="Only tests that are meant to runnable on the selected host"), + help="Only tests that are meant to runnable on the selected host", + ), Argument( - '--uid', - action='store', + "--uid", + action="store", default=None, - help='UID of a specific test item to run.'), + help="UID of a specific test item to run.", + ), Argument( - '--build-dir', - action='store', - help='Build directory for SCons'), + "--build-dir", action="store", help="Build directory for SCons" + ), Argument( - '--base-dir', - action='store', + "--base-dir", + action="store", default=config._defaults.base_dir, - help='Directory to change to in order to exec scons.'), + help="Directory to change to in order to exec scons.", + ), Argument( - '-j', '--threads', - action='store', + "-j", + "--threads", + action="store", default=1, - help='Number of threads to run SCons with.'), + help="Number of threads to run SCons with.", + ), Argument( - '-t', '--test-threads', - action='store', + "-t", + "--test-threads", + action="store", default=1, - help='Number of threads to spawn to run concurrent tests with.'), + help="Number of threads to spawn to run concurrent tests with.", + ), Argument( - '-v', - action='count', - dest='verbose', + "-v", + action="count", + dest="verbose", default=_StickyInt(), - help='Increase verbosity'), + help="Increase verbosity", + ), Argument( - '--config-path', - action='store', + "--config-path", + action="store", default=os.getcwd(), - help='Path to read a testing.ini config in' + help="Path to read a testing.ini config in", ), Argument( - '--skip-build', - action='store_true', + "--skip-build", + action="store_true", default=False, - help='Skip the building component of SCons targets.' + help="Skip the building component of SCons targets.", ), Argument( - '--result-path', - action='store', - help='The path to store results in.' + "--result-path", + action="store", + help="The path to store results in.", ), Argument( - '--bin-path', - action='store', + "--bin-path", + action="store", default=config._defaults.resource_path, - help='Path where resources are stored (downloaded if not present)' + help="Path where resources are stored (downloaded if not present)", ), Argument( - '--resource-url', - action='store', + "--resource-url", + action="store", default=config._defaults.resource_url, - help='The URL where the resources reside.' + help="The URL where the resources reside.", ), - ] # NOTE: There is a limitation which arises due to this format. If you have @@ -610,9 +651,10 @@ def define_common_args(config): # e.g. if you have a -v argument which increments verbosity level and # a separate --verbose flag which 'store's verbosity level. the final # one in the list will be saved. - common_args = AttrDict({arg.name:arg for arg in common_args}) + common_args = AttrDict({arg.name: arg for arg in common_args}) -class ArgParser(object, metaclass=abc.ABCMeta): + +class ArgParser(metaclass=abc.ABCMeta): class ExtendAction(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): items = getattr(namespace, self.dest, []) @@ -622,10 +664,10 @@ class ArgParser(object, metaclass=abc.ABCMeta): def __init__(self, parser): # Copy public methods of the parser. for attr in dir(parser): - if not attr.startswith('_'): + if not attr.startswith("_"): setattr(self, attr, getattr(parser, attr)) self.parser = parser - self.parser.register('action', 'extend', ArgParser.ExtendAction) + self.parser.register("action", "extend", ArgParser.ExtendAction) self.add_argument = self.parser.add_argument # Argument will be added to all parsers and subparsers. @@ -633,27 +675,26 @@ class ArgParser(object, metaclass=abc.ABCMeta): class CommandParser(ArgParser): - ''' + """ Main parser which parses command strings and uses those to direct to a subparser. - ''' + """ + def __init__(self): parser = argparse.ArgumentParser() - super(CommandParser, self).__init__(parser) - self.subparser = self.add_subparsers(dest='command') + super().__init__(parser) + self.subparser = self.add_subparsers(dest="command") class RunParser(ArgParser): - ''' + """ Parser for the \'run\' command. - ''' - def __init__(self, subparser): - parser = subparser.add_parser( - 'run', - help='''Run Tests.''' - ) + """ - super(RunParser, self).__init__(parser) + def __init__(self, subparser): + parser = subparser.add_parser("run", help="""Run Tests.""") + + super().__init__(parser) common_args.uid.add_to(parser) common_args.skip_build.add_to(parser) @@ -672,46 +713,58 @@ class RunParser(ArgParser): class ListParser(ArgParser): - ''' + """ Parser for the \'list\' command. - ''' + """ + def __init__(self, subparser): parser = subparser.add_parser( - 'list', - help='''List and query test metadata.''' + "list", help="""List and query test metadata.""" ) - super(ListParser, self).__init__(parser) + super().__init__(parser) Argument( - '--suites', - action='store_true', + "--suites", + action="store_true", default=False, - help='List all test suites.' + help="List all test suites.", ).add_to(parser) Argument( - '--tests', - action='store_true', + "--tests", + action="store_true", default=False, - help='List all test cases.' + help="List all test cases.", ).add_to(parser) Argument( - '--fixtures', - action='store_true', + "--fixtures", + action="store_true", default=False, - help='List all fixtures.' + help="List all fixtures.", ).add_to(parser) Argument( - '--all-tags', - action='store_true', + "--all-tags", + action="store_true", default=False, - help='List all tags.' + help="List all tags.", ).add_to(parser) Argument( - '-q', - dest='quiet', - action='store_true', + "--build-targets", + action="store_true", default=False, - help='Quiet output (machine readable).' + help="List all the gem5 build targets.", + ).add_to(parser) + Argument( + "-q", + dest="quiet", + action="store_true", + default=False, + help="Quiet output (machine readable).", + ).add_to(parser) + Argument( + "--uid", + action="store", + default=None, + help="UID of a specific test item to list.", ).add_to(parser) common_args.directories.add_to(parser) @@ -726,11 +779,8 @@ class ListParser(ArgParser): class RerunParser(ArgParser): def __init__(self, subparser): - parser = subparser.add_parser( - 'rerun', - help='''Rerun failed tests.''' - ) - super(RerunParser, self).__init__(parser) + parser = subparser.add_parser("rerun", help="""Rerun failed tests.""") + super().__init__(parser) common_args.skip_build.add_to(parser) common_args.directories.add_to(parser) @@ -744,6 +794,7 @@ class RerunParser(ArgParser): common_args.length.add_to(parser) common_args.host.add_to(parser) + config = _Config() define_constants(config.constants) @@ -752,14 +803,16 @@ define_constants(config.constants) config.constants = FrozenAttrDict(config.constants.__dict__) constants = config.constants -''' +""" This config object is the singleton config object available throughout the framework. -''' +""" + + def initialize_config(): - ''' + """ Parse the commandline arguments and setup the config varibles. - ''' + """ global config # Setup constants and defaults diff --git a/ext/testlib/fixture.py b/ext/testlib/fixture.py index bcd22d9220..14d42c4724 100644 --- a/ext/testlib/fixture.py +++ b/ext/testlib/fixture.py @@ -26,18 +26,23 @@ # # Authors: Sean Wilson +from typing import Optional + import testlib.helper as helper +from testlib.configuration import constants + class SkipException(Exception): def __init__(self, fixture, testitem): - self.msg = 'Fixture "%s" raised SkipException for "%s".' % ( - fixture.name, testitem.name + self.msg = 'Fixture "{}" raised SkipException for "{}".'.format( + fixture.name, + testitem.name, ) - super(SkipException, self).__init__(self.msg) + super().__init__(self.msg) -class Fixture(object): - ''' +class Fixture: + """ Base Class for a test Fixture. Fixtures are items which possibly require setup and/or tearing down after @@ -50,11 +55,12 @@ class Fixture(object): .. note:: In order for Fixtures to be enumerated by the test system this class' :code:`__new__` method must be called. - ''' + """ + collector = helper.InstanceCollector() def __new__(klass, *args, **kwargs): - obj = super(Fixture, klass).__new__(klass) + obj = super().__new__(klass) Fixture.collector.collect(obj) return obj @@ -76,6 +82,21 @@ class Fixture(object): def teardown(self, testitem): pass + def get_get_build_info(self) -> Optional[dict]: + # If this is a gem5 build it will return the target gem5 build path + # and any additional build information. E.g.: + # + # /path/to/gem5/build/NULL/gem5.opt--default=NULL PROTOCOL=MI_example + # + # In this example this may be passed to scons to build gem5 in + # accordance to the test's build requirements. + # + # If this fixtures is not a build of gem5, None is returned. + return None + + def __str__(self): + return f"{self.name} fixture" + def set_global(self): self._is_global = True diff --git a/ext/testlib/handlers.py b/ext/testlib/handlers.py index fa7aea9c89..283e04f170 100644 --- a/ext/testlib/handlers.py +++ b/ext/testlib/handlers.py @@ -26,37 +26,40 @@ # # Authors: Sean Wilson -''' +""" Handlers for the testlib Log. -''' +""" import multiprocessing import os import sys import threading import time import traceback +from queue import ( + Empty, + Queue, +) import testlib.helper as helper import testlib.log as log import testlib.result as result import testlib.state as state import testlib.terminal as terminal - -from queue import Queue, Empty from testlib.configuration import constants -class _TestStreamManager(object): +class _TestStreamManager: def __init__(self): self._writers = {} def open_writer(self, test_result): if test_result in self._writers: - raise ValueError('Cannot have multiple writters on a single test.') - self._writers[test_result] = _TestStreams(test_result.stdout, - test_result.stderr) + raise ValueError("Cannot have multiple writters on a single test.") + self._writers[test_result] = _TestStreams( + test_result.stdout, test_result.stderr + ) def get_writer(self, test_result): if test_result not in self._writers: @@ -73,89 +76,94 @@ class _TestStreamManager(object): writer.close() self._writers.clear() -class _TestStreams(object): + +class _TestStreams: def __init__(self, stdout, stderr): helper.mkdir_p(os.path.dirname(stdout)) helper.mkdir_p(os.path.dirname(stderr)) - self.stdout = open(stdout, 'w') - self.stderr = open(stderr, 'w') + self.stdout = open(stdout, "w") + self.stderr = open(stderr, "w") def close(self): self.stdout.close() self.stderr.close() -class ResultHandler(object): - ''' + +class ResultHandler: + """ Log handler which listens for test results and output saving data as it is reported. When the handler is closed it writes out test results in the python pickle format. - ''' + """ + def __init__(self, schedule, directory): - ''' + """ :param schedule: The entire schedule as a :class:`LoadedLibrary` object. :param directory: Directory to save test stdout/stderr and aggregate results to. - ''' + """ self.directory = directory - self.internal_results = result.InternalLibraryResults(schedule, - directory) + self.internal_results = result.InternalLibraryResults( + schedule, directory + ) self.test_stream_manager = _TestStreamManager() self._closed = False self.mapping = { log.LibraryStatus.type_id: self.handle_library_status, - log.SuiteResult.type_id: self.handle_suite_result, log.TestResult.type_id: self.handle_test_result, - log.TestStderr.type_id: self.handle_stderr, log.TestStdout.type_id: self.handle_stdout, } def handle(self, record): if not self._closed: - self.mapping.get(record.type_id, lambda _:None)(record) + self.mapping.get(record.type_id, lambda _: None)(record) def handle_library_status(self, record): - if record['status'] in (state.Status.Complete, state.Status.Avoided): + if record["status"] in (state.Status.Complete, state.Status.Avoided): self.test_stream_manager.close() def handle_suite_result(self, record): suite_result = self.internal_results.get_suite_result( - record['metadata'].uid) - suite_result.result = record['result'] + record["metadata"].uid + ) + suite_result.result = record["result"] def handle_test_result(self, record): test_result = self._get_test_result(record) - test_result.result = record['result'] + test_result.result = record["result"] def handle_stderr(self, record): self.test_stream_manager.get_writer( self._get_test_result(record) - ).stderr.write(record['buffer']) + ).stderr.write(record["buffer"]) def handle_stdout(self, record): self.test_stream_manager.get_writer( self._get_test_result(record) - ).stdout.write(record['buffer']) + ).stdout.write(record["buffer"]) def _get_test_result(self, test_record): return self.internal_results.get_test_result( - test_record['metadata'].uid, - test_record['metadata'].suite_uid) + test_record["metadata"].uid, test_record["metadata"].suite_uid + ) def _save(self): - #FIXME Hardcoded path name + # FIXME Hardcoded path name result.InternalSavedResults.save( self.internal_results, - os.path.join(self.directory, constants.pickle_filename)) + os.path.join(self.directory, constants.pickle_filename), + ) result.JUnitSavedResults.save( self.internal_results, - os.path.join(self.directory, constants.xml_filename)) + os.path.join(self.directory, constants.xml_filename), + ) def close(self): if self._closed: @@ -164,11 +172,11 @@ class ResultHandler(object): self._save() def unsuccessful(self): - ''' + """ Performs an or reduce on all of the results. Returns true if at least one test is unsuccessful, false when all tests pass - ''' + """ for suite_result in self.internal_results: if suite_result.unsuccessful: return True @@ -176,20 +184,21 @@ class ResultHandler(object): return False -#TODO Change from a handler to an internal post processor so it can be used +# TODO Change from a handler to an internal post processor so it can be used # to reprint results -class SummaryHandler(object): - ''' +class SummaryHandler: + """ A log handler which listens to the log for test results and reports the aggregate results when closed. - ''' + """ + color = terminal.get_termcap() reset = color.Normal colormap = { - state.Result.Errored: color.Red, - state.Result.Failed: color.Red, - state.Result.Passed: color.Green, - state.Result.Skipped: color.Cyan, + state.Result.Errored: color.Red, + state.Result.Failed: color.Red, + state.Result.Passed: color.Green, + state.Result.Skipped: color.Cyan, } def __init__(self): @@ -201,24 +210,28 @@ class SummaryHandler(object): self.results = [] def handle_library_status(self, record): - if record['status'] == state.Status.Building: + if record["status"] == state.Status.Building: self._timer.restart() def handle_testresult(self, record): - result = record['result'].value - if result in (state.Result.Skipped, state.Result.Failed, - state.Result.Passed, state.Result.Errored): + result = record["result"].value + if result in ( + state.Result.Skipped, + state.Result.Failed, + state.Result.Passed, + state.Result.Errored, + ): self.results.append(result) def handle(self, record): - self.mapping.get(record.type_id, lambda _:None)(record) + self.mapping.get(record.type_id, lambda _: None)(record) def close(self): print(self._display_summary()) def _display_summary(self): most_severe_outcome = None - outcome_fmt = ' {count} {outcome}' + outcome_fmt = " {count} {outcome}" strings = [] outcome_count = [0] * len(state.Result.enums) @@ -228,24 +241,31 @@ class SummaryHandler(object): # Iterate over enums so they are in order of severity for outcome in state.Result.enums: outcome = getattr(state.Result, outcome) - count = outcome_count[outcome] + count = outcome_count[outcome] if count: - strings.append(outcome_fmt.format(count=count, - outcome=state.Result.enums[outcome])) + strings.append( + outcome_fmt.format( + count=count, outcome=state.Result.enums[outcome] + ) + ) most_severe_outcome = outcome - string = ','.join(strings) + string = ",".join(strings) if most_severe_outcome is None: - string = ' No testing done' + string = " No testing done" most_severe_outcome = state.Result.Passed else: - string = ' Results:' + string + ' in {:.2} seconds '.format( - self._timer.active_time()) - string += ' ' + string = ( + " Results:" + + string + + f" in {self._timer.active_time():.2} seconds " + ) + string += " " return terminal.insert_separator( - string, - color=self.colormap[most_severe_outcome] + self.color.Bold) + string, color=self.colormap[most_severe_outcome] + self.color.Bold + ) -class TerminalHandler(object): + +class TerminalHandler: color = terminal.get_termcap() verbosity_mapping = { log.LogLevel.Warn: color.Yellow, @@ -268,75 +288,85 @@ class TerminalHandler(object): } def _display_outcome(self, name, outcome, reason=None): - print(self.color.Bold - + SummaryHandler.colormap[outcome] - + name - + ' ' - + state.Result.enums[outcome] - + SummaryHandler.reset) + print( + self.color.Bold + + SummaryHandler.colormap[outcome] + + name + + " " + + state.Result.enums[outcome] + + SummaryHandler.reset + ) if reason is not None: - log.test_log.info('') - log.test_log.info('Reason:') + log.test_log.info("") + log.test_log.info("Reason:") log.test_log.info(reason) - log.test_log.info(terminal.separator('-')) + log.test_log.info(terminal.separator("-")) def handle_teststatus(self, record): - if record['status'] == state.Status.Running: - log.test_log.debug('Starting Test Case: %s' %\ - record['metadata'].name) + if record["status"] == state.Status.Running: + log.test_log.debug( + "Starting Test Case: %s" % record["metadata"].name + ) def handle_testresult(self, record): self._display_outcome( - 'Test: %s' % record['metadata'].name, - record['result'].value) + "Test: %s" % record["metadata"].name, record["result"].value + ) def handle_suitestatus(self, record): - if record['status'] == state.Status.Running: - log.test_log.debug('Starting Test Suite: %s ' %\ - record['metadata'].name) + if record["status"] == state.Status.Running: + log.test_log.debug( + "Starting Test Suite: %s " % record["metadata"].name + ) def handle_stderr(self, record): if self.stream: - print(record.data['buffer'], file=sys.stderr, end='') + print(record.data["buffer"], file=sys.stderr, end="") def handle_stdout(self, record): if self.stream: - print(record.data['buffer'], file=sys.stdout, end='') + print(record.data["buffer"], file=sys.stdout, end="") def handle_testmessage(self, record): if self.stream: - print(self._colorize(record['message'], record['level'])) + print(self._colorize(record["message"], record["level"])) def handle_librarymessage(self, record): - if not self.machine_only or record.data.get('machine_readable', False): - print(self._colorize(record['message'], record['level'], - record['bold'])) + if not self.machine_only or record.data.get("machine_readable", False): + print( + self._colorize( + record["message"], record["level"], record["bold"] + ) + ) def _colorize(self, message, level, bold=False): - return '%s%s%s%s' % ( - self.color.Bold if bold else '', - self.verbosity_mapping.get(level, ''), - message, - self.default) + return "{}{}{}{}".format( + self.color.Bold if bold else "", + self.verbosity_mapping.get(level, ""), + message, + self.default, + ) def handle(self, record): - if record.data.get('level', self.verbosity) > self.verbosity: + if record.data.get("level", self.verbosity) > self.verbosity: return - self.mapping.get(record.type_id, lambda _:None)(record) + self.mapping.get(record.type_id, lambda _: None)(record) def close(self): pass -class MultiprocessingHandlerWrapper(object): - ''' + +class MultiprocessingHandlerWrapper: + """ A handler class which forwards log records to subhandlers, enabling logging across multiprocessing python processes. The 'parent' side of the handler should execute either :func:`async_process` or :func:`process` to forward log records to subhandlers. - ''' + """ + def __init__(self, *subhandlers): # Create thread to spin handing recipt of messages # Create queue to push onto @@ -350,7 +380,7 @@ class MultiprocessingHandlerWrapper(object): def add_handler(self, handler): self._handler_lock.acquire() - self._subhandlers = (handler, ) + self._subhandlers + self._subhandlers = (handler,) + self._subhandlers self._handler_lock.release() def _with_handlers(self, callback): @@ -405,7 +435,7 @@ class MultiprocessingHandlerWrapper(object): self.queue.put(record) def _close(self): - if hasattr(self, 'thread'): + if hasattr(self, "thread"): self.thread.join() _wrap(self._drain) self._with_handlers(lambda handler: _wrap(handler.close)) @@ -415,9 +445,9 @@ class MultiprocessingHandlerWrapper(object): # This sleep adds some time for the sender threads on this process to # finish pickling the object and complete shutdown after the queue is # closed. - time.sleep(.2) + time.sleep(0.2) self.queue.close() - time.sleep(.2) + time.sleep(0.2) def close(self): if not self._shutdown.is_set(): diff --git a/ext/testlib/helper.py b/ext/testlib/helper.py index ea102f262b..a66ce8ad2c 100644 --- a/ext/testlib/helper.py +++ b/ext/testlib/helper.py @@ -38,12 +38,9 @@ # # Authors: Sean Wilson -''' +""" Helper classes for writing tests with this test library. -''' -from collections import namedtuple -from collections.abc import MutableSet - +""" import difflib import errno import os @@ -54,8 +51,11 @@ import subprocess import tempfile import threading import time +from collections import namedtuple +from collections.abc import MutableSet -class TimedWaitPID(object): + +class TimedWaitPID: """Utility to monkey-patch os.waitpid() with os.wait4(). This allows process usage time to be obtained directly from the OS @@ -69,9 +69,10 @@ class TimedWaitPID(object): it is read. """ - TimeRecord = namedtuple( "_TimeRecord", "user_time system_time" ) - class Wrapper(object): + TimeRecord = namedtuple("_TimeRecord", "user_time system_time") + + class Wrapper: def __init__(self): self._time_for_pid = {} self._access_lock = threading.Lock() @@ -79,11 +80,8 @@ class TimedWaitPID(object): def __call__(self, pid, options): pid, status, resource_usage = os.wait4(pid, options) with self._access_lock: - self._time_for_pid[pid] = ( - TimedWaitPID.TimeRecord( - resource_usage.ru_utime, - resource_usage.ru_stime - ) + self._time_for_pid[pid] = TimedWaitPID.TimeRecord( + resource_usage.ru_utime, resource_usage.ru_stime ) return (pid, status) @@ -94,7 +92,7 @@ class TimedWaitPID(object): def get_time_for_pid(self, pid): with self._access_lock: if pid not in self._time_for_pid: - raise Exception("No resource usage for pid {}".format(pid)) + raise Exception(f"No resource usage for pid {pid}") time_for_pid = self._time_for_pid[pid] del self._time_for_pid[pid] return time_for_pid @@ -108,14 +106,14 @@ class TimedWaitPID(object): with TimedWaitPID._wrapper_lock: if TimedWaitPID._wrapper is None: TimedWaitPID._wrapper = TimedWaitPID.Wrapper() - if TimedWaitPID._original_os_waitpid is None : + if TimedWaitPID._original_os_waitpid is None: TimedWaitPID._original_os_waitpid = os.waitpid os.waitpid = TimedWaitPID._wrapper @staticmethod def restore(): with TimedWaitPID._wrapper_lock: - if TimedWaitPID._original_os_waitpid is not None : + if TimedWaitPID._original_os_waitpid is not None: os.waitpid = TimedWaitPID._original_os_waitpid TimedWaitPID._original_os_waitpid = None @@ -129,12 +127,14 @@ class TimedWaitPID(object): with TimedWaitPID._wrapper_lock: return TimedWaitPID._wrapper.get_time_for_pid(pid) + # Patch os.waitpid() TimedWaitPID.install() -#TODO Tear out duplicate logic from the sandbox IOManager + +# TODO Tear out duplicate logic from the sandbox IOManager def log_call(logger, command, time, *popenargs, **kwargs): - ''' + """ Calls the given process and automatically logs the command and output. If stdout or stderr are provided output will also be piped into those @@ -145,7 +145,7 @@ def log_call(logger, command, time, *popenargs, **kwargs): :params stderr: Iterable of items to write to as we read from the subprocess. - ''' + """ if isinstance(command, str): cmdstr = command else: @@ -159,33 +159,35 @@ def log_call(logger, command, time, *popenargs, **kwargs): raise e logger_callback = logger.trace - logger.trace('Logging call to command: %s' % cmdstr) + logger.trace("Logging call to command: %s" % cmdstr) - stdout_redirect = kwargs.get('stdout', tuple()) - stderr_redirect = kwargs.get('stderr', tuple()) + stdout_redirect = kwargs.get("stdout", tuple()) + stderr_redirect = kwargs.get("stderr", tuple()) - if hasattr(stdout_redirect, 'write'): + if hasattr(stdout_redirect, "write"): stdout_redirect = (stdout_redirect,) - if hasattr(stderr_redirect, 'write'): + if hasattr(stderr_redirect, "write"): stderr_redirect = (stderr_redirect,) - kwargs['stdout'] = subprocess.PIPE - kwargs['stderr'] = subprocess.PIPE + kwargs["stdout"] = subprocess.PIPE + kwargs["stderr"] = subprocess.PIPE p = subprocess.Popen(command, *popenargs, **kwargs) def log_output(log_callback, pipe, redirects=tuple()): # Read iteractively, don't allow input to fill the pipe. - for line in iter(pipe.readline, b''): + for line in iter(pipe.readline, b""): line = line.decode("utf-8") for r in redirects: r.write(line) log_callback(line.rstrip()) - stdout_thread = threading.Thread(target=log_output, - args=(logger_callback, p.stdout, stdout_redirect)) + stdout_thread = threading.Thread( + target=log_output, args=(logger_callback, p.stdout, stdout_redirect) + ) stdout_thread.setDaemon(True) - stderr_thread = threading.Thread(target=log_output, - args=(logger_callback, p.stderr, stderr_redirect)) + stderr_thread = threading.Thread( + target=log_output, args=(logger_callback, p.stderr, stderr_redirect) + ) stderr_thread.setDaemon(True) stdout_thread.start() @@ -197,25 +199,26 @@ def log_call(logger, command, time, *popenargs, **kwargs): if time is not None and TimedWaitPID.has_time_for_pid(p.pid): resource_usage = TimedWaitPID.get_time_for_pid(p.pid) - time['user_time'] = resource_usage.user_time - time['system_time'] = resource_usage.system_time + time["user_time"] = resource_usage.user_time + time["system_time"] = resource_usage.system_time # Return the return exit code of the process. if retval != 0: raise subprocess.CalledProcessError(retval, cmdstr) + # lru_cache stuff (Introduced in python 3.2+) # Renamed and modified to cacheresult class _HashedSeq(list): - ''' + """ This class guarantees that hash() will be called no more than once per element. This is important because the cacheresult() will hash the key multiple times on a cache miss. .. note:: From cpython 3.7 - ''' + """ - __slots__ = 'hashvalue' + __slots__ = "hashvalue" def __init__(self, tup, hash=hash): self[:] = tup @@ -224,11 +227,18 @@ class _HashedSeq(list): def __hash__(self): return self.hashvalue -def _make_key(args, kwds, typed, - kwd_mark = (object(),), - fasttypes = {int, str, frozenset, type(None)}, - tuple=tuple, type=type, len=len): - ''' + +def _make_key( + args, + kwds, + typed, + kwd_mark=(object(),), + fasttypes={int, str, frozenset, type(None)}, + tuple=tuple, + type=type, + len=len, +): + """ Make a cache key from optionally typed positional and keyword arguments. The key is constructed in a way that is flat as possible rather than as a nested structure that would take more memory. If there is only a single @@ -237,7 +247,7 @@ def _make_key(args, kwds, typed, lookup speed. .. note:: From cpython 3.7 - ''' + """ key = args if kwds: key += kwd_mark @@ -253,15 +263,16 @@ def _make_key(args, kwds, typed, def cacheresult(function, typed=False): - ''' + """ :param typed: If typed is True, arguments of different types will be cached separately. I.e. f(3.0) and f(3) will be treated as distinct calls with distinct results. .. note:: From cpython 3.7 - ''' - sentinel = object() # unique object used to signal cache misses + """ + sentinel = object() # unique object used to signal cache misses cache = {} + def wrapper(*args, **kwds): # Simple caching without ordering or size limit key = _make_key(args, kwds, typed) @@ -271,19 +282,21 @@ def cacheresult(function, typed=False): result = function(*args, **kwds) cache[key] = result return result + return wrapper + class OrderedSet(MutableSet): - ''' + """ Maintain ordering of insertion in items to the set with quick iteration. http://code.activestate.com/recipes/576694/ - ''' + """ def __init__(self, iterable=None): self.end = end = [] - end += [None, end, end] # sentinel node for doubly linked list - self.map = {} # key --> [key, prev, next] + end += [None, end, end] # sentinel node for doubly linked list + self.map = {} # key --> [key, prev, next] if iterable is not None: self |= iterable @@ -325,35 +338,38 @@ class OrderedSet(MutableSet): def pop(self, last=True): if not self: - raise KeyError('set is empty') + raise KeyError("set is empty") key = self.end[1][0] if last else self.end[2][0] self.discard(key) return key def __repr__(self): if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, list(self)) + return f"{self.__class__.__name__}()" + return f"{self.__class__.__name__}({list(self)!r})" def __eq__(self, other): if isinstance(other, OrderedSet): return len(self) == len(other) and list(self) == list(other) return set(self) == set(other) + def absdirpath(path): - ''' + """ Return the directory component of the absolute path of the given path. - ''' + """ return os.path.dirname(os.path.abspath(path)) + joinpath = os.path.join + def mkdir_p(path): - ''' + """ Same thing as mkdir -p https://stackoverflow.com/a/600612 - ''' + """ try: os.makedirs(path) except OSError as exc: # Python >2.5 @@ -364,12 +380,14 @@ def mkdir_p(path): class FrozenSetException(Exception): - '''Signals one tried to set a value in a 'frozen' object.''' + """Signals one tried to set a value in a 'frozen' object.""" + pass -class AttrDict(object): - '''Object which exposes its own internal dictionary through attributes.''' +class AttrDict: + """Object which exposes its own internal dictionary through attributes.""" + def __init__(self, dict_={}): self.update(dict_) @@ -377,7 +395,7 @@ class AttrDict(object): dict_ = self.__dict__ if attr in dict_: return dict_[attr] - raise AttributeError('Could not find %s attribute' % attr) + raise AttributeError("Could not find %s attribute" % attr) def __setattr__(self, attr, val): self.__dict__[attr] = val @@ -393,29 +411,33 @@ class AttrDict(object): class FrozenAttrDict(AttrDict): - '''An AttrDict whose attributes cannot be modified directly.''' + """An AttrDict whose attributes cannot be modified directly.""" + __initialized = False + def __init__(self, dict_={}): - super(FrozenAttrDict, self).__init__(dict_) + super().__init__(dict_) self.__initialized = True def __setattr__(self, attr, val): if self.__initialized: raise FrozenSetException( - 'Cannot modify an attribute in a FozenAttrDict') + "Cannot modify an attribute in a FozenAttrDict" + ) else: - super(FrozenAttrDict, self).__setattr__(attr, val) + super().__setattr__(attr, val) def update(self, items): if self.__initialized: raise FrozenSetException( - 'Cannot modify an attribute in a FozenAttrDict') + "Cannot modify an attribute in a FozenAttrDict" + ) else: - super(FrozenAttrDict, self).update(items) + super().update(items) -class InstanceCollector(object): - ''' +class InstanceCollector: + """ A class used to simplify collecting of Classes. >> instance_list = collector.create() @@ -423,7 +445,8 @@ class InstanceCollector(object): >> # instance_list contains all instances created since >> # collector.create was called >> collector.remove(instance_list) - ''' + """ + def __init__(self): self.collectors = [] @@ -441,16 +464,17 @@ class InstanceCollector(object): def append_dictlist(dict_, key, value): - ''' + """ Append the `value` to a list associated with `key` in `dict_`. If `key` doesn't exist, create a new list in the `dict_` with value in it. - ''' + """ list_ = dict_.get(key, []) list_.append(value) dict_[key] = list_ + def _filter_file(fname, filters): - with open(fname, "r") as file_: + with open(fname) as file_: for line in file_: for regex in filters: if re.match(regex, line): @@ -460,19 +484,19 @@ def _filter_file(fname, filters): def _copy_file_keep_perms(source, target): - '''Copy a file keeping the original permisions of the target.''' + """Copy a file keeping the original permisions of the target.""" st = os.stat(target) shutil.copy2(source, target) os.chown(target, st[stat.ST_UID], st[stat.ST_GID]) def _filter_file_inplace(fname, dir, filters): - ''' + """ Filter the given file writing filtered lines out to a temporary file, then copy that tempfile back into the original file. - ''' + """ (_, tfname) = tempfile.mkstemp(dir=dir, text=True) - with open(tfname, 'w') as tempfile_: + with open(tfname, "w") as tempfile_: for line in _filter_file(fname, filters): tempfile_.write(line) @@ -481,39 +505,45 @@ def _filter_file_inplace(fname, dir, filters): def diff_out_file(ref_file, out_file, logger, ignore_regexes=tuple()): - '''Diff two files returning the diff as a string.''' + """Diff two files returning the diff as a string.""" if not os.path.exists(ref_file): - raise OSError("%s doesn't exist in reference directory"\ - % ref_file) + raise OSError("%s doesn't exist in reference directory" % ref_file) if not os.path.exists(out_file): raise OSError("%s doesn't exist in output directory" % out_file) _filter_file_inplace(out_file, os.path.dirname(out_file), ignore_regexes) _filter_file_inplace(ref_file, os.path.dirname(out_file), ignore_regexes) - #try : + # try : (_, tfname) = tempfile.mkstemp(dir=os.path.dirname(out_file), text=True) - with open(tfname, 'r+') as tempfile_: + with open(tfname, "r+") as tempfile_: try: - log_call(logger, ['diff', out_file, ref_file], - time=None, stdout=tempfile_) + log_call( + logger, + ["diff", out_file, ref_file], + time=None, + stdout=tempfile_, + ) except OSError: # Likely signals that diff does not exist on this system. fallback # to difflib - with open(out_file, 'r') as outf, open(ref_file, 'r') as reff: - diff = difflib.unified_diff(iter(reff.readline, ''), - iter(outf.readline, ''), - fromfile=ref_file, - tofile=out_file) - return ''.join(diff) + with open(out_file) as outf, open(ref_file) as reff: + diff = difflib.unified_diff( + iter(reff.readline, ""), + iter(outf.readline, ""), + fromfile=ref_file, + tofile=out_file, + ) + return "".join(diff) except subprocess.CalledProcessError: tempfile_.seek(0) - return ''.join(tempfile_.readlines()) + return "".join(tempfile_.readlines()) else: return None -class Timer(): + +class Timer: def __init__(self): self.restart() diff --git a/ext/testlib/loader.py b/ext/testlib/loader.py index 58b1b2e777..d74f76181d 100644 --- a/ext/testlib/loader.py +++ b/ext/testlib/loader.py @@ -26,7 +26,7 @@ # # Authors: Sean Wilson -''' +""" Contains the :class:`Loader` which is responsible for discovering and loading tests. @@ -63,58 +63,66 @@ a :class:`TestSuite` by the test writer will be placed into a :class:`TestSuite` named after the module. .. seealso:: :func:`load_file` -''' +""" import os import re import sys import traceback +import testlib.fixture as fixture_mod import testlib.log as log import testlib.suite as suite_mod import testlib.test_util as test_mod -import testlib.fixture as fixture_mod -import testlib.wrappers as wrappers import testlib.uid as uid +import testlib.wrappers as wrappers + class DuplicateTestItemException(Exception): - ''' + """ Exception indicates multiple test items with the same UID were discovered. - ''' + """ + pass # Match filenames that either begin or end with 'test' or tests and use # - or _ to separate additional name components. default_filepath_regex = re.compile( - r'(((.+[_])?tests?)|(tests?([-_].+)?))\.py$') + r"(((.+[_])?tests?)|(tests?([-_].+)?))\.py$" +) + def default_filepath_filter(filepath): - '''The default filter applied to filepaths to marks as test sources.''' + """The default filter applied to filepaths to marks as test sources.""" filepath = os.path.basename(filepath) if default_filepath_regex.match(filepath): # Make sure doesn't start with . - return not filepath.startswith('.') + return not filepath.startswith(".") return False + def path_as_modulename(filepath): - '''Return the given filepath as a module name.''' + """Return the given filepath as a module name.""" # Remove the file extention (.py) return os.path.splitext(os.path.basename(filepath))[0] + def path_as_suitename(filepath): - return os.path.split(os.path.dirname(os.path.abspath((filepath))))[-1] + return os.path.split(os.path.dirname(os.path.abspath(filepath)))[-1] + def _assert_files_in_same_dir(files): if __debug__: if files: directory = os.path.dirname(files[0]) for f in files: - assert(os.path.dirname(f) == directory) + assert os.path.dirname(f) == directory -class Loader(object): - ''' + +class Loader: + """ Class for discovering tests. Discovered :class:`TestCase` and :class:`TestSuite` objects are wrapped by @@ -135,7 +143,8 @@ class Loader(object): .. warn:: This class is extremely thread-unsafe. It modifies the sys path and global config. Use with care. - ''' + """ + def __init__(self): self.suites = [] self.suite_uids = {} @@ -153,16 +162,15 @@ class Loader(object): for file_ in files: self.load_file(file_) - return wrappers.LoadedLibrary( - [self.suite_uids[id_] for id_ in uids]) + return wrappers.LoadedLibrary([self.suite_uids[id_] for id_ in uids]) def _verify_no_duplicate_suites(self, new_suites): new_suite_uids = self.suite_uids.copy() for suite in new_suites: if suite.uid in new_suite_uids: raise DuplicateTestItemException( - "More than one suite with UID '%s' was defined" %\ - suite.uid) + "More than one suite with UID '%s' was defined" % suite.uid + ) new_suite_uids[suite.uid] = suite def _verify_no_duplicate_tests_in_suites(self, new_suites): @@ -170,17 +178,17 @@ class Loader(object): test_uids = set() for test in suite: if test.uid in test_uids: - raise DuplicateTestItemException( - "More than one test with UID '%s' was defined" - " in suite '%s'" - % (test.uid, suite.uid)) + raise DuplicateTestItemException( + "More than one test with UID '%s' was defined" + " in suite '%s'" % (test.uid, suite.uid) + ) test_uids.add(test.uid) def load_root(self, root): - ''' + """ Load files from the given root directory which match `self.filepath_filter`. - ''' + """ for directory in self._discover_files(root): directory = list(directory) if directory: @@ -193,17 +201,18 @@ class Loader(object): if path in self._files: if not self._files[path]: - raise Exception('Attempted to load a file which already' - ' failed to load') + raise Exception( + "Attempted to load a file which already" " failed to load" + ) else: - log.test_log.debug('Tried to reload: %s' % path) + log.test_log.debug("Tried to reload: %s" % path) return # Create a custom dictionary for the loaded module. newdict = { - '__builtins__':__builtins__, - '__name__': path_as_modulename(path), - '__file__': path, + "__builtins__": __builtins__, + "__name__": path_as_modulename(path), + "__file__": path, } # Add the file's containing directory to the system path. So it can do @@ -222,9 +231,9 @@ class Loader(object): except Exception as e: log.test_log.debug(traceback.format_exc()) log.test_log.warn( - 'Exception thrown while loading "%s"\n' - 'Ignoring all tests in this file.' - % (path)) + 'Exception thrown while loading "%s"\n' + "Ignoring all tests in this file." % (path) + ) # Clean up sys.path[:] = old_path os.chdir(cwd) @@ -247,27 +256,34 @@ class Loader(object): # tests. # NOTE: This is automatically collected (we still have the # collector active.) - suite_mod.TestSuite(tests=orphan_tests, - name=path_as_suitename(path)) + suite_mod.TestSuite( + tests=orphan_tests, name=path_as_suitename(path) + ) try: - loaded_suites = [wrappers.LoadedSuite(suite, path) - for suite in new_suites] + loaded_suites = [ + wrappers.LoadedSuite(suite, path) for suite in new_suites + ] self._verify_no_duplicate_suites(loaded_suites) self._verify_no_duplicate_tests_in_suites(loaded_suites) except Exception as e: - log.test_log.warn('%s\n' - 'Exception thrown while loading "%s"\n' - 'Ignoring all tests in this file.' - % (traceback.format_exc(), path)) + log.test_log.warn( + "%s\n" + 'Exception thrown while loading "%s"\n' + "Ignoring all tests in this file." + % (traceback.format_exc(), path) + ) else: - log.test_log.info('Discovered %d tests and %d suites in %s' - '' % (len(new_tests), len(loaded_suites), path)) + log.test_log.info( + "Discovered %d tests and %d suites in %s" + "" % (len(new_tests), len(loaded_suites), path) + ) self.suites.extend(loaded_suites) - self.suite_uids.update({suite.uid: suite - for suite in loaded_suites}) + self.suite_uids.update( + {suite.uid: suite for suite in loaded_suites} + ) # Clean up sys.path[:] = old_path os.chdir(cwd) @@ -276,18 +292,19 @@ class Loader(object): fixture_mod.Fixture.collector.remove(new_fixtures) def _discover_files(self, root): - ''' + """ Recurse down from the given root directory returning a list of directories which contain a list of files matching `self.filepath_filter`. - ''' + """ # Will probably want to order this traversal. for root, dirnames, filenames in os.walk(root): dirnames.sort() if filenames: filenames.sort() - filepaths = [os.path.join(root, filename) \ - for filename in filenames] + filepaths = [ + os.path.join(root, filename) for filename in filenames + ] filepaths = filter(self.filepath_filter, filepaths) if filepaths: yield filepaths diff --git a/ext/testlib/log.py b/ext/testlib/log.py index fb5907cd5c..e66d1bbc02 100644 --- a/ext/testlib/log.py +++ b/ext/testlib/log.py @@ -26,49 +26,51 @@ # # Authors: Sean Wilson -''' +""" This module supplies the global `test_log` object which all testing results and messages are reported through. -''' +""" import testlib.wrappers as wrappers -class LogLevel(): + +class LogLevel: Fatal = 0 Error = 1 - Warn = 2 - Info = 3 + Warn = 2 + Info = 3 Debug = 4 Trace = 5 class RecordTypeCounterMetaclass(type): - ''' + """ Record type metaclass. Adds a static integer value in addition to typeinfo so identifiers are common across processes, networks and module reloads. - ''' + """ + counter = 0 + def __init__(cls, name, bases, dct): cls.type_id = RecordTypeCounterMetaclass.counter RecordTypeCounterMetaclass.counter += 1 -class Record(object, metaclass=RecordTypeCounterMetaclass): - ''' +class Record(metaclass=RecordTypeCounterMetaclass): + """ A generic object that is passed to the :class:`Log` and its handlers. ..note: Although not statically enforced, all items in the record should be be pickleable. This enables logging accross multiple processes. - ''' + """ def __init__(self, **data): self.data = data def __getitem__(self, item): if item not in self.data: - raise KeyError('%s not in record %s' %\ - (item, self.__class__.__name__)) + raise KeyError(f"{item} not in record {self.__class__.__name__}") return self.data[item] def __str__(self): @@ -78,35 +80,57 @@ class Record(object, metaclass=RecordTypeCounterMetaclass): class StatusRecord(Record): def __init__(self, obj, status): Record.__init__(self, metadata=obj.metadata, status=status) + + class ResultRecord(Record): def __init__(self, obj, result): Record.__init__(self, metadata=obj.metadata, result=result) -#TODO Refactor this shit... Not ideal. Should just specify attributes. + + +# TODO Refactor this shit... Not ideal. Should just specify attributes. class TestStatus(StatusRecord): pass + + class SuiteStatus(StatusRecord): pass + + class LibraryStatus(StatusRecord): pass + + class TestResult(ResultRecord): pass + + class SuiteResult(ResultRecord): pass + + class LibraryResult(ResultRecord): pass + + # Test Output Types class TestStderr(Record): pass + + class TestStdout(Record): pass + + # Message (Raw String) Types class TestMessage(Record): pass + + class LibraryMessage(Record): pass -class Log(object): +class Log: _result_typemap = { wrappers.LoadedLibrary.__name__: LibraryResult, wrappers.LoadedSuite.__name__: SuiteResult, @@ -121,8 +145,8 @@ class Log(object): def __init__(self, test=None): self.test = test self.handlers = [] - self._opened = False # TODO Guards to methods - self._closed = False # TODO Guards to methods + self._opened = False # TODO Guards to methods + self._closed = False # TODO Guards to methods def finish_init(self): self._opened = True @@ -136,19 +160,25 @@ class Log(object): if not self._opened: self.finish_init() if self._closed: - raise Exception('The log has been closed' - ' and is no longer available.') + raise Exception( + "The log has been closed" " and is no longer available." + ) for handler in self.handlers: handler.handle(record) def message(self, message, level=LogLevel.Info, bold=False, **metadata): if self.test: - record = TestMessage(message=message, level=level, - test_uid=self.test.uid, suite_uid=self.test.parent_suite.uid) + record = TestMessage( + message=message, + level=level, + test_uid=self.test.uid, + suite_uid=self.test.parent_suite.uid, + ) else: - record = LibraryMessage(message=message, level=level, - bold=bold, **metadata) + record = LibraryMessage( + message=message, level=level, bold=bold, **metadata + ) self.log(record) @@ -168,20 +198,19 @@ class Log(object): self.message(message, LogLevel.Trace) def status_update(self, obj, status): - self.log( - self._status_typemap[obj.__class__.__name__](obj, status)) + self.log(self._status_typemap[obj.__class__.__name__](obj, status)) def result_update(self, obj, result): - self.log( - self._result_typemap[obj.__class__.__name__](obj, result)) + self.log(self._result_typemap[obj.__class__.__name__](obj, result)) def add_handler(self, handler): if self._opened: - raise Exception('Unable to add a handler once the log is open.') + raise Exception("Unable to add a handler once the log is open.") self.handlers.append(handler) def close_handler(self, handler): handler.close() self.handlers.remove(handler) + test_log = Log() diff --git a/ext/testlib/main.py b/ext/testlib/main.py index b9d8e93e66..d93f167a77 100644 --- a/ext/testlib/main.py +++ b/ext/testlib/main.py @@ -26,8 +26,8 @@ # # Authors: Sean Wilson -import os import itertools +import os import testlib.configuration as configuration import testlib.handlers as handlers @@ -39,21 +39,26 @@ import testlib.runner as runner import testlib.terminal as terminal import testlib.uid as uid + def entry_message(): log.test_log.message("Running the new gem5 testing script.") log.test_log.message("For more information see TESTING.md.") - log.test_log.message("To see details as the testing scripts are" - " running, use the option" - " -v, -vv, or -vvv") + log.test_log.message( + "To see details as the testing scripts are" + " running, use the option" + " -v, -vv, or -vvv" + ) -class RunLogHandler(): + +class RunLogHandler: def __init__(self): term_handler = handlers.TerminalHandler( - verbosity=configuration.config.verbose+log.LogLevel.Info + verbosity=configuration.config.verbose + log.LogLevel.Info ) summary_handler = handlers.SummaryHandler() self.mp_handler = handlers.MultiprocessingHandlerWrapper( - summary_handler, term_handler) + summary_handler, term_handler + ) self.mp_handler.async_process() log.test_log.add_handler(self.mp_handler) entry_message() @@ -61,7 +66,8 @@ class RunLogHandler(): def schedule_finalized(self, test_schedule): # Create the result handler object. self.result_handler = handlers.ResultHandler( - test_schedule, configuration.config.result_path) + test_schedule, configuration.config.result_path + ) self.mp_handler.add_handler(self.result_handler) def finish_testing(self): @@ -78,35 +84,43 @@ class RunLogHandler(): self.mp_handler.close() def unsuccessful(self): - ''' + """ Performs an or reduce on all of the results. Returns true if at least one test is unsuccessful, false when all tests pass - ''' + """ return self.result_handler.unsuccessful() + def get_config_tags(): - return getattr(configuration.config, - configuration.StorePositionalTagsAction.position_kword) + return getattr( + configuration.config, + configuration.StorePositionalTagsAction.position_kword, + ) + def filter_with_config_tags(loaded_library): tags = get_config_tags() final_tags = [] - regex_fmt = '^%s$' + regex_fmt = "^%s$" cfg = configuration.config def _append_inc_tag_filter(name): if hasattr(cfg, name): tag_opts = getattr(cfg, name) for tag in tag_opts: - final_tags.append(configuration.TagRegex(True, regex_fmt % tag)) + final_tags.append( + configuration.TagRegex(True, regex_fmt % tag) + ) def _append_rem_tag_filter(name): if hasattr(cfg, name): tag_opts = getattr(cfg, name) for tag in cfg.constants.supported_tags[name]: if tag not in tag_opts: - final_tags.append(configuration.TagRegex(False, regex_fmt % tag)) + final_tags.append( + configuration.TagRegex(False, regex_fmt % tag) + ) # Append additional tags for the isa, length, and variant options. # They apply last (they take priority) @@ -114,7 +128,7 @@ def filter_with_config_tags(loaded_library): cfg.constants.isa_tag_type, cfg.constants.length_tag_type, cfg.constants.host_isa_tag_type, - cfg.constants.variant_tag_type + cfg.constants.variant_tag_type, ) for tagname in special_tags: @@ -126,15 +140,15 @@ def filter_with_config_tags(loaded_library): tags = tuple() filters = list(itertools.chain(final_tags, tags)) - string = 'Filtering suites with tags as follows:\n' - filter_string = '\t\n'.join((str(f) for f in filters)) + string = "Filtering suites with tags as follows:\n" + filter_string = "\t\n".join(str(f) for f in filters) log.test_log.trace(string + filter_string) return filter_with_tags(loaded_library, filters) def filter_with_tags(loaded_library, filters): - ''' + """ Filter logic supports two filter types: --include-tags --exclude-tags @@ -168,7 +182,7 @@ def filter_with_tags(loaded_library, filters): set() # Removed all suites which have tags # Process --include-tags "X86" set(suite_X86) - ''' + """ if not filters: return @@ -182,6 +196,7 @@ def filter_with_tags(loaded_library, filters): def exclude(excludes): return suites - excludes + def include(includes): return suites | includes @@ -189,38 +204,65 @@ def filter_with_tags(loaded_library, filters): matched_tags = (tag for tag in tags if tag_regex.regex.search(tag)) for tag in matched_tags: matched_suites = set(query_runner.suites_with_tag(tag)) - suites = include(matched_suites) if tag_regex.include \ - else exclude(matched_suites) + suites = ( + include(matched_suites) + if tag_regex.include + else exclude(matched_suites) + ) # Set the library's suites to only those which where accepted by our filter - loaded_library.suites = [suite for suite in loaded_library.suites - if suite in suites] + loaded_library.suites = [ + suite for suite in loaded_library.suites if suite in suites + ] + # TODO Add results command for listing previous results. + def load_tests(): - ''' + """ Create a TestLoader and load tests for the directory given by the config. - ''' + """ testloader = loader_mod.Loader() log.test_log.message(terminal.separator()) - log.test_log.message('Loading Tests', bold=True) + log.test_log.message("Loading Tests", bold=True) for root in configuration.config.directories: testloader.load_root(root) return testloader + def do_list(): term_handler = handlers.TerminalHandler( - verbosity=configuration.config.verbose+log.LogLevel.Info, - machine_only=configuration.config.quiet + verbosity=configuration.config.verbose + log.LogLevel.Info, + machine_only=configuration.config.quiet, ) log.test_log.add_handler(term_handler) entry_message() - test_schedule = load_tests().schedule + if configuration.config.uid: + uid_ = uid.UID.from_uid(configuration.config.uid) + if isinstance(uid_, uid.TestUID): + log.test_log.error( + "Unable to list a standalone test.\n" + "Gem5 expects test suites to be the smallest unit " + " of test.\n\n" + "Pass a SuiteUID instead." + ) + return + test_schedule = loader_mod.Loader().load_schedule_for_suites(uid_) + if get_config_tags(): + log.test_log.warn( + "The '--uid' flag was supplied," + " '--include-tags' and '--exclude-tags' will be ignored." + ) + else: + test_schedule = load_tests().schedule + # Filter tests based on tags + filter_with_config_tags(test_schedule) + filter_with_config_tags(test_schedule) qrunner = query.QueryRunner(test_schedule) @@ -231,15 +273,21 @@ def do_list(): qrunner.list_tests() elif configuration.config.all_tags: qrunner.list_tags() + elif configuration.config.fixtures: + qrunner.list_fixtures() + elif configuration.config.build_targets: + qrunner.list_build_targets() else: qrunner.list_suites() qrunner.list_tests() qrunner.list_tags() + qrunner.list_build_targets() return 0 + def run_schedule(test_schedule, log_handler): - ''' + """ Test Phases ----------- * Test Collection @@ -253,15 +301,18 @@ def run_schedule(test_schedule, log_handler): * Test Fixture Teardown * Suite Fixture Teardown * Global Fixture Teardown - ''' + """ log_handler.schedule_finalized(test_schedule) log.test_log.message(terminal.separator()) - log.test_log.message('Running Tests from {} suites' - .format(len(test_schedule.suites)), bold=True) - log.test_log.message("Results will be stored in {}".format( - configuration.config.result_path)) + log.test_log.message( + f"Running Tests from {len(test_schedule.suites)} suites", + bold=True, + ) + log.test_log.message( + f"Results will be stored in {configuration.config.result_path}" + ) log.test_log.message(terminal.separator()) # Build global fixtures and exectute scheduled test suites. @@ -278,16 +329,19 @@ def run_schedule(test_schedule, log_handler): return 1 if failed else 0 + def do_run(): # Initialize early parts of the log. with RunLogHandler() as log_handler: if configuration.config.uid: uid_ = uid.UID.from_uid(configuration.config.uid) if isinstance(uid_, uid.TestUID): - log.test_log.error('Unable to run a standalone test.\n' - 'Gem5 expects test suites to be the smallest unit ' - ' of test.\n\n' - 'Pass a SuiteUID instead.') + log.test_log.error( + "Unable to run a standalone test.\n" + "Gem5 expects test suites to be the smallest unit " + " of test.\n\n" + "Pass a SuiteUID instead." + ) return test_schedule = loader_mod.Loader().load_schedule_for_suites(uid_) if get_config_tags(): @@ -302,13 +356,17 @@ def do_run(): # Execute the tests return run_schedule(test_schedule, log_handler) + def do_rerun(): # Init early parts of log with RunLogHandler() as log_handler: # Load previous results results = result.InternalSavedResults.load( - os.path.join(configuration.config.result_path, - configuration.constants.pickle_filename)) + os.path.join( + configuration.config.result_path, + configuration.constants.pickle_filename, + ) + ) rerun_suites = (suite.uid for suite in results if suite.unsuccessful) @@ -319,16 +377,17 @@ def do_rerun(): # Execute the tests return run_schedule(test_schedule, log_handler) + def main(): - ''' + """ Main entrypoint for the testlib test library. Returns 0 on success and 1 otherwise so it can be used as a return code for scripts. - ''' + """ configuration.initialize_config() # 'do' the given command. - result = globals()['do_'+configuration.config.command]() + result = globals()["do_" + configuration.config.command]() log.test_log.close() return result diff --git a/ext/testlib/query.py b/ext/testlib/query.py index 174af626fe..b2c21f8fb4 100644 --- a/ext/testlib/query.py +++ b/ext/testlib/query.py @@ -26,12 +26,13 @@ # # Authors: Sean Wilson -import testlib.terminal as terminal import testlib.log as log +import testlib.terminal as terminal + # TODO Refactor print logic out of this so the objects # created are separate from print logic. -class QueryRunner(object): +class QueryRunner: def __init__(self, test_schedule): self.schedule = test_schedule @@ -49,23 +50,42 @@ class QueryRunner(object): def list_tests(self): log.test_log.message(terminal.separator()) - log.test_log.message('Listing all Test Cases.', bold=True) + log.test_log.message("Listing all Test Cases.", bold=True) log.test_log.message(terminal.separator()) for suite in self.schedule: for test in suite: log.test_log.message(test.uid, machine_readable=True) + def list_fixtures(self): + log.test_log.message(terminal.separator()) + log.test_log.message("Listing all Test Fixtures.", bold=True) + log.test_log.message(terminal.separator()) + for fixture in self.schedule.all_fixtures(): + log.test_log.message(fixture, machine_readable=True) + + def list_build_targets(self): + log.test_log.message(terminal.separator()) + log.test_log.message("Listing all gem5 Build Targets.", bold=True) + log.test_log.message(terminal.separator()) + builds = [] + for fixture in self.schedule.all_fixtures(): + build = fixture.get_get_build_info() + if build and build not in builds: + builds.append(build) + for build in builds: + log.test_log.message(build, machine_readable=True) + def list_suites(self): log.test_log.message(terminal.separator()) - log.test_log.message('Listing all Test Suites.', bold=True) + log.test_log.message("Listing all Test Suites.", bold=True) log.test_log.message(terminal.separator()) for suite in self.suites(): log.test_log.message(suite.uid, machine_readable=True) def list_tags(self): log.test_log.message(terminal.separator()) - log.test_log.message('Listing all Test Tags.', bold=True) + log.test_log.message("Listing all Test Tags.", bold=True) log.test_log.message(terminal.separator()) for tag in self.tags(): - log.test_log.message(tag, machine_readable=True) \ No newline at end of file + log.test_log.message(tag, machine_readable=True) diff --git a/ext/testlib/result.py b/ext/testlib/result.py index 786febde2a..73c8cea5d5 100644 --- a/ext/testlib/result.py +++ b/ext/testlib/result.py @@ -42,9 +42,10 @@ import os import pickle import xml.sax.saxutils -from testlib.configuration import config import testlib.helper as helper import testlib.state as state +from testlib.configuration import config + def _create_uid_index(iterable): index = {} @@ -58,12 +59,15 @@ class _CommonMetadataMixin: @property def name(self): return self._metadata.name + @property def uid(self): return self._metadata.uid + @property def result(self): return self._metadata.result + @result.setter def result(self, result): self._metadata.result = result @@ -83,12 +87,10 @@ class InternalTestResult(_CommonMetadataMixin): self.suite = suite self.stderr = os.path.join( - InternalSavedResults.output_path(self.uid, suite.uid), - 'stderr' + InternalSavedResults.output_path(self.uid, suite.uid), "stderr" ) self.stdout = os.path.join( - InternalSavedResults.output_path(self.uid, suite.uid), - 'stdout' + InternalSavedResults.output_path(self.uid, suite.uid), "stdout" ) @@ -99,8 +101,9 @@ class InternalSuiteResult(_CommonMetadataMixin): self._wrap_tests(obj) def _wrap_tests(self, obj): - self._tests = [InternalTestResult(test, self, self.directory) - for test in obj] + self._tests = [ + InternalTestResult(test, self, self.directory) for test in obj + ] self._tests_index = _create_uid_index(self._tests) def get_test(self, uid): @@ -129,13 +132,14 @@ class InternalLibraryResults(_CommonMetadataMixin): return iter(self._suites) def _wrap_suites(self, obj): - self._suites = [InternalSuiteResult(suite, self.directory) - for suite in obj] + self._suites = [ + InternalSuiteResult(suite, self.directory) for suite in obj + ] self._suites_index = _create_uid_index(self._suites) def add_suite(self, suite): if suite.uid in self._suites: - raise ValueError('Cannot have duplicate suite UIDs.') + raise ValueError("Cannot have duplicate suite UIDs.") self._suites[suite.uid] = suite def get_suite_result(self, suite_uid): @@ -151,84 +155,85 @@ class InternalLibraryResults(_CommonMetadataMixin): helper.append_dictlist(results, test.result.value, test) return results + class InternalSavedResults: @staticmethod def output_path(test_uid, suite_uid, base=None): - ''' + """ Return the path which results for a specific test case should be stored. - ''' + """ if base is None: base = config.result_path return os.path.join( - base, - str(suite_uid).replace(os.path.sep, '-'), - str(test_uid).replace(os.path.sep, '-')) + base, + str(suite_uid).replace(os.path.sep, "-"), + str(test_uid).replace(os.path.sep, "-"), + ) @staticmethod def save(results, path, protocol=pickle.HIGHEST_PROTOCOL): if not os.path.exists(os.path.dirname(path)): - try: - os.makedirs(os.path.dirname(path)) - except OSError as exc: # Guard against race condition - if exc.errno != errno.EEXIST: - raise + try: + os.makedirs(os.path.dirname(path)) + except OSError as exc: # Guard against race condition + if exc.errno != errno.EEXIST: + raise - with open(path, 'wb') as f: + with open(path, "wb") as f: pickle.dump(results, f, protocol) @staticmethod def load(path): - with open(path, 'rb') as f: + with open(path, "rb") as f: return pickle.load(f) -class XMLElement(object): +class XMLElement: def write(self, file_): self.begin(file_) self.end(file_) def begin(self, file_): - file_.write('<') + file_.write("<") file_.write(self.name) - if hasattr(self, 'attributes'): + if hasattr(self, "attributes"): for attr in self.attributes: - file_.write(' ') + file_.write(" ") attr.write(file_) - file_.write('>') + file_.write(">") self.body(file_) def body(self, file_): - if hasattr(self, 'elements'): + if hasattr(self, "elements"): for elem in self.elements: - file_.write('\n') + file_.write("\n") elem.write(file_) - if hasattr(self, 'content'): - file_.write('\n') - file_.write( - xml.sax.saxutils.escape(self.content)) - file_.write('\n') + if hasattr(self, "content"): + file_.write("\n") + file_.write(xml.sax.saxutils.escape(self.content)) + file_.write("\n") def end(self, file_): - file_.write('' % self.name) + file_.write("" % self.name) -class XMLAttribute(object): + +class XMLAttribute: def __init__(self, name, value): self.name = name self.value = value def write(self, file_): - file_.write('%s=%s' % (self.name, - xml.sax.saxutils.quoteattr(self.value))) + file_.write(f"{self.name}={xml.sax.saxutils.quoteattr(self.value)}") class JUnitTestSuites(XMLElement): - name = 'testsuites' + name = "testsuites" result_map = { - state.Result.Errored: 'errors', - state.Result.Failed: 'failures', - state.Result.Passed: 'tests' + state.Result.Errored: "errors", + state.Result.Failed: "failures", + state.Result.Passed: "tests", } def __init__(self, internal_results): @@ -236,8 +241,9 @@ class JUnitTestSuites(XMLElement): self.attributes = [] for result, tests in results.items(): - self.attributes.append(self.result_attribute(result, - str(len(tests)))) + self.attributes.append( + self.result_attribute(result, str(len(tests))) + ) self.elements = [] for suite in internal_results: @@ -246,24 +252,24 @@ class JUnitTestSuites(XMLElement): def result_attribute(self, result, count): return XMLAttribute(self.result_map[result], count) + class JUnitTestSuite(JUnitTestSuites): - name = 'testsuite' + name = "testsuite" result_map = { - state.Result.Errored: 'errors', - state.Result.Failed: 'failures', - state.Result.Passed: 'tests', - state.Result.Skipped: 'skipped' + state.Result.Errored: "errors", + state.Result.Failed: "failures", + state.Result.Passed: "tests", + state.Result.Skipped: "skipped", } def __init__(self, suite_result): results = suite_result.aggregate_test_results() - self.attributes = [ - XMLAttribute('name', suite_result.name) - ] + self.attributes = [XMLAttribute("name", suite_result.name)] for result, tests in results.items(): - self.attributes.append(self.result_attribute(result, - str(len(tests)))) + self.attributes.append( + self.result_attribute(result, str(len(tests))) + ) self.elements = [] for test in suite_result: @@ -272,40 +278,42 @@ class JUnitTestSuite(JUnitTestSuites): def result_attribute(self, result, count): return XMLAttribute(self.result_map[result], count) + class JUnitTestCase(XMLElement): - name = 'testcase' + name = "testcase" + def __init__(self, test_result): self.attributes = [ - XMLAttribute('name', test_result.name), - # TODO JUnit expects class of test.. add as test metadata. - XMLAttribute('classname', str(test_result.uid)), - XMLAttribute('status', str(test_result.result)), - XMLAttribute('time', str(test_result.time["user_time"])), + XMLAttribute("name", test_result.name), + # TODO JUnit expects class of test.. add as test metadata. + XMLAttribute("classname", str(test_result.uid)), + XMLAttribute("status", str(test_result.result)), + XMLAttribute("time", str(test_result.time["user_time"])), ] # TODO JUnit expects a message for the reason a test was # skipped or errored, save this with the test metadata. # http://llg.cubic.org/docs/junit/ self.elements = [ - LargeFileElement('system-err', test_result.stderr), - LargeFileElement('system-out', test_result.stdout), + LargeFileElement("system-err", test_result.stderr), + LargeFileElement("system-out", test_result.stdout), ] - if str(test_result.result) == 'Failed': - self.elements.append(JUnitFailure( - 'Test failed', - str(test_result.result.reason)) + if str(test_result.result) == "Failed": + self.elements.append( + JUnitFailure("Test failed", str(test_result.result.reason)) ) class JUnitFailure(XMLElement): - name = 'failure' + name = "failure" + def __init__(self, message, cause): self.attributes = [ - XMLAttribute('message', message), + XMLAttribute("message", message), ] cause_element = XMLElement() - cause_element.name = 'cause' + cause_element.name = "cause" cause_element.content = cause self.elements = [cause_element] @@ -318,10 +326,10 @@ class LargeFileElement(XMLElement): def body(self, file_): try: - with open(self.filename, 'r') as f: + with open(self.filename) as f: for line in f: file_.write(xml.sax.saxutils.escape(line)) - except IOError: + except OSError: # TODO Better error logic, this is sometimes O.K. # if there was no stdout/stderr captured for the test # @@ -330,15 +338,13 @@ class LargeFileElement(XMLElement): pass - class JUnitSavedResults: @staticmethod def save(results, path): - ''' + """ Compile the internal results into JUnit format writting it to the given file. - ''' + """ results = JUnitTestSuites(results) - with open(path, 'w') as f: + with open(path, "w") as f: results.write(f) - diff --git a/ext/testlib/runner.py b/ext/testlib/runner.py index 16ff952985..540acb35df 100644 --- a/ext/testlib/runner.py +++ b/ext/testlib/runner.py @@ -43,18 +43,21 @@ import traceback import testlib.helper as helper import testlib.log as log - -from testlib.state import Status, Result from testlib.fixture import SkipException +from testlib.state import ( + Result, + Status, +) + def compute_aggregate_result(iterable): - ''' + """ Status of the test suite by default is: * Passed if all contained tests passed * Errored if any contained tests errored * Failed if no tests errored, but one or more failed. * Skipped if all contained tests were skipped - ''' + """ failed = [] skipped = [] for testitem in iterable: @@ -73,18 +76,18 @@ def compute_aggregate_result(iterable): else: return Result(Result.Passed) -class TestParameters(object): + +class TestParameters: def __init__(self, test, suite): self.test = test self.suite = suite self.log = log.test_log self.log.test = test - self.time = { - "user_time" : 0, "system_time" : 0} + self.time = {"user_time": 0, "system_time": 0} @helper.cacheresult def _fixtures(self): - fixtures = {fixture.name:fixture for fixture in self.suite.fixtures} + fixtures = {fixture.name: fixture for fixture in self.suite.fixtures} for fixture in self.test.fixtures: fixtures[fixture.name] = fixture return fixtures @@ -139,18 +142,18 @@ class RunnerPattern: else: self.testable.status = Status.Complete + class TestRunner(RunnerPattern): def test(self): - test_params = TestParameters( - self.testable, - self.testable.parent_suite) + test_params = TestParameters(self.testable, self.testable.parent_suite) try: # Running the test test_params.test.test(test_params) except Exception: - self.testable.result = Result(Result.Failed, - traceback.format_exc()) + self.testable.result = Result( + Result.Failed, traceback.format_exc() + ) else: self.testable.result = Result(Result.Passed) @@ -161,8 +164,7 @@ class SuiteRunner(RunnerPattern): def test(self): for test in self.testable: test.runner(test).run() - self.testable.result = compute_aggregate_result( - iter(self.testable)) + self.testable.result = compute_aggregate_result(iter(self.testable)) class LibraryRunner(SuiteRunner): @@ -175,23 +177,23 @@ class LibraryParallelRunner(RunnerPattern): def test(self): pool = multiprocessing.dummy.Pool(self.threads) - pool.map(lambda suite : suite.runner(suite).run(), self.testable) - self.testable.result = compute_aggregate_result( - iter(self.testable)) + pool.map(lambda suite: suite.runner(suite).run(), self.testable) + self.testable.result = compute_aggregate_result(iter(self.testable)) class BrokenFixtureException(Exception): def __init__(self, fixture, testitem, trace): self.trace = trace - self.msg = ('%s\n' - 'Exception raised building "%s" raised SkipException' - ' for "%s".' % - (trace, fixture.name, testitem.name) + self.msg = ( + "%s\n" + 'Exception raised building "%s" raised SkipException' + ' for "%s".' % (trace, fixture.name, testitem.name) ) - super(BrokenFixtureException, self).__init__(self.msg) + super().__init__(self.msg) -class FixtureBuilder(object): + +class FixtureBuilder: def __init__(self, fixtures): self.fixtures = fixtures self.built_fixtures = [] @@ -207,12 +209,15 @@ class FixtureBuilder(object): raise except Exception as e: exc = traceback.format_exc() - msg = 'Exception raised while setting up fixture for %s' %\ - testitem.uid - log.test_log.warn('%s\n%s' % (exc, msg)) + msg = ( + "Exception raised while setting up fixture for %s" + % testitem.uid + ) + log.test_log.warn(f"{exc}\n{msg}") - raise BrokenFixtureException(fixture, testitem, - traceback.format_exc()) + raise BrokenFixtureException( + fixture, testitem, traceback.format_exc() + ) def post_test_procedure(self, testitem): for fixture in self.built_fixtures: @@ -225,6 +230,8 @@ class FixtureBuilder(object): except Exception: # Log exception but keep cleaning up. exc = traceback.format_exc() - msg = 'Exception raised while tearing down fixture for %s' %\ - testitem.uid - log.test_log.warn('%s\n%s' % (exc, msg)) + msg = ( + "Exception raised while tearing down fixture for %s" + % testitem.uid + ) + log.test_log.warn(f"{exc}\n{msg}") diff --git a/ext/testlib/state.py b/ext/testlib/state.py index d220bb1019..21a23628bc 100644 --- a/ext/testlib/state.py +++ b/ext/testlib/state.py @@ -24,14 +24,15 @@ # # Authors: Sean Wilson + class Result: - enums = ''' + enums = """ NotRun Skipped Passed Failed Errored - '''.split() + """.split() for idx, enum in enumerate(enums): locals()[enum] = idx @@ -46,15 +47,16 @@ class Result: def __str__(self): return self.name(self.value) + class Status: - enums = ''' + enums = """ Unscheduled Building Running TearingDown Complete Avoided - '''.split() + """.split() for idx, enum in enumerate(enums): locals()[enum] = idx diff --git a/ext/testlib/suite.py b/ext/testlib/suite.py index eae52fd922..4a24c9c481 100644 --- a/ext/testlib/suite.py +++ b/ext/testlib/suite.py @@ -30,8 +30,9 @@ import testlib.helper as helper import testlib.runner as runner_mod -class TestSuite(object): - ''' + +class TestSuite: + """ An object grouping a collection of tests. It provides tags which enable filtering during list and run selection. All tests held in the suite must have a unique name. @@ -44,7 +45,8 @@ class TestSuite(object): To reduce test definition boilerplate, the :func:`init` method is forwarded all `*args` and `**kwargs`. This means derived classes can define init without boilerplate super().__init__(*args, **kwargs). - ''' + """ + runner = runner_mod.SuiteRunner collector = helper.InstanceCollector() fixtures = [] @@ -52,12 +54,18 @@ class TestSuite(object): tags = set() def __new__(klass, *args, **kwargs): - obj = super(TestSuite, klass).__new__(klass) + obj = super().__new__(klass) TestSuite.collector.collect(obj) return obj - def __init__(self, name=None, fixtures=tuple(), tests=tuple(), - tags=tuple(), **kwargs): + def __init__( + self, + name=None, + fixtures=tuple(), + tests=tuple(), + tags=tuple(), + **kwargs + ): self.fixtures = self.fixtures + list(fixtures) self.tags = self.tags | set(tags) self.tests = self.tests + list(tests) @@ -66,4 +74,4 @@ class TestSuite(object): self.name = name def __iter__(self): - return iter(self.tests) \ No newline at end of file + return iter(self.tests) diff --git a/ext/testlib/terminal.py b/ext/testlib/terminal.py index be489f5296..fa3531093e 100644 --- a/ext/testlib/terminal.py +++ b/ext/testlib/terminal.py @@ -24,10 +24,10 @@ # # Author: Steve Reinhardt -import sys import fcntl -import termios import struct +import sys +import termios # Intended usage example: # @@ -41,7 +41,7 @@ import struct # ANSI color names in index order color_names = "Black Red Green Yellow Blue Magenta Cyan White".split() -default_separator = '=' +default_separator = "=" # Character attribute capabilities. Note that not all terminals # support all of these capabilities, or support them @@ -54,39 +54,46 @@ default_separator = '=' # Please feel free to add information about other terminals here. # capability_map = { - 'Bold': 'bold', - 'Dim': 'dim', - 'Blink': 'blink', - 'Underline': 'smul', - 'Reverse': 'rev', - 'Standout': 'smso', - 'Normal': 'sgr0' + "Bold": "bold", + "Dim": "dim", + "Blink": "blink", + "Underline": "smul", + "Reverse": "rev", + "Standout": "smso", + "Normal": "sgr0", } capability_names = capability_map.keys() + def null_cap_string(s, *args): - return '' + return "" + try: import curses + curses.setupterm() + def cap_string(s, *args): cap = curses.tigetstr(s) if cap: return curses.tparm(cap, *args).decode("utf-8") else: - return '' + return "" + except: cap_string = null_cap_string -class ColorStrings(object): + +class ColorStrings: def __init__(self, cap_string): for i, c in enumerate(color_names): - setattr(self, c, cap_string('setaf', i)) + setattr(self, c, cap_string("setaf", i)) for name, cap in capability_map.items(): setattr(self, name, cap_string(cap)) + termcap = ColorStrings(cap_string) no_termcap = ColorStrings(null_cap_string) @@ -95,7 +102,8 @@ if sys.stdout.isatty(): else: tty_termcap = no_termcap -def get_termcap(use_colors = None): + +def get_termcap(use_colors=None): if use_colors: return termcap elif use_colors is None: @@ -104,65 +112,78 @@ def get_termcap(use_colors = None): else: return no_termcap + def terminal_size(): - '''Return the (width, heigth) of the terminal screen.''' + """Return the (width, heigth) of the terminal screen.""" try: - h, w, hp, wp = struct.unpack('HHHH', - fcntl.ioctl(0, termios.TIOCGWINSZ, - struct.pack('HHHH', 0, 0, 0, 0))) + h, w, hp, wp = struct.unpack( + "HHHH", + fcntl.ioctl( + 0, termios.TIOCGWINSZ, struct.pack("HHHH", 0, 0, 0, 0) + ), + ) return w, h - except IOError: + except OSError: # It's possible that in sandboxed environments the above ioctl is not # allowed (e.g., some jenkins setups) return 80, 24 def separator(char=default_separator, color=None): - ''' + """ Return a separator of the given character that is the length of the full width of the terminal screen. - ''' + """ (w, h) = terminal_size() if color: - return color + char*w + termcap.Normal + return color + char * w + termcap.Normal else: - return char*w + return char * w -def insert_separator(inside, char=default_separator, - min_barrier=3, color=None): - ''' + +def insert_separator( + inside, char=default_separator, min_barrier=3, color=None +): + """ Place the given string inside of the separator. If it does not fit inside, expand the separator to fit it with at least min_barrier. .. seealso:: :func:`separator` - ''' + """ # Use a bytearray so it's efficient to manipulate - string = bytearray(separator(char, color=color), 'utf-8') + string = bytearray(separator(char, color=color), "utf-8") # Check if we can fit inside with at least min_barrier. gap = (len(string) - len(inside)) - min_barrier * 2 if gap > 0: # We'll need to expand the string to fit us. - string.extend([ char for _ in range(-gap)]) + string.extend([char for _ in range(-gap)]) # Emplace inside - middle = (len(string)-1)//2 - start_idx = middle - len(inside)//2 - string[start_idx:len(inside)+start_idx] = str.encode(inside) + middle = (len(string) - 1) // 2 + start_idx = middle - len(inside) // 2 + string[start_idx : len(inside) + start_idx] = str.encode(inside) return str(string.decode("utf-8")) -if __name__ == '__main__': +if __name__ == "__main__": + def test_termcap(obj): for c_name in color_names: c_str = getattr(obj, c_name) print(c_str + c_name + obj.Normal) for attr_name in capability_names: - if attr_name == 'Normal': + if attr_name == "Normal": continue attr_str = getattr(obj, attr_name) print(attr_str + c_str + attr_name + " " + c_name + obj.Normal) - print(obj.Bold + obj.Underline + \ - c_name + "Bold Underline " + c_str + obj.Normal) + print( + obj.Bold + + obj.Underline + + c_name + + "Bold Underline " + + c_str + + obj.Normal + ) print("=== termcap enabled ===") test_termcap(termcap) diff --git a/ext/testlib/test_util.py b/ext/testlib/test_util.py index 22e2c973f6..3ef26191c3 100644 --- a/ext/testlib/test_util.py +++ b/ext/testlib/test_util.py @@ -29,14 +29,16 @@ import testlib.helper as helper import testlib.runner as runner_mod -class TestCase(object): - ''' + +class TestCase: + """ Base class for all tests. ..note:: The :func:`__new__` method enables collection of test cases, it must be called in order for test cases to be collected. - ''' + """ + fixtures = [] # TODO, remove explicit dependency. Use the loader to set the @@ -45,7 +47,7 @@ class TestCase(object): collector = helper.InstanceCollector() def __new__(cls, *args, **kwargs): - obj = super(TestCase, cls).__new__(cls) + obj = super().__new__(cls) TestCase.collector.collect(obj) return obj @@ -55,10 +57,12 @@ class TestCase(object): name = self.__class__.__name__ self.name = name + class TestFunction(TestCase): - ''' + """ TestCase implementation which uses a callable object as a test. - ''' + """ + def __init__(self, function, name=None, **kwargs): self.test_function = function if name is None: diff --git a/ext/testlib/uid.py b/ext/testlib/uid.py index f8951a28da..70d653bc20 100644 --- a/ext/testlib/uid.py +++ b/ext/testlib/uid.py @@ -26,13 +26,14 @@ # # Authors: Sean Wilson -import os import itertools +import os import testlib.configuration as configuration -class UID(object): - sep = ':' + +class UID: + sep = ":" type_idx, path_idx = range(2) def __init__(self, path, *args): @@ -41,9 +42,10 @@ class UID(object): @staticmethod def _shorten_path(path): - return os.path.relpath(path, - os.path.commonprefix((configuration.constants.testing_base, - path))) + return os.path.relpath( + path, + os.path.commonprefix((configuration.constants.testing_base, path)), + ) @staticmethod def _full_path(short_path): @@ -75,11 +77,11 @@ class UID(object): def __str__(self): common_opts = { self.path_idx: self.path, - self.type_idx: self.__class__.__name__ + self.type_idx: self.__class__.__name__, } - return self.sep.join(itertools.chain( - [common_opts[0], common_opts[1]], - self.attributes)) + return self.sep.join( + itertools.chain([common_opts[0], common_opts[1]], self.attributes) + ) def __hash__(self): return hash(str(self)) diff --git a/ext/testlib/wrappers.py b/ext/testlib/wrappers.py index b2b887b0f9..4e1be80427 100644 --- a/ext/testlib/wrappers.py +++ b/ext/testlib/wrappers.py @@ -38,16 +38,20 @@ # # Authors: Sean Wilson -''' +""" Module contains wrappers for test items that have been loaded by the testlib :class:`testlib.loader.Loader`. -''' +""" import itertools import testlib.uid as uid -from testlib.state import Status, Result +from testlib.state import ( + Result, + Status, +) -class TestCaseMetadata(): + +class TestCaseMetadata: def __init__(self, name, uid, path, result, status, suite_uid): self.name = name self.uid = uid @@ -57,7 +61,7 @@ class TestCaseMetadata(): self.suite_uid = suite_uid -class TestSuiteMetadata(): +class TestSuiteMetadata: def __init__(self, name, uid, tags, path, status, result): self.name = name self.uid = uid @@ -67,21 +71,22 @@ class TestSuiteMetadata(): self.result = result -class LibraryMetadata(): +class LibraryMetadata: def __init__(self, name, result, status): self.name = name self.result = result self.status = status -class LoadedTestable(object): - ''' +class LoadedTestable: + """ Base class for loaded test items. :property:`result` and :property:`status` setters notify testlib via the :func:`log_result` and :func:`log_status` of the updated status. - ''' + """ + def __init__(self, obj): self.obj = obj self.metadata = self._generate_metadata() @@ -135,10 +140,12 @@ class LoadedTestable(object): # TODO Change log to provide status_update, result_update for all types. def log_status(self, status): import testlib.log as log + log.test_log.status_update(self, status) def log_result(self, result): import testlib.log as log + log.test_log.result_update(self, result) def __iter__(self): @@ -155,16 +162,18 @@ class LoadedTest(LoadedTestable): self.obj.test(*args, **kwargs) def _generate_metadata(self): - return TestCaseMetadata( **{ - 'name':self.obj.name, - 'path': self._path, - 'uid': uid.TestUID(self._path, - self.obj.name, - self.parent_suite.name), - 'status': Status.Unscheduled, - 'result': Result(Result.NotRun), - 'suite_uid': self.parent_suite.metadata.uid - }) + return TestCaseMetadata( + **{ + "name": self.obj.name, + "path": self._path, + "uid": uid.TestUID( + self._path, self.obj.name, self.parent_suite.name + ), + "status": Status.Unscheduled, + "result": Result(Result.NotRun), + "suite_uid": self.parent_suite.metadata.uid, + } + ) class LoadedSuite(LoadedTestable): @@ -174,18 +183,21 @@ class LoadedSuite(LoadedTestable): self.tests = self._wrap_children(suite_obj) def _wrap_children(self, suite_obj): - return [LoadedTest(test, self, self.metadata.path) - for test in suite_obj] + return [ + LoadedTest(test, self, self.metadata.path) for test in suite_obj + ] def _generate_metadata(self): - return TestSuiteMetadata( **{ - 'name': self.obj.name, - 'tags':self.obj.tags, - 'path': self._path, - 'uid': uid.SuiteUID(self._path, self.obj.name), - 'status': Status.Unscheduled, - 'result': Result(Result.NotRun) - }) + return TestSuiteMetadata( + **{ + "name": self.obj.name, + "tags": self.obj.tags, + "path": self._path, + "uid": uid.SuiteUID(self._path, self.obj.name), + "status": Status.Unscheduled, + "result": Result(Result.NotRun), + } + ) def __iter__(self): return iter(self.tests) @@ -196,41 +208,44 @@ class LoadedSuite(LoadedTestable): class LoadedLibrary(LoadedTestable): - ''' + """ Wraps a collection of all loaded test suites and provides utility functions for accessing fixtures. - ''' + """ + def __init__(self, suites): LoadedTestable.__init__(self, suites) def _generate_metadata(self): - return LibraryMetadata( **{ - 'name': 'Test Library', - 'status': Status.Unscheduled, - 'result': Result(Result.NotRun) - }) + return LibraryMetadata( + **{ + "name": "Test Library", + "status": Status.Unscheduled, + "result": Result(Result.NotRun), + } + ) def __iter__(self): - ''' + """ :returns: an iterator over contained :class:`TestSuite` objects. - ''' + """ return iter(self.obj) def all_fixtures(self): - ''' + """ :returns: an interator overall all global, suite, and test fixtures - ''' - return itertools.chain(itertools.chain( - *(suite.fixtures for suite in self.obj)), + """ + return itertools.chain( + itertools.chain(*(suite.fixtures for suite in self.obj)), *(self.test_fixtures(suite) for suite in self.obj) ) def test_fixtures(self, suite): - ''' + """ :returns: an interator over all fixtures of each test contained in the given suite - ''' + """ return itertools.chain(*(test.fixtures for test in suite)) @property diff --git a/pyproject.toml b/pyproject.toml index a06d464de7..d32cf5f06b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,12 @@ [tool.black] line-length = 79 include = '\.pyi?$' +[tool.isort] +profile = "black" +multi_line_output = 3 +line_length = 79 +force_grid_wrap = 2 +known_m5 = "m5" +known_underscore_m5 = "_m5,slicc" +known_gem5 = "gem5" +sections="FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,M5,UNDERSCORE_M5,GEM5,LOCALFOLDER" diff --git a/requirements.txt b/requirements.txt index 561cab79cf..4b820f51ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ +mypy==1.5.1 pre-commit==2.20.0 diff --git a/site_scons/gem5_scons/__init__.py b/site_scons/gem5_scons/__init__.py index 6d6226cdc6..bed4b48157 100644 --- a/site_scons/gem5_scons/__init__.py +++ b/site_scons/gem5_scons/__init__.py @@ -39,16 +39,17 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import os +import os.path import pickle import sys import tempfile import textwrap -from gem5_scons.util import get_termcap -from gem5_scons.configure import Configure -from gem5_scons.defaults import EnvDefaults import SCons.Node.Python import SCons.Script +from gem5_scons.configure import Configure +from gem5_scons.defaults import EnvDefaults +from gem5_scons.util import get_termcap termcap = get_termcap() @@ -56,7 +57,7 @@ termcap = get_termcap() def strip_build_path(path, env): path = str(path) build_base = "build/" - variant_base = env["BUILDROOT"] + os.path.sep + variant_base = os.path.dirname(env["BUILDDIR"]) + os.path.sep if path.startswith(variant_base): path = path[len(variant_base) :] elif path.startswith(build_base): @@ -88,7 +89,7 @@ def TempFileSpawn(scons_env): # Generate a string of the form: # common/path/prefix/src1, src2 -> tgt1, tgt2 # to print while building. -class Transform(object): +class Transform: # all specific color settings should be here and nowhere else tool_color = termcap.Normal pfx_color = termcap.Yellow @@ -117,7 +118,7 @@ class Transform(object): source = source[0 : self.max_sources] def strip(f): - return strip_build_path(str(f), env) + return strip_build_path(f, env) if len(source) > 0: srcs = list(map(strip, source)) @@ -255,18 +256,21 @@ def error(*args, **kwargs): def parse_build_path(target): path_dirs = target.split("/") - # Pop off the target file. - path_dirs.pop() - - # Search backwards for the "build" directory. Whatever was just before it - # was the name of the variant. - variant_dir = path_dirs.pop() - while path_dirs and path_dirs[-1] != "build": - variant_dir = path_dirs.pop() - if not path_dirs: - error("No non-leaf 'build' dir found on target path.", target) - - return os.path.join("/", *path_dirs), variant_dir + # Search backwards for a directory with a gem5.build sub-directory, or a + # directory who's parent is called "build". gem5.build identifies an + # existing gem5 build directory. A directory called "build" is an anchor + # for a legacy "build/${VARIANT}/${TARGET}" style build path, where the + # variant selects a default config to use. + while path_dirs: + dot_gem5 = os.path.join("/", *path_dirs, "gem5.build") + if ( + os.path.isdir(dot_gem5) + or len(path_dirs) > 1 + and path_dirs[-2] == "build" + ): + return os.path.join("/", *path_dirs) + path_dirs.pop() + error(f"No existing build directory and no variant for {target}") # The MakeAction wrapper, and a SCons tool to set up the *COMSTR variables. diff --git a/site_scons/gem5_scons/builders/blob.py b/site_scons/gem5_scons/builders/blob.py index f4e6d3a0ea..74d5b85259 100644 --- a/site_scons/gem5_scons/builders/blob.py +++ b/site_scons/gem5_scons/builders/blob.py @@ -39,12 +39,13 @@ import os.path -from gem5_scons import Transform, MakeAction -from blob import bytesToCppArray - -from code_formatter import code_formatter - import SCons.Node.Python +from blob import bytesToCppArray +from code_formatter import code_formatter +from gem5_scons import ( + MakeAction, + Transform, +) def build_blob(target, source, env): diff --git a/site_scons/gem5_scons/builders/config_file.py b/site_scons/gem5_scons/builders/config_file.py index 2ab7bf87b4..28eb975631 100755 --- a/site_scons/gem5_scons/builders/config_file.py +++ b/site_scons/gem5_scons/builders/config_file.py @@ -38,7 +38,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from gem5_scons import Transform, MakeAction +from gem5_scons import ( + MakeAction, + Transform, +) ################################################### # @@ -53,7 +56,7 @@ def ConfigFile(env): # operands are the name of the variable and a Value node containing the # value of the variable. def build_config_file(target, source, env): - (variable, value) = [s.get_contents().decode("utf-8") for s in source] + (variable, value) = (s.get_contents().decode("utf-8") for s in source) with open(str(target[0].abspath), "w") as f: print("#define", variable, value, file=f) return None diff --git a/site_scons/gem5_scons/builders/switching_headers.py b/site_scons/gem5_scons/builders/switching_headers.py index 92bd613508..497179422f 100755 --- a/site_scons/gem5_scons/builders/switching_headers.py +++ b/site_scons/gem5_scons/builders/switching_headers.py @@ -40,7 +40,10 @@ import os.path -from gem5_scons import Transform, MakeAction +from gem5_scons import ( + MakeAction, + Transform, +) ################################################### # diff --git a/site_scons/gem5_scons/configure.py b/site_scons/gem5_scons/configure.py index d04cdd49cb..c1b9fb56cc 100644 --- a/site_scons/gem5_scons/configure.py +++ b/site_scons/gem5_scons/configure.py @@ -80,17 +80,17 @@ def CheckLinkFlag(context, flag, autoadd=True, set_for_shared=True): def CheckMember(context, include, decl, member, include_quotes="<>"): context.Message(f"Checking for member {member} in {decl}...") text = """ -#include %(header)s -int main(){ - %(decl)s test; - (void)test.%(member)s; +#include {header} +int main(){{ + {decl} test; + (void)test.{member}; return 0; -}; -""" % { - "header": include_quotes[0] + include + include_quotes[1], - "decl": decl, - "member": member, - } +}}; +""".format( + header=include_quotes[0] + include + include_quotes[1], + decl=decl, + member=member, + ) ret = context.TryCompile(text, extension=".cc") context.Result(ret) diff --git a/site_scons/gem5_scons/defaults.py b/site_scons/gem5_scons/defaults.py index 996cfd495f..4de9a93339 100644 --- a/site_scons/gem5_scons/defaults.py +++ b/site_scons/gem5_scons/defaults.py @@ -44,31 +44,31 @@ from gem5_python_paths import extra_python_paths def EnvDefaults(env): - # export TERM so that clang reports errors in color - use_vars = set( - [ - "AS", - "AR", - "CC", - "CXX", - "HOME", - "LD_LIBRARY_PATH", - "LIBRARY_PATH", - "PATH", - "PKG_CONFIG_PATH", - "PROTOC", - "PYTHONPATH", - "RANLIB", - "TERM", - "PYTHON_CONFIG", - "CCFLAGS_EXTRA", - "GEM5PY_CCFLAGS_EXTRA", - "GEM5PY_LINKFLAGS_EXTRA", - "LINKFLAGS_EXTRA", - "LANG", - "LC_CTYPE", - ] - ) + # initialize the toolchain related env with host environment + use_vars = { + "AS", + "AR", + "CC", + "CXX", + "HOME", + "CPATH", + "LD_LIBRARY_PATH", + "LIBRARY_PATH", + "PATH", + "PKG_CONFIG_PATH", + "PROTOC", + "PYTHONPATH", + "RANLIB", + "TERM", # for clang reports errors in color + "PYTHON_CONFIG", # gem5 specific build env + "CCFLAGS_EXTRA", # gem5 specific build env + "GEM5PY_CCFLAGS_EXTRA", # gem5 specific build env + "GEM5PY_LINKFLAGS_EXTRA", # gem5 specific build env + "LINKFLAGS_EXTRA", # gem5 specific build env + "LANG", # for work with non-ascii directory path + "LC_CTYPE", # for work with non-ascii directory path + "DISPLAY", # for gui program, ex kconfig guiconfig + } use_prefixes = [ "ASAN_", # address sanitizer symbolizer path and settings diff --git a/site_scons/gem5_scons/kconfig.py b/site_scons/gem5_scons/kconfig.py new file mode 100644 index 0000000000..b3b49d27c5 --- /dev/null +++ b/site_scons/gem5_scons/kconfig.py @@ -0,0 +1,229 @@ +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import os + +import kconfiglib + +from . import error + +_kconfig_helpers = { + "DEFCONFIG_PY": "defconfig.py", + "GUICONFIG_PY": "guiconfig.py", + "LISTNEWCONFIG_PY": "listnewconfig.py", + "MENUCONFIG_PY": "menuconfig.py", + "OLDCONFIG_PY": "oldconfig.py", + "OLDDEFCONFIG_PY": "olddefconfig.py", + "SAVEDEFCONFIG_PY": "savedefconfig.py", + "SETCONFIG_PY": "setconfig.py", +} + + +def _prep_env(env, base_kconfig, config_path=None): + """ + Prepare the required env vars for Kconfiglib + return the Scons env with Kconfiglib env + + :param env: Scons env + :param base_kconfig: path to the Top-level Kconfig file + :param config_path: path to the configuration file + """ + kconfig_env = env.Clone() + for key, val in kconfig_env["CONF"].items(): + if isinstance(val, bool): + val = "y" if val else "n" + kconfig_env["ENV"][key] = val + kconfig_env["ENV"]["CONFIG_"] = "" + if config_path: + kconfig_env["ENV"]["KCONFIG_CONFIG"] = config_path + + kconfig_env["BASE_KCONFIG"] = base_kconfig + + ext = env.Dir("#ext") + kconfiglib_dir = ext.Dir("Kconfiglib") + for key, name in _kconfig_helpers.items(): + kconfig_env[key] = kconfiglib_dir.File(name) + return kconfig_env + + +def _process_kconfig(env, base_kconfig): + """ + Create the kconfig instance by given Scons env vars + + :param env: Scons env + :param base_kconfig: path to the Top-level Kconfig file + """ + saved_env = os.environ + try: + os.environ.update({key: str(val) for key, val in env["ENV"].items()}) + kconfig = kconfiglib.Kconfig(filename=base_kconfig) + finally: + os.environ = saved_env + return kconfig + + +def defconfig(env, base_kconfig, config_in, config_out): + """ + Interface of handling defconfig.py of Kconfiglib + """ + kconfig_env = _prep_env(env, base_kconfig, config_out) + kconfig_env["CONFIG_IN"] = config_in + if ( + kconfig_env.Execute( + '"${DEFCONFIG_PY}" --kconfig "${BASE_KCONFIG}" ' '"${CONFIG_IN}"' + ) + != 0 + ): + error("Failed to run defconfig") + + +def guiconfig(env, base_kconfig, config_path, main_menu_text): + """ + Interface of handling guiconfig.py of Kconfiglib + """ + kconfig_env = _prep_env(env, base_kconfig, config_path) + kconfig_env["ENV"]["MAIN_MENU_TEXT"] = main_menu_text + if kconfig_env.Execute('"${GUICONFIG_PY}" "${BASE_KCONFIG}"') != 0: + error("Failed to run guiconfig") + + +def listnewconfig(env, base_kconfig, config_path): + """ + Interface of handling listnewconfig.py of Kconfiglib + """ + kconfig_env = _prep_env(env, base_kconfig, config_path) + # Provide a little visual separation between SCons output and + # listnewconfig output. + print() + if kconfig_env.Execute('"${LISTNEWCONFIG_PY}" "${BASE_KCONFIG}"') != 0: + error("Failed to run listnewconfig") + + +def oldconfig(env, base_kconfig, config_path): + """ + Interface of handling oldconfig.py of Kconfiglib + """ + kconfig_env = _prep_env(env, base_kconfig, config_path) + if kconfig_env.Execute('"${OLDCONFIG_PY}" "${BASE_KCONFIG}"') != 0: + error("Failed to run oldconfig") + + +def olddefconfig(env, base_kconfig, config_path): + """ + Interface of handling olddefconfig.py of Kconfiglib + """ + kconfig_env = _prep_env(env, base_kconfig, config_path) + if kconfig_env.Execute('"${OLDDEFCONFIG_PY}" "${BASE_KCONFIG}"') != 0: + error("Failed to run oldconfig") + + +def menuconfig( + env, base_kconfig, config_path, main_menu_text, style="aquatic" +): + """ + Interface of handling menuconfig.py of Kconfiglib + """ + kconfig_env = _prep_env(env, base_kconfig, config_path) + kconfig_env["ENV"]["MENUCONFIG_STYLE"] = style + kconfig_env["ENV"]["MAIN_MENU_TEXT"] = main_menu_text + if kconfig_env.Execute('"${MENUCONFIG_PY}" "${BASE_KCONFIG}"') != 0: + error("Failed to run menuconfig") + + +def savedefconfig(env, base_kconfig, config_in, config_out): + """ + Interface of handling savedefconfig.py of Kconfiglib + """ + kconfig_env = _prep_env(env, base_kconfig, config_in) + kconfig_env["CONFIG_OUT"] = config_out + if ( + kconfig_env.Execute( + '"${SAVEDEFCONFIG_PY}" ' + '--kconfig "${BASE_KCONFIG}" --out "${CONFIG_OUT}"' + ) + != 0 + ): + error("Failed to run savedefconfig") + + +def setconfig(env, base_kconfig, config_path, assignments): + """ + Interface of handling setconfig.py of Kconfiglib + """ + kconfig_env = _prep_env(env, base_kconfig, config_path) + + kconfig = _process_kconfig(kconfig_env, base_kconfig) + sym_names = list(sym.name for sym in kconfig.unique_defined_syms) + + filtered = dict( + {key: val for key, val in assignments.items() if key in sym_names} + ) + + setconfig_cmd_parts = ['"${SETCONFIG_PY}" --kconfig "${BASE_KCONFIG}"'] + for key, val in filtered.items(): + if isinstance(val, bool): + val = "y" if val else "n" + setconfig_cmd_parts.append(f"{key}={val}") + setconfig_cmd = " ".join(setconfig_cmd_parts) + if kconfig_env.Execute(setconfig_cmd) != 0: + error("Failed to run setconfig") + + +def update_env(env, base_kconfig, config_path): + """ + Update the Scons' env["CONF"] options from kconfig env + + :param env: Scons env + :param base_kconfig: path to the Top-level Kconfig file + :param config_path: path to the configuration file + """ + kconfig_env = _prep_env(env, base_kconfig, config_path) + + kconfig = _process_kconfig(kconfig_env, base_kconfig) + + kconfig.load_config(config_path) + for sym in kconfig.unique_defined_syms: + val = sym.str_value + if sym.type == kconfiglib.BOOL: + env["CONF"][sym.name] = True if val == "y" else False + elif sym.type == kconfiglib.TRISTATE: + warning("No way to configure modules for now") + env["CONF"][sym.name] = True if val == "y" else False + elif sym.type == kconfiglib.INT: + if not val: + val = "0" + env["CONF"][sym.name] = int(val, 0) + elif sym.type == kconfiglib.HEX: + if not val: + val = "0" + env["CONF"][sym.name] = int(val, 16) + elif sym.type == kconfiglib.STRING: + env["CONF"][sym.name] = val + elif sym.type == kconfiglib.UNKNOWN: + warning(f'Config symbol "{sym.name}" has unknown type') + env["CONF"][sym.name] = val + else: + type_name = kconfiglib.TYPE_TO_STR[sym.type] + error(f"Unrecognized symbol type {type_name}") diff --git a/site_scons/gem5_scons/sources.py b/site_scons/gem5_scons/sources.py index 54aeb24de1..46392a1ac0 100644 --- a/site_scons/gem5_scons/sources.py +++ b/site_scons/gem5_scons/sources.py @@ -126,7 +126,7 @@ def resolve_tags(env, tags): return tags -class SourceFilter(object): +class SourceFilter: factories = {} def __init__(self, predicate): @@ -209,11 +209,11 @@ class SourceMeta(type): particular type.""" def __init__(cls, name, bases, dict): - super(SourceMeta, cls).__init__(name, bases, dict) + super().__init__(name, bases, dict) cls.all = SourceList() -class SourceItem(object, metaclass=SourceMeta): +class SourceItem(metaclass=SourceMeta): """Base object that encapsulates the notion of a source component for gem5. This specifies a set of tags which help group components into groups based on arbitrary properties.""" diff --git a/site_scons/gem5_scons/util.py b/site_scons/gem5_scons/util.py index 045fd4ef32..be0a5a5880 100644 --- a/site_scons/gem5_scons/util.py +++ b/site_scons/gem5_scons/util.py @@ -66,7 +66,11 @@ def readCommand(cmd, **kwargs): :returns: command stdout :rtype: string """ - from subprocess import Popen, PIPE, STDOUT + from subprocess import ( + PIPE, + STDOUT, + Popen, + ) if isinstance(cmd, str): cmd = cmd.split() @@ -100,7 +104,7 @@ def compareVersions(v1, v2): return v elif isinstance(v, str): return list( - map(lambda x: int(re.match("\d+", x).group()), v.split(".")) + map(lambda x: int(re.match(r"\d+", x).group()), v.split(".")) ) else: raise TypeError() diff --git a/site_scons/site_init.py b/site_scons/site_init.py index 480dfa74da..39c893ea38 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -38,7 +38,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from __future__ import print_function # Check for recent-enough Python and SCons versions. try: diff --git a/site_scons/site_tools/git.py b/site_scons/site_tools/git.py index 362c20b105..dea6d8e152 100644 --- a/site_scons/site_tools/git.py +++ b/site_scons/site_tools/git.py @@ -38,10 +38,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from asyncio import subprocess import os -import sys import subprocess +import sys import gem5_scons.util import SCons.Script diff --git a/src/Doxyfile b/src/Doxyfile index 1ffbb7cce5..68d9b3b44b 100644 --- a/src/Doxyfile +++ b/src/Doxyfile @@ -31,7 +31,7 @@ PROJECT_NAME = gem5 # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = v23.0.1.0 +PROJECT_NUMBER = v23.1.0.0 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. diff --git a/util/dockerfiles/ubuntu-18.04_all-dependencies/Dockerfile b/src/Kconfig similarity index 69% rename from util/dockerfiles/ubuntu-18.04_all-dependencies/Dockerfile rename to src/Kconfig index 629fc5d614..2d24aad1ad 100644 --- a/util/dockerfiles/ubuntu-18.04_all-dependencies/Dockerfile +++ b/src/Kconfig @@ -1,5 +1,4 @@ -# Copyright (c) 2020 The Regents of the University of California -# All Rights Reserved. +# Copyright 2022 Google LLC # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -24,16 +23,31 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -FROM ubuntu:18.04 +mainmenu "$(MAIN_MENU_TEXT)" -RUN apt -y update && apt -y upgrade && \ - apt -y install build-essential git m4 scons zlib1g zlib1g-dev \ - libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev \ - python3-dev python3 doxygen libboost-all-dev \ - libhdf5-serial-dev python3-pydot libpng-dev libelf-dev pkg-config \ - python3-pip python3-venv +config BATCH + bool "Use batch pool for build and test" + default n -RUN pip3 install black mypy pre-commit +config BATCH_CMD + string "Batch pool submission command name" + default "qdo" + depends on BATCH -RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 10 -RUN update-alternatives --install /usr/bin/python python /usr/bin/python2 1 +config M5_BUILD_CACHE + string "Cache built objects in this directory" + default "" + +config USE_EFENCE + bool "Link with Electric Fence malloc debugger" + default n + +rsource "base/Kconfig" +rsource "mem/ruby/Kconfig" +rsource "learning_gem5/part3/Kconfig" +rsource "proto/Kconfig" +rsource "dev/net/Kconfig" +rsource "arch/Kconfig" +rsource "cpu/Kconfig" +rsource "systemc/Kconfig" +rsource "gpu-compute/Kconfig" diff --git a/src/SConscript b/src/SConscript index b41921bb2f..52051b3256 100644 --- a/src/SConscript +++ b/src/SConscript @@ -634,11 +634,12 @@ PySource('m5', 'python/m5/info.py') gem5py_m5_env = gem5py_env.Clone() gem5py_env.Append(CPPPATH=env['CPPPATH']) gem5py_env.Append(LIBS='z') +gem5py_env.Append(LINKFLAGS='-rdynamic') gem5py_env.Program(gem5py, 'python/gem5py.cc')[0] m5_module_source = \ Source.all.with_all_tags(env, 'm5_module', 'gem5 lib') m5_module_static = list(map(lambda s: s.static(gem5py_env), m5_module_source)) -gem5py_env.Program(gem5py_m5, [ 'python/gem5py.cc' ] + m5_module_static) +gem5py_env.Program(gem5py_m5, [ 'python/gem5py_m5.cc' ] + m5_module_static) # version tags @@ -655,6 +656,7 @@ env.AlwaysBuild(tags) # env['SHOBJSUFFIX'] = '${OBJSUFFIX}s' +env.Append(LINKFLAGS='-rdynamic') envs = { 'debug': env.Clone(ENV_LABEL='debug', OBJSUFFIX='.do'), diff --git a/src/arch/SConsopts b/src/arch/Kconfig similarity index 81% rename from src/arch/SConsopts rename to src/arch/Kconfig index f05bdef14b..729265c317 100644 --- a/src/arch/SConsopts +++ b/src/arch/Kconfig @@ -1,4 +1,4 @@ -# Copyright 2020 Google, Inc. +# Copyright 2022 Google LLC # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -23,11 +23,26 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Import('*') +config TARGET_GPU_ISA + string -def add_isa_lists(): - sticky_vars.AddVariables( - EnumVariable('TARGET_GPU_ISA', 'Target GPU ISA', 'gcn3', - sorted(set(main.Split('${ALL_GPU_ISAS}')))), - ) -AfterSConsopts(add_isa_lists) +rsource "amdgpu/Kconfig" + +config BUILD_ISA + bool "Build the arch ISA" + default n + +menu "ISA" + +if BUILD_ISA + +rsource "arm/Kconfig" +rsource "mips/Kconfig" +rsource "power/Kconfig" +rsource "riscv/Kconfig" +rsource "sparc/Kconfig" +rsource "x86/Kconfig" + +endif + +endmenu diff --git a/src/arch/SConscript b/src/arch/SConscript index 7285c0ec59..2426401d73 100644 --- a/src/arch/SConscript +++ b/src/arch/SConscript @@ -56,20 +56,17 @@ Import('*') # ################################################################# -if env['CONF']['USE_ARM_ISA']: - isa = 'arm' -elif env['CONF']['USE_MIPS_ISA']: - isa = 'mips' -elif env['CONF']['USE_POWER_ISA']: - isa = 'power' -elif env['CONF']['USE_RISCV_ISA']: - isa = 'riscv' -elif env['CONF']['USE_SPARC_ISA']: - isa = 'sparc' -elif env['CONF']['USE_X86_ISA']: - isa = 'x86' -elif env['CONF']['USE_NULL_ISA']: - isa = 'null' +if env['CONF']['BUILD_ISA']: + if ( + not env['CONF']['USE_ARM_ISA'] and + not env['CONF']['USE_MIPS_ISA'] and + not env['CONF']['USE_POWER_ISA'] and + not env['CONF']['USE_RISCV_ISA'] and + not env['CONF']['USE_SPARC_ISA'] and + not env['CONF']['USE_X86_ISA'] + ): + error("At least one ISA need to be set") + amdgpu_isa = ['gcn3', 'vega'] diff --git a/src/arch/power/SConsopts b/src/arch/amdgpu/Kconfig similarity index 90% rename from src/arch/power/SConsopts rename to src/arch/amdgpu/Kconfig index 099f37553a..5140f2b103 100644 --- a/src/arch/power/SConsopts +++ b/src/arch/amdgpu/Kconfig @@ -1,4 +1,4 @@ -# Copyright 2021 Google, Inc. +# Copyright 2022 Google LLC # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -23,6 +23,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Import('*') -sticky_vars.Add(BoolVariable('USE_POWER_ISA', 'Enable POWER ISA support', - False)) +if BUILD_GPU +choice "TARGET_GPU_ISA" +prompt "GPU ISA" +endchoice +endif + +rsource "gcn3/Kconfig" +rsource "vega/Kconfig" diff --git a/src/arch/amdgpu/common/X86GPUTLB.py b/src/arch/amdgpu/common/X86GPUTLB.py index 59cc549d17..407443b8d7 100644 --- a/src/arch/amdgpu/common/X86GPUTLB.py +++ b/src/arch/amdgpu/common/X86GPUTLB.py @@ -28,10 +28,9 @@ # POSSIBILITY OF SUCH DAMAGE. from m5.defines import buildEnv +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * - -from m5.objects.ClockedObject import ClockedObject from m5.SimObject import SimObject diff --git a/src/arch/amdgpu/common/tlb_coalescer.cc b/src/arch/amdgpu/common/tlb_coalescer.cc index 0be1387977..0d2715fca3 100644 --- a/src/arch/amdgpu/common/tlb_coalescer.cc +++ b/src/arch/amdgpu/common/tlb_coalescer.cc @@ -482,7 +482,7 @@ TLBCoalescer::processProbeTLBEvent() stats.localqueuingCycles += (curTick() * pkt_cnt); } - DPRINTF(GPUTLB, "Successfully sent TLB request for page %#x", + DPRINTF(GPUTLB, "Successfully sent TLB request for page %#x\n", virt_page_addr); //copy coalescedReq to issuedTranslationsTable diff --git a/src/arch/amdgpu/gcn3/Kconfig b/src/arch/amdgpu/gcn3/Kconfig new file mode 100644 index 0000000000..2ba21a6521 --- /dev/null +++ b/src/arch/amdgpu/gcn3/Kconfig @@ -0,0 +1,33 @@ +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +config TARGET_GPU_ISA + default "gcn3" if GCN3_GPU_ISA + +cont_choice "TARGET_GPU_ISA" + config GCN3_GPU_ISA + depends on BUILD_GPU + bool "GCN3" +endchoice diff --git a/src/arch/amdgpu/gcn3/insts/instructions.cc b/src/arch/amdgpu/gcn3/insts/instructions.cc index 478b1d38d0..b9d29a2204 100644 --- a/src/arch/amdgpu/gcn3/insts/instructions.cc +++ b/src/arch/amdgpu/gcn3/insts/instructions.cc @@ -29692,7 +29692,7 @@ namespace Gcn3ISA for (int i = 0; i < 4 ; ++i) { VecElemU32 permuted_val = permute(selector, 0xFF & ((VecElemU32)src2[lane] >> (8 * i))); - vdst[lane] |= (permuted_val << i); + vdst[lane] |= (permuted_val << (8 * i)); } DPRINTF(GCN3, "v_perm result: 0x%08x\n", vdst[lane]); diff --git a/src/arch/amdgpu/vega/Kconfig b/src/arch/amdgpu/vega/Kconfig new file mode 100644 index 0000000000..81f8be3551 --- /dev/null +++ b/src/arch/amdgpu/vega/Kconfig @@ -0,0 +1,33 @@ +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +config TARGET_GPU_ISA + default 'vega' if VEGA_GPU_ISA + +cont_choice "TARGET_GPU_ISA" + config VEGA_GPU_ISA + depends on BUILD_GPU + bool "VEGA" +endchoice diff --git a/src/arch/amdgpu/vega/VegaGPUTLB.py b/src/arch/amdgpu/vega/VegaGPUTLB.py index 96b940c544..6ef9630d18 100644 --- a/src/arch/amdgpu/vega/VegaGPUTLB.py +++ b/src/arch/amdgpu/vega/VegaGPUTLB.py @@ -28,11 +28,10 @@ # POSSIBILITY OF SUCH DAMAGE. from m5.defines import buildEnv +from m5.objects.AMDGPU import AMDGPUDevice +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * - -from m5.objects.ClockedObject import ClockedObject -from m5.objects.AMDGPU import AMDGPUDevice from m5.SimObject import SimObject diff --git a/src/arch/amdgpu/vega/decoder.cc b/src/arch/amdgpu/vega/decoder.cc index fd3a803bb8..065f8c8493 100644 --- a/src/arch/amdgpu/vega/decoder.cc +++ b/src/arch/amdgpu/vega/decoder.cc @@ -495,7 +495,7 @@ namespace VegaISA &Decoder::decode_invalid, &Decoder::decode_invalid, &Decoder::subDecode_OP_FLAT, - &Decoder::decode_invalid, + &Decoder::subDecode_OP_FLAT, &Decoder::subDecode_OP_FLAT, &Decoder::subDecode_OP_FLAT, &Decoder::decode_invalid, @@ -3140,8 +3140,8 @@ namespace VegaISA &Decoder::decode_OP_VOP1__V_CVT_NORM_I16_F16, &Decoder::decode_OP_VOP1__V_CVT_NORM_U16_F16, &Decoder::decode_OP_VOP1__V_SAT_PK_U8_I16, - &Decoder::decode_OP_VOP1__V_SWAP_B32, &Decoder::decode_invalid, + &Decoder::decode_OP_VOP1__V_SWAP_B32, &Decoder::decode_invalid, &Decoder::decode_invalid, &Decoder::decode_invalid, @@ -7020,7 +7020,7 @@ namespace VegaISA GPUStaticInst* Decoder::decode_OPU_VOP3__V_OR3_B32(MachInst iFmt) { - return new Inst_VOP3__V_OR_B32(&iFmt->iFmt_VOP3A); + return new Inst_VOP3__V_OR3_B32(&iFmt->iFmt_VOP3A); } GPUStaticInst* diff --git a/src/arch/amdgpu/vega/insts/instructions.cc b/src/arch/amdgpu/vega/insts/instructions.cc index 6c014bc107..cd4ad74e6e 100644 --- a/src/arch/amdgpu/vega/insts/instructions.cc +++ b/src/arch/amdgpu/vega/insts/instructions.cc @@ -4728,6 +4728,7 @@ namespace VegaISA Inst_SOPP__S_SETPRIO::Inst_SOPP__S_SETPRIO(InFmt_SOPP *iFmt) : Inst_SOPP(iFmt, "s_setprio") { + setFlag(ALU); } // Inst_SOPP__S_SETPRIO Inst_SOPP__S_SETPRIO::~Inst_SOPP__S_SETPRIO() @@ -4742,7 +4743,10 @@ namespace VegaISA void Inst_SOPP__S_SETPRIO::execute(GPUDynInstPtr gpuDynInst) { - panicUnimplemented(); + ScalarRegU16 simm16 = instData.SIMM16; + ScalarRegU32 userPrio = simm16 & 0x3; + + warn_once("S_SETPRIO ignored -- Requested priority %d\n", userPrio); } // execute // --- Inst_SOPP__S_SENDMSG class methods --- @@ -6384,65 +6388,17 @@ namespace VegaISA void Inst_VOP2__V_MUL_U32_U24::execute(GPUDynInstPtr gpuDynInst) { - Wavefront *wf = gpuDynInst->wavefront(); - ConstVecOperandU32 src0(gpuDynInst, instData.SRC0); - VecOperandU32 src1(gpuDynInst, instData.VSRC1); - VecOperandU32 vdst(gpuDynInst, instData.VDST); - - src0.readSrc(); - src1.read(); - - if (isSDWAInst()) { - VecOperandU32 src0_sdwa(gpuDynInst, extData.iFmt_VOP_SDWA.SRC0); - // use copies of original src0, src1, and dest during selecting - VecOperandU32 origSrc0_sdwa(gpuDynInst, - extData.iFmt_VOP_SDWA.SRC0); - VecOperandU32 origSrc1(gpuDynInst, instData.VSRC1); - VecOperandU32 origVdst(gpuDynInst, instData.VDST); - - src0_sdwa.read(); - origSrc0_sdwa.read(); - origSrc1.read(); - - DPRINTF(VEGA, "Handling V_MUL_U32_U24 SRC SDWA. SRC0: register " - "v[%d], DST_SEL: %d, DST_U: %d, CLMP: %d, SRC0_SEL: " - "%d, SRC0_SEXT: %d, SRC0_NEG: %d, SRC0_ABS: %d, SRC1_SEL: " - "%d, SRC1_SEXT: %d, SRC1_NEG: %d, SRC1_ABS: %d\n", - extData.iFmt_VOP_SDWA.SRC0, extData.iFmt_VOP_SDWA.DST_SEL, - extData.iFmt_VOP_SDWA.DST_U, - extData.iFmt_VOP_SDWA.CLMP, - extData.iFmt_VOP_SDWA.SRC0_SEL, - extData.iFmt_VOP_SDWA.SRC0_SEXT, - extData.iFmt_VOP_SDWA.SRC0_NEG, - extData.iFmt_VOP_SDWA.SRC0_ABS, - extData.iFmt_VOP_SDWA.SRC1_SEL, - extData.iFmt_VOP_SDWA.SRC1_SEXT, - extData.iFmt_VOP_SDWA.SRC1_NEG, - extData.iFmt_VOP_SDWA.SRC1_ABS); - - processSDWA_src(extData.iFmt_VOP_SDWA, src0_sdwa, origSrc0_sdwa, - src1, origSrc1); - - for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) { - if (wf->execMask(lane)) { - vdst[lane] = bits(src0_sdwa[lane], 23, 0) * - bits(src1[lane], 23, 0); - origVdst[lane] = vdst[lane]; // keep copy consistent - } - } - - processSDWA_dst(extData.iFmt_VOP_SDWA, vdst, origVdst); - } else { + auto opImpl = [](VecOperandU32& src0, VecOperandU32& src1, + VecOperandU32& vdst, Wavefront* wf) { for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) { if (wf->execMask(lane)) { vdst[lane] = bits(src0[lane], 23, 0) * bits(src1[lane], 23, 0); } } - } + }; - - vdst.write(); + vop2Helper(gpuDynInst, opImpl); } // execute // --- Inst_VOP2__V_MUL_HI_U32_U24 class methods --- @@ -32715,7 +32671,7 @@ namespace VegaISA for (int i = 0; i < 4 ; ++i) { VecElemU32 permuted_val = permute(selector, 0xFF & ((VecElemU32)src2[lane] >> (8 * i))); - vdst[lane] |= (permuted_val << i); + vdst[lane] |= (permuted_val << (8 * i)); } DPRINTF(VEGA, "v_perm result: 0x%08x\n", vdst[lane]); @@ -36019,6 +35975,11 @@ namespace VegaISA */ wf->computeUnit->vrf[wf->simdId]-> scheduleWriteOperandsFromLoad(wf, gpuDynInst); + /** + * Similarly, this counter could build up over time, even across + * multiple wavefronts, and cause a deadlock. + */ + wf->rdLmReqsInPipe--; } // execute // --- Inst_DS__DS_PERMUTE_B32 class methods --- @@ -36102,6 +36063,11 @@ namespace VegaISA */ wf->computeUnit->vrf[wf->simdId]-> scheduleWriteOperandsFromLoad(wf, gpuDynInst); + /** + * Similarly, this counter could build up over time, even across + * multiple wavefronts, and cause a deadlock. + */ + wf->rdLmReqsInPipe--; } // execute // --- Inst_DS__DS_BPERMUTE_B32 class methods --- @@ -36185,6 +36151,11 @@ namespace VegaISA */ wf->computeUnit->vrf[wf->simdId]-> scheduleWriteOperandsFromLoad(wf, gpuDynInst); + /** + * Similarly, this counter could build up over time, even across + * multiple wavefronts, and cause a deadlock. + */ + wf->rdLmReqsInPipe--; } // execute // --- Inst_DS__DS_ADD_U64 class methods --- @@ -40614,8 +40585,87 @@ namespace VegaISA void Inst_MUBUF__BUFFER_ATOMIC_CMPSWAP::execute(GPUDynInstPtr gpuDynInst) { - panicUnimplemented(); + Wavefront *wf = gpuDynInst->wavefront(); + + if (gpuDynInst->exec_mask.none()) { + wf->decVMemInstsIssued(); + return; + } + + gpuDynInst->execUnitId = wf->execUnitId; + gpuDynInst->latency.init(gpuDynInst->computeUnit()); + gpuDynInst->latency.set(gpuDynInst->computeUnit()->clockPeriod()); + + ConstVecOperandU32 addr0(gpuDynInst, extData.VADDR); + ConstVecOperandU32 addr1(gpuDynInst, extData.VADDR + 1); + ConstScalarOperandU128 rsrcDesc(gpuDynInst, extData.SRSRC * 4); + ConstScalarOperandU32 offset(gpuDynInst, extData.SOFFSET); + ConstVecOperandU32 src(gpuDynInst, extData.VDATA); + ConstVecOperandU32 cmp(gpuDynInst, extData.VDATA + 1); + + rsrcDesc.read(); + offset.read(); + src.read(); + cmp.read(); + + int inst_offset = instData.OFFSET; + + if (!instData.IDXEN && !instData.OFFEN) { + calcAddr(gpuDynInst, + addr0, addr1, rsrcDesc, offset, inst_offset); + } else if (!instData.IDXEN && instData.OFFEN) { + addr0.read(); + calcAddr(gpuDynInst, + addr0, addr1, rsrcDesc, offset, inst_offset); + } else if (instData.IDXEN && !instData.OFFEN) { + addr0.read(); + calcAddr(gpuDynInst, + addr1, addr0, rsrcDesc, offset, inst_offset); + } else { + addr0.read(); + addr1.read(); + calcAddr(gpuDynInst, + addr1, addr0, rsrcDesc, offset, inst_offset); + } + + for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) { + if (gpuDynInst->exec_mask[lane]) { + (reinterpret_cast(gpuDynInst->x_data))[lane] + = src[lane]; + (reinterpret_cast(gpuDynInst->a_data))[lane] + = cmp[lane]; + } + } + + gpuDynInst->computeUnit()->globalMemoryPipe.issueRequest(gpuDynInst); } // execute + + void + Inst_MUBUF__BUFFER_ATOMIC_CMPSWAP::initiateAcc(GPUDynInstPtr gpuDynInst) + { + initAtomicAccess(gpuDynInst); + } // initiateAcc + + void + Inst_MUBUF__BUFFER_ATOMIC_CMPSWAP::completeAcc(GPUDynInstPtr gpuDynInst) + { + if (isAtomicRet()) { + VecOperandU32 vdst(gpuDynInst, extData.VDATA); + + for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) { + if (gpuDynInst->exec_mask[lane]) { + vdst[lane] = (reinterpret_cast( + gpuDynInst->d_data))[lane]; + } + } + + vdst.write(); + } + } // completeAcc // --- Inst_MUBUF__BUFFER_ATOMIC_ADD class methods --- Inst_MUBUF__BUFFER_ATOMIC_ADD @@ -43927,9 +43977,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } return; } @@ -44011,9 +44063,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } return; } @@ -44096,9 +44150,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } return; } @@ -44151,9 +44207,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } return; } @@ -44206,9 +44264,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } return; } @@ -44270,9 +44330,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } return; } @@ -44337,9 +44399,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } wf->decExpInstsIssued(); return; } @@ -44394,9 +44458,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } wf->decExpInstsIssued(); return; } @@ -44451,9 +44517,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } wf->decExpInstsIssued(); return; } @@ -44509,9 +44577,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } wf->decExpInstsIssued(); return; } @@ -44567,9 +44637,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } wf->decExpInstsIssued(); return; } @@ -44633,9 +44705,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } wf->decExpInstsIssued(); return; } @@ -44710,9 +44784,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } return; } @@ -44790,9 +44866,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } return; } @@ -44870,9 +44948,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } return; } @@ -45403,9 +45483,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } return; } @@ -45484,9 +45566,11 @@ namespace VegaISA { Wavefront *wf = gpuDynInst->wavefront(); - if (gpuDynInst->exec_mask.none() && isFlat()) { + if (gpuDynInst->exec_mask.none()) { wf->decVMemInstsIssued(); - wf->decLGKMInstsIssued(); + if (isFlat()) { + wf->decLGKMInstsIssued(); + } return; } diff --git a/src/arch/amdgpu/vega/insts/instructions.hh b/src/arch/amdgpu/vega/insts/instructions.hh index 0e4ec04764..ca349c365f 100644 --- a/src/arch/amdgpu/vega/insts/instructions.hh +++ b/src/arch/amdgpu/vega/insts/instructions.hh @@ -37220,6 +37220,8 @@ namespace VegaISA } // getOperandSize void execute(GPUDynInstPtr) override; + void initiateAcc(GPUDynInstPtr) override; + void completeAcc(GPUDynInstPtr) override; }; // Inst_MUBUF__BUFFER_ATOMIC_CMPSWAP class Inst_MUBUF__BUFFER_ATOMIC_ADD : public Inst_MUBUF diff --git a/src/arch/amdgpu/vega/insts/op_encodings.cc b/src/arch/amdgpu/vega/insts/op_encodings.cc index cc650fbbd0..c934094d9b 100644 --- a/src/arch/amdgpu/vega/insts/op_encodings.cc +++ b/src/arch/amdgpu/vega/insts/op_encodings.cc @@ -1546,6 +1546,8 @@ namespace VegaISA // The SEG field specifies FLAT(0) SCRATCH(1) or GLOBAL(2) if (iFmt->SEG == 0) { setFlag(Flat); + } else if (iFmt->SEG == 1) { + setFlag(FlatScratch); } else if (iFmt->SEG == 2) { setFlag(FlatGlobal); } else { @@ -1573,12 +1575,12 @@ namespace VegaISA Inst_FLAT::initOperandInfo() { // One of the flat subtypes should be specified via flags - assert(isFlat() ^ isFlatGlobal()); + assert(isFlat() ^ isFlatGlobal() ^ isFlatScratch()); if (isFlat()) { initFlatOperandInfo(); - } else if (isFlatGlobal()) { - initGlobalOperandInfo(); + } else if (isFlatGlobal() || isFlatScratch()) { + initGlobalScratchOperandInfo(); } else { panic("Unknown flat subtype!\n"); } @@ -1622,7 +1624,7 @@ namespace VegaISA } void - Inst_FLAT::initGlobalOperandInfo() + Inst_FLAT::initGlobalScratchOperandInfo() { //3 formats: // 1 dst + 2 src (load) @@ -1691,12 +1693,12 @@ namespace VegaISA Inst_FLAT::generateDisassembly() { // One of the flat subtypes should be specified via flags - assert(isFlat() ^ isFlatGlobal()); + assert(isFlat() ^ isFlatGlobal() ^ isFlatScratch()); if (isFlat()) { generateFlatDisassembly(); - } else if (isFlatGlobal()) { - generateGlobalDisassembly(); + } else if (isFlatGlobal() || isFlatScratch()) { + generateGlobalScratchDisassembly(); } else { panic("Unknown flat subtype!\n"); } @@ -1720,11 +1722,16 @@ namespace VegaISA } void - Inst_FLAT::generateGlobalDisassembly() + Inst_FLAT::generateGlobalScratchDisassembly() { // Replace flat_ with global_ in assembly string std::string global_opcode = _opcode; - global_opcode.replace(0, 4, "global"); + if (isFlatGlobal()) { + global_opcode.replace(0, 4, "global"); + } else { + assert(isFlatScratch()); + global_opcode.replace(0, 4, "scratch"); + } std::stringstream dis_stream; dis_stream << global_opcode << " "; diff --git a/src/arch/amdgpu/vega/insts/op_encodings.hh b/src/arch/amdgpu/vega/insts/op_encodings.hh index 1071eada0e..a1c5e99c91 100644 --- a/src/arch/amdgpu/vega/insts/op_encodings.hh +++ b/src/arch/amdgpu/vega/insts/op_encodings.hh @@ -272,6 +272,111 @@ namespace VegaISA InstFormat extData; uint32_t varSize; + template + T sdwaSrcHelper(GPUDynInstPtr gpuDynInst, T & src1) + { + T src0_sdwa(gpuDynInst, extData.iFmt_VOP_SDWA.SRC0); + // use copies of original src0, src1, and dest during selecting + T origSrc0_sdwa(gpuDynInst, extData.iFmt_VOP_SDWA.SRC0); + T origSrc1(gpuDynInst, instData.VSRC1); + + src0_sdwa.read(); + origSrc0_sdwa.read(); + origSrc1.read(); + + DPRINTF(VEGA, "Handling %s SRC SDWA. SRC0: register v[%d], " + "DST_SEL: %d, DST_U: %d, CLMP: %d, SRC0_SEL: %d, SRC0_SEXT: " + "%d, SRC0_NEG: %d, SRC0_ABS: %d, SRC1_SEL: %d, SRC1_SEXT: %d, " + "SRC1_NEG: %d, SRC1_ABS: %d\n", + opcode().c_str(), extData.iFmt_VOP_SDWA.SRC0, + extData.iFmt_VOP_SDWA.DST_SEL, extData.iFmt_VOP_SDWA.DST_U, + extData.iFmt_VOP_SDWA.CLMP, extData.iFmt_VOP_SDWA.SRC0_SEL, + extData.iFmt_VOP_SDWA.SRC0_SEXT, + extData.iFmt_VOP_SDWA.SRC0_NEG, extData.iFmt_VOP_SDWA.SRC0_ABS, + extData.iFmt_VOP_SDWA.SRC1_SEL, + extData.iFmt_VOP_SDWA.SRC1_SEXT, + extData.iFmt_VOP_SDWA.SRC1_NEG, + extData.iFmt_VOP_SDWA.SRC1_ABS); + + processSDWA_src(extData.iFmt_VOP_SDWA, src0_sdwa, origSrc0_sdwa, + src1, origSrc1); + + return src0_sdwa; + } + + template + void sdwaDstHelper(GPUDynInstPtr gpuDynInst, T & vdst) + { + T origVdst(gpuDynInst, instData.VDST); + + Wavefront *wf = gpuDynInst->wavefront(); + for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) { + if (wf->execMask(lane)) { + origVdst[lane] = vdst[lane]; // keep copy consistent + } + } + + processSDWA_dst(extData.iFmt_VOP_SDWA, vdst, origVdst); + } + + template + T dppHelper(GPUDynInstPtr gpuDynInst, T & src1) + { + T src0_dpp(gpuDynInst, extData.iFmt_VOP_DPP.SRC0); + src0_dpp.read(); + + DPRINTF(VEGA, "Handling %s SRC DPP. SRC0: register v[%d], " + "DPP_CTRL: 0x%#x, SRC0_ABS: %d, SRC0_NEG: %d, SRC1_ABS: %d, " + "SRC1_NEG: %d, BC: %d, BANK_MASK: %d, ROW_MASK: %d\n", + opcode().c_str(), extData.iFmt_VOP_DPP.SRC0, + extData.iFmt_VOP_DPP.DPP_CTRL, extData.iFmt_VOP_DPP.SRC0_ABS, + extData.iFmt_VOP_DPP.SRC0_NEG, extData.iFmt_VOP_DPP.SRC1_ABS, + extData.iFmt_VOP_DPP.SRC1_NEG, extData.iFmt_VOP_DPP.BC, + extData.iFmt_VOP_DPP.BANK_MASK, extData.iFmt_VOP_DPP.ROW_MASK); + + processDPP(gpuDynInst, extData.iFmt_VOP_DPP, src0_dpp, src1); + + return src0_dpp; + } + + template + void vop2Helper(GPUDynInstPtr gpuDynInst, + void (*fOpImpl)(T&, T&, T&, Wavefront*)) + { + Wavefront *wf = gpuDynInst->wavefront(); + T src0(gpuDynInst, instData.SRC0); + T src1(gpuDynInst, instData.VSRC1); + T vdst(gpuDynInst, instData.VDST); + + src0.readSrc(); + src1.read(); + + if (isSDWAInst()) { + T src0_sdwa = sdwaSrcHelper(gpuDynInst, src1); + fOpImpl(src0_sdwa, src1, vdst, wf); + sdwaDstHelper(gpuDynInst, vdst); + } else if (isDPPInst()) { + T src0_dpp = dppHelper(gpuDynInst, src1); + fOpImpl(src0_dpp, src1, vdst, wf); + } else { + // src0 is unmodified. We need to use the const container + // type to allow reading scalar operands from src0. Only + // src0 can index scalar operands. We copy this to vdst + // temporarily to pass to the lambda so the instruction + // does not need to write two lambda functions (one for + // a const src0 and one of a mutable src0). + ConstT const_src0(gpuDynInst, instData.SRC0); + const_src0.readSrc(); + + for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) { + vdst[lane] = const_src0[lane]; + } + fOpImpl(vdst, src1, vdst, wf); + } + + vdst.write(); + } + private: bool hasSecondDword(InFmt_VOP2 *); }; // Inst_VOP2 @@ -608,6 +713,19 @@ namespace VegaISA gpuDynInst->exec_mask = old_exec_mask; } + template + void + initAtomicAccess(GPUDynInstPtr gpuDynInst) + { + // temporarily modify exec_mask to supress memory accesses to oob + // regions. Only issue memory requests for lanes that have their + // exec_mask set and are not out of bounds. + VectorMask old_exec_mask = gpuDynInst->exec_mask; + gpuDynInst->exec_mask &= ~oobMask; + initMemReqHelper(gpuDynInst, MemCmd::SwapReq, true); + gpuDynInst->exec_mask = old_exec_mask; + } + void injectGlobalMemFence(GPUDynInstPtr gpuDynInst) { @@ -821,7 +939,8 @@ namespace VegaISA void initMemRead(GPUDynInstPtr gpuDynInst) { - if (gpuDynInst->executedAs() == enums::SC_GLOBAL) { + if (gpuDynInst->executedAs() == enums::SC_GLOBAL || + gpuDynInst->executedAs() == enums::SC_PRIVATE) { initMemReqHelper(gpuDynInst, MemCmd::ReadReq); } else if (gpuDynInst->executedAs() == enums::SC_GROUP) { Wavefront *wf = gpuDynInst->wavefront(); @@ -839,7 +958,8 @@ namespace VegaISA void initMemRead(GPUDynInstPtr gpuDynInst) { - if (gpuDynInst->executedAs() == enums::SC_GLOBAL) { + if (gpuDynInst->executedAs() == enums::SC_GLOBAL || + gpuDynInst->executedAs() == enums::SC_PRIVATE) { initMemReqHelper(gpuDynInst, MemCmd::ReadReq); } else if (gpuDynInst->executedAs() == enums::SC_GROUP) { Wavefront *wf = gpuDynInst->wavefront(); @@ -861,7 +981,8 @@ namespace VegaISA void initMemWrite(GPUDynInstPtr gpuDynInst) { - if (gpuDynInst->executedAs() == enums::SC_GLOBAL) { + if (gpuDynInst->executedAs() == enums::SC_GLOBAL || + gpuDynInst->executedAs() == enums::SC_PRIVATE) { initMemReqHelper(gpuDynInst, MemCmd::WriteReq); } else if (gpuDynInst->executedAs() == enums::SC_GROUP) { Wavefront *wf = gpuDynInst->wavefront(); @@ -879,7 +1000,8 @@ namespace VegaISA void initMemWrite(GPUDynInstPtr gpuDynInst) { - if (gpuDynInst->executedAs() == enums::SC_GLOBAL) { + if (gpuDynInst->executedAs() == enums::SC_GLOBAL || + gpuDynInst->executedAs() == enums::SC_PRIVATE) { initMemReqHelper(gpuDynInst, MemCmd::WriteReq); } else if (gpuDynInst->executedAs() == enums::SC_GROUP) { Wavefront *wf = gpuDynInst->wavefront(); @@ -901,6 +1023,10 @@ namespace VegaISA void initAtomicAccess(GPUDynInstPtr gpuDynInst) { + // Flat scratch requests may not be atomic according to ISA manual + // up to MI200. See MI200 manual Table 45. + assert(gpuDynInst->executedAs() != enums::SC_PRIVATE); + if (gpuDynInst->executedAs() == enums::SC_GLOBAL) { initMemReqHelper(gpuDynInst, MemCmd::SwapReq, true); } else if (gpuDynInst->executedAs() == enums::SC_GROUP) { @@ -939,7 +1065,8 @@ namespace VegaISA // If saddr = 0x7f there is no scalar reg to read and address will // be a 64-bit address. Otherwise, saddr is the reg index for a // scalar reg used as the base address for a 32-bit address. - if ((saddr == 0x7f && isFlatGlobal()) || isFlat()) { + if ((saddr == 0x7f && (isFlatGlobal() || isFlatScratch())) + || isFlat()) { ConstVecOperandU64 vbase(gpuDynInst, vaddr); vbase.read(); @@ -958,9 +1085,13 @@ namespace VegaISA if (isFlat()) { gpuDynInst->resolveFlatSegment(gpuDynInst->exec_mask); - } else { + } else if (isFlatGlobal()) { gpuDynInst->staticInstruction()->executed_as = enums::SC_GLOBAL; + } else { + assert(isFlatScratch()); + gpuDynInst->staticInstruction()->executed_as = + enums::SC_PRIVATE; } } @@ -976,7 +1107,9 @@ namespace VegaISA gpuDynInst->computeUnit()->localMemoryPipe .issueRequest(gpuDynInst); } else { - fatal("Unsupported scope for flat instruction.\n"); + assert(gpuDynInst->executedAs() == enums::SC_PRIVATE); + gpuDynInst->computeUnit()->globalMemoryPipe + .issueRequest(gpuDynInst); } } @@ -993,10 +1126,10 @@ namespace VegaISA private: void initFlatOperandInfo(); - void initGlobalOperandInfo(); + void initGlobalScratchOperandInfo(); void generateFlatDisassembly(); - void generateGlobalDisassembly(); + void generateGlobalScratchDisassembly(); void calcAddrSgpr(GPUDynInstPtr gpuDynInst, ConstVecOperandU32 &vaddr, diff --git a/src/arch/amdgpu/vega/pagetable_walker.cc b/src/arch/amdgpu/vega/pagetable_walker.cc index 96ac0fe179..6a71b14838 100644 --- a/src/arch/amdgpu/vega/pagetable_walker.cc +++ b/src/arch/amdgpu/vega/pagetable_walker.cc @@ -239,9 +239,22 @@ Walker::WalkerState::walkStateMachine(PageTableEntry &pte, Addr &nextRead, Addr part2 = 0; PageDirectoryEntry pde = static_cast(pte); - // For a four level page table block fragment size should not be needed. - // For now issue a panic to prevent strange behavior if it is non-zero. - panic_if(pde.blockFragmentSize, "PDE blockFragmentSize must be 0"); + // Block fragment size can change the size of the pages pointed to while + // moving to the next PDE. A value of 0 implies native page size. A + // non-zero value implies the next leaf in the page table is a PTE unless + // the F bit is set. If we see a non-zero value, set it here and print + // for debugging. + if (pde.blockFragmentSize) { + DPRINTF(GPUPTWalker, + "blockFragmentSize: %d, pde: %#016lx, state: %d\n", + pde.blockFragmentSize, pde, state); + blockFragmentSize = pde.blockFragmentSize; + + // At this time, only a value of 9 is used in the driver: + // https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/ + // amd/amdgpu/gmc_v9_0.c#L1165 + assert(pde.blockFragmentSize == 9); + } switch(state) { case PDE2: @@ -287,7 +300,7 @@ Walker::WalkerState::walkStateMachine(PageTableEntry &pte, Addr &nextRead, nextState = PDE0; break; case PDE0: - if (pde.p) { + if (pde.p || (blockFragmentSize && !pte.f)) { DPRINTF(GPUPTWalker, "Treating PDE0 as PTE: %#016x frag: %d\n", (uint64_t)pte, pte.fragment); entry.pte = pte; @@ -299,7 +312,15 @@ Walker::WalkerState::walkStateMachine(PageTableEntry &pte, Addr &nextRead, } // Read the PteAddr part1 = ((((uint64_t)pte) >> 6) << 3); - part2 = offsetFunc(vaddr, 9, 0); + if (pte.f) { + // For F bit we want to use the blockFragmentSize in the previous + // PDE and the blockFragmentSize in this PTE for offset function. + part2 = offsetFunc(vaddr, + blockFragmentSize, + pde.blockFragmentSize); + } else { + part2 = offsetFunc(vaddr, 9, 0); + } nextRead = ((part1 + part2) << 3) & mask(48); DPRINTF(GPUPTWalker, "Got PDE0 entry %#016x. write:%s->%#016x va:%#016x\n", @@ -369,6 +390,7 @@ bool Walker::sendTiming(WalkerState* sending_walker, PacketPtr pkt) return true; } else { (void)pkt->popSenderState(); + delete walker_state; } return false; diff --git a/src/arch/amdgpu/vega/pagetable_walker.hh b/src/arch/amdgpu/vega/pagetable_walker.hh index 2ad0748c14..232be5de70 100644 --- a/src/arch/amdgpu/vega/pagetable_walker.hh +++ b/src/arch/amdgpu/vega/pagetable_walker.hh @@ -99,11 +99,13 @@ class Walker : public ClockedObject bool started; bool timing; PacketPtr tlbPkt; + int blockFragmentSize; public: WalkerState(Walker *_walker, PacketPtr pkt, bool is_functional = false) : walker(_walker), state(Ready), nextState(Ready), dataSize(8), - enableNX(true), retrying(false), started(false), tlbPkt(pkt) + enableNX(true), retrying(false), started(false), tlbPkt(pkt), + blockFragmentSize(0) { DPRINTF(GPUPTWalker, "Walker::WalkerState %p %p %d\n", this, walker, state); diff --git a/src/arch/arm/ArmCPU.py b/src/arch/arm/ArmCPU.py index 52c3ba8a0a..35ed712cba 100644 --- a/src/arch/arm/ArmCPU.py +++ b/src/arch/arm/ArmCPU.py @@ -23,18 +23,17 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.proxy import Self - -from m5.objects.BaseAtomicSimpleCPU import BaseAtomicSimpleCPU -from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU -from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU -from m5.objects.BaseO3CPU import BaseO3CPU -from m5.objects.BaseO3Checker import BaseO3Checker -from m5.objects.BaseMinorCPU import BaseMinorCPU from m5.objects.ArmDecoder import ArmDecoder -from m5.objects.ArmMMU import ArmMMU from m5.objects.ArmInterrupts import ArmInterrupts from m5.objects.ArmISA import ArmISA +from m5.objects.ArmMMU import ArmMMU +from m5.objects.BaseAtomicSimpleCPU import BaseAtomicSimpleCPU +from m5.objects.BaseMinorCPU import BaseMinorCPU +from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU +from m5.objects.BaseO3Checker import BaseO3Checker +from m5.objects.BaseO3CPU import BaseO3CPU +from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU +from m5.proxy import Self class ArmCPU: diff --git a/src/arch/arm/ArmDecoder.py b/src/arch/arm/ArmDecoder.py index d4b82e3f4f..f92c81debe 100644 --- a/src/arch/arm/ArmDecoder.py +++ b/src/arch/arm/ArmDecoder.py @@ -35,8 +35,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.InstDecoder import InstDecoder +from m5.params import * class ArmDecoder(InstDecoder): diff --git a/src/arch/arm/ArmFsWorkload.py b/src/arch/arm/ArmFsWorkload.py index a9474fe119..6d2804cb9b 100644 --- a/src/arch/arm/ArmFsWorkload.py +++ b/src/arch/arm/ArmFsWorkload.py @@ -33,10 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.options import * -from m5.SimObject import * from m5.objects.Workload import KernelWorkload +from m5.options import * +from m5.params import * +from m5.SimObject import * class ArmMachineType(Enum): diff --git a/src/arch/arm/ArmISA.py b/src/arch/arm/ArmISA.py index 8c1ee5ae42..8e8d2b641c 100644 --- a/src/arch/arm/ArmISA.py +++ b/src/arch/arm/ArmISA.py @@ -33,13 +33,17 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.ArmPMU import ArmPMU +from m5.objects.ArmSystem import ( + ArmRelease, + SmeVectorLength, + SveVectorLength, +) +from m5.objects.BaseISA import BaseISA from m5.params import * from m5.proxy import * - from m5.SimObject import SimObject -from m5.objects.ArmPMU import ArmPMU -from m5.objects.ArmSystem import SveVectorLength, SmeVectorLength, ArmRelease -from m5.objects.BaseISA import BaseISA + # Enum for DecoderFlavor class DecoderFlavor(Enum): diff --git a/src/arch/arm/ArmMMU.py b/src/arch/arm/ArmMMU.py index dba6618567..8cafc2c624 100644 --- a/src/arch/arm/ArmMMU.py +++ b/src/arch/arm/ArmMMU.py @@ -36,12 +36,16 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.objects.ArmSystem import ArmRelease -from m5.objects.ArmTLB import ArmTLB, ArmStage2TLB +from m5.objects.ArmTLB import ( + ArmStage2TLB, + ArmTLB, +) from m5.objects.BaseMMU import BaseMMU from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * + # Basic stage 1 translation objects class ArmTableWalker(ClockedObject): type = "ArmTableWalker" diff --git a/src/arch/arm/ArmNativeTrace.py b/src/arch/arm/ArmNativeTrace.py index 0c795a6426..09462e1bf6 100644 --- a/src/arch/arm/ArmNativeTrace.py +++ b/src/arch/arm/ArmNativeTrace.py @@ -24,9 +24,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * from m5.objects.CPUTracers import NativeTrace +from m5.params import * +from m5.SimObject import SimObject class ArmNativeTrace(NativeTrace): diff --git a/src/arch/arm/ArmPMU.py b/src/arch/arm/ArmPMU.py index a4a2ebe843..27a7fa224d 100644 --- a/src/arch/arm/ArmPMU.py +++ b/src/arch/arm/ArmPMU.py @@ -35,15 +35,18 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.defines import buildEnv -from m5.SimObject import * +from m5.objects.Gic import ( + ArmInterruptPin, + ArmPPI, +) from m5.params import * from m5.params import isNullPointer from m5.proxy import * -from m5.objects.Gic import ArmInterruptPin, ArmPPI +from m5.SimObject import * from m5.util.fdthelper import * -class ProbeEvent(object): +class ProbeEvent: def __init__(self, pmu, _eventId, obj, *listOfNames): self.obj = obj self.names = listOfNames @@ -58,7 +61,7 @@ class ProbeEvent(object): ) -class SoftwareIncrement(object): +class SoftwareIncrement: def __init__(self, pmu, _eventId): self.eventId = _eventId self.pmu = pmu diff --git a/src/arch/arm/ArmSeWorkload.py b/src/arch/arm/ArmSeWorkload.py index 1bf4e3edf3..e01853baeb 100644 --- a/src/arch/arm/ArmSeWorkload.py +++ b/src/arch/arm/ArmSeWorkload.py @@ -23,9 +23,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * - from m5.objects.Workload import SEWorkload +from m5.params import * class ArmSEWorkload(SEWorkload): diff --git a/src/arch/arm/ArmSemihosting.py b/src/arch/arm/ArmSemihosting.py index 54322cdec0..8c8375e208 100644 --- a/src/arch/arm/ArmSemihosting.py +++ b/src/arch/arm/ArmSemihosting.py @@ -33,11 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.SimObject import * - from m5.objects.Serial import SerialDevice from m5.objects.Terminal import Terminal +from m5.params import * +from m5.SimObject import * class ArmSemihosting(SimObject): diff --git a/src/arch/arm/ArmSystem.py b/src/arch/arm/ArmSystem.py index 40a3a04b90..dc138cafc3 100644 --- a/src/arch/arm/ArmSystem.py +++ b/src/arch/arm/ArmSystem.py @@ -1,4 +1,4 @@ -# Copyright (c) 2009, 2012-2013, 2015-2022 ARM Limited +# Copyright (c) 2009, 2012-2013, 2015-2023 Arm Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -33,16 +33,15 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * +from typing import Any + +from m5.objects.ArmSemihosting import ArmSemihosting +from m5.objects.System import System from m5.options import * +from m5.params import * from m5.SimObject import * from m5.util.fdthelper import * -from m5.objects.System import System -from m5.objects.ArmSemihosting import ArmSemihosting - -from typing import Any - class SveVectorLength(UInt8): min = 1 @@ -89,6 +88,7 @@ class ArmExtension(ScopedEnum): # Armv8.4 "FEAT_SEL2", "FEAT_TLBIOS", + "FEAT_TLBIRANGE", "FEAT_FLAGM", "FEAT_IDST", # Armv8.5 @@ -96,8 +96,13 @@ class ArmExtension(ScopedEnum): "FEAT_RNG", "FEAT_RNG_TRAP", "FEAT_EVT", + # Armv8.6 + "FEAT_FGT", # Armv8.7 "FEAT_HCX", + # Armv8.9 + "FEAT_SCTLR2", + "FEAT_TCR2", # Armv9.2 "FEAT_SME", # Optional in Armv9.2 # Others @@ -181,11 +186,14 @@ class ArmDefaultRelease(Armv8): # Armv8.4 "FEAT_SEL2", "FEAT_TLBIOS", + "FEAT_TLBIRANGE", "FEAT_FLAGM", "FEAT_IDST", # Armv8.5 "FEAT_FLAGM2", "FEAT_EVT", + # Armv8.6 + "FEAT_FGT", # Armv8.7 "FEAT_HCX", # Armv9.2 @@ -225,6 +233,7 @@ class Armv84(Armv83): extensions = Armv83.extensions + [ "FEAT_SEL2", "FEAT_TLBIOS", + "FEAT_TLBIRANGE", "FEAT_FLAGM", "FEAT_IDST", ] @@ -239,14 +248,24 @@ class Armv85(Armv84): ] -class Armv87(Armv85): +class Armv86(Armv85): extensions = Armv85.extensions + [ + "FEAT_FGT", + ] + + +class Armv87(Armv86): + extensions = Armv86.extensions + [ "FEAT_HCX", ] -class Armv92(Armv87): - extensions = Armv87.extensions + ["FEAT_SME"] +class Armv89(Armv87): + extensions = Armv87.extensions + ["FEAT_SCTLR2", "FEAT_TCR2"] + + +class Armv92(Armv89): + extensions = Armv89.extensions + ["FEAT_SME"] class ArmAllRelease(ArmRelease): diff --git a/src/arch/arm/ArmTLB.py b/src/arch/arm/ArmTLB.py index 8475a56b5b..c868a322f1 100644 --- a/src/arch/arm/ArmTLB.py +++ b/src/arch/arm/ArmTLB.py @@ -35,10 +35,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject +from m5.objects.BaseTLB import BaseTLB from m5.params import * from m5.proxy import * -from m5.objects.BaseTLB import BaseTLB +from m5.SimObject import SimObject class ArmLookupLevel(Enum): diff --git a/src/arch/x86/SConsopts b/src/arch/arm/Kconfig similarity index 92% rename from src/arch/x86/SConsopts rename to src/arch/arm/Kconfig index 425c92145f..d75a21a267 100644 --- a/src/arch/x86/SConsopts +++ b/src/arch/arm/Kconfig @@ -1,4 +1,4 @@ -# Copyright 2021 Google, Inc. +# Copyright 2022 Google LLC # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -23,5 +23,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Import('*') -sticky_vars.Add(BoolVariable('USE_X86_ISA', 'Enable X86 ISA support', False)) +config USE_ARM_ISA + bool "ARM ISA support" + +rsource "fastmodel/Kconfig" diff --git a/src/arch/arm/SConscript b/src/arch/arm/SConscript index ee5efebf13..0aa4e66659 100644 --- a/src/arch/arm/SConscript +++ b/src/arch/arm/SConscript @@ -40,7 +40,7 @@ Import('*') -if env['USE_ARM_ISA']: +if env['CONF']['USE_ARM_ISA']: env.TagImplies('arm isa', 'gem5 lib') # The GTest function does not have a 'tags' parameter. We therefore apply this @@ -48,7 +48,7 @@ if env['USE_ARM_ISA']: # # Note: This will need reconfigured for multi-isa. E.g., if this is # incorporated: https://gem5-review.googlesource.com/c/public/gem5/+/52491 -if env['USE_ARM_ISA']: +if env['CONF']['USE_ARM_ISA']: GTest('aapcs64.test', 'aapcs64.test.cc', '../../base/debug.cc', '../../cpu/reg_class.cc', diff --git a/src/arch/arm/decoder.hh b/src/arch/arm/decoder.hh index 83690936c0..75488b6750 100644 --- a/src/arch/arm/decoder.hh +++ b/src/arch/arm/decoder.hh @@ -138,6 +138,7 @@ class Decoder : public InstDecoder StaticInstPtr si = defaultCache.decode(this, mach_inst, addr); DPRINTF(Decode, "Decode: Decoded %s instruction: %#x\n", si->getName(), mach_inst); + si->size((!emi.thumb || emi.bigThumb) ? 4 : 2); return si; } diff --git a/src/arch/arm/fastmodel/CortexA76/FastModelCortexA76.py b/src/arch/arm/fastmodel/CortexA76/FastModelCortexA76.py index f690fb5097..4c332f88ad 100644 --- a/src/arch/arm/fastmodel/CortexA76/FastModelCortexA76.py +++ b/src/arch/arm/fastmodel/CortexA76/FastModelCortexA76.py @@ -23,20 +23,25 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.ArmInterrupts import ArmInterrupts +from m5.objects.ArmISA import ArmISA +from m5.objects.FastModel import ( + AmbaInitiatorSocket, + AmbaTargetSocket, +) +from m5.objects.FastModelGIC import Gicv3CommsTargetSocket +from m5.objects.Gic import ArmPPI +from m5.objects.IntPin import IntSinkPin +from m5.objects.Iris import IrisBaseCPU +from m5.objects.ResetPort import ResetResponsePort +from m5.objects.SystemC import SystemC_ScModule from m5.params import * from m5.proxy import * from m5.SimObject import SimObject - -from m5.objects.ArmInterrupts import ArmInterrupts -from m5.objects.ArmISA import ArmISA -from m5.objects.FastModel import AmbaInitiatorSocket, AmbaTargetSocket -from m5.objects.FastModelGIC import Gicv3CommsTargetSocket -from m5.objects.ResetPort import ResetResponsePort -from m5.objects.IntPin import IntSinkPin -from m5.objects.Gic import ArmPPI -from m5.objects.Iris import IrisBaseCPU -from m5.objects.SystemC import SystemC_ScModule -from m5.util.fdthelper import FdtNode, FdtPropertyWords +from m5.util.fdthelper import ( + FdtNode, + FdtPropertyWords, +) class FastModelCortexA76(IrisBaseCPU): diff --git a/src/arch/arm/fastmodel/CortexA76/SConscript b/src/arch/arm/fastmodel/CortexA76/SConscript index f3c66063de..8ed2cce634 100644 --- a/src/arch/arm/fastmodel/CortexA76/SConscript +++ b/src/arch/arm/fastmodel/CortexA76/SConscript @@ -25,6 +25,9 @@ Import('*') +if not env['CONF']['USE_ARM_FASTMODEL']: + Return() + protocol_dir = Dir('..').Dir('protocol') for name in ('x1', 'x2', 'x3', 'x4'): diff --git a/src/arch/arm/fastmodel/CortexA76/thread_context.cc b/src/arch/arm/fastmodel/CortexA76/thread_context.cc index c6704852fc..eb936b8ea4 100644 --- a/src/arch/arm/fastmodel/CortexA76/thread_context.cc +++ b/src/arch/arm/fastmodel/CortexA76/thread_context.cc @@ -228,7 +228,7 @@ Iris::ThreadContext::IdxNameMap CortexA76TC::miscRegIdxNameMap({ // ArmISA::MISCREG_SCTLR_RST? { ArmISA::MISCREG_SEV_MAILBOX, "SEV_STATE" }, - // AArch32 CP14 registers (debug/trace/ThumbEE/Jazelle control) + // AArch32 CP14 registers (debug/trace control) // ArmISA::MISCREG_DBGDIDR? // ArmISA::MISCREG_DBGDSCRint? // ArmISA::MISCREG_DBGDCCINT? diff --git a/src/arch/arm/fastmodel/CortexR52/FastModelCortexR52.py b/src/arch/arm/fastmodel/CortexR52/FastModelCortexR52.py index fe81e72bd0..acf5087867 100644 --- a/src/arch/arm/fastmodel/CortexR52/FastModelCortexR52.py +++ b/src/arch/arm/fastmodel/CortexR52/FastModelCortexR52.py @@ -23,18 +23,24 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.ArmInterrupts import ArmInterrupts +from m5.objects.ArmISA import ArmISA +from m5.objects.FastModel import ( + AmbaInitiatorSocket, + AmbaTargetSocket, +) +from m5.objects.IntPin import ( + IntSinkPin, + IntSourcePin, + VectorIntSinkPin, +) +from m5.objects.Iris import IrisBaseCPU +from m5.objects.ResetPort import ResetResponsePort +from m5.objects.SystemC import SystemC_ScModule from m5.params import * from m5.proxy import * from m5.SimObject import SimObject -from m5.objects.ArmInterrupts import ArmInterrupts -from m5.objects.ArmISA import ArmISA -from m5.objects.FastModel import AmbaInitiatorSocket, AmbaTargetSocket -from m5.objects.ResetPort import ResetResponsePort -from m5.objects.IntPin import IntSourcePin, IntSinkPin, VectorIntSinkPin -from m5.objects.Iris import IrisBaseCPU -from m5.objects.SystemC import SystemC_ScModule - class FastModelCortexR52(IrisBaseCPU): type = "FastModelCortexR52" diff --git a/src/arch/arm/fastmodel/CortexR52/SConscript b/src/arch/arm/fastmodel/CortexR52/SConscript index bf3df74483..1ecaad86da 100644 --- a/src/arch/arm/fastmodel/CortexR52/SConscript +++ b/src/arch/arm/fastmodel/CortexR52/SConscript @@ -25,6 +25,9 @@ Import('*') +if not env['CONF']['USE_ARM_FASTMODEL']: + Return() + protocol_dir = Dir('..').Dir('protocol') for name in ('x1', 'x2', 'x3', 'x4'): diff --git a/src/arch/arm/fastmodel/CortexR52/thread_context.cc b/src/arch/arm/fastmodel/CortexR52/thread_context.cc index a20f8e0a89..b88bd7d99b 100644 --- a/src/arch/arm/fastmodel/CortexR52/thread_context.cc +++ b/src/arch/arm/fastmodel/CortexR52/thread_context.cc @@ -188,7 +188,7 @@ Iris::ThreadContext::IdxNameMap CortexR52TC::miscRegIdxNameMap({ // ArmISA::MISCREG_SCTLR_RST? // ArmISA::MISCREG_SEV_MAILBOX? - // AArch32 CP14 registers (debug/trace/ThumbEE/Jazelle control) + // AArch32 CP14 registers (debug/trace control) // ArmISA::MISCREG_DBGDIDR? // ArmISA::MISCREG_DBGDSCRint? // ArmISA::MISCREG_DBGDCCINT? diff --git a/src/arch/arm/fastmodel/FastModel.py b/src/arch/arm/fastmodel/FastModel.py index 8ba537623a..3ba5d8bb78 100644 --- a/src/arch/arm/fastmodel/FastModel.py +++ b/src/arch/arm/fastmodel/FastModel.py @@ -23,12 +23,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.SystemC import SystemC_ScModule +from m5.objects.Tlm import ( + TlmInitiatorSocket, + TlmTargetSocket, +) from m5.params import * from m5.proxy import * -from m5.objects.SystemC import SystemC_ScModule -from m5.objects.Tlm import TlmInitiatorSocket, TlmTargetSocket - def AMBA_TARGET_ROLE(width): return "AMBA TARGET %d" % width diff --git a/src/arch/arm/fastmodel/GIC/FastModelGIC.py b/src/arch/arm/fastmodel/GIC/FastModelGIC.py index b1a9a3c8a1..9d0b5e0fcc 100644 --- a/src/arch/arm/fastmodel/GIC/FastModelGIC.py +++ b/src/arch/arm/fastmodel/GIC/FastModelGIC.py @@ -35,15 +35,17 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.util.fdthelper import * -from m5.SimObject import SimObject - -from m5.objects.FastModel import AmbaInitiatorSocket, AmbaTargetSocket +from m5.objects.FastModel import ( + AmbaInitiatorSocket, + AmbaTargetSocket, +) from m5.objects.Gic import BaseGic from m5.objects.IntPin import VectorIntSourcePin from m5.objects.ResetPort import ResetResponsePort from m5.objects.SystemC import SystemC_ScModule +from m5.params import * +from m5.SimObject import SimObject +from m5.util.fdthelper import * GICV3_COMMS_TARGET_ROLE = "GICV3 COMMS TARGET" GICV3_COMMS_INITIATOR_ROLE = "GICV3 COMMS INITIATOR" diff --git a/src/arch/arm/fastmodel/GIC/SConscript b/src/arch/arm/fastmodel/GIC/SConscript index c56ddd537f..db16c96da0 100644 --- a/src/arch/arm/fastmodel/GIC/SConscript +++ b/src/arch/arm/fastmodel/GIC/SConscript @@ -25,6 +25,9 @@ Import('*') +if not env['CONF']['USE_ARM_FASTMODEL']: + Return() + protocol_dir = Dir('..').Dir('protocol') ArmFastModelComponent(File('GIC.sgproj'), File('GIC.lisa'), diff --git a/util/dockerfiles/ubuntu-18.04_gcc-version/Dockerfile b/src/arch/arm/fastmodel/Kconfig similarity index 59% rename from util/dockerfiles/ubuntu-18.04_gcc-version/Dockerfile rename to src/arch/arm/fastmodel/Kconfig index 3e94e8d0e6..f4eec968f9 100644 --- a/util/dockerfiles/ubuntu-18.04_gcc-version/Dockerfile +++ b/src/arch/arm/fastmodel/Kconfig @@ -1,5 +1,4 @@ -# Copyright (c) 2020 The Regents of the University of California -# All Rights Reserved. +# Copyright 2022 Google LLC # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -23,27 +22,39 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -FROM ubuntu:18.04 -# Valid version values: -# 4.8 -# 5 -# 6 -# 7 -# 8 -ARG version +menu "Fast Model" -RUN apt -y update && apt -y upgrade && \ - apt -y install git m4 scons zlib1g zlib1g-dev gcc-multilib \ - libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev \ - python3-dev python3 doxygen wget zip gcc-${version} \ - g++-${version} make + config USE_ARM_FASTMODEL + bool "Fast Model integration" + default n + depends on USE_ARM_ISA + depends on USE_SYSTEMC -RUN update-alternatives --install \ - /usr/bin/g++ g++ /usr/bin/g++-${version} 100 -RUN update-alternatives --install \ - /usr/bin/gcc gcc /usr/bin/gcc-${version} 100 -RUN update-alternatives --install \ - /usr/bin/c++ c++ /usr/bin/g++-${version} 100 -RUN update-alternatives --install \ - /usr/bin/cc cc /usr/bin/gcc-${version} 100 + if USE_ARM_FASTMODEL + config PVLIB_HOME + string "Fast Model portfolio directory" + default "$(PVLIB_HOME)" + + config PVLIB_FLAVOR + string "What build flavor of the Fast Model pvlib to use" + default "Linux64_GCC-7.3" + + config MAXCORE_HOME + string "Fast Model tools directory" + default "$(MAXCORE_HOME)" + + config ARMLMD_LICENSE_FILE + string "ARM license file location" + default "$(ARMLMD_LICENSE_FILE)" + + config ARMLMD_LICENSE_COUNT + int "The maximum number of ARM licenses to use concurrently" + default 1 + + config SIMGEN + string "simgen executable (leave unset for MAXCORE_HOME/bin/simgen" + default "" + endif + +endmenu diff --git a/src/arch/arm/fastmodel/PL330_DMAC/FastModelPL330.py b/src/arch/arm/fastmodel/PL330_DMAC/FastModelPL330.py index 21ead525d3..23b6f711a0 100644 --- a/src/arch/arm/fastmodel/PL330_DMAC/FastModelPL330.py +++ b/src/arch/arm/fastmodel/PL330_DMAC/FastModelPL330.py @@ -23,11 +23,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.objects.FastModel import AmbaInitiatorSocket, AmbaTargetSocket +from m5.objects.FastModel import ( + AmbaInitiatorSocket, + AmbaTargetSocket, +) from m5.objects.IntPin import IntSourcePin from m5.objects.ResetPort import ResetResponsePort from m5.objects.SystemC import SystemC_ScModule +from m5.params import * class FastModelPL330(SystemC_ScModule): diff --git a/src/arch/arm/fastmodel/PL330_DMAC/SConscript b/src/arch/arm/fastmodel/PL330_DMAC/SConscript index e93b45c1ff..3a68af21a3 100644 --- a/src/arch/arm/fastmodel/PL330_DMAC/SConscript +++ b/src/arch/arm/fastmodel/PL330_DMAC/SConscript @@ -25,6 +25,9 @@ Import('*') +if not env['CONF']['USE_ARM_FASTMODEL']: + Return() + protocol_dir = Dir('..').Dir('protocol') ArmFastModelComponent(File('PL330.sgproj'), File('PL330.lisa'), diff --git a/src/arch/arm/fastmodel/SConscript b/src/arch/arm/fastmodel/SConscript index 7c6019e2a8..925520a01c 100644 --- a/src/arch/arm/fastmodel/SConscript +++ b/src/arch/arm/fastmodel/SConscript @@ -48,15 +48,9 @@ from gem5_scons import Transform, warning, error import os.path if env['CONF']['USE_ARM_FASTMODEL']: - if env['CONF']['USE_SYSTEMC']: - env.TagImplies('arm fastmodel', 'arm isa') - else: - warning('ARM Fast Models require systemc support') - env['CONF']['USE_ARM_FASTMODEL'] = False - - -systemc_home = Dir('#/src/systemc/ext/systemc_home') -env['ENV']['SYSTEMC_HOME'] = systemc_home.abspath + env.TagImplies('arm fastmodel', 'arm isa') +else: + Return() def extract_var(name): val = env['CONF'].get(name, None) @@ -118,8 +112,6 @@ cpppaths = ( pvlib_home.Dir('include/fmruntime/eslapi'), pvlib_home.Dir('Iris/include'), - systemc_home.Dir('include'), - maxcore_home.Dir('AMBA-PV/include'), ) env.Append(CPPPATH=cpppaths) @@ -302,6 +294,10 @@ for header in common_headers: header_src = examples_common_dir.Dir('include').File(header) Command(header_target, header_src, Copy('${TARGET}', '${SOURCE}')) +if not env['CONF']['SIMGEN']: + env['CONF']['SIMGEN'] = os.path.join( + env['CONF']['MAXCORE_HOME'], 'bin', 'simgen') + class ArmFastModelComponent(object): def __init__(self, project_file, *extra_deps, tags=None): if not tags: diff --git a/src/arch/arm/fastmodel/SConsopts b/src/arch/arm/fastmodel/SConsopts index d3f898004d..e592bdbf55 100644 --- a/src/arch/arm/fastmodel/SConsopts +++ b/src/arch/arm/fastmodel/SConsopts @@ -1,5 +1,4 @@ -# Copyright 2019 Google, Inc. -# All rights reserved. +# Copyright 2019,2022 Google LLC # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -28,20 +27,12 @@ Import('*') import os -default_simgen = os.path.join('${MAXCORE_HOME}', 'bin', 'simgen') +def extract_var(name): + val = os.environ.get(name, None) + if val is not None: + main['CONF'][name] = val -sticky_vars.AddVariables( - BoolVariable('USE_ARM_FASTMODEL', - 'Build support for integrating ARM Fast Models', False), - ('PVLIB_HOME', 'Fast Model portfolio directory', - os.environ.get('PVLIB_HOME', '')), - ('PVLIB_FLAVOR', 'What build flavor of the Fast Model pvlib to use', - 'Linux64_GCC-7.3'), - ('MAXCORE_HOME', 'Fast Model tools directory', - os.environ.get('MAXCORE_HOME', '')), - ('ARMLMD_LICENSE_FILE', 'ARM license file location', - os.environ.get('ARMLMD_LICENSE_FILE', '')), - ('ARMLMD_LICENSE_COUNT', - 'The maximum number of ARM licenses to use concurrently', 1), - ('SIMGEN', 'simgen executable', os.environ.get('SIMGEN', default_simgen)), -) +# Make these environment variables in the host environment available when +# running kconfig tools by putting them in env['CONF']. +for var in 'PVLIB_HOME', 'MAXCORE_HOME', 'ARMLMD_LICENSE_FILE': + extract_var(var) diff --git a/src/arch/arm/fastmodel/arm_fast_model.py b/src/arch/arm/fastmodel/arm_fast_model.py index 5a38eb132b..df7b1b63b7 100644 --- a/src/arch/arm/fastmodel/arm_fast_model.py +++ b/src/arch/arm/fastmodel/arm_fast_model.py @@ -23,11 +23,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import datetime import logging import os import socket from m5.defines import buildEnv + import _m5.arm_fast_model ARM_LICENSE_ENV = "ARMLMD_LICENSE_FILE" @@ -44,11 +46,46 @@ def set_armlmd_license_file(force=False): os.environ[ARM_LICENSE_ENV] = license_file -def check_armlmd_license(timeout): +def check_armlmd_server(server, timeout): + """Check if the "server" passed as parameter is available. server + can also be a license file""" + if os.path.exists(server): + logging.debug(f"License file {server} exists.") + return True + + tuple = server.split("@") + if len(tuple) != 2: + # Probably not a server, and we know the file doesn't exist. + logging.debug(f'License file "{server}" does not exist.') + return False + + try: + start = datetime.datetime.now() + # Try to connect to license server. This doesn't attempt to + # communicate with it, just checking reachability. + s = socket.create_connection( + (tuple[1], int(tuple[0])), timeout=timeout + ) + end = datetime.datetime.now() + s.close() + time = end - start + logging.info( + f"License server {server} is reachable ({time.total_seconds()} seconds)." + ) + return True + except Exception as e: + logging.debug( + f"Cannot connect to license server {server} ({type(e).__name__}: {e})." + ) + return False + + +def check_armlmd_license(timeout, tries): """Check if any of the provided license server can be reached, or if a license file is provided. This allows to fail early and fast, as fastmodel code makes multiple lengthy attempts to connect to - license server. "timeout" is in seconds. + license server. "timeout" is in seconds. Makes "retries" attempt to + connect. """ servers = os.environ[ARM_LICENSE_ENV].split(":") @@ -62,33 +99,17 @@ def check_armlmd_license(timeout): if extra not in servers: servers.append(extra) - for server in servers: - if os.path.exists(server): - logging.debug(f"License file {server} exists.") - break - - tuple = server.split("@") - if len(tuple) != 2: - # Probably not a server, and we know the file doesn't exist. - logging.debug(f'License file "{server}" does not exist.') - continue - - try: - # Try to connect to license server. This doesn't attempt to - # communicate with it, just checking reachability. - s = socket.create_connection( - (tuple[1], int(tuple[0])), timeout=timeout + for try_count in range(1, tries + 1): + for server in servers: + if check_armlmd_server(server, timeout): + return + if try_count == tries: + raise ConnectionError( + f"Cannot connect to any of the license servers ({', '.join(servers)})." ) - s.close() - logging.debug(f"License server {server} is reachable.") - break - except Exception as e: - logging.debug( - f"Cannot connect to license server {server} ({type(e).__name__}: {e})." - ) - else: - raise ConnectionError( - f"Cannot connect to any of the license servers ({', '.join(servers)})." + # retry + logging.warning( + "Cannot connect to any of the license servers, retrying..." ) @@ -199,10 +220,11 @@ def setup_simulation( exit_on_dmi_warning=False, license_precheck=False, license_precheck_timeout=1, + license_precheck_tries=3, ): set_armlmd_license_file() if license_precheck: - check_armlmd_license(license_precheck_timeout) + check_armlmd_license(license_precheck_timeout, license_precheck_tries) scx_initialize(sim_name) scx_set_min_sync_latency(min_sync_latency) if exit_on_dmi_warning: diff --git a/src/arch/arm/fastmodel/iris/Iris.py b/src/arch/arm/fastmodel/iris/Iris.py index c38db908cc..5129136077 100644 --- a/src/arch/arm/fastmodel/iris/Iris.py +++ b/src/arch/arm/fastmodel/iris/Iris.py @@ -35,14 +35,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * - from m5.objects.BaseCPU import BaseCPU from m5.objects.BaseInterrupts import BaseInterrupts from m5.objects.BaseISA import BaseISA -from m5.objects.BaseTLB import BaseTLB from m5.objects.BaseMMU import BaseMMU +from m5.objects.BaseTLB import BaseTLB +from m5.params import * +from m5.proxy import * class IrisTLB(BaseTLB): diff --git a/src/arch/arm/fastmodel/iris/SConscript b/src/arch/arm/fastmodel/iris/SConscript index 33877195d9..f4d54faeb4 100644 --- a/src/arch/arm/fastmodel/iris/SConscript +++ b/src/arch/arm/fastmodel/iris/SConscript @@ -25,6 +25,9 @@ Import('*') +if not env['CONF']['USE_ARM_FASTMODEL']: + Return() + SimObject('Iris.py', sim_objects=[ 'IrisTLB', 'IrisMMU', 'IrisInterrupts', 'IrisISA', 'IrisBaseCPU'], tags='arm fastmodel') diff --git a/src/arch/arm/fastmodel/iris/thread_context.cc b/src/arch/arm/fastmodel/iris/thread_context.cc index 462995a19a..0919251a9b 100644 --- a/src/arch/arm/fastmodel/iris/thread_context.cc +++ b/src/arch/arm/fastmodel/iris/thread_context.cc @@ -581,8 +581,6 @@ ThreadContext::pcState() const pc.thumb(cpsr.t); pc.nextThumb(pc.thumb()); - pc.jazelle(cpsr.j); - pc.nextJazelle(cpsr.j); pc.aarch64(!cpsr.width); pc.nextAArch64(!cpsr.width); pc.illegalExec(false); diff --git a/src/arch/arm/fastmodel/reset_controller/FastModelResetControllerExample.py b/src/arch/arm/fastmodel/reset_controller/FastModelResetControllerExample.py index 225c5d917b..e2507cbf4c 100644 --- a/src/arch/arm/fastmodel/reset_controller/FastModelResetControllerExample.py +++ b/src/arch/arm/fastmodel/reset_controller/FastModelResetControllerExample.py @@ -23,12 +23,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * - from m5.objects.Device import BasicPioDevice from m5.objects.IntPin import IntSourcePin from m5.objects.Iris import IrisBaseCPU +from m5.params import * +from m5.proxy import * class FastModelResetControllerExample(BasicPioDevice): diff --git a/src/arch/arm/fastmodel/reset_controller/SConscript b/src/arch/arm/fastmodel/reset_controller/SConscript index 8e5de8ee8b..70ca576865 100644 --- a/src/arch/arm/fastmodel/reset_controller/SConscript +++ b/src/arch/arm/fastmodel/reset_controller/SConscript @@ -25,6 +25,9 @@ Import('*') +if not env['CONF']['USE_ARM_FASTMODEL']: + Return() + SimObject('FastModelResetControllerExample.py', sim_objects=[ 'FastModelResetControllerExample'], tags='arm fastmodel') diff --git a/src/arch/arm/faults.cc b/src/arch/arm/faults.cc index 379e761f98..4b906f226f 100644 --- a/src/arch/arm/faults.cc +++ b/src/arch/arm/faults.cc @@ -565,7 +565,6 @@ ArmFault::invoke32(ThreadContext *tc, const StaticInstPtr &inst) cpsr.i = 1; } cpsr.it1 = cpsr.it2 = 0; - cpsr.j = 0; cpsr.pan = span ? 1 : saved_cpsr.pan; tc->setMiscReg(MISCREG_CPSR, cpsr); @@ -622,8 +621,6 @@ ArmFault::invoke32(ThreadContext *tc, const StaticInstPtr &inst) PCState pc(new_pc); pc.thumb(cpsr.t); pc.nextThumb(pc.thumb()); - pc.jazelle(cpsr.j); - pc.nextJazelle(pc.jazelle()); pc.aarch64(!cpsr.width); pc.nextAArch64(!cpsr.width); pc.illegalExec(false); @@ -666,7 +663,6 @@ ArmFault::invoke64(ThreadContext *tc, const StaticInstPtr &inst) // Force some bitfields to 0 spsr.q = 0; spsr.it1 = 0; - spsr.j = 0; spsr.ge = 0; spsr.it2 = 0; spsr.t = 0; diff --git a/src/arch/arm/insts/misc64.cc b/src/arch/arm/insts/misc64.cc index 4f573fca83..4919d92da8 100644 --- a/src/arch/arm/insts/misc64.cc +++ b/src/arch/arm/insts/misc64.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2013,2017-2022 Arm Limited + * Copyright (c) 2011-2013,2017-2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -852,6 +852,354 @@ TlbiOp64::performTlbi(ExecContext *xc, MiscRegIndex dest_idx, RegVal value) cons } return; } + case MISCREG_TLBI_RVAE1_Xt: + { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + auto asid = asid_16bits ? bits(value, 63, 48) : + bits(value, 55, 48); + + ExceptionLevel target_el = EL1; + if (EL2Enabled(tc)) { + HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + if (hcr.tge && hcr.e2h) { + target_el = EL2; + } + } + + bool secure = release->has(ArmExtension::SECURITY) && !scr.ns; + TLBIRMVA tlbiOp(target_el, secure, value, asid, false); + + if (tlbiOp.valid()) + tlbiOp(tc); + return; + } + case MISCREG_TLBI_RVAE1IS_Xt: + case MISCREG_TLBI_RVAE1OS_Xt: + { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + auto asid = asid_16bits ? bits(value, 63, 48) : + bits(value, 55, 48); + + ExceptionLevel target_el = EL1; + if (EL2Enabled(tc)) { + HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + if (hcr.tge && hcr.e2h) { + target_el = EL2; + } + } + + bool secure = release->has(ArmExtension::SECURITY) && !scr.ns; + TLBIRMVA tlbiOp(target_el, secure, value, asid, false); + + if (tlbiOp.valid()) + tlbiOp.broadcast(tc); + return; + } + case MISCREG_TLBI_RVAAE1_Xt: + { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + + ExceptionLevel target_el = EL1; + if (EL2Enabled(tc)) { + HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + if (hcr.tge && hcr.e2h) { + target_el = EL2; + } + } + + bool secure = release->has(ArmExtension::SECURITY) && !scr.ns; + TLBIRMVAA tlbiOp(target_el, secure, value, false); + + if (tlbiOp.valid()) + tlbiOp(tc); + return; + } + case MISCREG_TLBI_RVAAE1IS_Xt: + case MISCREG_TLBI_RVAAE1OS_Xt: + { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + + ExceptionLevel target_el = EL1; + if (EL2Enabled(tc)) { + HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + if (hcr.tge && hcr.e2h) { + target_el = EL2; + } + } + + bool secure = release->has(ArmExtension::SECURITY) && !scr.ns; + TLBIRMVAA tlbiOp(target_el, secure, value, false); + + if (tlbiOp.valid()) + tlbiOp.broadcast(tc); + return; + } + case MISCREG_TLBI_RVALE1_Xt: + { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + auto asid = asid_16bits ? bits(value, 63, 48) : + bits(value, 55, 48); + + ExceptionLevel target_el = EL1; + if (EL2Enabled(tc)) { + HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + if (hcr.tge && hcr.e2h) { + target_el = EL2; + } + } + + bool secure = release->has(ArmExtension::SECURITY) && !scr.ns; + TLBIRMVA tlbiOp(target_el, secure, value, asid, true); + + if (tlbiOp.valid()) + tlbiOp(tc); + return; + } + case MISCREG_TLBI_RVALE1IS_Xt: + case MISCREG_TLBI_RVALE1OS_Xt: + { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + auto asid = asid_16bits ? bits(value, 63, 48) : + bits(value, 55, 48); + + ExceptionLevel target_el = EL1; + if (EL2Enabled(tc)) { + HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + if (hcr.tge && hcr.e2h) { + target_el = EL2; + } + } + + bool secure = release->has(ArmExtension::SECURITY) && !scr.ns; + TLBIRMVA tlbiOp(target_el, secure, value, asid, true); + + if (tlbiOp.valid()) + tlbiOp.broadcast(tc); + return; + } + case MISCREG_TLBI_RVAALE1_Xt: + { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + + ExceptionLevel target_el = EL1; + if (EL2Enabled(tc)) { + HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + if (hcr.tge && hcr.e2h) { + target_el = EL2; + } + } + + bool secure = release->has(ArmExtension::SECURITY) && !scr.ns; + TLBIRMVAA tlbiOp(target_el, secure, value, true); + + if (tlbiOp.valid()) + tlbiOp(tc); + return; + } + case MISCREG_TLBI_RVAALE1IS_Xt: + case MISCREG_TLBI_RVAALE1OS_Xt: + { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + + ExceptionLevel target_el = EL1; + if (EL2Enabled(tc)) { + HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + if (hcr.tge && hcr.e2h) { + target_el = EL2; + } + } + + bool secure = release->has(ArmExtension::SECURITY) && !scr.ns; + TLBIRMVAA tlbiOp(target_el, secure, value, true); + + if (tlbiOp.valid()) + tlbiOp.broadcast(tc); + return; + } + case MISCREG_TLBI_RIPAS2E1_Xt: + { + if (EL2Enabled(tc)) { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + + bool secure = release->has(ArmExtension::SECURITY) && + !scr.ns && !bits(value, 63); + + TLBIRIPA tlbiOp(EL1, secure, value, false); + + tlbiOp(tc); + } + return; + } + case MISCREG_TLBI_RIPAS2E1IS_Xt: + { + if (EL2Enabled(tc)) { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + + bool secure = release->has(ArmExtension::SECURITY) && + !scr.ns && !bits(value, 63); + + TLBIRIPA tlbiOp(EL1, secure, value, false); + + tlbiOp.broadcast(tc); + } + return; + } + case MISCREG_TLBI_RIPAS2LE1_Xt: + { + if (EL2Enabled(tc)) { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + + bool secure = release->has(ArmExtension::SECURITY) && + !scr.ns && !bits(value, 63); + + TLBIRIPA tlbiOp(EL1, secure, value, true); + + tlbiOp(tc); + } + return; + } + case MISCREG_TLBI_RIPAS2LE1IS_Xt: + { + if (EL2Enabled(tc)) { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + + bool secure = release->has(ArmExtension::SECURITY) && + !scr.ns && !bits(value, 63); + + TLBIRIPA tlbiOp(EL1, secure, value, true); + + tlbiOp.broadcast(tc); + } + return; + } + case MISCREG_TLBI_RVAE2_Xt: + { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + + bool secure = release->has(ArmExtension::SECURITY) && !scr.ns; + + if (hcr.e2h) { + // The asid will only be used when e2h == 1 + auto asid = asid_16bits ? bits(value, 63, 48) : + bits(value, 55, 48); + + TLBIRMVA tlbiOp(EL2, secure, value, asid, false); + + if (tlbiOp.valid()) + tlbiOp(tc); + } else { + TLBIRMVAA tlbiOp(EL2, secure, value, false); + + if (tlbiOp.valid()) + tlbiOp(tc); + } + return; + } + case MISCREG_TLBI_RVAE2IS_Xt: + case MISCREG_TLBI_RVAE2OS_Xt: + { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + + bool secure = release->has(ArmExtension::SECURITY) && !scr.ns; + + if (hcr.e2h) { + // The asid will only be used when e2h == 1 + auto asid = asid_16bits ? bits(value, 63, 48) : + bits(value, 55, 48); + + TLBIRMVA tlbiOp(EL2, secure, value, asid, false); + + if (tlbiOp.valid()) + tlbiOp.broadcast(tc); + } else { + TLBIRMVAA tlbiOp(EL2, secure, value, false); + + if (tlbiOp.valid()) + tlbiOp.broadcast(tc); + } + return; + } + case MISCREG_TLBI_RVALE2_Xt: + { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + + bool secure = release->has(ArmExtension::SECURITY) && !scr.ns; + + if (hcr.e2h) { + // The asid will only be used when e2h == 1 + auto asid = asid_16bits ? bits(value, 63, 48) : + bits(value, 55, 48); + + TLBIRMVA tlbiOp(EL2, secure, value, asid, true); + + if (tlbiOp.valid()) + tlbiOp(tc); + } else { + TLBIRMVAA tlbiOp(EL2, secure, value, true); + + if (tlbiOp.valid()) + tlbiOp(tc); + } + return; + } + case MISCREG_TLBI_RVALE2IS_Xt: + case MISCREG_TLBI_RVALE2OS_Xt: + { + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + + bool secure = release->has(ArmExtension::SECURITY) && !scr.ns; + + if (hcr.e2h) { + // The asid will only be used when e2h == 1 + auto asid = asid_16bits ? bits(value, 63, 48) : + bits(value, 55, 48); + + TLBIRMVA tlbiOp(EL2, secure, value, asid, true); + + if (tlbiOp.valid()) + tlbiOp.broadcast(tc); + } else { + TLBIRMVAA tlbiOp(EL2, secure, value, true); + + if (tlbiOp.valid()) + tlbiOp.broadcast(tc); + } + return; + } + case MISCREG_TLBI_RVAE3_Xt: + { + TLBIRMVAA tlbiOp(EL3, true, value, false); + if (tlbiOp.valid()) + tlbiOp(tc); + return; + } + case MISCREG_TLBI_RVAE3IS_Xt: + case MISCREG_TLBI_RVAE3OS_Xt: + { + TLBIRMVAA tlbiOp(EL3, true, value, false); + if (tlbiOp.valid()) + tlbiOp.broadcast(tc); + return; + } + case MISCREG_TLBI_RVALE3_Xt: + { + TLBIRMVAA tlbiOp(EL3, true, value, true); + if (tlbiOp.valid()) + tlbiOp(tc); + return; + } + case MISCREG_TLBI_RVALE3IS_Xt: + case MISCREG_TLBI_RVALE3OS_Xt: + { + TLBIRMVAA tlbiOp(EL3, true, value, true); + if (tlbiOp.valid()) + tlbiOp.broadcast(tc); + return; + } default: panic("Invalid TLBI\n"); } diff --git a/src/arch/arm/insts/pred_inst.hh b/src/arch/arm/insts/pred_inst.hh index da3db6c6a5..6f46831edb 100644 --- a/src/arch/arm/insts/pred_inst.hh +++ b/src/arch/arm/insts/pred_inst.hh @@ -378,6 +378,15 @@ class PredMacroOp : public PredOp std::string generateDisassembly( Addr pc, const loader::SymbolTable *symtab) const override; + + + void size(size_t newSize) override + { + for (int i = 0; i < numMicroops; i++) { + microOps[i]->size(newSize); + } + _size = newSize; + } }; /** diff --git a/src/arch/arm/insts/static_inst.cc b/src/arch/arm/insts/static_inst.cc index 54045f2fb1..a645b39270 100644 --- a/src/arch/arm/insts/static_inst.cc +++ b/src/arch/arm/insts/static_inst.cc @@ -401,8 +401,8 @@ ArmStaticInst::printTarget(std::ostream &os, Addr target, if (symtab) { auto it = symtab->findNearest(target); if (it != symtab->end()) { - ccprintf(os, "<%s", it->name); - Addr delta = target - it->address; + ccprintf(os, "<%s", it->name()); + Addr delta = target - it->address(); if (delta) ccprintf(os, "+%d>", delta); else @@ -486,9 +486,9 @@ ArmStaticInst::printMemSymbol(std::ostream &os, if (symtab) { auto it = symtab->findNearest(addr); if (it != symtab->end()) { - ccprintf(os, "%s%s", prefix, it->name); - if (it->address != addr) - ccprintf(os, "+%d", addr - it->address); + ccprintf(os, "%s%s", prefix, it->name()); + if (it->address() != addr) + ccprintf(os, "+%d", addr - it->address()); ccprintf(os, suffix); } } diff --git a/src/arch/arm/interrupts.cc b/src/arch/arm/interrupts.cc index 57b1334b77..8e8ee39da4 100644 --- a/src/arch/arm/interrupts.cc +++ b/src/arch/arm/interrupts.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2012-2013, 2016, 2019 ARM Limited + * Copyright (c) 2009, 2012-2013, 2016, 2019, 2023 Arm Limited * All rights reserved. * * The license below extends only to copyright in the software and shall @@ -43,12 +43,9 @@ namespace gem5 { bool -ArmISA::Interrupts::takeInt(InterruptTypes int_type) const +ArmISA::Interrupts::takeInt32(InterruptTypes int_type) const { - // Table G1-17~19 of ARM V8 ARM InterruptMask mask; - bool highest_el_is_64 = ArmSystem::highestELIs64(tc); - CPSR cpsr = tc->readMiscReg(MISCREG_CPSR); SCR scr = tc->readMiscReg(MISCREG_SCR_EL3);; HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); @@ -82,70 +79,330 @@ ArmISA::Interrupts::takeInt(InterruptTypes int_type) const if (hcr.tge) hcr_mask_override_bit = 1; - if (!highest_el_is_64) { - // AArch32 - if (!scr_routing_bit) { - // SCR IRQ == 0 - if (!hcr_mask_override_bit) - mask = INT_MASK_M; - else { - if (!is_secure && (el == EL0 || el == EL1)) - mask = INT_MASK_T; - else - mask = INT_MASK_M; - } - } else { - // SCR IRQ == 1 - if ((!is_secure) && - (hcr_mask_override_bit || - (!scr_fwaw_bit && !hcr_mask_override_bit))) + if (!scr_routing_bit) { + // SCR IRQ == 0 + if (!hcr_mask_override_bit) + mask = INT_MASK_M; + else { + if (!is_secure && (el == EL0 || el == EL1)) mask = INT_MASK_T; else mask = INT_MASK_M; } } else { - // AArch64 - if (!scr_routing_bit) { - // SCR IRQ == 0 - if (!scr.rw) { - // SCR RW == 0 - if (!hcr_mask_override_bit) { - if (el == EL3) - mask = INT_MASK_P; - else - mask = INT_MASK_M; + // SCR IRQ == 1 + if ((!is_secure) && + (hcr_mask_override_bit || + (!scr_fwaw_bit && !hcr_mask_override_bit))) + mask = INT_MASK_T; + else + mask = INT_MASK_M; + } + return ((mask == INT_MASK_T) || + ((mask == INT_MASK_M) && !cpsr_mask_bit)) && + (mask != INT_MASK_P); +} + + +bool +ArmISA::Interrupts::takeInt64(InterruptTypes int_type) const +{ + InterruptMask mask; + CPSR cpsr = tc->readMiscReg(MISCREG_CPSR); + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3);; + HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + ExceptionLevel el = currEL(tc); + bool cpsr_mask_bit, scr_routing_bit, hcr_mask_override_bit; + bool is_secure = isSecureBelowEL3(tc); + + switch(int_type) { + case INT_FIQ: + cpsr_mask_bit = cpsr.f; + scr_routing_bit = scr.fiq; + hcr_mask_override_bit = hcr.fmo; + break; + case INT_IRQ: + cpsr_mask_bit = cpsr.i; + scr_routing_bit = scr.irq; + hcr_mask_override_bit = hcr.imo; + break; + case INT_ABT: + cpsr_mask_bit = cpsr.a; + scr_routing_bit = scr.ea; + hcr_mask_override_bit = hcr.amo; + break; + default: + panic("Unhandled interrupt type!"); + } + + if (is_secure) { + if (!scr.eel2) { + if (!scr_routing_bit) { + // NS=0,EEL2=0,EAI/IRQ/FIQ=0 + if (el == EL3) + mask = INT_MASK_P; + else + mask = INT_MASK_M; + } else { + // NS=0,EEL2=0,EAI/IRQ/FIQ=1 + if (el == EL3) + mask = INT_MASK_M; + else + mask = INT_MASK_T; + } + } else { + if (!scr_routing_bit) { + if (!hcr.tge) { + if (!hcr_mask_override_bit) { + // NS=0,EEL2=1,EAI/IRQ/FIQ=0,TGE=0,AMO/IMO/FMO=0 + if (el == EL3 || el == EL2) + mask = INT_MASK_P; + else + mask = INT_MASK_M; + } else { + // NS=0,EEL2=1,EAI/IRQ/FIQ=0,TGE=0,AMO/IMO/FMO=1 + if (el == EL3) + mask = INT_MASK_P; + else if (el == EL2) + mask = INT_MASK_M; + else + mask = INT_MASK_T; + } } else { - if (el == EL3) - mask = INT_MASK_T; - else if (is_secure || el == EL2) - mask = INT_MASK_M; - else - mask = INT_MASK_T; + if (!hcr.e2h) { + // NS=0,EEL2=1,EAI/IRQ/FIQ=0,TGE=1,E2H=0 + if (el == EL3) + mask = INT_MASK_P; + else if (el == EL2) + mask = INT_MASK_M; + else + mask = INT_MASK_T; + } else { + // NS=0,EEL2=1,EAI/IRQ/FIQ=0,TGE=1,E2H=1 + if (el == EL3) + mask = INT_MASK_P; + else + mask = INT_MASK_M; + } } } else { - // SCR RW == 1 - if (!hcr_mask_override_bit) { - if (el == EL3 || el == EL2) - mask = INT_MASK_P; - else - mask = INT_MASK_M; - } else { + if (!hcr.tge) { + // NS=0,EEL2=1,EAI/IRQ/FIQ=1,TGE=0 + if (el == EL3) + mask = INT_MASK_M; + else + mask = INT_MASK_T; + } else { + // NS=0,EEL2=1,EAI/IRQ/FIQ=1,TGE=1 if (el == EL3) - mask = INT_MASK_P; - else if (is_secure || el == EL2) mask = INT_MASK_M; else mask = INT_MASK_T; } } + } + } else { + if (!scr_routing_bit) { + if (!scr.rw) { + if (!hcr.tge) { + if (!hcr_mask_override_bit) { + // NS=1,EAI/IRQ/FIQ=0,RW=0,TGE=0,AMO/IMO?/FMO=0 + if (el == EL3) + mask = INT_MASK_P; + else + mask = INT_MASK_M; + } else { + // NS=1,EAI/IRQ/FIQ=0,RW=0,TGE=0,AMO/IMO?/FMO=1 + if (el == EL3) + mask = INT_MASK_P; + else if (el == EL2) + mask = INT_MASK_M; + else + mask = INT_MASK_T; + } + } else { + // NS=1,EAI/IRQ/FIQ=0,RW=0,TGE=1 + if (el == EL3) + mask = INT_MASK_P; + else if (el == EL2) + mask = INT_MASK_M; + else + mask = INT_MASK_T; + } + } else { + if (!hcr.tge) { + if (!hcr_mask_override_bit) { + // NS=1,EAI/IRQ/FIQ=0,RW=1,TGE=0,AMO/IMO/FMO=0 + if (el == EL3 || el == EL2) + mask = INT_MASK_P; + else + mask = INT_MASK_M; + } else { + // NS=1,EAI/IRQ/FIQ=0,RW=1,TGE=0,AMO/IMO/FMO=1 + if (el == EL3) + mask = INT_MASK_P; + else if (el == EL2) + mask = INT_MASK_M; + else + mask = INT_MASK_T; + } + } else { + if (!hcr.e2h) { + // NS=1,EAI/IRQ/FIQ=0,RW=1,TGE=1,E2H=0 + if (el == EL3) + mask = INT_MASK_P; + else if (el == EL2) + mask = INT_MASK_M; + else + mask = INT_MASK_T; + } else { + // NS=1,EAI/IRQ/FIQ=0,RW=1,TGE=1,E2H=1 + if (el == EL3) + mask = INT_MASK_P; + else + mask = INT_MASK_M; + } + } + } } else { - // SCR IRQ == 1 if (el == EL3) mask = INT_MASK_M; else mask = INT_MASK_T; } } + return ((mask == INT_MASK_T) || + ((mask == INT_MASK_M) && !cpsr_mask_bit)) && + (mask != INT_MASK_P); +} + +bool +ArmISA::Interrupts::takeInt(InterruptTypes int_type) const +{ + // Table G1-17~19 of ARM V8 ARM + return ArmSystem::highestELIs64(tc) ? takeInt64(int_type) : + takeInt32(int_type); + +} + +bool +ArmISA::Interrupts::takeVirtualInt(InterruptTypes int_type) const +{ + return ArmSystem::highestELIs64(tc) ? takeVirtualInt64(int_type) : + takeVirtualInt32(int_type); + +} + +bool +ArmISA::Interrupts::takeVirtualInt32(InterruptTypes int_type) const +{ + CPSR cpsr = tc->readMiscReg(MISCREG_CPSR); + HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + + bool no_vhe = !HaveExt(tc, ArmExtension::FEAT_VHE); + bool amo, fmo, imo; + bool cpsr_mask_bit, hcr_mask_override_bit; + + if (hcr.tge == 1){ + amo = (no_vhe || hcr.e2h == 0); + fmo = (no_vhe || hcr.e2h == 0); + imo = (no_vhe || hcr.e2h == 0); + } else { + amo = hcr.amo; + fmo = hcr.fmo; + imo = hcr.imo; + } + + bool is_hyp_mode = currEL(tc) == EL2; + bool is_secure = ArmISA::isSecure(tc); + + switch(int_type) { + case INT_VIRT_FIQ: + cpsr_mask_bit = cpsr.f; + hcr_mask_override_bit = fmo; + break; + case INT_VIRT_IRQ: + cpsr_mask_bit = cpsr.i; + hcr_mask_override_bit = imo; + break; + case INT_VIRT_ABT: + cpsr_mask_bit = cpsr.a; + hcr_mask_override_bit = amo; + break; + default: + panic("Unhandled interrupt type!"); + } + return !cpsr_mask_bit && hcr_mask_override_bit && + !is_secure && !is_hyp_mode; +} + +bool +ArmISA::Interrupts::takeVirtualInt64(InterruptTypes int_type) const +{ + InterruptMask mask; + CPSR cpsr = tc->readMiscReg(MISCREG_CPSR); + HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + + ExceptionLevel el = currEL(tc); + bool cpsr_mask_bit, hcr_mask_override_bit; + bool is_secure = ArmISA::isSecureBelowEL3(tc); + + switch(int_type) { + case INT_VIRT_FIQ: + cpsr_mask_bit = cpsr.f; + hcr_mask_override_bit = hcr.fmo; + break; + case INT_VIRT_IRQ: + cpsr_mask_bit = cpsr.i; + hcr_mask_override_bit = hcr.imo; + break; + case INT_VIRT_ABT: + cpsr_mask_bit = cpsr.a; + hcr_mask_override_bit = hcr.amo; + break; + default: + panic("Unhandled interrupt type!"); + } + + if (is_secure) { + if (!scr.eel2) { + // NS=0,EEL2=0 + mask = INT_MASK_P; + } else { + if (!hcr.tge) { + if (!hcr_mask_override_bit) { + // NS=0,EEL2=1,TGE=0,AMO/IMO/FMO=0 + mask = INT_MASK_P; + } else { + // NS=0,EEL2=1,TGE=0,AMO/IMO/FMO=1 + if (el == EL2 || el == EL3) + mask = INT_MASK_P; + else + mask = INT_MASK_M; + } + } else { + // NS=0,EEL2=1,TGE=1 + mask = INT_MASK_P; + } + } + } else { + if (!hcr.tge) { + if (!hcr_mask_override_bit) { + // NS=1,TGE=0,AMO/IMO/FMO=0 + mask = INT_MASK_P; + } else { + // NS=1,TGE=0,AMO/IMO/FMO=1 + if (el == EL2 || el == EL3) + mask = INT_MASK_P; + else + mask = INT_MASK_M; + } + } else { + // NS=1,TGE=1 + mask = INT_MASK_P; + } + } return ((mask == INT_MASK_T) || ((mask == INT_MASK_M) && !cpsr_mask_bit)) && diff --git a/src/arch/arm/interrupts.hh b/src/arch/arm/interrupts.hh index 178ee6cea5..bddb932323 100644 --- a/src/arch/arm/interrupts.hh +++ b/src/arch/arm/interrupts.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2012-2013, 2016 ARM Limited + * Copyright (c) 2010, 2012-2013, 2016, 2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -65,7 +65,11 @@ enum InterruptTypes INT_SEV, // Special interrupt for recieving SEV's INT_VIRT_IRQ, INT_VIRT_FIQ, - NumInterruptTypes + NumInterruptTypes, + // Cannot be raised by an external signal + // (for now) from the IC so we don't instantiate a + // interrupt entry in the state array + INT_VIRT_ABT }; class Interrupts : public BaseInterrupts @@ -129,6 +133,12 @@ class Interrupts : public BaseInterrupts }; bool takeInt(InterruptTypes int_type) const; + bool takeInt32(InterruptTypes int_type) const; + bool takeInt64(InterruptTypes int_type) const; + + bool takeVirtualInt(InterruptTypes int_type) const; + bool takeVirtualInt32(InterruptTypes int_type) const; + bool takeVirtualInt64(InterruptTypes int_type) const; bool checkInterrupts() const override @@ -138,41 +148,15 @@ class Interrupts : public BaseInterrupts if (!(intStatus || hcr.va || hcr.vi || hcr.vf)) return false; - CPSR cpsr = tc->readMiscReg(MISCREG_CPSR); - - bool no_vhe = !HaveExt(tc, ArmExtension::FEAT_VHE); - bool amo, fmo, imo; - if (hcr.tge == 1){ - amo = (no_vhe || hcr.e2h == 0); - fmo = (no_vhe || hcr.e2h == 0); - imo = (no_vhe || hcr.e2h == 0); - } else { - amo = hcr.amo; - fmo = hcr.fmo; - imo = hcr.imo; - } - - bool isHypMode = currEL(tc) == EL2; - bool isSecure = ArmISA::isSecure(tc); - bool allowVIrq = !cpsr.i && imo && !isSecure && !isHypMode; - bool allowVFiq = !cpsr.f && fmo && !isSecure && !isHypMode; - bool allowVAbort = !cpsr.a && amo && !isSecure && !isHypMode; - - if ( !(intStatus || (hcr.vi && allowVIrq) || (hcr.vf && allowVFiq) || - (hcr.va && allowVAbort)) ) - return false; - - bool take_irq = takeInt(INT_IRQ); - bool take_fiq = takeInt(INT_FIQ); - bool take_ea = takeInt(INT_ABT); - - return ((interrupts[INT_IRQ] && take_irq) || - (interrupts[INT_FIQ] && take_fiq) || - (interrupts[INT_ABT] && take_ea) || - ((interrupts[INT_VIRT_IRQ] || hcr.vi) && allowVIrq) || - ((interrupts[INT_VIRT_FIQ] || hcr.vf) && allowVFiq) || - (hcr.va && allowVAbort) || - (interrupts[INT_RST]) || + return ((interrupts[INT_IRQ] && takeInt(INT_IRQ)) || + (interrupts[INT_FIQ] && takeInt(INT_FIQ)) || + (interrupts[INT_ABT] && takeInt(INT_ABT)) || + ((interrupts[INT_VIRT_IRQ] || hcr.vi) && + takeVirtualInt(INT_VIRT_IRQ)) || + ((interrupts[INT_VIRT_FIQ] || hcr.vf) && + takeVirtualInt(INT_VIRT_FIQ)) || + (hcr.va && takeVirtualInt(INT_VIRT_ABT)) || + (interrupts[INT_RST]) || (interrupts[INT_SEV]) ); } @@ -185,30 +169,29 @@ class Interrupts : public BaseInterrupts bool checkWfiWake(HCR hcr, CPSR cpsr, SCR scr) const { - uint64_t maskedIntStatus; - bool virtWake; + uint64_t masked_int_status; + bool virt_wake; - maskedIntStatus = intStatus & ~((1 << INT_VIRT_IRQ) | - (1 << INT_VIRT_FIQ)); - virtWake = (hcr.vi || interrupts[INT_VIRT_IRQ]) && hcr.imo; - virtWake |= (hcr.vf || interrupts[INT_VIRT_FIQ]) && hcr.fmo; - virtWake |= hcr.va && hcr.amo; - virtWake &= (cpsr.mode != MODE_HYP) && !isSecure(tc); - return maskedIntStatus || virtWake; + masked_int_status = intStatus & ~((1 << INT_VIRT_IRQ) | + (1 << INT_VIRT_FIQ)); + virt_wake = (hcr.vi || interrupts[INT_VIRT_IRQ]) && hcr.imo; + virt_wake |= (hcr.vf || interrupts[INT_VIRT_FIQ]) && hcr.fmo; + virt_wake |= hcr.va && hcr.amo; + virt_wake &= currEL(cpsr) < EL2 && EL2Enabled(tc); + return masked_int_status || virt_wake; } uint32_t getISR(HCR hcr, CPSR cpsr, SCR scr) { - bool useHcrMux; - CPSR isr = 0; // ARM ARM states ISR reg uses same bit possitions as CPSR + bool use_hcr_mux = currEL(cpsr) < EL2 && EL2Enabled(tc); + ISR isr = 0; - useHcrMux = (cpsr.mode != MODE_HYP) && !isSecure(tc); - isr.i = (useHcrMux & hcr.imo) ? (interrupts[INT_VIRT_IRQ] || hcr.vi) - : interrupts[INT_IRQ]; - isr.f = (useHcrMux & hcr.fmo) ? (interrupts[INT_VIRT_FIQ] || hcr.vf) - : interrupts[INT_FIQ]; - isr.a = (useHcrMux & hcr.amo) ? hcr.va : interrupts[INT_ABT]; + isr.i = (use_hcr_mux & hcr.imo) ? (interrupts[INT_VIRT_IRQ] || hcr.vi) + : interrupts[INT_IRQ]; + isr.f = (use_hcr_mux & hcr.fmo) ? (interrupts[INT_VIRT_FIQ] || hcr.vf) + : interrupts[INT_FIQ]; + isr.a = (use_hcr_mux & hcr.amo) ? hcr.va : interrupts[INT_ABT]; return isr; } @@ -237,44 +220,20 @@ class Interrupts : public BaseInterrupts assert(checkInterrupts()); HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); - CPSR cpsr = tc->readMiscReg(MISCREG_CPSR); - bool no_vhe = !HaveExt(tc, ArmExtension::FEAT_VHE); - bool amo, fmo, imo; - if (hcr.tge == 1){ - amo = (no_vhe || hcr.e2h == 0); - fmo = (no_vhe || hcr.e2h == 0); - imo = (no_vhe || hcr.e2h == 0); - } else { - amo = hcr.amo; - fmo = hcr.fmo; - imo = hcr.imo; - } - - // Calculate a few temp vars so we can work out if there's a pending - // virtual interrupt, and if its allowed to happen - // ARM ARM Issue C section B1.9.9, B1.9.11, and B1.9.13 - bool isHypMode = currEL(tc) == EL2; - bool isSecure = ArmISA::isSecure(tc); - bool allowVIrq = !cpsr.i && imo && !isSecure && !isHypMode; - bool allowVFiq = !cpsr.f && fmo && !isSecure && !isHypMode; - bool allowVAbort = !cpsr.a && amo && !isSecure && !isHypMode; - - bool take_irq = takeInt(INT_IRQ); - bool take_fiq = takeInt(INT_FIQ); - bool take_ea = takeInt(INT_ABT); - - if (interrupts[INT_IRQ] && take_irq) + if (interrupts[INT_IRQ] && takeInt(INT_IRQ)) return std::make_shared(); - if ((interrupts[INT_VIRT_IRQ] || hcr.vi) && allowVIrq) + if ((interrupts[INT_VIRT_IRQ] || hcr.vi) && + takeVirtualInt(INT_VIRT_IRQ)) return std::make_shared(); - if (interrupts[INT_FIQ] && take_fiq) + if (interrupts[INT_FIQ] && takeInt(INT_FIQ)) return std::make_shared(); - if ((interrupts[INT_VIRT_FIQ] || hcr.vf) && allowVFiq) + if ((interrupts[INT_VIRT_FIQ] || hcr.vf) && + takeVirtualInt(INT_VIRT_FIQ)) return std::make_shared(); - if (interrupts[INT_ABT] && take_ea) + if (interrupts[INT_ABT] && takeInt(INT_ABT)) return std::make_shared(); - if (hcr.va && allowVAbort) + if (hcr.va && takeVirtualInt(INT_VIRT_ABT)) return std::make_shared( 0, TlbEntry::DomainType::NoAccess, false, ArmFault::AsynchronousExternalAbort); diff --git a/src/arch/arm/isa.cc b/src/arch/arm/isa.cc index 02129266cf..f961a2d2c4 100644 --- a/src/arch/arm/isa.cc +++ b/src/arch/arm/isa.cc @@ -320,6 +320,8 @@ ISA::redirectRegVHE(int misc_reg) return ELIsInHost(tc, currEL()) ? MISCREG_CNTPCT_EL0 : misc_reg; case MISCREG_SCTLR_EL12: return MISCREG_SCTLR_EL1; + case MISCREG_SCTLR2_EL12: + return MISCREG_SCTLR2_EL1; case MISCREG_CPACR_EL12: return MISCREG_CPACR_EL1; case MISCREG_ZCR_EL12: @@ -330,6 +332,8 @@ ISA::redirectRegVHE(int misc_reg) return MISCREG_TTBR1_EL1; case MISCREG_TCR_EL12: return MISCREG_TCR_EL1; + case MISCREG_TCR2_EL12: + return MISCREG_TCR2_EL1; case MISCREG_SPSR_EL12: return MISCREG_SPSR_EL1; case MISCREG_ELR_EL12: @@ -403,7 +407,6 @@ ISA::readMiscReg(RegIndex idx) if (idx == MISCREG_CPSR) { cpsr = miscRegs[idx]; auto pc = tc->pcState().as(); - cpsr.j = pc.jazelle() ? 1 : 0; cpsr.t = pc.thumb() ? 1 : 0; return cpsr; } @@ -674,7 +677,6 @@ ISA::setMiscReg(RegIndex idx, RegVal val) miscRegs[idx], cpsr, cpsr.f, cpsr.i, cpsr.a, cpsr.mode); PCState pc = tc->pcState().as(); pc.nextThumb(cpsr.t); - pc.nextJazelle(cpsr.j); pc.illegalExec(cpsr.il == 1); selfDebug->setDebugMask(cpsr.d == 1); diff --git a/src/arch/arm/isa/formats/aarch64.isa b/src/arch/arm/isa/formats/aarch64.isa index 68a741a831..30f9009121 100644 --- a/src/arch/arm/isa/formats/aarch64.isa +++ b/src/arch/arm/isa/formats/aarch64.isa @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2022 Arm Limited +// Copyright (c) 2011-2023 Arm Limited // All rights reserved // // The license below extends only to copyright in the software and shall @@ -542,6 +542,16 @@ namespace Aarch64 case MISCREG_TLBI_VAALE1_Xt: case MISCREG_TLBI_IPAS2E1_Xt: case MISCREG_TLBI_IPAS2LE1_Xt: + case MISCREG_TLBI_RVAE1_Xt: + case MISCREG_TLBI_RVAAE1_Xt: + case MISCREG_TLBI_RVALE1_Xt: + case MISCREG_TLBI_RVAALE1_Xt: + case MISCREG_TLBI_RIPAS2E1_Xt: + case MISCREG_TLBI_RIPAS2LE1_Xt: + case MISCREG_TLBI_RVAE2_Xt: + case MISCREG_TLBI_RVALE2_Xt: + case MISCREG_TLBI_RVAE3_Xt: + case MISCREG_TLBI_RVALE3_Xt: return new Tlbi64LocalHub( machInst, miscReg, rt); case MISCREG_TLBI_ALLE3IS: @@ -576,6 +586,26 @@ namespace Aarch64 case MISCREG_TLBI_IPAS2E1OS_Xt: case MISCREG_TLBI_IPAS2LE1IS_Xt: case MISCREG_TLBI_IPAS2LE1OS_Xt: + case MISCREG_TLBI_RVAE1IS_Xt: + case MISCREG_TLBI_RVAE1OS_Xt: + case MISCREG_TLBI_RVAAE1IS_Xt: + case MISCREG_TLBI_RVAAE1OS_Xt: + case MISCREG_TLBI_RVALE1IS_Xt: + case MISCREG_TLBI_RVALE1OS_Xt: + case MISCREG_TLBI_RVAALE1IS_Xt: + case MISCREG_TLBI_RVAALE1OS_Xt: + case MISCREG_TLBI_RIPAS2E1IS_Xt: + case MISCREG_TLBI_RIPAS2E1OS_Xt: + case MISCREG_TLBI_RIPAS2LE1IS_Xt: + case MISCREG_TLBI_RIPAS2LE1OS_Xt: + case MISCREG_TLBI_RVAE2IS_Xt: + case MISCREG_TLBI_RVAE2OS_Xt: + case MISCREG_TLBI_RVALE2IS_Xt: + case MISCREG_TLBI_RVALE2OS_Xt: + case MISCREG_TLBI_RVAE3IS_Xt: + case MISCREG_TLBI_RVAE3OS_Xt: + case MISCREG_TLBI_RVALE3IS_Xt: + case MISCREG_TLBI_RVALE3OS_Xt: return new Tlbi64ShareableHub( machInst, miscReg, rt, dec.dvmEnabled); default: diff --git a/src/arch/arm/isa/formats/branch.isa b/src/arch/arm/isa/formats/branch.isa index ff6bfda54e..8213ab9ff8 100644 --- a/src/arch/arm/isa/formats/branch.isa +++ b/src/arch/arm/isa/formats/branch.isa @@ -212,10 +212,6 @@ def format Thumb32BranchesAndMiscCtrl() {{ { const uint32_t op = bits(machInst, 7, 4); switch (op) { - case 0x0: - return new Leavex(machInst); - case 0x1: - return new Enterx(machInst); case 0x2: return new Clrex(machInst); case 0x4: diff --git a/src/arch/arm/isa/insts/amo64.isa b/src/arch/arm/isa/insts/amo64.isa index 72eea89518..3de9a41bfe 100644 --- a/src/arch/arm/isa/insts/amo64.isa +++ b/src/arch/arm/isa/insts/amo64.isa @@ -827,35 +827,35 @@ let {{ ret_op=False, flavor="release").emit(OP_DICT['MIN']) AtomicArithmeticSingleOp("swpb", "SWPB", 1, unsign=True, - ret_op=False, flavor="normal").emit(OP_DICT['SWP']) + flavor="normal").emit(OP_DICT['SWP']) AtomicArithmeticSingleOp("swplb", "SWPLB", 1, unsign=True, - ret_op=False, flavor="release").emit(OP_DICT['SWP']) + flavor="release").emit(OP_DICT['SWP']) AtomicArithmeticSingleOp("swpab", "SWPAB", 1, unsign=True, - ret_op=False, flavor="acquire").emit(OP_DICT['SWP']) + flavor="acquire").emit(OP_DICT['SWP']) AtomicArithmeticSingleOp("swplab", "SWPLAB", 1, unsign=True, - ret_op=False, flavor="acquire_release").emit(OP_DICT['SWP']) + flavor="acquire_release").emit(OP_DICT['SWP']) AtomicArithmeticSingleOp("swph", "SWPH", 2, unsign=True, - ret_op=False, flavor="normal").emit(OP_DICT['SWP']) + flavor="normal").emit(OP_DICT['SWP']) AtomicArithmeticSingleOp("swplh", "SWPLH", 2, unsign=True, - ret_op=False, flavor="release").emit(OP_DICT['SWP']) + flavor="release").emit(OP_DICT['SWP']) AtomicArithmeticSingleOp("swpah", "SWPAH", 2, unsign=True, - ret_op=False, flavor="acquire").emit(OP_DICT['SWP']) + flavor="acquire").emit(OP_DICT['SWP']) AtomicArithmeticSingleOp("swplah", "SWPLAH", 2, unsign=True, - ret_op=False, flavor="acquire_release").emit(OP_DICT['SWP']) + flavor="acquire_release").emit(OP_DICT['SWP']) AtomicArithmeticSingleOp("swp", "SWP", 4, unsign=True, - ret_op=False, flavor="normal").emit(OP_DICT['SWP']) + flavor="normal").emit(OP_DICT['SWP']) AtomicArithmeticSingleOp("swpl", "SWPL", 4, unsign=True, - ret_op=False, flavor="release").emit(OP_DICT['SWP']) + flavor="release").emit(OP_DICT['SWP']) AtomicArithmeticSingleOp("swpa", "SWPA", 4, unsign=True, - ret_op=False, flavor="acquire").emit(OP_DICT['SWP']) + flavor="acquire").emit(OP_DICT['SWP']) AtomicArithmeticSingleOp("swpla", "SWPLA", 4, unsign=True, - ret_op=False, flavor="acquire_release").emit(OP_DICT['SWP']) + flavor="acquire_release").emit(OP_DICT['SWP']) AtomicArithmeticSingleOp("swp64", "SWP64", 8, unsign=True, - ret_op=False, flavor="normal").emit(OP_DICT['SWP']) + flavor="normal").emit(OP_DICT['SWP']) AtomicArithmeticSingleOp("swpl64", "SWPL64", 8, unsign=True, - ret_op=False, flavor="release").emit(OP_DICT['SWP']) + flavor="release").emit(OP_DICT['SWP']) AtomicArithmeticSingleOp("swpa64", "SWPA64", 8, unsign=True, - ret_op=False, flavor="acquire").emit(OP_DICT['SWP']) + flavor="acquire").emit(OP_DICT['SWP']) AtomicArithmeticSingleOp("swpla64", "SWPLA64", 8, unsign=True, - ret_op=False, flavor="acquire_release").emit(OP_DICT['SWP']) + flavor="acquire_release").emit(OP_DICT['SWP']) }}; diff --git a/src/arch/arm/isa/insts/branch64.isa b/src/arch/arm/isa/insts/branch64.isa index f437651af3..5910a434f0 100644 --- a/src/arch/arm/isa/insts/branch64.isa +++ b/src/arch/arm/isa/insts/branch64.isa @@ -1,6 +1,6 @@ // -*- mode:c++ -*- -// Copyright (c) 2011-2013, 2016, 2018, 2020 ARM Limited +// Copyright (c) 2011-2013, 2016, 2018, 2020, 2023 Arm Limited // All rights reserved // // The license below extends only to copyright in the software and shall @@ -200,11 +200,21 @@ let {{ HtmFailureFaultCause::EXCEPTION); return fault; } + Addr newPc; CPSR cpsr = Cpsr; CPSR spsr = Spsr; ExceptionLevel curr_el = currEL(cpsr); + + if (fgtEnabled(xc->tcBase()) && curr_el == EL1 && + static_cast( + xc->tcBase()->readMiscReg(MISCREG_HFGITR_EL2)).eret) + { + return std::make_shared( + machInst, %(trap_iss)d, ExceptionClass::TRAPPED_ERET); + } + switch (curr_el) { case EL3: newPc = xc->tcBase()->readMiscReg(MISCREG_ELR_EL3); @@ -268,7 +278,7 @@ let {{ ''' instFlags = ['IsSerializeAfter', 'IsNonSpeculative', 'IsSquashAfter'] bIop = ArmInstObjParams('eret', 'Eret64', "BranchEret64", - bCode%{'op': ''}, instFlags) + bCode%{'op': '', 'trap_iss' : 0b00}, instFlags) header_output += BasicDeclare.subst(bIop) decoder_output += BasicConstructor64.subst(bIop) exec_output += BasicExecute.subst(bIop) @@ -278,7 +288,8 @@ let {{ fault = authIA(xc->tcBase(), newPc, XOp1, &newPc); ''' bIop = ArmInstObjParams('eretaa', 'Eretaa', "BranchEretA64", - bCode % {'op': pac_code} , instFlags) + bCode % {'op': pac_code, 'trap_iss' : 0b10}, + instFlags) header_output += BasicDeclare.subst(bIop) decoder_output += BasicConstructor64.subst(bIop) exec_output += BasicExecute.subst(bIop) @@ -288,7 +299,8 @@ let {{ fault = authIB(xc->tcBase(), newPc, XOp1, &newPc); ''' bIop = ArmInstObjParams('eretab', 'Eretab', "BranchEretA64", - bCode % {'op': pac_code} , instFlags) + bCode % {'op': pac_code, 'trap_iss' : 0b11}, + instFlags) header_output += BasicDeclare.subst(bIop) decoder_output += BasicConstructor64.subst(bIop) exec_output += BasicExecute.subst(bIop) diff --git a/src/arch/arm/isa/insts/data.isa b/src/arch/arm/isa/insts/data.isa index 31fc172883..cec761905e 100644 --- a/src/arch/arm/isa/insts/data.isa +++ b/src/arch/arm/isa/insts/data.isa @@ -268,7 +268,6 @@ let {{ CondCodesGE = new_cpsr.ge; NextThumb = (new_cpsr).t; - NextJazelle = (new_cpsr).j; NextItState = (((new_cpsr).it2 << 2) & 0xFC) | ((new_cpsr).it1 & 0x3); SevMailbox = 1; diff --git a/src/arch/arm/isa/insts/data64.isa b/src/arch/arm/isa/insts/data64.isa index a617dc3ebb..87f87130ce 100644 --- a/src/arch/arm/isa/insts/data64.isa +++ b/src/arch/arm/isa/insts/data64.isa @@ -1,6 +1,6 @@ // -*- mode:c++ -*- -// Copyright (c) 2011-2013, 2016-2022 Arm Limited +// Copyright (c) 2011-2013, 2016-2023 Arm Limited // All rights reserved // // The license below extends only to copyright in the software and shall @@ -335,13 +335,18 @@ let {{ decoder_output += RegMiscRegOp64Constructor.subst(mrsIop) exec_output += BasicExecute.subst(mrsIop) - buildDataXRegInst("mrsNZCV", 1, ''' + mrsNZCVCode = ''' CPSR cpsr = 0; cpsr.nz = CondCodesNZ; cpsr.c = CondCodesC; cpsr.v = CondCodesV; XDest = cpsr; - ''') + ''' + mrsNZCViop = ArmInstObjParams("mrs", "MrsNZCV64", + "RegMiscRegImmOp64", mrsNZCVCode) + header_output += RegMiscRegOp64Declare.subst(mrsNZCViop) + decoder_output += RegMiscRegOp64Constructor.subst(mrsNZCViop) + exec_output += BasicExecute.subst(mrsNZCViop) msrCode = msr_check_code + ''' MiscDest_ud = XOp1; @@ -382,12 +387,17 @@ let {{ exec_output += DvmInitiateAcc.subst(msrTlbiSIop) exec_output += DvmCompleteAcc.subst(msrTlbiSIop) - buildDataXRegInst("msrNZCV", 1, ''' + msrNZCVCode = ''' CPSR cpsr = XOp1; CondCodesNZ = cpsr.nz; CondCodesC = cpsr.c; CondCodesV = cpsr.v; - ''') + ''' + msrNZCVIop = ArmInstObjParams("msr", "MsrNZCV64", "MiscRegRegImmOp64", + msrNZCVCode) + header_output += MiscRegRegOp64Declare.subst(msrNZCVIop) + decoder_output += MiscRegRegOp64Constructor.subst(msrNZCVIop) + exec_output += BasicExecute.subst(msrNZCVIop) msrdczva_ea_code = msr_check_code diff --git a/src/arch/arm/isa/insts/m5ops.isa b/src/arch/arm/isa/insts/m5ops.isa index 4e508f0186..f912204fe7 100644 --- a/src/arch/arm/isa/insts/m5ops.isa +++ b/src/arch/arm/isa/insts/m5ops.isa @@ -1,5 +1,5 @@ // -// Copyright (c) 2010, 2012-2013 ARM Limited +// Copyright (c) 2010, 2012-2013, 2023 Arm Limited // All rights reserved // // The license below extends only to copyright in the software and shall @@ -47,7 +47,8 @@ let {{ { "code": gem5OpCode % "RegABI64" + 'X0 = ret;', "predicate_test": predicateTest }, - [ "IsNonSpeculative", "IsUnverifiable" ]); + [ "IsNonSpeculative", "IsUnverifiable", + "IsPseudo" ]); header_output += BasicDeclare.subst(gem5OpIop) decoder_output += BasicConstructor.subst(gem5OpIop) exec_output += PredOpExecute.subst(gem5OpIop) @@ -57,7 +58,8 @@ let {{ 'R0 = bits(ret, 31, 0);\n' + \ 'R1 = bits(ret, 63, 32);', "predicate_test": predicateTest }, - [ "IsNonSpeculative", "IsUnverifiable" ]); + [ "IsNonSpeculative", "IsUnverifiable", + "IsPseudo" ]); header_output += BasicDeclare.subst(gem5OpIop) decoder_output += BasicConstructor.subst(gem5OpIop) exec_output += PredOpExecute.subst(gem5OpIop) diff --git a/src/arch/arm/isa/insts/macromem.isa b/src/arch/arm/isa/insts/macromem.isa index edd7228cf8..5e11c3ba95 100644 --- a/src/arch/arm/isa/insts/macromem.isa +++ b/src/arch/arm/isa/insts/macromem.isa @@ -668,7 +668,6 @@ let {{ 0xF, true, sctlr.nmfi, xc->tcBase()); Cpsr = ~CondCodesMask & new_cpsr; NextThumb = new_cpsr.t; - NextJazelle = new_cpsr.j; NextItState = ((((CPSR)URb).it2 << 2) & 0xFC) | (((CPSR)URb).it1 & 0x3); CondCodesNZ = new_cpsr.nz; diff --git a/src/arch/arm/isa/insts/misc.isa b/src/arch/arm/isa/insts/misc.isa index bfcb69340d..9ee753e385 100644 --- a/src/arch/arm/isa/insts/misc.isa +++ b/src/arch/arm/isa/insts/misc.isa @@ -1,6 +1,6 @@ // -*- mode:c++ -*- -// Copyright (c) 2010-2013,2017-2021 Arm Limited +// Copyright (c) 2010-2013,2017-2021,2023 Arm Limited // All rights reserved // // The license below extends only to copyright in the software and shall @@ -40,6 +40,13 @@ let {{ svcCode = ''' ThreadContext *tc = xc->tcBase(); + if (fgtEnabled(tc) && currEL(tc) == EL0 && !ELIsInHost(tc, EL0) && + ELIs64(tc, EL1) && static_cast( + tc->readMiscReg(MISCREG_HFGITR_EL2)).svcEL0) { + return std::make_shared( + machInst, imm, ExceptionClass::SVC); + } + bool have_semi = ArmSystem::haveSemihosting(tc); if (have_semi && Thumb && imm == ArmSemihosting::T32Imm) { // Enable gem5 extensions since we can't distinguish in thumb. @@ -166,9 +173,8 @@ let {{ CondCodesGE = new_cpsr.ge; NextThumb = (new_cpsr).t; - NextJazelle = (new_cpsr).j; - NextItState = (((new_cpsr).it2 << 2) & 0xFC) - | ((new_cpsr).it1 & 0x3); + NextItState = (((new_cpsr).it2 << 2) & 0xFC) + | ((new_cpsr).it1 & 0x3); NPC = (old_cpsr.mode == MODE_HYP) ? ElrHyp : LR; ''' @@ -1076,28 +1082,6 @@ let {{ exec_output += PredOpExecute.subst(mcrr15Iop) - enterxCode = ''' - NextThumb = true; - NextJazelle = true; - ''' - enterxIop = ArmInstObjParams("enterx", "Enterx", "PredOp", - { "code": enterxCode, - "predicate_test": predicateTest }, []) - header_output += BasicDeclare.subst(enterxIop) - decoder_output += BasicConstructor.subst(enterxIop) - exec_output += PredOpExecute.subst(enterxIop) - - leavexCode = ''' - NextThumb = true; - NextJazelle = false; - ''' - leavexIop = ArmInstObjParams("leavex", "Leavex", "PredOp", - { "code": leavexCode, - "predicate_test": predicateTest }, []) - header_output += BasicDeclare.subst(leavexIop) - decoder_output += BasicConstructor.subst(leavexIop) - exec_output += PredOpExecute.subst(leavexIop) - setendCode = ''' CPSR cpsr = Cpsr; cpsr.e = imm; diff --git a/src/arch/arm/isa/insts/misc64.isa b/src/arch/arm/isa/insts/misc64.isa index 46d72d21c3..5678195415 100644 --- a/src/arch/arm/isa/insts/misc64.isa +++ b/src/arch/arm/isa/insts/misc64.isa @@ -1,6 +1,6 @@ // -*- mode:c++ -*- -// Copyright (c) 2011-2013, 2016-2018, 2020-2021 Arm Limited +// Copyright (c) 2011-2013, 2016-2018, 2020-2021, 2023 Arm Limited // All rights reserved // // The license below extends only to copyright in the software and shall @@ -43,7 +43,29 @@ let {{ HtmFailureFaultCause::EXCEPTION); return fault; } - fault = std::make_shared(machInst, bits(machInst, 20, 5)); + + const uint32_t iss = bits(machInst, 20, 5); + if (fgtEnabled(xc->tcBase())) { + ExceptionLevel curr_el = currEL(xc->tcBase()); + HFGITR hfgitr = xc->tcBase()->readMiscReg(MISCREG_HFGITR_EL2); + switch (curr_el) { + case EL0: + if (!ELIsInHost(xc->tcBase(), curr_el) && hfgitr.svcEL0) { + return std::make_shared( + machInst, iss, ExceptionClass::SVC_64); + } + break; + case EL1: + if (hfgitr.svcEL1) { + return std::make_shared( + machInst, iss, ExceptionClass::SVC_64); + } + break; + default: + break; + } + } + fault = std::make_shared(machInst, iss); ''' svcIop = ArmInstObjParams("svc", "Svc64", "ImmOp64", diff --git a/src/arch/arm/isa/operands.isa b/src/arch/arm/isa/operands.isa index 5bba00f138..5e6506d0e8 100644 --- a/src/arch/arm/isa/operands.isa +++ b/src/arch/arm/isa/operands.isa @@ -526,7 +526,6 @@ def operands {{ 'IWNPC': PCStateReg('instIWNPC', srtPC), 'Thumb': PCStateReg('thumb', srtPC), 'NextThumb': PCStateReg('nextThumb', srtMode), - 'NextJazelle': PCStateReg('nextJazelle', srtMode), 'NextItState': PCStateReg('nextItstate', srtMode), 'Itstate': PCStateReg('itstate', srtMode), 'NextAArch64': PCStateReg('nextAArch64', srtMode), diff --git a/src/arch/arm/kvm/ArmKvmCPU.py b/src/arch/arm/kvm/ArmKvmCPU.py index 56770a5b0a..d3829f5c71 100644 --- a/src/arch/arm/kvm/ArmKvmCPU.py +++ b/src/arch/arm/kvm/ArmKvmCPU.py @@ -33,8 +33,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.BaseKvmCPU import BaseKvmCPU +from m5.params import * class ArmKvmCPU(BaseKvmCPU): diff --git a/src/arch/arm/kvm/ArmV8KvmCPU.py b/src/arch/arm/kvm/ArmV8KvmCPU.py index a6d83bb610..39f9d0d1ba 100644 --- a/src/arch/arm/kvm/ArmV8KvmCPU.py +++ b/src/arch/arm/kvm/ArmV8KvmCPU.py @@ -33,8 +33,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.BaseArmKvmCPU import BaseArmKvmCPU +from m5.params import * class ArmV8KvmCPU(BaseArmKvmCPU): diff --git a/src/arch/arm/kvm/BaseArmKvmCPU.py b/src/arch/arm/kvm/BaseArmKvmCPU.py index f896256063..415cf78a9e 100644 --- a/src/arch/arm/kvm/BaseArmKvmCPU.py +++ b/src/arch/arm/kvm/BaseArmKvmCPU.py @@ -33,10 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.ArmCPU import ArmCPU from m5.objects.ArmMMU import ArmMMU from m5.objects.BaseKvmCPU import BaseKvmCPU +from m5.params import * class BaseArmKvmCPU(BaseKvmCPU, ArmCPU): diff --git a/src/arch/arm/kvm/KvmGic.py b/src/arch/arm/kvm/KvmGic.py index 1002d3c2f5..a9e1e9341d 100644 --- a/src/arch/arm/kvm/KvmGic.py +++ b/src/arch/arm/kvm/KvmGic.py @@ -33,11 +33,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.Gic import ( + GicV2, + Gicv3, +) from m5.params import * from m5.proxy import * -from m5.objects.Gic import GicV2, Gicv3 - class MuxingKvmGicV2(GicV2): type = "MuxingKvmGicV2" diff --git a/src/arch/arm/kvm/arm_cpu.hh b/src/arch/arm/kvm/arm_cpu.hh index 849aa769a5..302d40e17a 100644 --- a/src/arch/arm/kvm/arm_cpu.hh +++ b/src/arch/arm/kvm/arm_cpu.hh @@ -100,7 +100,7 @@ class ArmKvmCPU : public BaseKvmCPU void stutterPC(PCStateBase &pc) const { - pc.as().setNPC(pc->instAddr()); + pc.as().setNPC(pc->instAddr()); } /** diff --git a/src/arch/arm/linux/fs_workload.cc b/src/arch/arm/linux/fs_workload.cc index 26146ef001..81bc5ae1ca 100644 --- a/src/arch/arm/linux/fs_workload.cc +++ b/src/arch/arm/linux/fs_workload.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013, 2016, 2020 ARM Limited + * Copyright (c) 2010-2013, 2016, 2020, 2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -228,21 +228,22 @@ FsLinux::startup() } } - const std::string dmesg_output = name() + ".dmesg"; - if (params().panic_on_panic) { - kernelPanic = addKernelFuncEventOrPanic( - "panic", "Kernel panic in simulated kernel", dmesg_output); - } else { - kernelPanic = addKernelFuncEventOrPanic( - "panic", "Kernel panic in simulated kernel", dmesg_output); + const std::string dmesg_output_fname = name() + ".dmesg"; + + kernelPanic = addKernelFuncEvent( + "panic", "Kernel panic in simulated kernel", + dmesg_output_fname, params().on_panic); + if (kernelPanic == nullptr) { + warn("Could not add Kernel Panic event handler. " + "`panic` symbol not found."); } - if (params().panic_on_oops) { - kernelOops = addKernelFuncEventOrPanic( - "oops_exit", "Kernel oops in guest", dmesg_output); - } else { - kernelOops = addKernelFuncEventOrPanic( - "oops_exit", "Kernel oops in guest", dmesg_output); + kernelOops = addKernelFuncEvent( + "oops_exit", "Kernel oops in guest", + dmesg_output_fname, params().on_oops); + if (kernelOops == nullptr) { + warn("Could not add Kernel Oops event handler. " + "`oops_exit` symbol not found."); } // With ARM udelay() is #defined to __udelay diff --git a/src/arch/arm/mmu.cc b/src/arch/arm/mmu.cc index 824974ab21..956f95d3b3 100644 --- a/src/arch/arm/mmu.cc +++ b/src/arch/arm/mmu.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013, 2016-2022 Arm Limited + * Copyright (c) 2010-2013, 2016-2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -263,14 +263,17 @@ MMU::translateSe(const RequestPtr &req, ThreadContext *tc, Mode mode, } } - Addr paddr; Process *p = tc->getProcessPtr(); - - if (!p->pTable->translate(vaddr, paddr)) + if (const auto pte = p->pTable->lookup(vaddr); !pte) { return std::make_shared(vaddr_tainted); - req->setPaddr(paddr); + } else { + req->setPaddr(pte->paddr + p->pTable->pageOffset(vaddr)); - return finalizePhysical(req, tc, mode); + if (pte->flags & EmulationPageTable::Uncacheable) + req->setFlags(Request::UNCACHEABLE); + + return finalizePhysical(req, tc, mode); + } } Fault diff --git a/src/arch/arm/pagetable.hh b/src/arch/arm/pagetable.hh index 8300175144..a1e9028e8f 100644 --- a/src/arch/arm/pagetable.hh +++ b/src/arch/arm/pagetable.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2012-2013, 2021 Arm Limited + * Copyright (c) 2010, 2012-2013, 2021, 2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -186,6 +186,12 @@ struct TlbEntry : public Serializable { // virtual address Addr va = 0; + // lookup size: + // * != 0 -> this is a range based lookup. + // end_address = va + size + // * == 0 -> This is a normal lookup. size should + // be ignored + Addr size = 0; // context id/address space id to use uint16_t asn = 0; // if on lookup asn should be ignored @@ -219,6 +225,7 @@ struct TlbEntry : public Serializable uint16_t asid; // Address Space Identifier vmid_t vmid; // Virtual machine Identifier + GrainSize tg; // Translation Granule Size uint8_t N; // Number of bits in pagesize uint8_t innerAttrs; uint8_t outerAttrs; @@ -263,7 +270,7 @@ struct TlbEntry : public Serializable bool uncacheable, bool read_only) : pfn(_paddr >> PageShift), size(PageBytes - 1), vpn(_vaddr >> PageShift), attributes(0), lookupLevel(LookupLevel::L1), - asid(_asn), vmid(0), N(0), + asid(_asn), vmid(0), tg(Grain4KB), N(0), innerAttrs(0), outerAttrs(0), ap(read_only ? 0x3 : 0), hap(0x3), domain(DomainType::Client), mtype(MemoryType::StronglyOrdered), longDescFormat(false), isHyp(false), global(false), valid(true), @@ -281,7 +288,7 @@ struct TlbEntry : public Serializable TlbEntry() : pfn(0), size(0), vpn(0), attributes(0), lookupLevel(LookupLevel::L1), - asid(0), vmid(0), N(0), + asid(0), vmid(0), tg(ReservedGrain), N(0), innerAttrs(0), outerAttrs(0), ap(0), hap(0x3), domain(DomainType::Client), mtype(MemoryType::StronglyOrdered), longDescFormat(false), isHyp(false), global(false), valid(false), @@ -306,12 +313,25 @@ struct TlbEntry : public Serializable return pfn << PageShift; } + bool + matchAddress(const Lookup &lookup) const + { + Addr page_addr = vpn << N; + if (lookup.size) { + // This is a range based loookup + return lookup.va <= page_addr + size && + lookup.va + lookup.size > page_addr; + } else { + // This is a normal lookup + return lookup.va >= page_addr && lookup.va <= page_addr + size; + } + } + bool match(const Lookup &lookup) const { bool match = false; - Addr v = vpn << N; - if (valid && lookup.va >= v && lookup.va <= v + size && + if (valid && matchAddress(lookup) && (lookup.secure == !nstid) && (lookup.hyp == isHyp)) { match = checkELMatch(lookup.targetEL, lookup.inHost); @@ -319,8 +339,8 @@ struct TlbEntry : public Serializable if (match && !lookup.ignoreAsn) { match = global || (lookup.asn == asid); } - if (match && nstid) { - match = isHyp || (lookup.vmid == vmid); + if (match && useVMID(lookup.targetEL, lookup.inHost)) { + match = lookup.vmid == vmid; } } return match; diff --git a/src/arch/arm/pcstate.hh b/src/arch/arm/pcstate.hh index 7b75ed8184..98e3202eb3 100644 --- a/src/arch/arm/pcstate.hh +++ b/src/arch/arm/pcstate.hh @@ -75,7 +75,6 @@ class PCState : public GenericISA::UPCState<4> enum FlagBits { ThumbBit = (1 << 0), - JazelleBit = (1 << 1), AArch64Bit = (1 << 2) }; @@ -92,7 +91,7 @@ class PCState : public GenericISA::UPCState<4> public: void - set(Addr val) + set(Addr val) override { Base::set(val); npc(val + (thumb() ? 2 : 4)); @@ -202,36 +201,6 @@ class PCState : public GenericISA::UPCState<4> } - bool - jazelle() const - { - return flags & JazelleBit; - } - - void - jazelle(bool val) - { - if (val) - flags |= JazelleBit; - else - flags &= ~JazelleBit; - } - - bool - nextJazelle() const - { - return nextFlags & JazelleBit; - } - - void - nextJazelle(bool val) - { - if (val) - nextFlags |= JazelleBit; - else - nextFlags &= ~JazelleBit; - } - bool aarch64() const { @@ -354,29 +323,18 @@ class PCState : public GenericISA::UPCState<4> void instIWNPC(Addr val) { - bool thumbEE = (thumb() && jazelle()); - - Addr newPC = val; - if (thumbEE) { - if (bits(newPC, 0)) { - newPC = newPC & ~mask(1); - } // else we have a bad interworking address; do not call - // panic() since the instruction could be executed - // speculatively + if (bits(val, 0)) { + nextThumb(true); + val = val & ~mask(1); + } else if (!bits(val, 1)) { + nextThumb(false); } else { - if (bits(newPC, 0)) { - nextThumb(true); - newPC = newPC & ~mask(1); - } else if (!bits(newPC, 1)) { - nextThumb(false); - } else { - // This state is UNPREDICTABLE in the ARM architecture - // The easy thing to do is just mask off the bit and - // stay in the current mode, so we'll do that. - newPC &= ~mask(2); - } + // This state is UNPREDICTABLE in the ARM architecture + // The easy thing to do is just mask off the bit and + // stay in the current mode, so we'll do that. + val &= ~mask(2); } - npc(newPC); + npc(val); } // Perform an interworking branch in ARM mode, a regular branch @@ -384,7 +342,7 @@ class PCState : public GenericISA::UPCState<4> void instAIWNPC(Addr val) { - if (!thumb() && !jazelle()) + if (!thumb()) instIWNPC(val); else instNPC(val); diff --git a/src/arch/arm/process.cc b/src/arch/arm/process.cc index 9aa519fe36..b169f849d1 100644 --- a/src/arch/arm/process.cc +++ b/src/arch/arm/process.cc @@ -169,7 +169,7 @@ ArmProcess32::armHwcapImpl() const }; return Arm_Swp | Arm_Half | Arm_Thumb | Arm_FastMult | - Arm_Vfp | Arm_Edsp | Arm_ThumbEE | Arm_Neon | + Arm_Vfp | Arm_Edsp | Arm_Neon | Arm_Vfpv3 | Arm_Vfpv3d16; } diff --git a/src/arch/arm/regs/misc.cc b/src/arch/arm/regs/misc.cc index f1c69cc007..e768edeee3 100644 --- a/src/arch/arm/regs/misc.cc +++ b/src/arch/arm/regs/misc.cc @@ -759,12 +759,24 @@ std::unordered_map miscRegNumToIdx{ { MiscRegNum64(1, 0, 8, 1, 3), MISCREG_TLBI_VAAE1OS_Xt }, { MiscRegNum64(1, 0, 8, 1, 5), MISCREG_TLBI_VALE1OS_Xt }, { MiscRegNum64(1, 0, 8, 1, 7), MISCREG_TLBI_VAALE1OS_Xt }, + { MiscRegNum64(1, 0, 8, 2, 1), MISCREG_TLBI_RVAE1IS_Xt }, + { MiscRegNum64(1, 0, 8, 2, 3), MISCREG_TLBI_RVAAE1IS_Xt }, + { MiscRegNum64(1, 0, 8, 2, 5), MISCREG_TLBI_RVALE1IS_Xt }, + { MiscRegNum64(1, 0, 8, 2, 7), MISCREG_TLBI_RVAALE1IS_Xt }, { MiscRegNum64(1, 0, 8, 3, 0), MISCREG_TLBI_VMALLE1IS }, { MiscRegNum64(1, 0, 8, 3, 1), MISCREG_TLBI_VAE1IS_Xt }, { MiscRegNum64(1, 0, 8, 3, 2), MISCREG_TLBI_ASIDE1IS_Xt }, { MiscRegNum64(1, 0, 8, 3, 3), MISCREG_TLBI_VAAE1IS_Xt }, { MiscRegNum64(1, 0, 8, 3, 5), MISCREG_TLBI_VALE1IS_Xt }, { MiscRegNum64(1, 0, 8, 3, 7), MISCREG_TLBI_VAALE1IS_Xt }, + { MiscRegNum64(1, 0, 8, 5, 1), MISCREG_TLBI_RVAE1OS_Xt }, + { MiscRegNum64(1, 0, 8, 5, 3), MISCREG_TLBI_RVAAE1OS_Xt }, + { MiscRegNum64(1, 0, 8, 5, 5), MISCREG_TLBI_RVALE1OS_Xt }, + { MiscRegNum64(1, 0, 8, 5, 7), MISCREG_TLBI_RVAALE1OS_Xt }, + { MiscRegNum64(1, 0, 8, 6, 1), MISCREG_TLBI_RVAE1_Xt }, + { MiscRegNum64(1, 0, 8, 6, 3), MISCREG_TLBI_RVAAE1_Xt }, + { MiscRegNum64(1, 0, 8, 6, 5), MISCREG_TLBI_RVALE1_Xt }, + { MiscRegNum64(1, 0, 8, 6, 7), MISCREG_TLBI_RVAALE1_Xt }, { MiscRegNum64(1, 0, 8, 7, 0), MISCREG_TLBI_VMALLE1 }, { MiscRegNum64(1, 0, 8, 7, 1), MISCREG_TLBI_VAE1_Xt }, { MiscRegNum64(1, 0, 8, 7, 2), MISCREG_TLBI_ASIDE1_Xt }, @@ -783,12 +795,16 @@ std::unordered_map miscRegNumToIdx{ { MiscRegNum64(1, 4, 7, 8, 6), MISCREG_AT_S12E0R_Xt }, { MiscRegNum64(1, 4, 7, 8, 7), MISCREG_AT_S12E0W_Xt }, { MiscRegNum64(1, 4, 8, 0, 1), MISCREG_TLBI_IPAS2E1IS_Xt }, + { MiscRegNum64(1, 4, 8, 0, 2), MISCREG_TLBI_RIPAS2E1IS_Xt }, { MiscRegNum64(1, 4, 8, 0, 5), MISCREG_TLBI_IPAS2LE1IS_Xt }, { MiscRegNum64(1, 4, 8, 1, 0), MISCREG_TLBI_ALLE2OS }, { MiscRegNum64(1, 4, 8, 1, 1), MISCREG_TLBI_VAE2OS_Xt }, { MiscRegNum64(1, 4, 8, 1, 4), MISCREG_TLBI_ALLE1OS }, { MiscRegNum64(1, 4, 8, 1, 5), MISCREG_TLBI_VALE2OS_Xt }, { MiscRegNum64(1, 4, 8, 1, 6), MISCREG_TLBI_VMALLS12E1OS }, + { MiscRegNum64(1, 4, 8, 0, 6), MISCREG_TLBI_RIPAS2LE1IS_Xt }, + { MiscRegNum64(1, 4, 8, 2, 1), MISCREG_TLBI_RVAE2IS_Xt }, + { MiscRegNum64(1, 4, 8, 2, 5), MISCREG_TLBI_RVALE2IS_Xt }, { MiscRegNum64(1, 4, 8, 3, 0), MISCREG_TLBI_ALLE2IS }, { MiscRegNum64(1, 4, 8, 3, 1), MISCREG_TLBI_VAE2IS_Xt }, { MiscRegNum64(1, 4, 8, 3, 4), MISCREG_TLBI_ALLE1IS }, @@ -796,8 +812,16 @@ std::unordered_map miscRegNumToIdx{ { MiscRegNum64(1, 4, 8, 3, 6), MISCREG_TLBI_VMALLS12E1IS }, { MiscRegNum64(1, 4, 8, 4, 0), MISCREG_TLBI_IPAS2E1OS_Xt }, { MiscRegNum64(1, 4, 8, 4, 1), MISCREG_TLBI_IPAS2E1_Xt }, + { MiscRegNum64(1, 4, 8, 4, 2), MISCREG_TLBI_RIPAS2E1_Xt }, + { MiscRegNum64(1, 4, 8, 4, 3), MISCREG_TLBI_RIPAS2E1OS_Xt }, { MiscRegNum64(1, 4, 8, 4, 4), MISCREG_TLBI_IPAS2LE1OS_Xt }, { MiscRegNum64(1, 4, 8, 4, 5), MISCREG_TLBI_IPAS2LE1_Xt }, + { MiscRegNum64(1, 4, 8, 4, 6), MISCREG_TLBI_RIPAS2LE1_Xt }, + { MiscRegNum64(1, 4, 8, 4, 7), MISCREG_TLBI_RIPAS2LE1OS_Xt }, + { MiscRegNum64(1, 4, 8, 5, 1), MISCREG_TLBI_RVAE2OS_Xt }, + { MiscRegNum64(1, 4, 8, 5, 5), MISCREG_TLBI_RVALE2OS_Xt }, + { MiscRegNum64(1, 4, 8, 6, 1), MISCREG_TLBI_RVAE2_Xt }, + { MiscRegNum64(1, 4, 8, 6, 5), MISCREG_TLBI_RVALE2_Xt }, { MiscRegNum64(1, 4, 8, 7, 0), MISCREG_TLBI_ALLE2 }, { MiscRegNum64(1, 4, 8, 7, 1), MISCREG_TLBI_VAE2_Xt }, { MiscRegNum64(1, 4, 8, 7, 4), MISCREG_TLBI_ALLE1 }, @@ -808,9 +832,15 @@ std::unordered_map miscRegNumToIdx{ { MiscRegNum64(1, 6, 8, 1, 0), MISCREG_TLBI_ALLE3OS }, { MiscRegNum64(1, 6, 8, 1, 1), MISCREG_TLBI_VAE3OS_Xt }, { MiscRegNum64(1, 6, 8, 1, 5), MISCREG_TLBI_VALE3OS_Xt }, + { MiscRegNum64(1, 6, 8, 2, 1), MISCREG_TLBI_RVAE3IS_Xt }, + { MiscRegNum64(1, 6, 8, 2, 5), MISCREG_TLBI_RVALE3IS_Xt }, { MiscRegNum64(1, 6, 8, 3, 0), MISCREG_TLBI_ALLE3IS }, { MiscRegNum64(1, 6, 8, 3, 1), MISCREG_TLBI_VAE3IS_Xt }, { MiscRegNum64(1, 6, 8, 3, 5), MISCREG_TLBI_VALE3IS_Xt }, + { MiscRegNum64(1, 6, 8, 5, 1), MISCREG_TLBI_RVAE3OS_Xt }, + { MiscRegNum64(1, 6, 8, 5, 5), MISCREG_TLBI_RVALE3OS_Xt }, + { MiscRegNum64(1, 6, 8, 6, 1), MISCREG_TLBI_RVAE3_Xt }, + { MiscRegNum64(1, 6, 8, 6, 5), MISCREG_TLBI_RVALE3_Xt }, { MiscRegNum64(1, 6, 8, 7, 0), MISCREG_TLBI_ALLE3 }, { MiscRegNum64(1, 6, 8, 7, 1), MISCREG_TLBI_VAE3_Xt }, { MiscRegNum64(1, 6, 8, 7, 5), MISCREG_TLBI_VALE3_Xt }, @@ -951,7 +981,7 @@ std::unordered_map miscRegNumToIdx{ { MiscRegNum64(3, 0, 0, 7, 0), MISCREG_ID_AA64MMFR0_EL1 }, { MiscRegNum64(3, 0, 0, 7, 1), MISCREG_ID_AA64MMFR1_EL1 }, { MiscRegNum64(3, 0, 0, 7, 2), MISCREG_ID_AA64MMFR2_EL1 }, - { MiscRegNum64(3, 0, 0, 7, 3), MISCREG_RAZ }, + { MiscRegNum64(3, 0, 0, 7, 3), MISCREG_ID_AA64MMFR3_EL1 }, { MiscRegNum64(3, 0, 0, 7, 4), MISCREG_RAZ }, { MiscRegNum64(3, 0, 0, 7, 5), MISCREG_RAZ }, { MiscRegNum64(3, 0, 0, 7, 6), MISCREG_RAZ }, @@ -959,12 +989,14 @@ std::unordered_map miscRegNumToIdx{ { MiscRegNum64(3, 0, 1, 0, 0), MISCREG_SCTLR_EL1 }, { MiscRegNum64(3, 0, 1, 0, 1), MISCREG_ACTLR_EL1 }, { MiscRegNum64(3, 0, 1, 0, 2), MISCREG_CPACR_EL1 }, + { MiscRegNum64(3, 0, 1, 0, 3), MISCREG_SCTLR2_EL1 }, { MiscRegNum64(3, 0, 1, 2, 0), MISCREG_ZCR_EL1 }, { MiscRegNum64(3, 0, 1, 2, 4), MISCREG_SMPRI_EL1 }, { MiscRegNum64(3, 0, 1, 2, 6), MISCREG_SMCR_EL1 }, { MiscRegNum64(3, 0, 2, 0, 0), MISCREG_TTBR0_EL1 }, { MiscRegNum64(3, 0, 2, 0, 1), MISCREG_TTBR1_EL1 }, { MiscRegNum64(3, 0, 2, 0, 2), MISCREG_TCR_EL1 }, + { MiscRegNum64(3, 0, 2, 0, 3), MISCREG_TCR2_EL1 }, { MiscRegNum64(3, 0, 2, 1, 0), MISCREG_APIAKeyLo_EL1 }, { MiscRegNum64(3, 0, 2, 1, 1), MISCREG_APIAKeyHi_EL1 }, { MiscRegNum64(3, 0, 2, 1, 2), MISCREG_APIBKeyLo_EL1 }, @@ -1108,12 +1140,14 @@ std::unordered_map miscRegNumToIdx{ { MiscRegNum64(3, 4, 0, 0, 5), MISCREG_VMPIDR_EL2 }, { MiscRegNum64(3, 4, 1, 0, 0), MISCREG_SCTLR_EL2 }, { MiscRegNum64(3, 4, 1, 0, 1), MISCREG_ACTLR_EL2 }, + { MiscRegNum64(3, 4, 1, 0, 3), MISCREG_SCTLR2_EL2 }, { MiscRegNum64(3, 4, 1, 1, 0), MISCREG_HCR_EL2 }, { MiscRegNum64(3, 4, 1, 1, 1), MISCREG_MDCR_EL2 }, { MiscRegNum64(3, 4, 1, 1, 2), MISCREG_CPTR_EL2 }, { MiscRegNum64(3, 4, 1, 1, 3), MISCREG_HSTR_EL2 }, { MiscRegNum64(3, 4, 1, 1, 4), MISCREG_HFGRTR_EL2 }, { MiscRegNum64(3, 4, 1, 1, 5), MISCREG_HFGWTR_EL2 }, + { MiscRegNum64(3, 4, 1, 1, 6), MISCREG_HFGITR_EL2 }, { MiscRegNum64(3, 4, 1, 1, 7), MISCREG_HACR_EL2 }, { MiscRegNum64(3, 4, 1, 2, 0), MISCREG_ZCR_EL2 }, { MiscRegNum64(3, 4, 1, 2, 2), MISCREG_HCRX_EL2 }, @@ -1122,6 +1156,7 @@ std::unordered_map miscRegNumToIdx{ { MiscRegNum64(3, 4, 2, 0, 0), MISCREG_TTBR0_EL2 }, { MiscRegNum64(3, 4, 2, 0, 1), MISCREG_TTBR1_EL2 }, { MiscRegNum64(3, 4, 2, 0, 2), MISCREG_TCR_EL2 }, + { MiscRegNum64(3, 4, 2, 0, 3), MISCREG_TCR2_EL2 }, { MiscRegNum64(3, 4, 2, 1, 0), MISCREG_VTTBR_EL2 }, { MiscRegNum64(3, 4, 2, 1, 2), MISCREG_VTCR_EL2 }, { MiscRegNum64(3, 4, 2, 6, 0), MISCREG_VSTTBR_EL2 }, @@ -1196,11 +1231,13 @@ std::unordered_map miscRegNumToIdx{ { MiscRegNum64(3, 4, 14, 5, 2), MISCREG_CNTHPS_CVAL_EL2 }, { MiscRegNum64(3, 5, 1, 0, 0), MISCREG_SCTLR_EL12 }, { MiscRegNum64(3, 5, 1, 0, 2), MISCREG_CPACR_EL12 }, + { MiscRegNum64(3, 5, 1, 0, 3), MISCREG_SCTLR2_EL12 }, { MiscRegNum64(3, 5, 1, 2, 0), MISCREG_ZCR_EL12 }, { MiscRegNum64(3, 5, 1, 2, 6), MISCREG_SMCR_EL12 }, { MiscRegNum64(3, 5, 2, 0, 0), MISCREG_TTBR0_EL12 }, { MiscRegNum64(3, 5, 2, 0, 1), MISCREG_TTBR1_EL12 }, { MiscRegNum64(3, 5, 2, 0, 2), MISCREG_TCR_EL12 }, + { MiscRegNum64(3, 5, 2, 0, 3), MISCREG_TCR2_EL12 }, { MiscRegNum64(3, 5, 4, 0, 0), MISCREG_SPSR_EL12 }, { MiscRegNum64(3, 5, 4, 0, 1), MISCREG_ELR_EL12 }, { MiscRegNum64(3, 5, 5, 1, 0), MISCREG_AFSR0_EL12 }, @@ -1220,6 +1257,7 @@ std::unordered_map miscRegNumToIdx{ { MiscRegNum64(3, 5, 14, 3, 2), MISCREG_CNTV_CVAL_EL02 }, { MiscRegNum64(3, 6, 1, 0, 0), MISCREG_SCTLR_EL3 }, { MiscRegNum64(3, 6, 1, 0, 1), MISCREG_ACTLR_EL3 }, + { MiscRegNum64(3, 6, 1, 0, 3), MISCREG_SCTLR2_EL3 }, { MiscRegNum64(3, 6, 1, 1, 0), MISCREG_SCR_EL3 }, { MiscRegNum64(3, 6, 1, 1, 1), MISCREG_SDER32_EL3 }, { MiscRegNum64(3, 6, 1, 1, 2), MISCREG_CPTR_EL3 }, @@ -1249,6 +1287,164 @@ std::unordered_map miscRegNumToIdx{ { MiscRegNum64(3, 7, 14, 2, 2), MISCREG_CNTPS_CVAL_EL1 } }; +template +HFGTR +fgtRegister(ThreadContext *tc) +{ + if constexpr (read) { + return tc->readMiscReg(MISCREG_HFGRTR_EL2); + } else { + return tc->readMiscReg(MISCREG_HFGWTR_EL2); + } +} + +/** + * Template helper for fine grained traps at EL0 + * + * @tparam read: is this a read access to the register? + * @tparam r_bitfield: register (HFGTR) bitfield + */ +template +Fault +faultFgtEL0(const MiscRegLUTEntry &entry, + ThreadContext *tc, const MiscRegOp64 &inst) +{ + const HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + const bool in_host = EL2Enabled(tc) && hcr.e2h && hcr.tge; + if (fgtEnabled(tc) && !in_host && + fgtRegister(tc).*r_bitfield) { + return inst.generateTrap(EL2); + } else { + return NoFault; + } +} + +/** + * Template helper for fine grained traps at EL1 + * + * @tparam read: is this a read access to the register? + * @tparam r_bitfield: register (HFGTR) bitfield + */ +template +Fault +faultFgtEL1(const MiscRegLUTEntry &entry, + ThreadContext *tc, const MiscRegOp64 &inst) +{ + if (fgtEnabled(tc) && fgtRegister(tc).*r_bitfield) { + return inst.generateTrap(EL2); + } else { + return NoFault; + } +} + +/** + * Template helper for fine grained traps at EL1 + * + * @tparam r_bitfield: register (HFGITR) bitfield + */ +template +Fault +faultFgtInstEL1(const MiscRegLUTEntry &entry, + ThreadContext *tc, const MiscRegOp64 &inst) +{ + if (fgtEnabled(tc) && + static_cast(tc->readMiscReg(MISCREG_HFGITR_EL2)).*r_bitfield) { + return inst.generateTrap(EL2); + } else { + return NoFault; + } +} + +/** + * Template helper for fine grained traps at EL1 + * + * @tparam g_bitfield: group (HCR) bitfield + */ +template +Fault +faultHcrEL1(const MiscRegLUTEntry &entry, + ThreadContext *tc, const MiscRegOp64 &inst) +{ + const HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + if (EL2Enabled(tc) && hcr.*g_bitfield) { + return inst.generateTrap(EL2); + } else { + return NoFault; + } +} + +/** + * Template helper for fine grained traps at EL0 + * + * @tparam read: is this a read access to the register? + * @tparam g_bitfield: group (HCR) bitfield + * @tparam r_bitfield: register (HFGTR) bitfield + */ +template +Fault +faultHcrFgtEL0(const MiscRegLUTEntry &entry, + ThreadContext *tc, const MiscRegOp64 &inst) +{ + const HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + const bool in_host = EL2Enabled(tc) && hcr.e2h && hcr.tge; + + if (EL2Enabled(tc) && !in_host && hcr.*g_bitfield) { + return inst.generateTrap(EL2); + } else if (auto fault = faultFgtEL0(entry, tc, inst); + fault != NoFault) { + return fault; + } else { + return NoFault; + } +} + +/** + * Template helper for fine grained traps at EL1 + * + * @tparam read: is this a read access to the register? + * @tparam g_bitfield: group (HCR) bitfield + * @tparam r_bitfield: register (HFGTR) bitfield + */ +template +Fault +faultHcrFgtEL1(const MiscRegLUTEntry &entry, + ThreadContext *tc, const MiscRegOp64 &inst) +{ + const HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + + if (EL2Enabled(tc) && hcr.*g_bitfield) { + return inst.generateTrap(EL2); + } else if (auto fault = faultFgtEL1(entry, tc, inst); + fault != NoFault) { + return fault; + } else { + return NoFault; + } +} + +/** + * Template helper for fine grained traps at EL1 + * + * @tparam g_bitfield: group (HCR) bitfield + * @tparam r_bitfield: register (HFGITR) bitfield + */ +template +Fault +faultHcrFgtInstEL1(const MiscRegLUTEntry &entry, + ThreadContext *tc, const MiscRegOp64 &inst) +{ + const HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); + + if (EL2Enabled(tc) && hcr.*g_bitfield) { + return inst.generateTrap(EL2); + } else if (auto fault = faultFgtInstEL1(entry, tc, inst); + fault != NoFault) { + return fault; + } else { + return NoFault; + } +} + Fault faultSpEL0(const MiscRegLUTEntry &entry, ThreadContext *tc, const MiscRegOp64 &inst) @@ -1455,6 +1651,7 @@ faultPouEL0(const MiscRegLUTEntry &entry, } } +template Fault faultPouEL1(const MiscRegLUTEntry &entry, ThreadContext *tc, const MiscRegOp64 &inst) @@ -1466,11 +1663,15 @@ faultPouEL1(const MiscRegLUTEntry &entry, } else if (el2_enabled && HaveExt(tc, ArmExtension::FEAT_EVT) && hcr.tocu) { return inst.generateTrap(EL2); + } else if (auto fault = faultFgtInstEL1(entry, tc, inst); + fault != NoFault) { + return fault; } else { return NoFault; } } +template Fault faultPouIsEL1(const MiscRegLUTEntry &entry, ThreadContext *tc, const MiscRegOp64 &inst) @@ -1482,6 +1683,9 @@ faultPouIsEL1(const MiscRegLUTEntry &entry, } else if (el2_enabled && HaveExt(tc, ArmExtension::FEAT_EVT) && hcr.ticab) { return inst.generateTrap(EL2); + } else if (auto fault = faultFgtInstEL1(entry, tc, inst); + fault != NoFault) { + return fault; } else { return NoFault; } @@ -1503,8 +1707,10 @@ faultCtrEL0(const MiscRegLUTEntry &entry, } else { return inst.generateTrap(EL1); } - } else if (el2_enabled && !in_host && hcr.tid2) { - return inst.generateTrap(EL2); + } else if (auto fault = faultHcrFgtEL0< + true, &HCR::tid2, &HFGTR::ctrEL0>(entry, tc, inst); + fault != NoFault) { + return fault; } else if (el2_enabled && in_host && !sctlr2.uct) { return inst.generateTrap(EL2); } else { @@ -1714,14 +1920,173 @@ faultIccSgiEL2(const MiscRegLUTEntry &entry, } } +template +Fault +faultSctlr2EL1(const MiscRegLUTEntry &entry, + ThreadContext *tc, const MiscRegOp64 &inst) +{ + if (HaveExt(tc, ArmExtension::FEAT_SCTLR2)) { + const SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + const HCRX hcrx = tc->readMiscReg(MISCREG_HCRX_EL2); + if ( + auto fault = faultHcrFgtEL1 + ( + entry, + tc, + inst + ); + fault != NoFault + ) { + return fault; + } else if ( + EL2Enabled(tc) && (!isHcrxEL2Enabled(tc) || !hcrx.sctlr2En) + ) { + return inst.generateTrap(EL2); + } else if (ArmSystem::haveEL(tc, EL3) && !scr.sctlr2En) { + return inst.generateTrap(EL3); + } else { + return NoFault; + } + } else { + return inst.undefined(); + } +} + +Fault +faultSctlr2EL2(const MiscRegLUTEntry &entry, + ThreadContext *tc, const MiscRegOp64 &inst) +{ + if (HaveExt(tc, ArmExtension::FEAT_SCTLR2)) { + const SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + if (ArmSystem::haveEL(tc, EL3) && !scr.sctlr2En) { + return inst.generateTrap(EL3); + } else { + return NoFault; + } + } else { + return inst.undefined(); + } +} + +Fault +faultSctlr2VheEL2(const MiscRegLUTEntry &entry, + ThreadContext *tc, const MiscRegOp64 &inst) +{ + if (HaveExt(tc, ArmExtension::FEAT_SCTLR2)) { + const HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2); + const SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + if (hcr.e2h) { + if (ArmSystem::haveEL(tc, EL3) && !scr.sctlr2En) { + return inst.generateTrap(EL3); + } else { + return NoFault; + } + } else { + return inst.undefined(); + } + } else { + return inst.undefined(); + } +} + +template +Fault +faultTcr2EL1(const MiscRegLUTEntry &entry, + ThreadContext *tc, const MiscRegOp64 &inst) +{ + if (HaveExt(tc, ArmExtension::FEAT_TCR2)) { + const SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + const HCRX hcrx = tc->readMiscReg(MISCREG_HCRX_EL2); + if ( + auto fault = faultHcrFgtEL1 + ( + entry, + tc, + inst + ); + fault != NoFault + ) { + return fault; + } else if (EL2Enabled(tc) && (!isHcrxEL2Enabled(tc) || !hcrx.tcr2En)) { + return inst.generateTrap(EL2); + } else if (ArmSystem::haveEL(tc, EL3) && !scr.tcr2En) { + return inst.generateTrap(EL3); + } else { + return NoFault; + } + } else { + return inst.undefined(); + } +} + +Fault +faultTcr2EL2(const MiscRegLUTEntry &entry, + ThreadContext *tc, const MiscRegOp64 &inst) +{ + if (HaveExt(tc, ArmExtension::FEAT_TCR2)) { + const SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + if (ArmSystem::haveEL(tc, EL3) && !scr.tcr2En) { + return inst.generateTrap(EL3); + } else { + return NoFault; + } + } else { + return inst.undefined(); + } +} + +Fault +faultTcr2VheEL2(const MiscRegLUTEntry &entry, + ThreadContext *tc, const MiscRegOp64 &inst) +{ + if (HaveExt(tc, ArmExtension::FEAT_TCR2)) { + const HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2); + const SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + if (hcr.e2h) { + if (ArmSystem::haveEL(tc, EL3) && !scr.tcr2En) { + return inst.generateTrap(EL3); + } else { + return NoFault; + } + } else { + return inst.undefined(); + } + } else { + return inst.undefined(); + } +} + +Fault +faultTcr2VheEL3(const MiscRegLUTEntry &entry, + ThreadContext *tc, const MiscRegOp64 &inst) +{ + if (HaveExt(tc, ArmExtension::FEAT_TCR2)) { + const HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2); + const bool el2_host = EL2Enabled(tc) && hcr.e2h; + if (el2_host) { + return NoFault; + } else { + return inst.undefined(); + } + } else { + return inst.undefined(); + } +} + +template Fault faultCpacrEL1(const MiscRegLUTEntry &entry, ThreadContext *tc, const MiscRegOp64 &inst) { const CPTR cptr_el2 = tc->readMiscReg(MISCREG_CPTR_EL2); const CPTR cptr_el3 = tc->readMiscReg(MISCREG_CPTR_EL3); - if (EL2Enabled(tc) && cptr_el2.tcpac) { + + const bool el2_enabled = EL2Enabled(tc); + if (el2_enabled && cptr_el2.tcpac) { return inst.generateTrap(EL2); + } else if (auto fault = faultFgtEL1(entry, tc, inst); + fault != NoFault) { + return fault; } else if (ArmSystem::haveEL(tc, EL3) && cptr_el3.tcpac) { return inst.generateTrap(EL3); } else { @@ -1753,17 +2118,7 @@ faultCpacrVheEL2(const MiscRegLUTEntry &entry, } } -#define HCR_TRAP(bitfield) [] (const MiscRegLUTEntry &entry, \ - ThreadContext *tc, const MiscRegOp64 &inst) -> Fault \ -{ \ - const HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); \ - if (EL2Enabled(tc) && hcr.bitfield) { \ - return inst.generateTrap(EL2); \ - } else { \ - return NoFault; \ - } \ -} - +template Fault faultTlbiOsEL1(const MiscRegLUTEntry &entry, ThreadContext *tc, const MiscRegOp64 &inst) @@ -1775,11 +2130,15 @@ faultTlbiOsEL1(const MiscRegLUTEntry &entry, } else if (el2_enabled && HaveExt(tc, ArmExtension::FEAT_EVT) && hcr.ttlbos) { return inst.generateTrap(EL2); + } else if (auto fault = faultFgtInstEL1(entry, tc, inst); + fault != NoFault) { + return fault; } else { return NoFault; } } +template Fault faultTlbiIsEL1(const MiscRegLUTEntry &entry, ThreadContext *tc, const MiscRegOp64 &inst) @@ -1791,11 +2150,15 @@ faultTlbiIsEL1(const MiscRegLUTEntry &entry, } else if (el2_enabled && HaveExt(tc, ArmExtension::FEAT_EVT) && hcr.ttlbis) { return inst.generateTrap(EL2); + } else if (auto fault = faultFgtInstEL1(entry, tc, inst); + fault != NoFault) { + return fault; } else { return NoFault; } } +template Fault faultCacheEL1(const MiscRegLUTEntry &entry, ThreadContext *tc, const MiscRegOp64 &inst) @@ -1807,19 +2170,28 @@ faultCacheEL1(const MiscRegLUTEntry &entry, } else if (el2_enabled && HaveExt(tc, ArmExtension::FEAT_EVT) && hcr.tid4) { return inst.generateTrap(EL2); + } else if (auto fault = faultFgtEL1(entry, tc, inst); + fault != NoFault) { + return fault; } else { return NoFault; } } +template Fault faultPauthEL1(const MiscRegLUTEntry &entry, ThreadContext *tc, const MiscRegOp64 &inst) { const HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2); const SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); - if (EL2Enabled(tc) && !hcr.apk) { + const bool el2_enabled = EL2Enabled(tc); + + if (el2_enabled && !hcr.apk) { return inst.generateTrap(EL2); + } else if (auto fault = faultFgtEL1(entry, tc, inst); + fault != NoFault) { + return fault; } else if (ArmSystem::haveEL(tc, EL3) && !scr.apk) { return inst.generateTrap(EL3); } else { @@ -2140,6 +2512,22 @@ faultRng(const MiscRegLUTEntry &entry, } } +Fault +faultFgtCtrlRegs(const MiscRegLUTEntry &entry, + ThreadContext *tc, const MiscRegOp64 &inst) +{ + if (HaveExt(tc, ArmExtension::FEAT_FGT)) { + const SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); + if (ArmSystem::haveEL(tc, EL3) && !scr.fgten) { + return inst.generateTrap(EL3); + } else { + return NoFault; + } + } else { + return inst.undefined(); + } +} + Fault faultIdst(const MiscRegLUTEntry &entry, ThreadContext *tc, const MiscRegOp64 &inst) @@ -3528,18 +3916,18 @@ ISA::initializeMiscRegMetadata() InitReg(MISCREG_MDCCINT_EL1) .fault(EL1, faultMdccsrEL1) .fault(EL2, faultMdccsrEL2) - .allPrivileges(); + .allPrivileges().exceptUserMode(); InitReg(MISCREG_OSDTRRX_EL1) - .allPrivileges() + .allPrivileges().exceptUserMode() .mapsTo(MISCREG_DBGDTRRXext); InitReg(MISCREG_MDSCR_EL1) - .allPrivileges() + .allPrivileges().exceptUserMode() .mapsTo(MISCREG_DBGDSCRext); InitReg(MISCREG_OSDTRTX_EL1) - .allPrivileges() + .allPrivileges().exceptUserMode() .mapsTo(MISCREG_DBGDTRTXext); InitReg(MISCREG_OSECCR_EL1) - .allPrivileges() + .allPrivileges().exceptUserMode() .mapsTo(MISCREG_DBGOSECCR); InitReg(MISCREG_DBGBVR0_EL1) .allPrivileges().exceptUserMode() @@ -3878,28 +4266,28 @@ ISA::initializeMiscRegMetadata() .fault(EL2, faultDebugEL2) .mapsTo(MISCREG_DBGVCR); InitReg(MISCREG_MDRAR_EL1) - .allPrivileges().monSecureWrite(0).monNonSecureWrite(0) + .allPrivileges().exceptUserMode().writes(0) .mapsTo(MISCREG_DBGDRAR); InitReg(MISCREG_OSLAR_EL1) - .allPrivileges().monSecureRead(0).monNonSecureRead(0) + .allPrivileges().exceptUserMode().reads(0) .mapsTo(MISCREG_DBGOSLAR); InitReg(MISCREG_OSLSR_EL1) - .allPrivileges().monSecureWrite(0).monNonSecureWrite(0) + .allPrivileges().exceptUserMode().writes(0) .mapsTo(MISCREG_DBGOSLSR); InitReg(MISCREG_OSDLR_EL1) - .allPrivileges() + .allPrivileges().exceptUserMode() .mapsTo(MISCREG_DBGOSDLR); InitReg(MISCREG_DBGPRCR_EL1) - .allPrivileges() + .allPrivileges().exceptUserMode() .mapsTo(MISCREG_DBGPRCR); InitReg(MISCREG_DBGCLAIMSET_EL1) - .allPrivileges() + .allPrivileges().exceptUserMode() .mapsTo(MISCREG_DBGCLAIMSET); InitReg(MISCREG_DBGCLAIMCLR_EL1) - .allPrivileges() + .allPrivileges().exceptUserMode() .mapsTo(MISCREG_DBGCLAIMCLR); InitReg(MISCREG_DBGAUTHSTATUS_EL1) - .allPrivileges().monSecureWrite(0).monNonSecureWrite(0) + .allPrivileges().exceptUserMode().writes(0) .mapsTo(MISCREG_DBGAUTHSTATUS); InitReg(MISCREG_TEECR32_EL1); InitReg(MISCREG_TEEHBR32_EL1); @@ -3908,108 +4296,110 @@ ISA::initializeMiscRegMetadata() InitReg(MISCREG_MIDR_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) + .faultRead(EL1, faultFgtEL1) .mapsTo(MISCREG_MIDR); InitReg(MISCREG_MPIDR_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) + .faultRead(EL1, faultFgtEL1) .mapsTo(MISCREG_MPIDR); InitReg(MISCREG_REVIDR_EL1) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid1)) + .faultRead(EL1, faultHcrFgtEL1) .allPrivileges().exceptUserMode().writes(0); InitReg(MISCREG_ID_PFR0_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_PFR0); InitReg(MISCREG_ID_PFR1_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_PFR1); InitReg(MISCREG_ID_DFR0_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_DFR0); InitReg(MISCREG_ID_AFR0_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_AFR0); InitReg(MISCREG_ID_MMFR0_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_MMFR0); InitReg(MISCREG_ID_MMFR1_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_MMFR1); InitReg(MISCREG_ID_MMFR2_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_MMFR2); InitReg(MISCREG_ID_MMFR3_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_MMFR3); InitReg(MISCREG_ID_MMFR4_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_MMFR4); InitReg(MISCREG_ID_ISAR0_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_ISAR0); InitReg(MISCREG_ID_ISAR1_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_ISAR1); InitReg(MISCREG_ID_ISAR2_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_ISAR2); InitReg(MISCREG_ID_ISAR3_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_ISAR3); InitReg(MISCREG_ID_ISAR4_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_ISAR4); InitReg(MISCREG_ID_ISAR5_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_ISAR5); InitReg(MISCREG_ID_ISAR6_EL1) .allPrivileges().exceptUserMode().writes(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .mapsTo(MISCREG_ID_ISAR6); InitReg(MISCREG_MVFR0_EL1) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().exceptUserMode().writes(0) .mapsTo(MISCREG_MVFR0); InitReg(MISCREG_MVFR1_EL1) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().exceptUserMode().writes(0) .mapsTo(MISCREG_MVFR1); InitReg(MISCREG_MVFR2_EL1) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().exceptUserMode().writes(0); InitReg(MISCREG_ID_AA64PFR0_EL1) .reset([this,release=release,tc=tc](){ @@ -4025,14 +4415,14 @@ ISA::initializeMiscRegMetadata() }()) .unserialize(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().writes(0); InitReg(MISCREG_ID_AA64PFR1_EL1) .reset(release->has(ArmExtension::FEAT_SME) ? 0x1 << 24 : 0) .unserialize(0) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().writes(0); InitReg(MISCREG_ID_AA64DFR0_EL1) .reset([p](){ @@ -4041,22 +4431,22 @@ ISA::initializeMiscRegMetadata() return dfr0_el1; }()) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().writes(0); InitReg(MISCREG_ID_AA64DFR1_EL1) .reset(p.id_aa64dfr1_el1) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().writes(0); InitReg(MISCREG_ID_AA64AFR0_EL1) .reset(p.id_aa64afr0_el1) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().writes(0); InitReg(MISCREG_ID_AA64AFR1_EL1) .reset(p.id_aa64afr1_el1) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().writes(0); InitReg(MISCREG_ID_AA64ISAR0_EL1) .reset([p,release=release](){ @@ -4076,7 +4466,9 @@ ISA::initializeMiscRegMetadata() isar0_el1.atomic = release->has(ArmExtension::FEAT_LSE) ? 0x2 : 0x0; isar0_el1.rdm = release->has(ArmExtension::FEAT_RDM) ? 0x1 : 0x0; isar0_el1.tme = release->has(ArmExtension::TME) ? 0x1 : 0x0; - isar0_el1.tlb = release->has(ArmExtension::FEAT_TLBIOS) ? 0x1 : 0x0; + isar0_el1.tlb = release->has(ArmExtension::FEAT_TLBIRANGE) ? + 0x2 : release->has(ArmExtension::FEAT_TLBIOS) ? + 0x1 : 0x0; isar0_el1.ts = release->has(ArmExtension::FEAT_FLAGM2) ? 0x2 : release->has(ArmExtension::FEAT_FLAGM) ? 0x1 : 0x0; @@ -4084,7 +4476,7 @@ ISA::initializeMiscRegMetadata() return isar0_el1; }()) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().writes(0); InitReg(MISCREG_ID_AA64ISAR1_EL1) .reset([p,release=release](){ @@ -4097,7 +4489,7 @@ ISA::initializeMiscRegMetadata() return isar1_el1; }()) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().writes(0); InitReg(MISCREG_ID_AA64MMFR0_EL1) .reset([p,asidbits=haveLargeAsid64,parange=physAddrRange](){ @@ -4107,12 +4499,13 @@ ISA::initializeMiscRegMetadata() return mmfr0_el1; }()) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().writes(0); InitReg(MISCREG_ID_AA64MMFR1_EL1) .reset([p,release=release](){ AA64MMFR1 mmfr1_el1 = p.id_aa64mmfr1_el1; - mmfr1_el1.vmidbits = release->has(ArmExtension::FEAT_VMID16) ? 0x2 : 0x0; + mmfr1_el1.vmidbits = + release->has(ArmExtension::FEAT_VMID16) ? 0x2 : 0x0; mmfr1_el1.vh = release->has(ArmExtension::FEAT_VHE) ? 0x1 : 0x0; mmfr1_el1.hpds = release->has(ArmExtension::FEAT_HPDS) ? 0x1 : 0x0; mmfr1_el1.pan = release->has(ArmExtension::FEAT_PAN) ? 0x1 : 0x0; @@ -4120,7 +4513,7 @@ ISA::initializeMiscRegMetadata() return mmfr1_el1; }()) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().writes(0); InitReg(MISCREG_ID_AA64MMFR2_EL1) .reset([p,release=release](){ @@ -4132,73 +4525,97 @@ ISA::initializeMiscRegMetadata() return mmfr2_el1; }()) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) + .allPrivileges().writes(0); + InitReg(MISCREG_ID_AA64MMFR3_EL1) + .reset([p,release=release](){ + AA64MMFR3 mmfr3_el1 = 0; + mmfr3_el1.sctlrx = + release->has(ArmExtension::FEAT_SCTLR2) ? 0x1 : 0x0; + mmfr3_el1.tcrx = release->has(ArmExtension::FEAT_TCR2) ? 0x1 : 0x0; + return mmfr3_el1; + }()) + .faultRead(EL0, faultIdst) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().writes(0); InitReg(MISCREG_APDAKeyHi_EL1) - .fault(EL1, faultPauthEL1) + .faultRead(EL1, faultPauthEL1) + .faultWrite(EL1, faultPauthEL1) .fault(EL2, faultPauthEL2) .allPrivileges().exceptUserMode(); InitReg(MISCREG_APDAKeyLo_EL1) - .fault(EL1, faultPauthEL1) + .faultRead(EL1, faultPauthEL1) + .faultWrite(EL1, faultPauthEL1) .fault(EL2, faultPauthEL2) .allPrivileges().exceptUserMode(); InitReg(MISCREG_APDBKeyHi_EL1) - .fault(EL1, faultPauthEL1) + .faultRead(EL1, faultPauthEL1) + .faultWrite(EL1, faultPauthEL1) .fault(EL2, faultPauthEL2) .allPrivileges().exceptUserMode(); InitReg(MISCREG_APDBKeyLo_EL1) - .fault(EL1, faultPauthEL1) + .faultRead(EL1, faultPauthEL1) + .faultWrite(EL1, faultPauthEL1) .fault(EL2, faultPauthEL2) .allPrivileges().exceptUserMode(); InitReg(MISCREG_APGAKeyHi_EL1) - .fault(EL1, faultPauthEL1) + .faultRead(EL1, faultPauthEL1) + .faultWrite(EL1, faultPauthEL1) .fault(EL2, faultPauthEL2) .allPrivileges().exceptUserMode(); InitReg(MISCREG_APGAKeyLo_EL1) - .fault(EL1, faultPauthEL1) + .faultRead(EL1, faultPauthEL1) + .faultWrite(EL1, faultPauthEL1) .fault(EL2, faultPauthEL2) .allPrivileges().exceptUserMode(); InitReg(MISCREG_APIAKeyHi_EL1) - .fault(EL1, faultPauthEL1) + .faultRead(EL1, faultPauthEL1) + .faultWrite(EL1, faultPauthEL1) .fault(EL2, faultPauthEL2) .allPrivileges().exceptUserMode(); InitReg(MISCREG_APIAKeyLo_EL1) - .fault(EL1, faultPauthEL1) + .faultRead(EL1, faultPauthEL1) + .faultWrite(EL1, faultPauthEL1) .fault(EL2, faultPauthEL2) .allPrivileges().exceptUserMode(); InitReg(MISCREG_APIBKeyHi_EL1) - .fault(EL1, faultPauthEL1) + .faultRead(EL1, faultPauthEL1) + .faultWrite(EL1, faultPauthEL1) .fault(EL2, faultPauthEL2) .allPrivileges().exceptUserMode(); InitReg(MISCREG_APIBKeyLo_EL1) - .fault(EL1, faultPauthEL1) + .faultRead(EL1, faultPauthEL1) + .faultWrite(EL1, faultPauthEL1) .fault(EL2, faultPauthEL2) .allPrivileges().exceptUserMode(); InitReg(MISCREG_CCSIDR_EL1) .faultRead(EL0, faultIdst) - .faultRead(EL1, faultCacheEL1) + .faultRead(EL1, faultCacheEL1) .allPrivileges().writes(0); InitReg(MISCREG_CLIDR_EL1) .faultRead(EL0, faultIdst) - .faultRead(EL1, faultCacheEL1) + .faultRead(EL1, faultCacheEL1) .allPrivileges().writes(0); InitReg(MISCREG_AIDR_EL1) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid1)) + .faultRead(EL1, faultHcrFgtEL1) .allPrivileges().writes(0); InitReg(MISCREG_CSSELR_EL1) .allPrivileges().exceptUserMode() - .fault(EL1, faultCacheEL1) + .faultRead(EL1, faultCacheEL1) + .faultWrite(EL1, faultCacheEL1) .mapsTo(MISCREG_CSSELR_NS); InitReg(MISCREG_CTR_EL0) .faultRead(EL0, faultCtrEL0) - .faultRead(EL1, HCR_TRAP(tid2)) + .faultRead(EL1, faultHcrFgtEL1) .reads(1) .mapsTo(MISCREG_CTR); InitReg(MISCREG_DCZID_EL0) .reset(0x04) // DC ZVA clear 64-byte chunks + .faultRead(EL0, faultFgtEL0) + .faultRead(EL1, faultFgtEL1) .reads(1); InitReg(MISCREG_VPIDR_EL2) .hyp().mon() @@ -4210,8 +4627,8 @@ ISA::initializeMiscRegMetadata() .mapsTo(MISCREG_VMPIDR); InitReg(MISCREG_SCTLR_EL1) .allPrivileges().exceptUserMode() - .faultRead(EL1, HCR_TRAP(trvm)) - .faultWrite(EL1, HCR_TRAP(tvm)) + .faultRead(EL1, faultHcrFgtEL1) + .faultWrite(EL1, faultHcrFgtEL1) .res0( 0x20440 | (EnDB ? 0 : 0x2000) | (IESB ? 0 : 0x200000) | (EnDA ? 0 : 0x8000000) @@ -4233,13 +4650,23 @@ ISA::initializeMiscRegMetadata() | (nTLSMD ? 0 : 0x8000000) | (LSMAOE ? 0 : 0x10000000)) .mapsTo(MISCREG_SCTLR_EL1); + InitReg(MISCREG_SCTLR2_EL1) + .allPrivileges().exceptUserMode() + .faultRead(EL1, faultSctlr2EL1) + .faultWrite(EL1, faultSctlr2EL1) + .fault(EL2,faultSctlr2EL2); + InitReg(MISCREG_SCTLR2_EL12) + .fault(EL2, faultSctlr2VheEL2) + .fault(EL3, defaultFaultE2H_EL3) + .mapsTo(MISCREG_SCTLR2_EL1); InitReg(MISCREG_ACTLR_EL1) .allPrivileges().exceptUserMode() - .fault(EL1, HCR_TRAP(tacr)) + .fault(EL1, faultHcrEL1<&HCR::tacr>) .mapsTo(MISCREG_ACTLR_NS); InitReg(MISCREG_CPACR_EL1) .allPrivileges().exceptUserMode() - .fault(EL1, faultCpacrEL1) + .faultRead(EL1, faultCpacrEL1) + .faultWrite(EL1, faultCpacrEL1) .fault(EL2, faultCpacrEL2) .mapsTo(MISCREG_CPACR); InitReg(MISCREG_CPACR_EL12) @@ -4255,6 +4682,9 @@ ISA::initializeMiscRegMetadata() | (EnIA ? 0 : 0x80000000)) .res1(0x30c50830) .mapsTo(MISCREG_HSCTLR); + InitReg(MISCREG_SCTLR2_EL2) + .hyp().mon() + .fault(EL2, faultSctlr2EL2); InitReg(MISCREG_ACTLR_EL2) .hyp().mon() .mapsTo(MISCREG_HACTLR); @@ -4287,6 +4717,8 @@ ISA::initializeMiscRegMetadata() | (EnIB ? 0 : 0x40000000) | (EnIA ? 0 : 0x80000000)) .res1(0x30c50830); + InitReg(MISCREG_SCTLR2_EL3) + .mon(); InitReg(MISCREG_ACTLR_EL3) .mon(); InitReg(MISCREG_SCR_EL3) @@ -4302,8 +4734,8 @@ ISA::initializeMiscRegMetadata() .mapsTo(MISCREG_SDCR); InitReg(MISCREG_TTBR0_EL1) .allPrivileges().exceptUserMode() - .faultRead(EL1, HCR_TRAP(trvm)) - .faultWrite(EL1, HCR_TRAP(tvm)) + .faultRead(EL1, faultHcrFgtEL1) + .faultWrite(EL1, faultHcrFgtEL1) .mapsTo(MISCREG_TTBR0_NS); InitReg(MISCREG_TTBR0_EL12) .fault(EL2, defaultFaultE2H_EL2) @@ -4311,8 +4743,8 @@ ISA::initializeMiscRegMetadata() .mapsTo(MISCREG_TTBR0_EL1); InitReg(MISCREG_TTBR1_EL1) .allPrivileges().exceptUserMode() - .faultRead(EL1, HCR_TRAP(trvm)) - .faultWrite(EL1, HCR_TRAP(tvm)) + .faultRead(EL1, faultHcrFgtEL1) + .faultWrite(EL1, faultHcrFgtEL1) .mapsTo(MISCREG_TTBR1_NS); InitReg(MISCREG_TTBR1_EL12) .fault(EL2, defaultFaultE2H_EL2) @@ -4320,13 +4752,22 @@ ISA::initializeMiscRegMetadata() .mapsTo(MISCREG_TTBR1_EL1); InitReg(MISCREG_TCR_EL1) .allPrivileges().exceptUserMode() - .faultRead(EL1, HCR_TRAP(trvm)) - .faultWrite(EL1, HCR_TRAP(tvm)) + .faultRead(EL1, faultHcrFgtEL1) + .faultWrite(EL1, faultHcrFgtEL1) .mapsTo(MISCREG_TTBCR_NS); InitReg(MISCREG_TCR_EL12) .fault(EL2, defaultFaultE2H_EL2) .fault(EL3, defaultFaultE2H_EL3) .mapsTo(MISCREG_TTBCR_NS); + InitReg(MISCREG_TCR2_EL1) + .allPrivileges().exceptUserMode() + .faultRead(EL1, faultTcr2EL1) + .faultWrite(EL1, faultTcr2EL1) + .fault(EL2, faultTcr2EL2); + InitReg(MISCREG_TCR2_EL12) + .fault(EL2, faultTcr2VheEL2) + .fault(EL3, faultTcr2VheEL3) + .mapsTo(MISCREG_TCR2_EL1); InitReg(MISCREG_TTBR0_EL2) .hyp().mon() .mapsTo(MISCREG_HTTBR); @@ -4335,6 +4776,9 @@ ISA::initializeMiscRegMetadata() InitReg(MISCREG_TCR_EL2) .hyp().mon() .mapsTo(MISCREG_HTCR); + InitReg(MISCREG_TCR2_EL2) + .hyp().mon() + .fault(EL2, faultTcr2EL2); InitReg(MISCREG_VTTBR_EL2) .hyp().mon() .mapsTo(MISCREG_VTTBR); @@ -4424,8 +4868,8 @@ ISA::initializeMiscRegMetadata() .mon(); InitReg(MISCREG_AFSR0_EL1) .allPrivileges().exceptUserMode() - .faultRead(EL1, HCR_TRAP(trvm)) - .faultWrite(EL1, HCR_TRAP(tvm)) + .faultRead(EL1, faultHcrFgtEL1) + .faultWrite(EL1, faultHcrFgtEL1) .mapsTo(MISCREG_ADFSR_NS); InitReg(MISCREG_AFSR0_EL12) .fault(EL2, defaultFaultE2H_EL2) @@ -4433,16 +4877,16 @@ ISA::initializeMiscRegMetadata() .mapsTo(MISCREG_ADFSR_NS); InitReg(MISCREG_AFSR1_EL1) .allPrivileges().exceptUserMode() - .faultRead(EL1, HCR_TRAP(trvm)) - .faultWrite(EL1, HCR_TRAP(tvm)) + .faultRead(EL1, faultHcrFgtEL1) + .faultWrite(EL1, faultHcrFgtEL1) .mapsTo(MISCREG_AIFSR_NS); InitReg(MISCREG_AFSR1_EL12) .fault(EL2, defaultFaultE2H_EL2) .fault(EL3, defaultFaultE2H_EL3) .mapsTo(MISCREG_AIFSR_NS); InitReg(MISCREG_ESR_EL1) - .faultRead(EL1, HCR_TRAP(trvm)) - .faultWrite(EL1, HCR_TRAP(tvm)) + .faultRead(EL1, faultHcrFgtEL1) + .faultWrite(EL1, faultHcrFgtEL1) .allPrivileges().exceptUserMode(); InitReg(MISCREG_ESR_EL12) .fault(EL2, defaultFaultE2H_EL2) @@ -4472,8 +4916,8 @@ ISA::initializeMiscRegMetadata() .mon(); InitReg(MISCREG_FAR_EL1) .allPrivileges().exceptUserMode() - .faultRead(EL1, HCR_TRAP(trvm)) - .faultWrite(EL1, HCR_TRAP(tvm)) + .faultRead(EL1, faultHcrFgtEL1) + .faultWrite(EL1, faultHcrFgtEL1) .mapsTo(MISCREG_DFAR_NS, MISCREG_IFAR_NS); InitReg(MISCREG_FAR_EL12) .fault(EL2, defaultFaultE2H_EL2) @@ -4489,61 +4933,61 @@ ISA::initializeMiscRegMetadata() .mon(); InitReg(MISCREG_IC_IALLUIS) .warnNotFail() - .faultWrite(EL1, faultPouIsEL1) + .faultWrite(EL1, faultPouIsEL1<&HFGITR::icialluis>) .writes(1).exceptUserMode(); InitReg(MISCREG_PAR_EL1) .allPrivileges().exceptUserMode() .mapsTo(MISCREG_PAR_NS); InitReg(MISCREG_IC_IALLU) .warnNotFail() - .faultWrite(EL1, faultPouEL1) + .faultWrite(EL1, faultPouEL1<&HFGITR::iciallu>) .writes(1).exceptUserMode(); InitReg(MISCREG_DC_IVAC_Xt) - .faultWrite(EL1, HCR_TRAP(tpc)) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::tpc, &HFGITR::dcivac>) .writes(1).exceptUserMode(); InitReg(MISCREG_DC_ISW_Xt) .warnNotFail() - .faultWrite(EL1, HCR_TRAP(tsw)) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::tsw, &HFGITR::dcisw>) .writes(1).exceptUserMode(); InitReg(MISCREG_AT_S1E1R_Xt) - .faultWrite(EL1, HCR_TRAP(at)) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::at, &HFGITR::ats1e1r>) .writes(1).exceptUserMode(); InitReg(MISCREG_AT_S1E1W_Xt) - .faultWrite(EL1, HCR_TRAP(at)) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::at, &HFGITR::ats1e1w>) .writes(1).exceptUserMode(); InitReg(MISCREG_AT_S1E0R_Xt) - .faultWrite(EL1, HCR_TRAP(at)) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::at, &HFGITR::ats1e0r>) .writes(1).exceptUserMode(); InitReg(MISCREG_AT_S1E0W_Xt) - .faultWrite(EL1, HCR_TRAP(at)) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::at, &HFGITR::ats1e0w>) .writes(1).exceptUserMode(); InitReg(MISCREG_DC_CSW_Xt) .warnNotFail() - .faultWrite(EL1, HCR_TRAP(tsw)) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::tsw, &HFGITR::dccsw>) .writes(1).exceptUserMode(); InitReg(MISCREG_DC_CISW_Xt) .warnNotFail() - .faultWrite(EL1, HCR_TRAP(tsw)) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::tsw, &HFGITR::dccisw>) .writes(1).exceptUserMode(); InitReg(MISCREG_DC_ZVA_Xt) .writes(1) .faultWrite(EL0, faultDczvaEL0) - .faultWrite(EL1, HCR_TRAP(tdz)); + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::tdz, &HFGITR::dczva>); InitReg(MISCREG_IC_IVAU_Xt) .faultWrite(EL0, faultPouEL0) - .faultWrite(EL1, faultPouEL1) + .faultWrite(EL1, faultPouEL1<&HFGITR::icivau>) .writes(1); InitReg(MISCREG_DC_CVAC_Xt) .faultWrite(EL0, faultCvacEL0) - .faultWrite(EL1, HCR_TRAP(tpc)) + .faultWrite(EL1, faultHcrEL1<&HCR::tpc>) .writes(1); InitReg(MISCREG_DC_CVAU_Xt) .faultWrite(EL0, faultPouEL0) - .faultWrite(EL1, faultPouEL1) + .faultWrite(EL1, faultPouEL1<&HFGITR::dccvau>) .writes(1); InitReg(MISCREG_DC_CIVAC_Xt) .faultWrite(EL0, faultCvacEL0) - .faultWrite(EL1, HCR_TRAP(tpc)) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::tpc, &HFGITR::dccivac>) .writes(1); InitReg(MISCREG_AT_S1E2R_Xt) .monNonSecureWrite().hypWrite(); @@ -4562,58 +5006,58 @@ ISA::initializeMiscRegMetadata() InitReg(MISCREG_AT_S1E3W_Xt) .monSecureWrite().monNonSecureWrite(); InitReg(MISCREG_TLBI_VMALLE1OS) - .faultWrite(EL1, faultTlbiOsEL1) + .faultWrite(EL1, faultTlbiOsEL1<&HFGITR::tlbivmalle1os>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_VAE1OS_Xt) - .faultWrite(EL1, faultTlbiOsEL1) + .faultWrite(EL1, faultTlbiOsEL1<&HFGITR::tlbivae1os>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_ASIDE1OS_Xt) - .faultWrite(EL1, faultTlbiOsEL1) + .faultWrite(EL1, faultTlbiOsEL1<&HFGITR::tlbiaside1os>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_VAAE1OS_Xt) - .faultWrite(EL1, faultTlbiOsEL1) + .faultWrite(EL1, faultTlbiOsEL1<&HFGITR::tlbivaae1os>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_VALE1OS_Xt) - .faultWrite(EL1, faultTlbiOsEL1) + .faultWrite(EL1, faultTlbiOsEL1<&HFGITR::tlbivale1os>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_VAALE1OS_Xt) - .faultWrite(EL1, faultTlbiOsEL1) + .faultWrite(EL1, faultTlbiOsEL1<&HFGITR::tlbivaale1os>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_VMALLE1IS) - .faultWrite(EL1, faultTlbiIsEL1) + .faultWrite(EL1, faultTlbiIsEL1<&HFGITR::tlbivmalle1is>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_VAE1IS_Xt) - .faultWrite(EL1, faultTlbiIsEL1) + .faultWrite(EL1, faultTlbiIsEL1<&HFGITR::tlbivae1is>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_ASIDE1IS_Xt) - .faultWrite(EL1, faultTlbiIsEL1) + .faultWrite(EL1, faultTlbiIsEL1<&HFGITR::tlbiaside1is>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_VAAE1IS_Xt) - .faultWrite(EL1, faultTlbiIsEL1) + .faultWrite(EL1, faultTlbiIsEL1<&HFGITR::tlbivaae1is>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_VALE1IS_Xt) - .faultWrite(EL1, faultTlbiIsEL1) + .faultWrite(EL1, faultTlbiIsEL1<&HFGITR::tlbivale1is>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_VAALE1IS_Xt) - .faultWrite(EL1, faultTlbiIsEL1) + .faultWrite(EL1, faultTlbiIsEL1<&HFGITR::tlbivaale1is>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_VMALLE1) - .faultWrite(EL1, HCR_TRAP(ttlb)) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::ttlb, &HFGITR::tlbivmalle1>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_VAE1_Xt) - .faultWrite(EL1, HCR_TRAP(ttlb)) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::ttlb, &HFGITR::tlbivae1>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_ASIDE1_Xt) - .faultWrite(EL1, HCR_TRAP(ttlb)) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::ttlb, &HFGITR::tlbiaside1>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_VAAE1_Xt) - .faultWrite(EL1, HCR_TRAP(ttlb)) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::ttlb, &HFGITR::tlbivaae1>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_VALE1_Xt) - .faultWrite(EL1, HCR_TRAP(ttlb)) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::ttlb, &HFGITR::tlbivale1>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_VAALE1_Xt) - .faultWrite(EL1, HCR_TRAP(ttlb)) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::ttlb, &HFGITR::tlbivaale1>) .writes(1).exceptUserMode(); InitReg(MISCREG_TLBI_IPAS2E1OS_Xt) .hypWrite().monSecureWrite().monNonSecureWrite(); @@ -4675,6 +5119,79 @@ ISA::initializeMiscRegMetadata() .monSecureWrite().monNonSecureWrite(); InitReg(MISCREG_TLBI_VALE3_Xt) .monSecureWrite().monNonSecureWrite(); + + InitReg(MISCREG_TLBI_RVAE1_Xt) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::ttlb, &HFGITR::tlbirvae1>) + .writes(1).exceptUserMode(); + InitReg(MISCREG_TLBI_RVAAE1_Xt) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::ttlb, &HFGITR::tlbirvaae1>) + .writes(1).exceptUserMode(); + InitReg(MISCREG_TLBI_RVALE1_Xt) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::ttlb, &HFGITR::tlbirvale1>) + .writes(1).exceptUserMode(); + InitReg(MISCREG_TLBI_RVAALE1_Xt) + .faultWrite(EL1, faultHcrFgtInstEL1<&HCR::ttlb, &HFGITR::tlbirvaale1>) + .writes(1).exceptUserMode(); + InitReg(MISCREG_TLBI_RIPAS2E1_Xt) + .hypWrite().monWrite(); + InitReg(MISCREG_TLBI_RIPAS2LE1_Xt) + .hypWrite().monWrite(); + InitReg(MISCREG_TLBI_RVAE2_Xt) + .hypWrite().monWrite(); + InitReg(MISCREG_TLBI_RVALE2_Xt) + .hypWrite().monWrite(); + InitReg(MISCREG_TLBI_RVAE3_Xt) + .monWrite(); + InitReg(MISCREG_TLBI_RVALE3_Xt) + .monWrite(); + InitReg(MISCREG_TLBI_RVAE1IS_Xt) + .faultWrite(EL1, faultTlbiIsEL1<&HFGITR::tlbirvae1is>) + .writes(1).exceptUserMode(); + InitReg(MISCREG_TLBI_RVAAE1IS_Xt) + .faultWrite(EL1, faultTlbiIsEL1<&HFGITR::tlbirvaae1is>) + .writes(1).exceptUserMode(); + InitReg(MISCREG_TLBI_RVALE1IS_Xt) + .faultWrite(EL1, faultTlbiIsEL1<&HFGITR::tlbirvale1is>) + .writes(1).exceptUserMode(); + InitReg(MISCREG_TLBI_RVAALE1IS_Xt) + .faultWrite(EL1, faultTlbiIsEL1<&HFGITR::tlbirvaale1is>) + .writes(1).exceptUserMode(); + InitReg(MISCREG_TLBI_RIPAS2E1IS_Xt) + .hypWrite().monWrite(); + InitReg(MISCREG_TLBI_RIPAS2LE1IS_Xt) + .hypWrite().monWrite(); + InitReg(MISCREG_TLBI_RVAE2IS_Xt) + .hypWrite().monWrite(); + InitReg(MISCREG_TLBI_RVALE2IS_Xt) + .hypWrite().monWrite(); + InitReg(MISCREG_TLBI_RVAE3IS_Xt) + .monWrite(); + InitReg(MISCREG_TLBI_RVALE3IS_Xt) + .monWrite(); + InitReg(MISCREG_TLBI_RVAE1OS_Xt) + .faultWrite(EL1, faultTlbiOsEL1<&HFGITR::tlbirvae1os>) + .writes(1).exceptUserMode(); + InitReg(MISCREG_TLBI_RVAAE1OS_Xt) + .faultWrite(EL1, faultTlbiOsEL1<&HFGITR::tlbirvaae1os>) + .writes(1).exceptUserMode(); + InitReg(MISCREG_TLBI_RVALE1OS_Xt) + .faultWrite(EL1, faultTlbiOsEL1<&HFGITR::tlbirvale1os>) + .writes(1).exceptUserMode(); + InitReg(MISCREG_TLBI_RVAALE1OS_Xt) + .faultWrite(EL1, faultTlbiOsEL1<&HFGITR::tlbirvaale1os>) + .writes(1).exceptUserMode(); + InitReg(MISCREG_TLBI_RIPAS2E1OS_Xt) + .hypWrite().monWrite(); + InitReg(MISCREG_TLBI_RIPAS2LE1OS_Xt) + .hypWrite().monWrite(); + InitReg(MISCREG_TLBI_RVAE2OS_Xt) + .hypWrite().monWrite(); + InitReg(MISCREG_TLBI_RVALE2OS_Xt) + .hypWrite().monWrite(); + InitReg(MISCREG_TLBI_RVAE3OS_Xt) + .monWrite(); + InitReg(MISCREG_TLBI_RVALE3OS_Xt) + .monWrite(); InitReg(MISCREG_PMINTENSET_EL1) .allPrivileges().exceptUserMode() .mapsTo(MISCREG_PMINTENSET); @@ -4724,8 +5241,8 @@ ISA::initializeMiscRegMetadata() .mapsTo(MISCREG_PMOVSSET); InitReg(MISCREG_MAIR_EL1) .allPrivileges().exceptUserMode() - .faultRead(EL1, HCR_TRAP(trvm)) - .faultWrite(EL1, HCR_TRAP(tvm)) + .faultRead(EL1, faultHcrFgtEL1) + .faultWrite(EL1, faultHcrFgtEL1) .mapsTo(MISCREG_PRRR_NS, MISCREG_NMRR_NS); InitReg(MISCREG_MAIR_EL12) .fault(EL2, defaultFaultE2H_EL2) @@ -4733,8 +5250,8 @@ ISA::initializeMiscRegMetadata() .mapsTo(MISCREG_PRRR_NS, MISCREG_NMRR_NS); InitReg(MISCREG_AMAIR_EL1) .allPrivileges().exceptUserMode() - .faultRead(EL1, HCR_TRAP(trvm)) - .faultWrite(EL1, HCR_TRAP(tvm)) + .faultRead(EL1, faultHcrFgtEL1) + .faultWrite(EL1, faultHcrFgtEL1) .mapsTo(MISCREG_AMAIR0_NS, MISCREG_AMAIR1_NS); InitReg(MISCREG_AMAIR_EL12) .fault(EL2, defaultFaultE2H_EL2) @@ -4756,6 +5273,8 @@ ISA::initializeMiscRegMetadata() .allPrivileges().exceptUserMode(); InitReg(MISCREG_VBAR_EL1) .allPrivileges().exceptUserMode() + .faultRead(EL1, faultFgtEL1) + .faultWrite(EL1, faultFgtEL1) .mapsTo(MISCREG_VBAR_NS); InitReg(MISCREG_VBAR_EL12) .fault(EL2, defaultFaultE2H_EL2) @@ -4785,8 +5304,8 @@ ISA::initializeMiscRegMetadata() .mon(); InitReg(MISCREG_CONTEXTIDR_EL1) .allPrivileges().exceptUserMode() - .faultRead(EL1, HCR_TRAP(trvm)) - .faultWrite(EL1, HCR_TRAP(tvm)) + .faultRead(EL1, faultHcrFgtEL1) + .faultWrite(EL1, faultHcrFgtEL1) .mapsTo(MISCREG_CONTEXTIDR_NS); InitReg(MISCREG_CONTEXTIDR_EL12) .fault(EL2, defaultFaultE2H_EL2) @@ -4794,12 +5313,21 @@ ISA::initializeMiscRegMetadata() .mapsTo(MISCREG_CONTEXTIDR_NS); InitReg(MISCREG_TPIDR_EL1) .allPrivileges().exceptUserMode() + .faultRead(EL1, faultFgtEL1) + .faultWrite(EL1, faultFgtEL1) .mapsTo(MISCREG_TPIDRPRW_NS); InitReg(MISCREG_TPIDR_EL0) .allPrivileges() + .faultRead(EL0, faultFgtEL0) + .faultWrite(EL0, faultFgtEL0) + .faultRead(EL1, faultFgtEL1) + .faultWrite(EL1, faultFgtEL1) .mapsTo(MISCREG_TPIDRURW_NS); InitReg(MISCREG_TPIDRRO_EL0) .allPrivileges().userNonSecureWrite(0).userSecureWrite(0) + .faultRead(EL0, faultFgtEL0) + .faultRead(EL1, faultFgtEL1) + .faultWrite(EL1, faultFgtEL1) .mapsTo(MISCREG_TPIDRURO_NS); InitReg(MISCREG_TPIDR_EL2) .hyp().mon() @@ -5187,9 +5715,13 @@ ISA::initializeMiscRegMetadata() InitReg(MISCREG_ICC_IGRPEN0_EL1) .res0(0xFFFFFFFE) // [31:1] .allPrivileges().exceptUserMode() + .faultRead(EL1, faultFgtEL1) + .faultWrite(EL1, faultFgtEL1) .mapsTo(MISCREG_ICC_IGRPEN0); InitReg(MISCREG_ICC_IGRPEN1_EL1) .banked64() + .faultRead(EL1, faultFgtEL1) + .faultWrite(EL1, faultFgtEL1) .mapsTo(MISCREG_ICC_IGRPEN1); InitReg(MISCREG_ICC_IGRPEN1_EL1_NS) .bankedChild() @@ -5502,7 +6034,7 @@ ISA::initializeMiscRegMetadata() return zfr0_el1; }()) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().exceptUserMode().writes(0); InitReg(MISCREG_ZCR_EL3) .reset(sveVL - 1) @@ -5542,7 +6074,7 @@ ISA::initializeMiscRegMetadata() return smfr0_el1; }()) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid3)) + .faultRead(EL1, faultHcrEL1<&HCR::tid3>) .allPrivileges().writes(0); InitReg(MISCREG_SVCR) .res0([](){ @@ -5565,7 +6097,7 @@ ISA::initializeMiscRegMetadata() return smidr_el1; }()) .faultRead(EL0, faultIdst) - .faultRead(EL1, HCR_TRAP(tid1)) + .faultRead(EL1, faultHcrEL1<&HCR::tid1>) .allPrivileges().writes(0); InitReg(MISCREG_SMPRI_EL1) .res0(mask(63, 4)) @@ -5644,6 +6176,17 @@ ISA::initializeMiscRegMetadata() .unverifiable() .allPrivileges().writes(0); + // FEAT_FGT extension + InitReg(MISCREG_HFGRTR_EL2) + .fault(EL2, faultFgtCtrlRegs) + .hyp().mon(release->has(ArmExtension::FEAT_FGT)); + InitReg(MISCREG_HFGWTR_EL2) + .fault(EL2, faultFgtCtrlRegs) + .hyp().mon(release->has(ArmExtension::FEAT_FGT)); + InitReg(MISCREG_HFGITR_EL2) + .fault(EL2, faultFgtCtrlRegs) + .hyp().mon(release->has(ArmExtension::FEAT_FGT)); + // Dummy registers InitReg(MISCREG_NOP) .allPrivileges(); @@ -5691,14 +6234,6 @@ ISA::initializeMiscRegMetadata() .warnNotFail() .fault(faultUnimplemented); - // FGT extension (unimplemented) - InitReg(MISCREG_HFGRTR_EL2) - .unimplemented() - .warnNotFail(); - InitReg(MISCREG_HFGWTR_EL2) - .unimplemented() - .warnNotFail(); - // Register mappings for some unimplemented registers: // ESR_EL1 -> DFSR // RMR_EL1 -> RMR diff --git a/src/arch/arm/regs/misc.hh b/src/arch/arm/regs/misc.hh index cb03841848..065e5439c2 100644 --- a/src/arch/arm/regs/misc.hh +++ b/src/arch/arm/regs/misc.hh @@ -96,7 +96,7 @@ namespace ArmISA MISCREG_SEV_MAILBOX, MISCREG_TLBINEEDSYNC, - // AArch32 CP14 registers (debug/trace/ThumbEE/Jazelle control) + // AArch32 CP14 registers (debug/trace control) MISCREG_DBGDIDR, MISCREG_DBGDSCRint, MISCREG_DBGDCCINT, @@ -583,10 +583,13 @@ namespace ArmISA MISCREG_VMPIDR_EL2, MISCREG_SCTLR_EL1, MISCREG_SCTLR_EL12, + MISCREG_SCTLR2_EL1, + MISCREG_SCTLR2_EL12, MISCREG_ACTLR_EL1, MISCREG_CPACR_EL1, MISCREG_CPACR_EL12, MISCREG_SCTLR_EL2, + MISCREG_SCTLR2_EL2, MISCREG_ACTLR_EL2, MISCREG_HCR_EL2, MISCREG_HCRX_EL2, @@ -595,6 +598,7 @@ namespace ArmISA MISCREG_HSTR_EL2, MISCREG_HACR_EL2, MISCREG_SCTLR_EL3, + MISCREG_SCTLR2_EL3, MISCREG_ACTLR_EL3, MISCREG_SCR_EL3, MISCREG_SDER32_EL3, @@ -606,8 +610,11 @@ namespace ArmISA MISCREG_TTBR1_EL12, MISCREG_TCR_EL1, MISCREG_TCR_EL12, + MISCREG_TCR2_EL1, + MISCREG_TCR2_EL12, MISCREG_TTBR0_EL2, MISCREG_TCR_EL2, + MISCREG_TCR2_EL2, MISCREG_VTTBR_EL2, MISCREG_VTCR_EL2, MISCREG_VSTTBR_EL2, @@ -729,6 +736,36 @@ namespace ArmISA MISCREG_TLBI_ALLE3, MISCREG_TLBI_VAE3_Xt, MISCREG_TLBI_VALE3_Xt, + MISCREG_TLBI_RVAE1_Xt, + MISCREG_TLBI_RVAAE1_Xt, + MISCREG_TLBI_RVALE1_Xt, + MISCREG_TLBI_RVAALE1_Xt, + MISCREG_TLBI_RIPAS2E1_Xt, + MISCREG_TLBI_RIPAS2LE1_Xt, + MISCREG_TLBI_RVAE2_Xt, + MISCREG_TLBI_RVALE2_Xt, + MISCREG_TLBI_RVAE3_Xt, + MISCREG_TLBI_RVALE3_Xt, + MISCREG_TLBI_RVAE1IS_Xt, + MISCREG_TLBI_RVAAE1IS_Xt, + MISCREG_TLBI_RVALE1IS_Xt, + MISCREG_TLBI_RVAALE1IS_Xt, + MISCREG_TLBI_RIPAS2E1IS_Xt, + MISCREG_TLBI_RIPAS2LE1IS_Xt, + MISCREG_TLBI_RVAE2IS_Xt, + MISCREG_TLBI_RVALE2IS_Xt, + MISCREG_TLBI_RVAE3IS_Xt, + MISCREG_TLBI_RVALE3IS_Xt, + MISCREG_TLBI_RVAE1OS_Xt, + MISCREG_TLBI_RVAAE1OS_Xt, + MISCREG_TLBI_RVALE1OS_Xt, + MISCREG_TLBI_RVAALE1OS_Xt, + MISCREG_TLBI_RIPAS2E1OS_Xt, + MISCREG_TLBI_RIPAS2LE1OS_Xt, + MISCREG_TLBI_RVAE2OS_Xt, + MISCREG_TLBI_RVALE2OS_Xt, + MISCREG_TLBI_RVAE3OS_Xt, + MISCREG_TLBI_RVALE3OS_Xt, MISCREG_PMINTENSET_EL1, MISCREG_PMINTENCLR_EL1, MISCREG_PMCR_EL0, @@ -842,6 +879,7 @@ namespace ArmISA MISCREG_TTBR1_EL2, MISCREG_ID_AA64MMFR2_EL1, + MISCREG_ID_AA64MMFR3_EL1, //PAuth Key Regsiters MISCREG_APDAKeyHi_EL1, @@ -1096,6 +1134,11 @@ namespace ArmISA MISCREG_RNDR, MISCREG_RNDRRS, + // FEAT_FGT + MISCREG_HFGITR_EL2, + MISCREG_HFGRTR_EL2, + MISCREG_HFGWTR_EL2, + // NUM_PHYS_MISCREGS specifies the number of actual physical // registers, not considering the following pseudo-registers // (dummy registers), like MISCREG_UNKNOWN, MISCREG_IMPDEF_UNIMPL. @@ -1126,10 +1169,6 @@ namespace ArmISA MISCREG_VSESR_EL2, MISCREG_VDISR_EL2, - // FGT extension (unimplemented) - MISCREG_HFGRTR_EL2, - MISCREG_HFGWTR_EL2, - // PSTATE MISCREG_PAN, MISCREG_UAO, @@ -1502,6 +1541,13 @@ namespace ArmISA return *this; } chain + monWrite(bool v = true) const + { + monSecureWrite(v); + monNonSecureWrite(v); + return *this; + } + chain monSecure(bool v = true) const { monSecureRead(v); @@ -2264,10 +2310,13 @@ namespace ArmISA "vmpidr_el2", "sctlr_el1", "sctlr_el12", + "sctlr2_el1", + "sctlr2_el12", "actlr_el1", "cpacr_el1", "cpacr_el12", "sctlr_el2", + "sctlr2_el2", "actlr_el2", "hcr_el2", "hcrx_el2", @@ -2276,6 +2325,7 @@ namespace ArmISA "hstr_el2", "hacr_el2", "sctlr_el3", + "sctlr2_el3", "actlr_el3", "scr_el3", "sder32_el3", @@ -2287,8 +2337,11 @@ namespace ArmISA "ttbr1_el12", "tcr_el1", "tcr_el12", + "tcr2_el1", + "tcr2_el12", "ttbr0_el2", "tcr_el2", + "tcr2_el2", "vttbr_el2", "vtcr_el2", "vsttbr_el2", @@ -2410,6 +2463,36 @@ namespace ArmISA "tlbi_alle3", "tlbi_vae3_xt", "tlbi_vale3_xt", + "tlbi_rvae1_xt", + "tlbi_rvaae1_xt", + "tlbi_rvale1_xt", + "tlbi_rvaale1_xt", + "tlbi_ripas2e1_xt", + "tlbi_ripas2le1_xt", + "tlbi_rvae2_xt", + "tlbi_rvale2_xt", + "tlbi_rvae3_xt", + "tlbi_rvale3_xt", + "tlbi_rvae1is_xt", + "tlbi_rvaae1is_xt", + "tlbi_rvale1is_xt", + "tlbi_rvaale1is_xt", + "tlbi_ripas2e1is_xt", + "tlbi_ripas2le1is_xt", + "tlbi_rvae2is_xt", + "tlbi_rvale2is_xt", + "tlbi_rvae3is_xt", + "tlbi_rvale3is_xt", + "tlbi_rvae1os_xt", + "tlbi_rvaae1os_xt", + "tlbi_rvale1os_xt", + "tlbi_rvaale1os_xt", + "tlbi_ripas2e1os_xt", + "tlbi_ripas2le1os_xt", + "tlbi_rvae2os_xt", + "tlbi_rvale2os_xt", + "tlbi_rvae3os_xt", + "tlbi_rvale3os_xt", "pmintenset_el1", "pmintenclr_el1", "pmcr_el0", @@ -2517,6 +2600,7 @@ namespace ArmISA "ttbr1_el2", "id_aa64mmfr2_el1", + "id_aa64mmfr3_el1", "apdakeyhi_el1", "apdakeylo_el1", @@ -2766,6 +2850,10 @@ namespace ArmISA "rndr", "rndrrs", + "hfgitr_el2", + "hfgrtr_el2", + "hfgwtr_el2", + "num_phys_regs", // Dummy registers @@ -2784,8 +2872,6 @@ namespace ArmISA "disr_el1", "vsesr_el2", "vdisr_el2", - "hfgrtr_el2", - "hfgwtr_el2", // PSTATE "pan", diff --git a/src/arch/arm/regs/misc_types.hh b/src/arch/arm/regs/misc_types.hh index 00640dd339..3bc06ac97c 100644 --- a/src/arch/arm/regs/misc_types.hh +++ b/src/arch/arm/regs/misc_types.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Arm Limited + * Copyright (c) 2010-2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -54,7 +54,7 @@ namespace ArmISA Bitfield<28> v; Bitfield<27> q; Bitfield<26, 25> it1; - Bitfield<24> j; + Bitfield<24> dit; // AArch64 Bitfield<23> uao; // AArch64 Bitfield<22> pan; Bitfield<21> ss; // AArch64 @@ -75,6 +75,12 @@ namespace ArmISA Bitfield<0> sp; // AArch64 EndBitUnion(CPSR) + BitUnion32(ISR) + Bitfield<8> a; + Bitfield<7> i; + Bitfield<6> f; + EndBitUnion(ISR) + BitUnion32(ISAR5) Bitfield<31, 28> vcma; Bitfield<27, 24> rdm; @@ -187,6 +193,21 @@ namespace ArmISA Bitfield<3, 0> cnp; EndBitUnion(AA64MMFR2) + BitUnion64(AA64MMFR3) + Bitfield<47, 44> anerr; + Bitfield<43, 40> snerr; + Bitfield<39, 36> d128_2; + Bitfield<35, 32> d128; + Bitfield<31, 28> mec; + Bitfield<27, 24> aie; + Bitfield<23, 20> s2poe; + Bitfield<19, 16> s1poe; + Bitfield<15, 12> s2pie; + Bitfield<11, 8> s1pie; + Bitfield<7, 4> sctlrx; + Bitfield<3, 0> tcrx; + EndBitUnion(AA64MMFR3) + BitUnion64(AA64PFR0) Bitfield<63, 60> csv3; Bitfield<59, 56> csv2; @@ -361,8 +382,11 @@ namespace ArmISA EndBitUnion(NSACR) BitUnion64(SCR) + Bitfield<44> sctlr2En; + Bitfield<43> tcr2En; Bitfield<40> trndr; Bitfield<38> hxen; + Bitfield<27> fgten; Bitfield<21> fien; Bitfield<20> nmea; Bitfield<19> ease; @@ -439,8 +463,6 @@ namespace ArmISA Bitfield<7> itd; // IT disable // (ARMv8 AArch32 and AArch64 SCTLR_EL1 only) Bitfield<6, 3> rao4; // Read as one - Bitfield<6> thee; // ThumbEE enable - // (ARMv8 AArch32 and AArch64 SCTLR_EL1 only) Bitfield<5> cp15ben; // CP15 barrier enable // (AArch32 and AArch64 SCTLR_EL1 only) Bitfield<4> sa0; // Stack Alignment Check Enable for EL0 @@ -931,6 +953,122 @@ namespace ArmISA Bitfield<3,0> pcsample; EndBitUnion(DEVID) + BitUnion64(HFGITR) + Bitfield<54> dccvac; + Bitfield<53> svcEL1; + Bitfield<52> svcEL0; + Bitfield<51> eret; + Bitfield<47> tlbivaale1; + Bitfield<46> tlbivale1; + Bitfield<45> tlbivaae1; + Bitfield<44> tlbiaside1; + Bitfield<43> tlbivae1; + Bitfield<42> tlbivmalle1; + Bitfield<41> tlbirvaale1; + Bitfield<40> tlbirvale1; + Bitfield<39> tlbirvaae1; + Bitfield<38> tlbirvae1; + Bitfield<37> tlbirvaale1is; + Bitfield<36> tlbirvale1is; + Bitfield<35> tlbirvaae1is; + Bitfield<34> tlbirvae1is; + Bitfield<33> tlbivaale1is; + Bitfield<32> tlbivale1is; + Bitfield<31> tlbivaae1is; + Bitfield<30> tlbiaside1is; + Bitfield<29> tlbivae1is; + Bitfield<28> tlbivmalle1is; + Bitfield<27> tlbirvaale1os; + Bitfield<26> tlbirvale1os; + Bitfield<25> tlbirvaae1os; + Bitfield<24> tlbirvae1os; + Bitfield<23> tlbivaale1os; + Bitfield<22> tlbivale1os; + Bitfield<21> tlbivaae1os; + Bitfield<20> tlbiaside1os; + Bitfield<19> tlbivae1os; + Bitfield<18> tlbivmalle1os; + Bitfield<17> ats1e1wp; + Bitfield<16> ats1e1rp; + Bitfield<15> ats1e0w; + Bitfield<14> ats1e0r; + Bitfield<13> ats1e1w; + Bitfield<12> ats1e1r; + Bitfield<11> dczva; + Bitfield<10> dccivac; + Bitfield<9> dccvapd; + Bitfield<8> dccvap; + Bitfield<7> dccvau; + Bitfield<6> dccisw; + Bitfield<5> dccsw; + Bitfield<4> dcisw; + Bitfield<3> dcivac; + Bitfield<2> icivau; + Bitfield<1> iciallu; + Bitfield<0> icialluis; + EndBitUnion(HFGITR) + + // HFGRTR and HFGWTR. Some fields are + // for HFGRTR only (RO registers) + BitUnion64(HFGTR) + Bitfield<50> nAccdataEL1; + Bitfield<49> erxaddrEL1; + Bitfield<48> erxpfgcdnEL1; + Bitfield<47> erxpfgctlEL1; + Bitfield<46> erxpfgfEL1; // RES0 for HFGWTR + Bitfield<45> erxmiscNEL1; + Bitfield<44> erxstatusEL1; + Bitfield<43> erxctlrEL1; + Bitfield<42> erxfrEL1; + Bitfield<41> errselrEL1; + Bitfield<40> erridrEL1; // RES0 for HFGWTR + Bitfield<39> iccIgrpEnEL1; + Bitfield<38> vbarEL1; + Bitfield<37> ttbr1EL1; + Bitfield<36> ttbr0EL1; + Bitfield<35> tpidrEL0; + Bitfield<34> tpidrroEL0; + Bitfield<33> tpidrEL1; + Bitfield<32> tcrEL1; + Bitfield<31> scxtnumEL0; + Bitfield<30> scxtnumEL1; + Bitfield<29> sctlrEL1; + Bitfield<28> revidrEL1; // RES0 for HFGWTR + Bitfield<27> parEL1; + Bitfield<26> mpidrEL1; // RES0 for HFGWTR + Bitfield<25> midrEL1; // RES0 for HFGWTR + Bitfield<24> mairEL1; + Bitfield<23> lorsaEL1; + Bitfield<22> lornEL1; + Bitfield<21> loridEL1; // RES0 for HFGWTR + Bitfield<20> loreaEL1; + Bitfield<19> lorcEL1; + Bitfield<18> isrEL1; // RES0 for HFGWTR + Bitfield<17> farEL1; + Bitfield<16> esrEL1; + Bitfield<15> dczidEL0; // RES0 for HFGWTR + Bitfield<14> ctrEL0; // RES0 for HFGWTR + Bitfield<13> csselrEL1; + Bitfield<12> cpacrEL1; + Bitfield<11> contextidrEL1; + Bitfield<10> clidrEL1; // RES0 for HFGWTR + Bitfield<9> ccsidrEL1; // RES0 for HFGWTR + Bitfield<8> apibKey; + Bitfield<7> apiaKey; + Bitfield<6> apgaKey; + Bitfield<5> apdbKey; + Bitfield<4> apdaKey; + Bitfield<3> amairEL1; + Bitfield<2> aidrEL1; // RES0 for HFGWTR + Bitfield<1> afsr1EL1; + Bitfield<0> afsr0EL1; + EndBitUnion(HFGTR) + + BitUnion64(HCRX) + Bitfield<15> sctlr2En; + Bitfield<14> tcr2En; + EndBitUnion(HCRX) + } // namespace ArmISA } // namespace gem5 diff --git a/src/arch/arm/table_walker.cc b/src/arch/arm/table_walker.cc index 60f9e3f76e..5938755d86 100644 --- a/src/arch/arm/table_walker.cc +++ b/src/arch/arm/table_walker.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2012-2019, 2021-2022 Arm Limited + * Copyright (c) 2010, 2012-2019, 2021-2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -2305,6 +2305,7 @@ TableWalker::insertPartialTableEntry(LongDescriptor &descriptor) te.asid = currState->asid; te.vmid = currState->vmid; te.N = descriptor.offsetBits(); + te.tg = descriptor.grainSize; te.vpn = currState->vaddr >> te.N; te.size = (1ULL << te.N) - 1; te.pfn = descriptor.nextTableAddr(); @@ -2378,6 +2379,7 @@ TableWalker::insertTableEntry(DescriptorBase &descriptor, bool long_descriptor) LongDescriptor l_descriptor = dynamic_cast(descriptor); + te.tg = l_descriptor.grainSize; te.xn |= currState->xnTable; te.pxn = currState->pxnTable || l_descriptor.pxn(); if (isStage2) { diff --git a/src/arch/arm/tlbi_op.cc b/src/arch/arm/tlbi_op.cc index e89f411384..b49139bf3e 100644 --- a/src/arch/arm/tlbi_op.cc +++ b/src/arch/arm/tlbi_op.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2022 Arm Limited + * Copyright (c) 2018-2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -209,6 +209,22 @@ TLBIALLN::match(TlbEntry* te, vmid_t vmid) const te->checkELMatch(targetEL, false); } +TlbEntry::Lookup +TLBIMVAA::lookupGen(vmid_t vmid) const +{ + TlbEntry::Lookup lookup_data; + lookup_data.va = sext<56>(addr); + lookup_data.ignoreAsn = true; + lookup_data.vmid = vmid; + lookup_data.hyp = targetEL == EL2; + lookup_data.secure = secureLookup; + lookup_data.functional = true; + lookup_data.targetEL = targetEL; + lookup_data.inHost = inHost; + lookup_data.mode = BaseMMU::Read; + return lookup_data; +} + void TLBIMVAA::operator()(ThreadContext* tc) { @@ -224,10 +240,19 @@ TLBIMVAA::operator()(ThreadContext* tc) bool TLBIMVAA::match(TlbEntry* te, vmid_t vmid) const +{ + TlbEntry::Lookup lookup_data = lookupGen(vmid); + + return te->match(lookup_data) && (!lastLevel || !te->partial); +} + +TlbEntry::Lookup +TLBIMVA::lookupGen(vmid_t vmid) const { TlbEntry::Lookup lookup_data; lookup_data.va = sext<56>(addr); - lookup_data.ignoreAsn = true; + lookup_data.asn = asid; + lookup_data.ignoreAsn = false; lookup_data.vmid = vmid; lookup_data.hyp = targetEL == EL2; lookup_data.secure = secureLookup; @@ -236,7 +261,7 @@ TLBIMVAA::match(TlbEntry* te, vmid_t vmid) const lookup_data.inHost = inHost; lookup_data.mode = BaseMMU::Read; - return te->match(lookup_data) && (!lastLevel || !te->partial); + return lookup_data; } void @@ -255,17 +280,7 @@ TLBIMVA::operator()(ThreadContext* tc) bool TLBIMVA::match(TlbEntry* te, vmid_t vmid) const { - TlbEntry::Lookup lookup_data; - lookup_data.va = sext<56>(addr); - lookup_data.asn = asid; - lookup_data.ignoreAsn = false; - lookup_data.vmid = vmid; - lookup_data.hyp = targetEL == EL2; - lookup_data.secure = secureLookup; - lookup_data.functional = true; - lookup_data.targetEL = targetEL; - lookup_data.inHost = inHost; - lookup_data.mode = BaseMMU::Read; + TlbEntry::Lookup lookup_data = lookupGen(vmid); return te->match(lookup_data) && (!lastLevel || !te->partial); } @@ -305,5 +320,37 @@ TLBIIPA::operator()(ThreadContext* tc) } } +bool +TLBIRMVA::match(TlbEntry* te, vmid_t vmid) const +{ + TlbEntry::Lookup lookup_data = lookupGen(vmid); + lookup_data.size = rangeSize(); + + auto addr_match = te->match(lookup_data) && (!lastLevel || !te->partial); + if (addr_match) { + return tgMap[rangeData.tg] == te->tg && + (resTLBIttl(rangeData.tg, rangeData.ttl) || + rangeData.ttl == te->lookupLevel); + } else { + return false; + } +} + +bool +TLBIRMVAA::match(TlbEntry* te, vmid_t vmid) const +{ + TlbEntry::Lookup lookup_data = lookupGen(vmid); + lookup_data.size = rangeSize(); + + auto addr_match = te->match(lookup_data) && (!lastLevel || !te->partial); + if (addr_match) { + return tgMap[rangeData.tg] == te->tg && + (resTLBIttl(rangeData.tg, rangeData.ttl) || + rangeData.ttl == te->lookupLevel); + } else { + return false; + } +} + } // namespace ArmISA } // namespace gem5 diff --git a/src/arch/arm/tlbi_op.hh b/src/arch/arm/tlbi_op.hh index 4f4ea09ec8..38e8252869 100644 --- a/src/arch/arm/tlbi_op.hh +++ b/src/arch/arm/tlbi_op.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, 2022 Arm Limited + * Copyright (c) 2018-2020, 2022-2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -300,6 +300,8 @@ class TLBIALLN : public TLBIOp /** TLB Invalidate by VA, All ASID */ class TLBIMVAA : public TLBIOp { + protected: + TlbEntry::Lookup lookupGen(vmid_t vmid) const; public: TLBIMVAA(ExceptionLevel _targetEL, bool _secure, Addr _addr, bool last_level) @@ -319,6 +321,9 @@ class TLBIMVAA : public TLBIOp /** TLB Invalidate by VA */ class TLBIMVA : public TLBIOp { + protected: + TlbEntry::Lookup lookupGen(vmid_t vmid) const; + public: TLBIMVA(ExceptionLevel _targetEL, bool _secure, Addr _addr, uint16_t _asid, bool last_level) @@ -368,6 +373,61 @@ class DTLBIMVA : public TLBIMVA bool match(TlbEntry *entry, vmid_t curr_vmid) const override; }; +class TLBIRange +{ + public: + /** + * Is the range valid? This mainly depends on the specified + * translation granule. + */ + bool valid() const { return granule != ReservedGrain; } + + protected: + BitUnion64(RangeData) + Bitfield<47, 46> tg; + Bitfield<45, 44> scale; + Bitfield<43, 39> num; + Bitfield<38, 37> ttl; + Bitfield<36, 0> baseAddr; + EndBitUnion(RangeData) + + static constexpr std::array tgMap = { + ReservedGrain, + Grain4KB, + Grain16KB, + Grain64KB + }; + + TLBIRange(RegVal val) + : rangeData(val), granule(tgMap[rangeData.tg]) + {} + + Addr + startAddress() const + { + return sext<37>(rangeData.baseAddr) << granule; + } + + Addr + rangeSize() const + { + return (rangeData.num + 1) << (5 * rangeData.scale + 1 + granule); + } + + bool + resTLBIttl(uint8_t tg, uint8_t ttl) const + { + switch (ttl) { + case 0: return true; + case 1: return tgMap[tg] == Grain16KB; + default: return false; + } + } + + RangeData rangeData; + GrainSize granule; +}; + /** TLB Invalidate by Intermediate Physical Address */ class TLBIIPA : public TLBIOp { @@ -392,7 +452,7 @@ class TLBIIPA : public TLBIOp } /** TLBIIPA is basically a TLBIMVAA for stage2 TLBs */ - TLBIMVAA + virtual TLBIMVAA makeStage2() const { return TLBIMVAA(EL1, secureLookup, addr, lastLevel); @@ -402,6 +462,49 @@ class TLBIIPA : public TLBIOp bool lastLevel; }; +/** TLB Range Invalidate by VA */ +class TLBIRMVA : public TLBIRange, public TLBIMVA +{ + public: + TLBIRMVA(ExceptionLevel _targetEL, bool _secure, + RegVal val, uint16_t _asid, bool last_level) + : TLBIRange(val), + TLBIMVA(_targetEL, _secure, startAddress(), _asid, last_level) + {} + + bool match(TlbEntry *entry, vmid_t curr_vmid) const override; +}; + +/** TLB Range Invalidate by VA, All ASIDs */ +class TLBIRMVAA : public TLBIRange, public TLBIMVAA +{ + public: + TLBIRMVAA(ExceptionLevel _targetEL, bool _secure, + RegVal val, bool last_level) + : TLBIRange(val), + TLBIMVAA(_targetEL, _secure, startAddress(), last_level) + {} + + bool match(TlbEntry *entry, vmid_t curr_vmid) const override; +}; + +/** TLB Range Invalidate by VA, All ASIDs */ +class TLBIRIPA : public TLBIRange, public TLBIIPA +{ + public: + TLBIRIPA(ExceptionLevel _targetEL, bool _secure, + RegVal val, bool last_level) + : TLBIRange(val), + TLBIIPA(_targetEL, _secure, startAddress(), last_level) + {} + + virtual TLBIMVAA + makeStage2() const + { + return TLBIRMVAA(EL1, secureLookup, rangeData, lastLevel); + } +}; + } // namespace ArmISA } // namespace gem5 diff --git a/tests/configs/minor-timing-mp.py b/src/arch/arm/tracers/ArmCapstone.py similarity index 85% rename from tests/configs/minor-timing-mp.py rename to src/arch/arm/tracers/ArmCapstone.py index b6c56de512..0bceb6dbd6 100644 --- a/tests/configs/minor-timing-mp.py +++ b/src/arch/arm/tracers/ArmCapstone.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013 ARM Limited +# Copyright (c) 2023 Arm Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -10,9 +10,6 @@ # unmodified and in its entirety in all distributions of the software, # modified or unmodified, in source code or in binary form. # -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# All rights reserved. -# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: redistributions of source code must retain the above copyright @@ -36,13 +33,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * -from base_config import * +from m5.objects.Capstone import CapstoneDisassembler +from m5.params import * +from m5.SimObject import SimObject -nb_cores = 4 -root = BaseSESystem( - mem_mode="timing", - mem_class=DDR3_1600_8x8, - cpu_class=MinorCPU, - num_cpus=nb_cores, -).create_root() + +class ArmCapstoneDisassembler(CapstoneDisassembler): + type = "ArmCapstoneDisassembler" + cxx_class = "gem5::trace::ArmCapstoneDisassembler" + cxx_header = "arch/arm/tracers/capstone.hh" diff --git a/src/arch/arm/tracers/SConscript b/src/arch/arm/tracers/SConscript index 15945a4ac4..7b729ad065 100644 --- a/src/arch/arm/tracers/SConscript +++ b/src/arch/arm/tracers/SConscript @@ -1,4 +1,4 @@ -# Copyright (c) 2018 ARM Limited +# Copyright (c) 2018, 2023 Arm Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -42,3 +42,8 @@ Source('tarmac_parser.cc', tags='arm isa') Source('tarmac_tracer.cc', tags='arm isa') Source('tarmac_record.cc', tags='arm isa') Source('tarmac_record_v8.cc', tags='arm isa') + +if env['CONF']['USE_CAPSTONE']: + SimObject('ArmCapstone.py', sim_objects=['ArmCapstoneDisassembler'], + tags=['arm isa']) + Source('capstone.cc', tags=['arm isa']) diff --git a/src/arch/arm/tracers/TarmacTrace.py b/src/arch/arm/tracers/TarmacTrace.py index 82c447aada..e28b19bee4 100644 --- a/src/arch/arm/tracers/TarmacTrace.py +++ b/src/arch/arm/tracers/TarmacTrace.py @@ -33,9 +33,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * from m5.objects.InstTracer import InstTracer +from m5.params import * +from m5.SimObject import SimObject class TarmacParser(InstTracer): diff --git a/src/arch/arm/tracers/capstone.cc b/src/arch/arm/tracers/capstone.cc new file mode 100644 index 0000000000..469dc46568 --- /dev/null +++ b/src/arch/arm/tracers/capstone.cc @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023 Arm Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "arch/arm/tracers/capstone.hh" + +#include "arch/arm/insts/static_inst.hh" +#include "base/output.hh" + +namespace gem5 +{ + +namespace trace +{ + +using namespace ArmISA; + +ArmCapstoneDisassembler::ArmCapstoneDisassembler(const Params &p) + : CapstoneDisassembler(p) +{ + if (cs_open(CS_ARCH_ARM64, CS_MODE_ARM, &arm64Handle) != CS_ERR_OK) + panic("Unable to open capstone for arm64 disassembly"); + + if (cs_open(CS_ARCH_ARM, CS_MODE_ARM, &armHandle) != CS_ERR_OK) + panic("Unable to open capstone for arm disassembly"); +} + +const csh* +ArmCapstoneDisassembler::currHandle(const PCStateBase &_pc) const +{ + auto pc = _pc.as(); + if (pc.aarch64()) { + return &arm64Handle; + } else { + auto mode = pc.thumb() ? CS_MODE_THUMB : CS_MODE_ARM; + cs_option(armHandle, CS_OPT_MODE, mode); + return &armHandle; + } +} + +} // namespace trace +} // namespace gem5 diff --git a/src/arch/arm/tracers/capstone.hh b/src/arch/arm/tracers/capstone.hh new file mode 100644 index 0000000000..929fbad6f5 --- /dev/null +++ b/src/arch/arm/tracers/capstone.hh @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023 Arm Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ARCH_ARM_TRACERS_CAPSTONE_HH__ +#define __ARCH_ARM_TRACERS_CAPSTONE_HH__ + +#include "cpu/capstone.hh" +#include "params/ArmCapstoneDisassembler.hh" + +namespace gem5 +{ + +class ThreadContext; + +namespace trace +{ + +class ArmCapstoneDisassembler : public CapstoneDisassembler +{ + public: + PARAMS(ArmCapstoneDisassembler); + ArmCapstoneDisassembler(const Params &p); + + protected: + const csh* currHandle(const PCStateBase &pc) const override; + + protected: + csh arm64Handle; + csh armHandle; +}; + +} // namespace trace +} // namespace gem5 + +#endif // __ARCH_ARM_TRACERS_CAPSTONE_HH__ diff --git a/src/arch/arm/tracers/tarmac_base.cc b/src/arch/arm/tracers/tarmac_base.cc index 99ed3bb0f1..01add3037a 100644 --- a/src/arch/arm/tracers/tarmac_base.cc +++ b/src/arch/arm/tracers/tarmac_base.cc @@ -68,7 +68,6 @@ TarmacBaseRecord::InstEntry::InstEntry( : taken(predicate) , addr(pc.instAddr()) , opcode(staticInst->getEMI() & 0xffffffff), - disassemble(staticInst->disassemble(addr)), isetstate(pcToISetState(pc)), mode(MODE_USER) { @@ -76,11 +75,6 @@ TarmacBaseRecord::InstEntry::InstEntry( // Operating mode gained by reading the architectural register (CPSR) const CPSR cpsr = thread->readMiscRegNoEffect(MISCREG_CPSR); mode = (OperatingMode) (uint8_t)cpsr.mode; - - // In Tarmac, instruction names are printed in capital - // letters. - std::for_each(disassemble.begin(), disassemble.end(), - [](char& c) { c = toupper(c); }); } TarmacBaseRecord::RegEntry::RegEntry(const PCStateBase &pc) @@ -107,12 +101,12 @@ TarmacBaseRecord::pcToISetState(const PCStateBase &pc) if (apc.aarch64()) isetstate = TarmacBaseRecord::ISET_A64; - else if (!apc.thumb() && !apc.jazelle()) + else if (!apc.thumb()) isetstate = TarmacBaseRecord::ISET_ARM; - else if (apc.thumb() && !apc.jazelle()) + else if (apc.thumb()) isetstate = TarmacBaseRecord::ISET_THUMB; else - // No Jazelle state in TARMAC + // Unsupported state in TARMAC isetstate = TarmacBaseRecord::ISET_UNSUPPORTED; return isetstate; diff --git a/src/arch/arm/tracers/tarmac_base.hh b/src/arch/arm/tracers/tarmac_base.hh index 501eb1b008..9e80f6d1f1 100644 --- a/src/arch/arm/tracers/tarmac_base.hh +++ b/src/arch/arm/tracers/tarmac_base.hh @@ -93,7 +93,6 @@ class TarmacBaseRecord : public InstRecord bool taken; Addr addr; ArmISA::MachInst opcode; - std::string disassemble; ISetState isetstate; ArmISA::OperatingMode mode; }; diff --git a/src/arch/arm/tracers/tarmac_record.cc b/src/arch/arm/tracers/tarmac_record.cc index 59d6a18b39..5aa1f7e957 100644 --- a/src/arch/arm/tracers/tarmac_record.cc +++ b/src/arch/arm/tracers/tarmac_record.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 ARM Limited + * Copyright (c) 2017-2019, 2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -123,7 +123,8 @@ TarmacTracerRecord::TarmacTracerRecord(Tick _when, ThreadContext *_thread, TarmacTracerRecord::TraceInstEntry::TraceInstEntry( const TarmacContext& tarmCtx, bool predicate) - : InstEntry(tarmCtx.thread, *tarmCtx.pc, tarmCtx.staticInst, predicate) + : InstEntry(tarmCtx.thread, *tarmCtx.pc, tarmCtx.staticInst, predicate), + disassemble(tarmCtx.tracer.disassemble(tarmCtx.staticInst, *tarmCtx.pc)) { secureMode = isSecure(tarmCtx.thread); @@ -140,6 +141,11 @@ TarmacTracerRecord::TraceInstEntry::TraceInstEntry( // for 16bit (Thumb) instruction. opcode = arm_inst->encoding(); + // In Tarmac, instruction names are printed in capital + // letters. + std::for_each(disassemble.begin(), disassemble.end(), + [](char& c) { c = toupper(c); }); + // Update the instruction count: number of executed // instructions. instCount++; @@ -332,6 +338,7 @@ TarmacTracerRecord::dump() auto ®Queue = tracer.regQueue; const TarmacContext tarmCtx( + tracer, thread, staticInst->isMicroop()? macroStaticInst : staticInst, *pc diff --git a/src/arch/arm/tracers/tarmac_record.hh b/src/arch/arm/tracers/tarmac_record.hh index 009df5db29..d80121b1b9 100644 --- a/src/arch/arm/tracers/tarmac_record.hh +++ b/src/arch/arm/tracers/tarmac_record.hh @@ -115,6 +115,9 @@ class TarmacTracerRecord : public TarmacBaseRecord * 32 otherwise (ARM and BigThumb) */ uint8_t instSize; + + /** Instruction disassembly */ + std::string disassemble; }; /** Register Entry */ diff --git a/src/arch/arm/tracers/tarmac_tracer.hh b/src/arch/arm/tracers/tarmac_tracer.hh index f8c7b5ca53..71207b3860 100644 --- a/src/arch/arm/tracers/tarmac_tracer.hh +++ b/src/arch/arm/tracers/tarmac_tracer.hh @@ -58,6 +58,8 @@ class OutputStream; namespace trace { +class TarmacTracer; + /** * This object type is encapsulating the informations needed by * a Tarmac record to generate it's own entries. @@ -65,15 +67,18 @@ namespace trace { class TarmacContext { public: - TarmacContext(ThreadContext* _thread, + TarmacContext(const TarmacTracer &_tracer, + ThreadContext* _thread, const StaticInstPtr _staticInst, const PCStateBase &_pc) - : thread(_thread), staticInst(_staticInst), pc(_pc.clone()) + : tracer(_tracer), thread(_thread), staticInst(_staticInst), + pc(_pc.clone()) {} std::string tarmacCpuName() const; public: + const TarmacTracer &tracer; ThreadContext* thread; const StaticInstPtr staticInst; std::unique_ptr pc; diff --git a/src/arch/arm/types.hh b/src/arch/arm/types.hh index 2251d57c0b..f7b6cbf86b 100644 --- a/src/arch/arm/types.hh +++ b/src/arch/arm/types.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2012-2013, 2017-2018, 2022 Arm Limited + * Copyright (c) 2010, 2012-2013, 2017-2018, 2022-2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -323,6 +323,7 @@ namespace ArmISA SMC_64 = 0x17, TRAPPED_MSR_MRS_64 = 0x18, TRAPPED_SVE = 0x19, + TRAPPED_ERET = 0x1A, TRAPPED_SME = 0x1D, PREFETCH_ABORT_TO_HYP = 0x20, PREFETCH_ABORT_LOWER_EL = 0x20, // AArch64 alias diff --git a/src/arch/arm/utility.cc b/src/arch/arm/utility.cc index 05d1cab06c..926a7e3343 100644 --- a/src/arch/arm/utility.cc +++ b/src/arch/arm/utility.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2014, 2016-2020, 2022 Arm Limited + * Copyright (c) 2009-2014, 2016-2020, 2022-2023 Arm Limited * All rights reserved. * * The license below extends only to copyright in the software and shall @@ -1347,5 +1347,24 @@ syncVecElemsToRegs(ThreadContext *tc) } } +bool +fgtEnabled(ThreadContext *tc) +{ + return EL2Enabled(tc) && HaveExt(tc, ArmExtension::FEAT_FGT) && + (!ArmSystem::haveEL(tc, EL3) || + static_cast(tc->readMiscReg(MISCREG_SCR_EL3)).fgten); +} + +bool +isHcrxEL2Enabled(ThreadContext *tc) +{ + if (!ArmSystem::has(ArmExtension::FEAT_HCX, tc)) + return false; + if (ArmSystem::haveEL(tc, EL3) && + !static_cast(tc->readMiscReg(MISCREG_SCR_EL3)).hxen) + return false; + return EL2Enabled(tc); +} + } // namespace ArmISA } // namespace gem5 diff --git a/src/arch/arm/utility.hh b/src/arch/arm/utility.hh index b5a5dd72dd..8ccb251fa5 100644 --- a/src/arch/arm/utility.hh +++ b/src/arch/arm/utility.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2012-2013, 2016-2020, 2022 Arm Limited + * Copyright (c) 2010, 2012-2013, 2016-2020, 2022-2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -364,6 +364,15 @@ bool isUnpriviledgeAccess(ThreadContext *tc); void syncVecRegsToElems(ThreadContext *tc); void syncVecElemsToRegs(ThreadContext *tc); +bool fgtEnabled(ThreadContext *tc); +bool isHcrxEL2Enabled(ThreadContext *tc); + +static inline bool +useVMID(ExceptionLevel el, bool in_host) +{ + return el == EL1 || (el == EL0 && !in_host); +} + } // namespace ArmISA } // namespace gem5 diff --git a/src/arch/generic/isa.hh b/src/arch/generic/isa.hh index 58f66fc99b..e9e4d95d7b 100644 --- a/src/arch/generic/isa.hh +++ b/src/arch/generic/isa.hh @@ -70,7 +70,6 @@ class BaseISA : public SimObject public: virtual PCStateBase *newPCState(Addr new_inst_addr=0) const = 0; virtual void clear() {} - virtual void clearLoadReservation(ContextID cid) {} virtual RegVal readMiscRegNoEffect(RegIndex idx) const = 0; virtual RegVal readMiscReg(RegIndex idx) = 0; diff --git a/src/arch/generic/linux/threadinfo.hh b/src/arch/generic/linux/threadinfo.hh index 70511c47fa..8af6d6f1de 100644 --- a/src/arch/generic/linux/threadinfo.hh +++ b/src/arch/generic/linux/threadinfo.hh @@ -60,7 +60,7 @@ class ThreadInfo return false; } - data = TranslatingPortProxy(tc).read(it->address, byteOrder); + data = TranslatingPortProxy(tc).read(it->address(), byteOrder); return true; } diff --git a/src/arch/generic/memhelpers.hh b/src/arch/generic/memhelpers.hh index d5684a6af9..9cdd2a56eb 100644 --- a/src/arch/generic/memhelpers.hh +++ b/src/arch/generic/memhelpers.hh @@ -124,6 +124,24 @@ readMemAtomic(XC *xc, trace::InstRecord *traceData, Addr addr, MemT &mem, return fault; } +/// Read from memory in atomic mode. +template +Fault +readMemAtomic(XC *xc, trace::InstRecord *traceData, Addr addr, MemT &mem, + size_t size, Request::Flags flags) +{ + memset(&mem, 0, size); + static const std::vector byte_enable(size, true); + Fault fault = readMemAtomic(xc, addr, (uint8_t*)&mem, + size, flags, byte_enable); + if (fault == NoFault) { + mem = gtoh(mem, Order); + if (traceData) + traceData->setData(mem); + } + return fault; +} + template Fault readMemAtomicLE(XC *xc, trace::InstRecord *traceData, Addr addr, MemT &mem, @@ -133,6 +151,16 @@ readMemAtomicLE(XC *xc, trace::InstRecord *traceData, Addr addr, MemT &mem, xc, traceData, addr, mem, flags); } +template +Fault +readMemAtomicLE(XC *xc, trace::InstRecord *traceData, Addr addr, MemT &mem, + size_t size, Request::Flags flags) +{ + return readMemAtomic( + xc, traceData, addr, mem, size, flags); +} + + template Fault readMemAtomicBE(XC *xc, trace::InstRecord *traceData, Addr addr, MemT &mem, @@ -165,6 +193,20 @@ writeMemTiming(XC *xc, trace::InstRecord *traceData, MemT mem, Addr addr, sizeof(MemT), flags, res, byte_enable); } +template +Fault +writeMemTiming(XC *xc, trace::InstRecord *traceData, MemT mem, Addr addr, + size_t size, Request::Flags flags, uint64_t *res) +{ + if (traceData) { + traceData->setData(mem); + } + mem = htog(mem, Order); + static const std::vector byte_enable(size, true); + return writeMemTiming(xc, (uint8_t*)&mem, addr, + size, flags, res, byte_enable); +} + template Fault writeMemTimingLE(XC *xc, trace::InstRecord *traceData, MemT mem, Addr addr, @@ -174,6 +216,15 @@ writeMemTimingLE(XC *xc, trace::InstRecord *traceData, MemT mem, Addr addr, xc, traceData, mem, addr, flags, res); } +template +Fault +writeMemTimingLE(XC *xc, trace::InstRecord *traceData, MemT mem, Addr addr, + size_t size, Request::Flags flags, uint64_t *res) +{ + return writeMemTiming( + xc, traceData, mem, addr, size, flags, res); +} + template Fault writeMemTimingBE(XC *xc, trace::InstRecord *traceData, MemT mem, Addr addr, @@ -214,6 +265,27 @@ writeMemAtomic(XC *xc, trace::InstRecord *traceData, const MemT &mem, return fault; } +template +Fault +writeMemAtomic(XC *xc, trace::InstRecord *traceData, const MemT &mem, + Addr addr, size_t size, Request::Flags flags, uint64_t *res) +{ + if (traceData) { + traceData->setData(mem); + } + MemT host_mem = htog(mem, Order); + static const std::vector byte_enable(size, true); + Fault fault = writeMemAtomic(xc, (uint8_t*)&host_mem, + addr, size, flags, res, byte_enable); + if (fault == NoFault && res != NULL) { + if (flags & Request::MEM_SWAP || flags & Request::MEM_SWAP_COND) + *(MemT *)res = gtoh(*(MemT *)res, Order); + else + *res = gtoh(*res, Order); + } + return fault; +} + template Fault writeMemAtomicLE(XC *xc, trace::InstRecord *traceData, const MemT &mem, @@ -223,6 +295,15 @@ writeMemAtomicLE(XC *xc, trace::InstRecord *traceData, const MemT &mem, xc, traceData, mem, addr, flags, res); } +template +Fault +writeMemAtomicLE(XC *xc, trace::InstRecord *traceData, const MemT &mem, + size_t size, Addr addr, Request::Flags flags, uint64_t *res) +{ + return writeMemAtomic( + xc, traceData, mem, addr, size, flags, res); +} + template Fault writeMemAtomicBE(XC *xc, trace::InstRecord *traceData, const MemT &mem, diff --git a/src/arch/generic/pcstate.hh b/src/arch/generic/pcstate.hh index f1df6e7c39..25b3af69ea 100644 --- a/src/arch/generic/pcstate.hh +++ b/src/arch/generic/pcstate.hh @@ -1,5 +1,6 @@ /* * Copyright (c) 2020 ARM Limited + * Copyright (c) 2023 The University of Edinburgh * All rights reserved * * The license below extends only to copyright in the software and shall @@ -126,6 +127,13 @@ class PCStateBase : public Serializable _upc = 0; } + virtual void + set(Addr val) + { + _pc = val; + _upc = 0; + } + virtual void advance() = 0; virtual bool branching() const = 0; @@ -309,6 +317,14 @@ class PCStateWithNext : public PCStateBase _npc == ps._npc && _nupc == ps._nupc; } + void + set(Addr val) override + { + PCStateBase::set(val); + _npc = 0; + _nupc = 1; + } + void serialize(CheckpointOut &cp) const override { @@ -359,9 +375,9 @@ class SimplePCState : public PCStateWithNext * @param val The value to set the PC to. */ void - set(Addr val) + set(Addr val) override { - this->pc(val); + Base::set(val); this->npc(val + InstWidth); }; @@ -402,7 +418,7 @@ class UPCState : public SimplePCState } void - set(Addr val) + set(Addr val) override { Base::set(val); this->upc(0); @@ -473,7 +489,7 @@ class DelaySlotPCState : public SimplePCState void nnpc(Addr val) { _nnpc = val; } void - set(Addr val) + set(Addr val) override { Base::set(val); nnpc(val + 2 * InstWidth); @@ -547,7 +563,7 @@ class DelaySlotUPCState : public DelaySlotPCState } void - set(Addr val) + set(Addr val) override { Base::set(val); this->upc(0); diff --git a/src/arch/isa_parser/isa_parser.py b/src/arch/isa_parser/isa_parser.py index 5be50a11bf..0b74d00f29 100755 --- a/src/arch/isa_parser/isa_parser.py +++ b/src/arch/isa_parser/isa_parser.py @@ -46,6 +46,7 @@ import traceback from types import * from grammar import Grammar + from .operand_list import * from .operand_types import * from .util import * @@ -61,7 +62,7 @@ debug = False labelRE = re.compile(r"(? decoder-g.cc.inc for 'global' outputs def suffixize(self, s, sec): - extn = re.compile("(\.[^\.]+)$") # isolate extension + extn = re.compile(r"(\.[^\.]+)$") # isolate extension if self.namespace: return extn.sub(r"-ns\1.inc", s) # insert some text on either side else: @@ -682,7 +685,7 @@ class ISAParser(Grammar): # is guaranteed to have been written for parse to complete f.write(f'#include "{fn}"\n') - extn = re.compile("(\.[^\.]+)$") + extn = re.compile(r"(\.[^\.]+)$") # instruction constructors splits = self.splits[self.get_file("decoder")] @@ -1231,7 +1234,7 @@ del wrap """ using namespace gem5; StaticInstPtr -%(isa_name)s::Decoder::decodeInst(%(isa_name)s::ExtMachInst machInst) +%(isa_name)s::%(decoder_name)s::decodeInst(%(isa_name)s::ExtMachInst machInst) { using namespace %(namespace)s; """ @@ -1560,9 +1563,9 @@ StaticInstPtr operandsREString = r""" (?]+>\(\s*\))?\[[^\]]+\])?\s*=(?!=)", re.MULTILINE +) # # Munge a somewhat arbitrarily formatted piece of Python code @@ -155,7 +158,7 @@ def backtrace(filename_stack): # -class LineTracker(object): +class LineTracker: def __init__(self, filename, lineno=1): self.filename = filename self.lineno = lineno diff --git a/src/arch/micro_asm.py b/src/arch/micro_asm.py index 1c2183c07a..0329800896 100644 --- a/src/arch/micro_asm.py +++ b/src/arch/micro_asm.py @@ -25,15 +25,17 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import os -import sys import re +import sys import traceback # get type names from types import * -from ply import lex -from ply import yacc +from ply import ( + lex, + yacc, +) ########################################################################## # @@ -88,18 +90,18 @@ class Rom(MicroContainer): ########################################################################## -class Label(object): +class Label: def __init__(self): self.extern = False self.name = "" -class Block(object): +class Block: def __init__(self): self.statements = [] -class Statement(object): +class Statement: def __init__(self): self.is_microop = False self.is_directive = False @@ -187,6 +189,7 @@ def handle_statement(parser, container, statement): # ########################################################################## + # Error handler. Just call exit. Output formatted to work under # Emacs compile-mode. Optional 'print_traceback' arg, if set to True, # prints a Python stack backtrace too (can be handy when trying to @@ -231,6 +234,7 @@ reserved_map = {} for r in reserved: reserved_map[r.lower()] = r + # Ignore comments def t_ANY_COMMENT(t): r"\#[^\n]*(?=\n)" @@ -360,6 +364,7 @@ def t_ANY_error(t): # ########################################################################## + # Start symbol for a file which may have more than one macroop or rom # specification. def p_file(t): @@ -567,7 +572,7 @@ def p_error(t): error(0, "unknown syntax error", True) -class MicroAssembler(object): +class MicroAssembler: def __init__(self, macro_type, microops, rom=None, rom_macroop_type=None): self.lexer = lex.lex() self.parser = yacc.yacc(write_tables=False) diff --git a/src/arch/micro_asm_test.py b/src/arch/micro_asm_test.py index 609b8a4021..29724aa2d7 100755 --- a/src/arch/micro_asm_test.py +++ b/src/arch/micro_asm_test.py @@ -24,20 +24,25 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from micro_asm import MicroAssembler, CombinationalMacroop, RomMacroop, Rom +from micro_asm import ( + CombinationalMacroop, + MicroAssembler, + Rom, + RomMacroop, +) -class Bah(object): +class Bah: def __init__(self): self.mnemonic = "bah" -class Bah_Tweaked(object): +class Bah_Tweaked: def __init__(self): self.mnemonic = "bah_tweaked" -class Hoop(object): +class Hoop: def __init__(self, first_param, second_param): self.mnemonic = f"hoop_{first_param}_{second_param}" @@ -45,7 +50,7 @@ class Hoop(object): return f"{self.mnemonic}" -class Dah(object): +class Dah: def __init__(self): self.mnemonic = "dah" diff --git a/src/arch/arm/SConsopts b/src/arch/mips/Kconfig similarity index 92% rename from src/arch/arm/SConsopts rename to src/arch/mips/Kconfig index f760404957..20d70ded8c 100644 --- a/src/arch/arm/SConsopts +++ b/src/arch/mips/Kconfig @@ -1,4 +1,4 @@ -# Copyright 2021 Google, Inc. +# Copyright 2022 Google LLC # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -23,5 +23,5 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Import('*') -sticky_vars.Add(BoolVariable('USE_ARM_ISA', 'Enable ARM ISA support', False)) +config USE_MIPS_ISA + bool "MIPS ISA support" diff --git a/src/arch/mips/MipsCPU.py b/src/arch/mips/MipsCPU.py index 53b134ad79..558bbf8733 100644 --- a/src/arch/mips/MipsCPU.py +++ b/src/arch/mips/MipsCPU.py @@ -24,14 +24,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.objects.BaseAtomicSimpleCPU import BaseAtomicSimpleCPU -from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU -from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU -from m5.objects.BaseO3CPU import BaseO3CPU from m5.objects.BaseMinorCPU import BaseMinorCPU +from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU +from m5.objects.BaseO3CPU import BaseO3CPU +from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU from m5.objects.MipsDecoder import MipsDecoder -from m5.objects.MipsMMU import MipsMMU from m5.objects.MipsInterrupts import MipsInterrupts from m5.objects.MipsISA import MipsISA +from m5.objects.MipsMMU import MipsMMU class MipsCPU: diff --git a/src/arch/mips/MipsISA.py b/src/arch/mips/MipsISA.py index 01075dd1b0..f3eb85b8ed 100644 --- a/src/arch/mips/MipsISA.py +++ b/src/arch/mips/MipsISA.py @@ -33,11 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.BaseISA import BaseISA from m5.params import * from m5.proxy import * -from m5.objects.BaseISA import BaseISA - class MipsISA(BaseISA): type = "MipsISA" diff --git a/src/arch/mips/MipsSeWorkload.py b/src/arch/mips/MipsSeWorkload.py index b5f20cd892..ba73c112a4 100644 --- a/src/arch/mips/MipsSeWorkload.py +++ b/src/arch/mips/MipsSeWorkload.py @@ -23,9 +23,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * - from m5.objects.Workload import SEWorkload +from m5.params import * class MipsSEWorkload(SEWorkload): diff --git a/src/arch/mips/MipsTLB.py b/src/arch/mips/MipsTLB.py index 2051c662e0..43906f11d3 100644 --- a/src/arch/mips/MipsTLB.py +++ b/src/arch/mips/MipsTLB.py @@ -26,10 +26,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * - from m5.objects.BaseTLB import BaseTLB +from m5.params import * +from m5.SimObject import SimObject class MipsTLB(BaseTLB): diff --git a/src/arch/mips/SConscript b/src/arch/mips/SConscript index 3ef9deceee..05573ae74b 100644 --- a/src/arch/mips/SConscript +++ b/src/arch/mips/SConscript @@ -29,7 +29,7 @@ Import('*') -if env['USE_MIPS_ISA']: +if env['CONF']['USE_MIPS_ISA']: env.TagImplies('mips isa', 'gem5 lib') Source('decoder.cc', tags='mips isa') diff --git a/src/arch/mips/isa/formats/branch.isa b/src/arch/mips/isa/formats/branch.isa index 1b7c1057b6..0da32eb7ca 100644 --- a/src/arch/mips/isa/formats/branch.isa +++ b/src/arch/mips/isa/formats/branch.isa @@ -194,7 +194,7 @@ output decoder {{ loader::SymbolTable::const_iterator it; if (symtab && (it = symtab->find(target)) != symtab->end()) - ss << it->name; + ss << it->name(); else ccprintf(ss, "%#x", target); @@ -214,7 +214,7 @@ output decoder {{ } else if (_numSrcRegs == 0) { loader::SymbolTable::const_iterator it; if (symtab && (it = symtab->find(disp)) != symtab->end()) - ss << it->name; + ss << it->name(); else ccprintf(ss, "0x%x", disp); } else if (_numSrcRegs == 1) { diff --git a/src/arch/null/SConsopts b/src/arch/power/Kconfig similarity index 92% rename from src/arch/null/SConsopts rename to src/arch/power/Kconfig index 2d552a1dc8..f585a1e023 100644 --- a/src/arch/null/SConsopts +++ b/src/arch/power/Kconfig @@ -1,4 +1,4 @@ -# Copyright 2021 Google, Inc. +# Copyright 2022 Google LLC # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -23,5 +23,5 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Import('*') -sticky_vars.Add(BoolVariable('USE_NULL_ISA', 'Enable NULL ISA support', False)) +config USE_POWER_ISA + bool "POWER ISA support" diff --git a/src/arch/power/PowerCPU.py b/src/arch/power/PowerCPU.py index f19c6f6a2f..b634a78c4a 100644 --- a/src/arch/power/PowerCPU.py +++ b/src/arch/power/PowerCPU.py @@ -24,14 +24,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.objects.BaseAtomicSimpleCPU import BaseAtomicSimpleCPU -from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU -from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU -from m5.objects.BaseO3CPU import BaseO3CPU from m5.objects.BaseMinorCPU import BaseMinorCPU +from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU +from m5.objects.BaseO3CPU import BaseO3CPU +from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU from m5.objects.PowerDecoder import PowerDecoder -from m5.objects.PowerMMU import PowerMMU from m5.objects.PowerInterrupts import PowerInterrupts from m5.objects.PowerISA import PowerISA +from m5.objects.PowerMMU import PowerMMU class PowerCPU: diff --git a/src/arch/power/PowerSeWorkload.py b/src/arch/power/PowerSeWorkload.py index 162104d0dd..4a4116be4b 100644 --- a/src/arch/power/PowerSeWorkload.py +++ b/src/arch/power/PowerSeWorkload.py @@ -23,9 +23,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * - from m5.objects.Workload import SEWorkload +from m5.params import * class PowerSEWorkload(SEWorkload): diff --git a/src/arch/power/PowerTLB.py b/src/arch/power/PowerTLB.py index 32c4a68940..9217f24d30 100644 --- a/src/arch/power/PowerTLB.py +++ b/src/arch/power/PowerTLB.py @@ -26,10 +26,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * - from m5.objects.BaseTLB import BaseTLB +from m5.params import * +from m5.SimObject import SimObject class PowerTLB(BaseTLB): diff --git a/src/arch/power/SConscript b/src/arch/power/SConscript index 8ddb96694a..ede15cfd22 100644 --- a/src/arch/power/SConscript +++ b/src/arch/power/SConscript @@ -30,7 +30,7 @@ Import('*') -if env['USE_POWER_ISA']: +if env['CONF']['USE_POWER_ISA']: env.TagImplies('power isa', 'gem5 lib') Source('decoder.cc', tags='power isa') diff --git a/src/arch/power/faults.cc b/src/arch/power/faults.cc index 0d8f2ddd68..77fc8cba96 100644 --- a/src/arch/power/faults.cc +++ b/src/arch/power/faults.cc @@ -42,24 +42,28 @@ namespace PowerISA void UnimplementedOpcodeFault::invoke(ThreadContext *tc, const StaticInstPtr &inst) { - panic_if(tc->getSystemPtr()->trapToGdb(GDBSignal::ILL, tc->contextId()), - "Unimplemented opcode encountered at virtual address %#x\n", - tc->pcState().instAddr()); + if (! tc->getSystemPtr()->trapToGdb(GDBSignal::ILL, tc->contextId()) ) { + panic("Unimplemented opcode encountered at virtual address %#x\n", + tc->pcState().instAddr()); + } } void AlignmentFault::invoke(ThreadContext *tc, const StaticInstPtr &inst) { - panic_if(!tc->getSystemPtr()->trapToGdb(GDBSignal::BUS, tc->contextId()), - "Alignment fault when accessing virtual address %#x\n", vaddr); + if (! tc->getSystemPtr()->trapToGdb(GDBSignal::BUS, tc->contextId()) ) { + panic("Alignment fault when accessing virtual address %#x\n", + vaddr); + } } void TrapFault::invoke(ThreadContext *tc, const StaticInstPtr &inst) { - panic_if(tc->getSystemPtr()->trapToGdb(GDBSignal::TRAP, tc->contextId()), - "Trap encountered at virtual address %#x\n", - tc->pcState().instAddr()); + if (! tc->getSystemPtr()->trapToGdb(GDBSignal::TRAP, tc->contextId()) ) { + panic("Trap encountered at virtual address %#x\n", + tc->pcState().instAddr()); + } } } // namespace PowerISA diff --git a/src/arch/power/insts/branch.cc b/src/arch/power/insts/branch.cc index b68f89bf9b..c422c5d535 100644 --- a/src/arch/power/insts/branch.cc +++ b/src/arch/power/insts/branch.cc @@ -97,7 +97,7 @@ BranchOp::generateDisassembly( loader::SymbolTable::const_iterator it; if (symtab && (it = symtab->find(target)) != symtab->end()) - ss << it->name; + ss << it->name(); else ccprintf(ss, "%#x", target); @@ -149,7 +149,7 @@ BranchDispCondOp::generateDisassembly( loader::SymbolTable::const_iterator it; if (symtab && (it = symtab->find(target)) != symtab->end()) - ss << it->name; + ss << it->name(); else ccprintf(ss, "%#x", target); diff --git a/src/arch/power/process.cc b/src/arch/power/process.cc index d31aca2faf..88b462d7a2 100644 --- a/src/arch/power/process.cc +++ b/src/arch/power/process.cc @@ -122,8 +122,8 @@ PowerProcess::initState() loader::Symbol symbol = sym; // Try to read entry point from function descriptor - if (initVirtMem->tryReadBlob(sym.address, &entry, sizeof(Addr))) - symbol.address = gtoh(entry, byteOrder); + if (initVirtMem->tryReadBlob(sym.address(), &entry, sizeof(Addr))) + symbol.relocate(gtoh(entry, byteOrder)); symbolTable->insert(symbol); } diff --git a/src/arch/mips/SConsopts b/src/arch/riscv/Kconfig similarity index 92% rename from src/arch/mips/SConsopts rename to src/arch/riscv/Kconfig index 6c5061fd5d..7f09731e5e 100644 --- a/src/arch/mips/SConsopts +++ b/src/arch/riscv/Kconfig @@ -1,4 +1,4 @@ -# Copyright 2021 Google, Inc. +# Copyright 2022 Google LLC # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -23,5 +23,5 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Import('*') -sticky_vars.Add(BoolVariable('USE_MIPS_ISA', 'Enable MIPS ISA support', False)) +config USE_RISCV_ISA + bool "RISC-V ISA support" diff --git a/src/arch/riscv/PMAChecker.py b/src/arch/riscv/PMAChecker.py index 581560bd56..c456569b32 100644 --- a/src/arch/riscv/PMAChecker.py +++ b/src/arch/riscv/PMAChecker.py @@ -35,9 +35,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * from m5.proxy import * +from m5.SimObject import SimObject class PMAChecker(SimObject): diff --git a/src/arch/riscv/PMP.py b/src/arch/riscv/PMP.py index a3844c99fd..dc0608d643 100644 --- a/src/arch/riscv/PMP.py +++ b/src/arch/riscv/PMP.py @@ -24,9 +24,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * from m5.proxy import * +from m5.SimObject import SimObject class PMP(SimObject): diff --git a/src/arch/riscv/RiscvCPU.py b/src/arch/riscv/RiscvCPU.py index 1c77045c67..2b1b053b48 100644 --- a/src/arch/riscv/RiscvCPU.py +++ b/src/arch/riscv/RiscvCPU.py @@ -24,14 +24,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.objects.BaseAtomicSimpleCPU import BaseAtomicSimpleCPU -from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU -from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU -from m5.objects.BaseO3CPU import BaseO3CPU from m5.objects.BaseMinorCPU import BaseMinorCPU +from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU +from m5.objects.BaseO3CPU import BaseO3CPU +from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU from m5.objects.RiscvDecoder import RiscvDecoder -from m5.objects.RiscvMMU import RiscvMMU from m5.objects.RiscvInterrupts import RiscvInterrupts from m5.objects.RiscvISA import RiscvISA +from m5.objects.RiscvMMU import RiscvMMU class RiscvCPU: diff --git a/src/arch/riscv/RiscvDecoder.py b/src/arch/riscv/RiscvDecoder.py index 30c1077662..4100a3c5b3 100644 --- a/src/arch/riscv/RiscvDecoder.py +++ b/src/arch/riscv/RiscvDecoder.py @@ -24,6 +24,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.objects.InstDecoder import InstDecoder +from m5.params import * class RiscvDecoder(InstDecoder): diff --git a/src/arch/riscv/RiscvFsWorkload.py b/src/arch/riscv/RiscvFsWorkload.py index 9e158811da..24dff58828 100644 --- a/src/arch/riscv/RiscvFsWorkload.py +++ b/src/arch/riscv/RiscvFsWorkload.py @@ -27,10 +27,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * - from m5.objects.System import System -from m5.objects.Workload import Workload, KernelWorkload +from m5.objects.Workload import ( + KernelWorkload, + Workload, +) +from m5.params import * class RiscvBareMetal(Workload): @@ -52,3 +54,68 @@ class RiscvLinux(KernelWorkload): "", "File that contains the Device Tree Blob. Don't use DTB if empty." ) dtb_addr = Param.Addr(0x87E00000, "DTB address") + + # gem5 event upon guest's kernel panic + # Default to false because when the kernel is compiled into the bootloader + # it will not have symbols + exit_on_kernel_panic = Param.Bool( + False, "Generate gem5 panic upon the guest's kernel panic." + ) + exit_on_kernel_oops = Param.Bool( + False, "Generate gem5 panic upon the guest's kernel oops." + ) + + +class RiscvBootloaderKernelWorkload(Workload): + type = "RiscvBootloaderKernelWorkload" + cxx_class = "gem5::RiscvISA::BootloaderKernelWorkload" + cxx_header = "arch/riscv/linux/fs_workload.hh" + + bootloader_filename = Param.String( + "", "File that contains the bootloader. Don't use bootloader if empty." + ) + bootloader_addr = Param.Addr( + 0x0, "Where to place the bootloader in memory." + ) + object_file = Param.String("", "vmlinux file. Don't use kernel if empty.") + kernel_addr = Param.Addr( + 0x80200000, + "Where to place the kernel in memory. Typically, after the first " + "stage of booting is done, the bootloader will jump to where the " + "`start` symbol of the kernel is.", + ) + entry_point = Param.Addr( + 0x80000000, "Where to find the first instruction to execute." + ) + dtb_filename = Param.String( + "", "File that contains the Device Tree Blob. Don't use DTB if empty." + ) + dtb_addr = Param.Addr(0x87E00000, "Where to place the DTB in memory.") + + # booting parameters + command_line = Param.String( + "", "Booting arguments, to be passed to the kernel." + ) + + # gem5 event upon guest's kernel panic + # Note that if the kernel doesn't have symbols there will be a warning and + # gem5 will not exit + exit_on_kernel_panic = Param.Bool( + True, "Generate gem5 exit upon the guest's kernel panic." + ) + exit_on_kernel_oops = Param.Bool( + False, "Generate gem5 exit upon the guest's kernel oops." + ) + + # Note: Duplicated from KernelWorkload for now + on_panic = Param.KernelPanicOopsBehaviour( + "DumpDmesgAndExit", + "Define how gem5 should behave after a Linux Kernel Panic. " + "Handler might not be implemented for all architectures.", + ) + + on_oops = Param.KernelPanicOopsBehaviour( + "DumpDmesgAndExit", + "Define how gem5 should behave after a Linux Kernel Oops. " + "Handler might not be implemented for all architectures.", + ) diff --git a/src/arch/riscv/RiscvISA.py b/src/arch/riscv/RiscvISA.py index e2381fd158..0b0e8ce5a9 100644 --- a/src/arch/riscv/RiscvISA.py +++ b/src/arch/riscv/RiscvISA.py @@ -13,6 +13,7 @@ # # Copyright (c) 2016 RISC-V Foundation # Copyright (c) 2016 The University of Virginia +# Copyright (c) 2023 The Regents of the University of California # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -38,21 +39,108 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import Enum -from m5.params import Param from m5.objects.BaseISA import BaseISA +from m5.params import ( + Enum, + Param, + UInt32, +) + + +class RiscvVectorLength(UInt32): + min = 8 + max = 65536 + + def _check(self): + super()._check() + + # VLEN needs to be a whole power of 2. We already know value is + # not zero. Hence: + if self.value & (self.value - 1) != 0: + raise TypeError("VLEN is not a power of 2: %d" % self.value) + + +class RiscvVectorElementLength(UInt32): + min = 8 + max = 64 + + def _check(self): + super()._check() + + # ELEN needs to be a whole power of 2. We already know value is + # not zero. Hence: + if self.value & (self.value - 1) != 0: + raise TypeError("ELEN is not a power of 2: %d" % self.value) class RiscvType(Enum): vals = ["RV32", "RV64"] +class PrivilegeModeSet(Enum): + vals = [ + "M", # Machine privilege mode only + "MU", # Machine and user privlege modes implemented + "MNU", # MU privilege modes with user-mode trap + "MSU", # Machine, supervisor and user modes implemented + "MNSU", # MSU privilege modes with user-mode trap + ] + + class RiscvISA(BaseISA): type = "RiscvISA" cxx_class = "gem5::RiscvISA::ISA" cxx_header = "arch/riscv/isa.hh" check_alignment = Param.Bool( - False, "whether to check memory access alignment" + True, "whether to check memory access alignment" ) riscv_type = Param.RiscvType("RV64", "RV32 or RV64") + + enable_rvv = Param.Bool(True, "Enable vector extension") + vlen = Param.RiscvVectorLength( + 256, + "Length of each vector register in bits. \ + VLEN in Ch. 2 of RISC-V vector spec", + ) + elen = Param.RiscvVectorElementLength( + 64, + "Length of each vector element in bits. \ + ELEN in Ch. 2 of RISC-V vector spec", + ) + privilege_mode_set = Param.PrivilegeModeSet( + "MSU", + "The combination of privilege modes \ + in Privilege Levels section of RISC-V privileged spec", + ) + + enable_Zicbom_fs = Param.Bool(True, "Enable Zicbom extension in FS mode") + enable_Zicboz_fs = Param.Bool(True, "Enable Zicboz extension in FS mode") + + def get_isa_string(self): + isa_extensions = [] + # check for the base ISA type + if self.riscv_type.value == "RV32": + isa_extensions.append("rv32") + elif self.riscv_type.value == "RV64": + isa_extensions.append("rv64") + # use imafdc by default + isa_extensions.extend(["i", "m", "a", "f", "d", "c"]) + # check for the vector extension + if self.enable_rvv.value == True: + isa_extensions.append("v") + isa_string = "".join(isa_extensions) + + if self.enable_Zicbom_fs.value: + isa_string += "_Zicbom" # Cache-block Management Instructions + if self.enable_Zicboz_fs.value: + isa_string += "_Zicboz" # Cache-block Zero Instruction + isa_string += "_Zicntr" # Performance Couter Spec + isa_string += "_Zicsr" # RMW CSR Instructions (Privileged Spec) + isa_string += "_Zifencei" # FENCE.I Instruction (Unprivileged Spec) + isa_string += "_Zihpm" # Performance Couter Spec + isa_string += "_Zba" # Address Generation + isa_string += "_Zbb" # Basic Bit Manipulation + isa_string += "_Zbs" # Single-bit Instructions + + return isa_string diff --git a/src/arch/riscv/RiscvMMU.py b/src/arch/riscv/RiscvMMU.py index 312244a85d..6ef4182c88 100644 --- a/src/arch/riscv/RiscvMMU.py +++ b/src/arch/riscv/RiscvMMU.py @@ -35,12 +35,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * - from m5.objects.BaseMMU import BaseMMU -from m5.objects.RiscvTLB import RiscvTLB from m5.objects.PMAChecker import PMAChecker from m5.objects.PMP import PMP +from m5.objects.RiscvTLB import RiscvTLB +from m5.params import * class RiscvMMU(BaseMMU): diff --git a/src/arch/riscv/RiscvSeWorkload.py b/src/arch/riscv/RiscvSeWorkload.py index 5df6b786c3..45aa79ed5b 100644 --- a/src/arch/riscv/RiscvSeWorkload.py +++ b/src/arch/riscv/RiscvSeWorkload.py @@ -23,9 +23,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * - from m5.objects.Workload import SEWorkload +from m5.params import * class RiscvSEWorkload(SEWorkload): diff --git a/src/arch/riscv/RiscvTLB.py b/src/arch/riscv/RiscvTLB.py index e943d8ddab..05a1c71b19 100644 --- a/src/arch/riscv/RiscvTLB.py +++ b/src/arch/riscv/RiscvTLB.py @@ -28,11 +28,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * - from m5.objects.BaseTLB import BaseTLB from m5.objects.ClockedObject import ClockedObject +from m5.params import * +from m5.proxy import * class RiscvPagetableWalker(ClockedObject): diff --git a/src/arch/riscv/SConscript b/src/arch/riscv/SConscript index 924bba5915..41da97bcc1 100644 --- a/src/arch/riscv/SConscript +++ b/src/arch/riscv/SConscript @@ -43,7 +43,7 @@ Import('*') -if env['USE_RISCV_ISA']: +if env['CONF']['USE_RISCV_ISA']: env.TagImplies('riscv isa', 'gem5 lib') Source('decoder.cc', tags='riscv isa') @@ -66,12 +66,14 @@ Source('bare_metal/fs_workload.cc', tags='riscv isa') SimObject('PMAChecker.py', sim_objects=['PMAChecker'], tags='riscv isa') SimObject('PMP.py', sim_objects=['PMP'], tags='riscv isa') SimObject('RiscvDecoder.py', sim_objects=['RiscvDecoder'], tags='riscv isa') -SimObject('RiscvFsWorkload.py', sim_objects=['RiscvBareMetal', 'RiscvLinux'], +SimObject('RiscvFsWorkload.py', + sim_objects=['RiscvBareMetal', 'RiscvLinux', + 'RiscvBootloaderKernelWorkload'], tags='riscv isa') SimObject('RiscvInterrupts.py', sim_objects=['RiscvInterrupts'], tags='riscv isa') SimObject('RiscvISA.py', sim_objects=['RiscvISA'], - enums=['RiscvType'], tags='riscv isa') + enums=['RiscvType', 'PrivilegeModeSet'], tags='riscv isa') SimObject('RiscvMMU.py', sim_objects=['RiscvMMU'], tags='riscv isa') SimObject('RiscvSeWorkload.py', sim_objects=[ 'RiscvSEWorkload', 'RiscvEmuLinux'], tags='riscv isa') diff --git a/src/arch/riscv/SConsopts b/src/arch/riscv/SConsopts deleted file mode 100644 index 751311de5c..0000000000 --- a/src/arch/riscv/SConsopts +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2021 Google, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Import('*') -sticky_vars.Add(BoolVariable('USE_RISCV_ISA', 'Enable RISC-V ISA support', - False)) diff --git a/src/arch/riscv/decoder.cc b/src/arch/riscv/decoder.cc index 7faa310b1e..ee5d313587 100644 --- a/src/arch/riscv/decoder.cc +++ b/src/arch/riscv/decoder.cc @@ -28,6 +28,7 @@ */ #include "arch/riscv/decoder.hh" +#include "arch/riscv/isa.hh" #include "arch/riscv/types.hh" #include "base/bitfield.hh" #include "debug/Decode.hh" @@ -38,6 +39,14 @@ namespace gem5 namespace RiscvISA { +Decoder::Decoder(const RiscvDecoderParams &p) : InstDecoder(p, &machInst) +{ + ISA *isa = dynamic_cast(p.isa); + vlen = isa->getVecLenInBits(); + elen = isa->getVecElemLenInBits(); + reset(); +} + void Decoder::reset() { aligned = true; @@ -90,6 +99,8 @@ Decoder::decode(ExtMachInst mach_inst, Addr addr) if (!si) si = decodeInst(mach_inst); + si->size(compressed(mach_inst) ? 2 : 4); + DPRINTF(Decode, "Decode: Decoded %s instruction: %#x\n", si->getName(), mach_inst); return si; @@ -112,7 +123,11 @@ Decoder::decode(PCStateBase &_next_pc) next_pc.compressed(false); } + emi.vl = next_pc.vl(); + emi.vtype8 = next_pc.vtype() & 0xff; + emi.vill = next_pc.vtype().vill; emi.rv_type = static_cast(next_pc.rvType()); + return decode(emi, next_pc.instAddr()); } diff --git a/src/arch/riscv/decoder.hh b/src/arch/riscv/decoder.hh index 15cbefe39c..bf863fda22 100644 --- a/src/arch/riscv/decoder.hh +++ b/src/arch/riscv/decoder.hh @@ -32,6 +32,7 @@ #include "arch/generic/decode_cache.hh" #include "arch/generic/decoder.hh" +#include "arch/riscv/insts/vector.hh" #include "arch/riscv/types.hh" #include "base/logging.hh" #include "base/types.hh" @@ -59,7 +60,10 @@ class Decoder : public InstDecoder ExtMachInst emi; uint32_t machInst; - StaticInstPtr decodeInst(ExtMachInst mach_inst); + uint32_t vlen; + uint32_t elen; + + virtual StaticInstPtr decodeInst(ExtMachInst mach_inst); /// Decode a machine instruction. /// @param mach_inst The binary instruction to decode. @@ -67,14 +71,11 @@ class Decoder : public InstDecoder StaticInstPtr decode(ExtMachInst mach_inst, Addr addr); public: - Decoder(const RiscvDecoderParams &p) : InstDecoder(p, &machInst) - { - reset(); - } + Decoder(const RiscvDecoderParams &p); void reset() override; - inline bool compressed(ExtMachInst inst) { return (inst & 0x3) < 0x3; } + inline bool compressed(ExtMachInst inst) { return inst.quadRant < 0x3; } //Use this to give data to the decoder. This should be used //when there is control flow. diff --git a/src/arch/riscv/faults.cc b/src/arch/riscv/faults.cc index 8fb8f81261..2e583e3680 100644 --- a/src/arch/riscv/faults.cc +++ b/src/arch/riscv/faults.cc @@ -67,6 +67,7 @@ RiscvFault::invoke(ThreadContext *tc, const StaticInstPtr &inst) if (FullSystem) { PrivilegeMode pp = (PrivilegeMode)tc->readMiscReg(MISCREG_PRV); PrivilegeMode prv = PRV_M; + MISA misa = tc->readMiscRegNoEffect(MISCREG_ISA); STATUS status = tc->readMiscReg(MISCREG_STATUS); // According to riscv-privileged-v1.11, if a NMI occurs at the middle @@ -82,18 +83,18 @@ RiscvFault::invoke(ThreadContext *tc, const StaticInstPtr &inst) } else if (isInterrupt()) { if (pp != PRV_M && bits(tc->readMiscReg(MISCREG_MIDELEG), _code) != 0) { - prv = PRV_S; + prv = (misa.rvs) ? PRV_S : ((misa.rvn) ? PRV_U : PRV_M); } - if (pp == PRV_U && + if (pp == PRV_U && misa.rvs && misa.rvn && bits(tc->readMiscReg(MISCREG_SIDELEG), _code) != 0) { prv = PRV_U; } } else { if (pp != PRV_M && bits(tc->readMiscReg(MISCREG_MEDELEG), _code) != 0) { - prv = PRV_S; + prv = (misa.rvs) ? PRV_S : ((misa.rvn) ? PRV_U : PRV_M); } - if (pp == PRV_U && + if (pp == PRV_U && misa.rvs && misa.rvn && bits(tc->readMiscReg(MISCREG_SEDELEG), _code) != 0) { prv = PRV_U; } @@ -154,17 +155,14 @@ RiscvFault::invoke(ThreadContext *tc, const StaticInstPtr &inst) } // Clear load reservation address - tc->getIsaPtr()->clearLoadReservation(tc->contextId()); + auto isa = static_cast(tc->getIsaPtr()); + isa->clearLoadReservation(tc->contextId()); // Set PC to fault handler address - Addr addr = mbits(tc->readMiscReg(tvec), 63, 2); - if (isInterrupt() && bits(tc->readMiscReg(tvec), 1, 0) == 1) - addr += 4 * _code; + Addr addr = isa->getFaultHandlerAddr(tvec, _code, isInterrupt()); pc_state.set(addr); tc->pcState(pc_state); } else { - inst->advancePC(pc_state); - tc->pcState(pc_state); invokeSE(tc, inst); } } @@ -184,6 +182,10 @@ Reset::invoke(ThreadContext *tc, const StaticInstPtr &inst) std::unique_ptr new_pc(dynamic_cast( tc->getIsaPtr()->newPCState(workload->getEntry()))); panic_if(!new_pc, "Failed create new PCState from ISA pointer"); + VTYPE vtype = 0; + vtype.vill = 1; + new_pc->vtype(vtype); + new_pc->vl(0); tc->pcState(*new_pc); // Reset PMP Cfg @@ -206,9 +208,11 @@ UnknownInstFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst) void IllegalInstFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst) { - auto *rsi = static_cast(inst.get()); - panic("Illegal instruction 0x%08x at pc %s: %s", rsi->machInst, - tc->pcState(), reason.c_str()); + if (! tc->getSystemPtr()->trapToGdb(GDBSignal::ILL, tc->contextId()) ) { + auto *rsi = static_cast(inst.get()); + panic("Illegal instruction 0x%08x at pc %s: %s", rsi->machInst, + tc->pcState(), reason.c_str()); + } } void @@ -227,12 +231,20 @@ IllegalFrmFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst) void BreakpointFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst) { - schedRelBreak(0); + if (! tc->getSystemPtr()->trapToGdb(GDBSignal::TRAP, tc->contextId()) ) { + schedRelBreak(0); + } } void SyscallFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst) { + /* Advance the PC to next instruction so - once (simulated) syscall + is executed - execution continues. */ + auto pc_state = tc->pcState().as(); + inst->advancePC(pc_state); + tc->pcState(pc_state); + tc->getSystemPtr()->workload->syscall(tc); } diff --git a/src/arch/riscv/faults.hh b/src/arch/riscv/faults.hh index f687fd6f20..fa67e3b34c 100644 --- a/src/arch/riscv/faults.hh +++ b/src/arch/riscv/faults.hh @@ -173,7 +173,7 @@ class InstFault : public RiscvFault : RiscvFault(n, FaultType::OTHERS, INST_ILLEGAL), _inst(inst) {} - RegVal trap_value() const override { return bits(_inst, 31, 0); } + RegVal trap_value() const override { return _inst.instBits; } }; class UnknownInstFault : public InstFault diff --git a/src/arch/riscv/insts/SConscript b/src/arch/riscv/insts/SConscript index 704152c040..2822cf86b4 100644 --- a/src/arch/riscv/insts/SConscript +++ b/src/arch/riscv/insts/SConscript @@ -33,3 +33,4 @@ Source('compressed.cc', tags='riscv isa') Source('mem.cc', tags='riscv isa') Source('standard.cc', tags='riscv isa') Source('static_inst.cc', tags='riscv isa') +Source('vector.cc', tags='riscv isa') diff --git a/src/arch/riscv/insts/mem.cc b/src/arch/riscv/insts/mem.cc index 5f58a68a57..8ebda7406d 100644 --- a/src/arch/riscv/insts/mem.cc +++ b/src/arch/riscv/insts/mem.cc @@ -55,8 +55,13 @@ std::string Store::generateDisassembly(Addr pc, const loader::SymbolTable *symtab) const { std::stringstream ss; - ss << mnemonic << ' ' << registerName(srcRegIdx(1)) << ", " << - offset << '(' << registerName(srcRegIdx(0)) << ')'; + if (_numSrcRegs == 1) { + ss << mnemonic << ' ' << offset << '(' << registerName(srcRegIdx(0)) + << ")"; + } else { + ss << mnemonic << ' ' << registerName(srcRegIdx(1)) << ", " << + offset << '(' << registerName(srcRegIdx(0)) << ')'; + } return ss.str(); } diff --git a/src/arch/riscv/insts/static_inst.hh b/src/arch/riscv/insts/static_inst.hh index f835713505..2e4d94864a 100644 --- a/src/arch/riscv/insts/static_inst.hh +++ b/src/arch/riscv/insts/static_inst.hh @@ -33,6 +33,7 @@ #include #include "arch/riscv/pcstate.hh" +#include "arch/riscv/regs/misc.hh" #include "arch/riscv/types.hh" #include "cpu/exec_context.hh" #include "cpu/static_inst.hh" @@ -145,6 +146,15 @@ class RiscvMacroInst : public RiscvStaticInst { panic("Tried to execute a macroop directly!\n"); } + + void size(size_t newSize) override + { + for (int i = 0; i < microops.size(); i++) { + microops[i]->size(newSize); + } + _size = newSize; + } + }; /** diff --git a/src/arch/riscv/insts/vector.cc b/src/arch/riscv/insts/vector.cc new file mode 100644 index 0000000000..7f17bb055e --- /dev/null +++ b/src/arch/riscv/insts/vector.cc @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2022 PLCT Lab + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "arch/riscv/insts/vector.hh" + +#include +#include + +#include "arch/riscv/insts/static_inst.hh" +#include "arch/riscv/isa.hh" +#include "arch/riscv/regs/misc.hh" +#include "arch/riscv/regs/vector.hh" +#include "arch/riscv/utility.hh" +#include "cpu/static_inst.hh" + +namespace gem5 +{ + +namespace RiscvISA +{ + +/** + * This function translates the 3-bit value of vlmul bits to the corresponding + * lmul value as specified in RVV 1.0 spec p11-12 chapter 3.4.2. + * + * I.e., + * vlmul = -3 -> LMUL = 1/8 + * vlmul = -2 -> LMUL = 1/4 + * vlmul = -1 -> LMUL = 1/2 + * vlmul = 0 -> LMUL = 1 + * vlmul = 1 -> LMUL = 2 + * vlmul = 2 -> LMUL = 4 + * vlmul = 3 -> LMUL = 8 + * +**/ +float +getVflmul(uint32_t vlmul_encoding) +{ + int vlmul = sext<3>(vlmul_encoding & 7); + float vflmul = vlmul >= 0 ? 1 << vlmul : 1.0 / (1 << -vlmul); + return vflmul; +} + +uint32_t +getVlmax(VTYPE vtype, uint32_t vlen) +{ + uint32_t sew = getSew(vtype.vsew); + // vlmax is defined in RVV 1.0 spec p12 chapter 3.4.2. + uint32_t vlmax = (vlen/sew) * getVflmul(vtype.vlmul); + return vlmax; +} + +std::string +VConfOp::generateDisassembly(Addr pc, const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", "; + if (bit31 && bit30 == 0) { + ss << registerName(srcRegIdx(0)) << ", " << registerName(srcRegIdx(1)); + } else if (bit31 && bit30) { + ss << uimm << ", " << generateZimmDisassembly(); + } else { + ss << registerName(srcRegIdx(0)) << ", " << generateZimmDisassembly(); + } + return ss.str(); +} + +std::string +VConfOp::generateZimmDisassembly() const +{ + std::stringstream s; + + // VSETIVLI uses ZIMM10 and VSETVLI uses ZIMM11 + uint64_t zimm = (bit31 && bit30) ? zimm10 : zimm11; + + bool frac_lmul = bits(zimm, 2); + int sew = 1 << (bits(zimm, 5, 3) + 3); + int lmul = bits(zimm, 1, 0); + auto vta = bits(zimm, 6) == 1 ? "ta" : "tu"; + auto vma = bits(zimm, 7) == 1 ? "ma" : "mu"; + s << "e" << sew; + if (frac_lmul) { + std::string lmul_str = ""; + switch(lmul){ + case 3: + lmul_str = "f2"; + break; + case 2: + lmul_str = "f4"; + break; + case 1: + lmul_str = "f8"; + break; + default: + panic("Unsupport fractional LMUL"); + } + s << ", m" << lmul_str; + } else { + s << ", m" << (1 << lmul); + } + s << ", " << vta << ", " << vma; + return s.str(); +} + +std::string +VectorNonSplitInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", " + << registerName(srcRegIdx(0)); + if (machInst.vm == 0) ss << ", v0.t"; + return ss.str(); +} + +std::string VectorArithMicroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", "; + if (machInst.funct3 == 0x3) { + // OPIVI + ss << registerName(srcRegIdx(0)) << ", " << machInst.vecimm; + } else { + ss << registerName(srcRegIdx(1)) << ", " << registerName(srcRegIdx(0)); + } + if (machInst.vm == 0) ss << ", v0.t"; + return ss.str(); +} + +std::string VectorArithMacroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", "; + if (machInst.funct3 == 0x3) { + // OPIVI + ss << registerName(srcRegIdx(0)) << ", " << machInst.vecimm; + } else { + ss << registerName(srcRegIdx(1)) << ", " << registerName(srcRegIdx(0)); + } + if (machInst.vm == 0) ss << ", v0.t"; + return ss.str(); +} + +std::string VectorVMUNARY0MicroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)); + if (machInst.vm == 0) ss << ", v0.t"; + return ss.str(); +} + +std::string VectorVMUNARY0MacroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)); + if (machInst.vm == 0) ss << ", v0.t"; + return ss.str(); +} + +std::string VectorSlideMicroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", "; + if (machInst.funct3 == 0x3) { + ss << registerName(srcRegIdx(0)) << ", " << machInst.vecimm; + } else { + ss << registerName(srcRegIdx(1)) << ", " << registerName(srcRegIdx(0)); + } + if (machInst.vm == 0) ss << ", v0.t"; + return ss.str(); +} + +std::string VectorSlideMacroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", "; + if (machInst.funct3 == 0x3) { + ss << registerName(srcRegIdx(0)) << ", " << machInst.vecimm; + } else { + ss << registerName(srcRegIdx(1)) << ", " << registerName(srcRegIdx(0)); + } + if (machInst.vm == 0) ss << ", v0.t"; + return ss.str(); +} + +std::string VleMicroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + unsigned vlenb = vlen >> 3; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", " + << vlenb * microIdx << '(' << registerName(srcRegIdx(0)) << ')' << ", " + << registerName(srcRegIdx(1)); + if (!machInst.vm) ss << ", v0.t"; + return ss.str(); +} + +std::string VlWholeMicroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + unsigned vlenb = vlen >> 3; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", " + << vlenb * microIdx << '(' << registerName(srcRegIdx(0)) << ')'; + return ss.str(); +} + +std::string VseMicroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + unsigned vlenb = vlen >> 3; + ss << mnemonic << ' ' << registerName(srcRegIdx(1)) << ", " + << vlenb * microIdx << '(' << registerName(srcRegIdx(0)) << ')'; + if (!machInst.vm) ss << ", v0.t"; + return ss.str(); +} + +std::string VsWholeMicroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + unsigned vlenb = vlen >> 3; + ss << mnemonic << ' ' << registerName(srcRegIdx(1)) << ", " + << vlenb * microIdx << '(' << registerName(srcRegIdx(0)) << ')'; + return ss.str(); +} + +std::string VleMacroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", " << + '(' << registerName(srcRegIdx(0)) << ')'; + if (!machInst.vm) ss << ", v0.t"; + return ss.str(); +} + +std::string VlWholeMacroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", " << + '(' << registerName(srcRegIdx(0)) << ')'; + return ss.str(); +} + +std::string VseMacroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(srcRegIdx(1)) << ", " << + '(' << registerName(srcRegIdx(0)) << ')'; + if (!machInst.vm) ss << ", v0.t"; + return ss.str(); +} + +std::string VsWholeMacroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(srcRegIdx(1)) << ", " << + '(' << registerName(srcRegIdx(0)) << ')'; + return ss.str(); +} + +std::string VlStrideMacroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", " << + '(' << registerName(srcRegIdx(0)) << ')' << + ", " << registerName(srcRegIdx(1)); + if (!machInst.vm) ss << ", v0.t"; + return ss.str(); +} + +std::string VlStrideMicroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", " << + '(' << registerName(srcRegIdx(0)) << ')' << + ", "<< registerName(srcRegIdx(1)); + if (microIdx != 0 || machInst.vtype8.vma == 0 || machInst.vtype8.vta == 0) + ss << ", " << registerName(srcRegIdx(2)); + if (!machInst.vm) ss << ", v0.t"; + return ss.str(); +} + +std::string VsStrideMacroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(srcRegIdx(2)) << ", " << + '(' << registerName(srcRegIdx(0)) << ')' << + ", " << registerName(srcRegIdx(1)); + if (!machInst.vm) ss << ", v0.t"; + return ss.str(); +} + +std::string VsStrideMicroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(srcRegIdx(2)) << ", " << + '(' << registerName(srcRegIdx(0)) << ')' << + ", "<< registerName(srcRegIdx(1)); + if (microIdx != 0 || machInst.vtype8.vma == 0 || machInst.vtype8.vta == 0) + ss << ", " << registerName(srcRegIdx(2)); + if (!machInst.vm) ss << ", v0.t"; + return ss.str(); +} + +std::string VlIndexMacroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", " + << '(' << registerName(srcRegIdx(0)) << ")," + << registerName(srcRegIdx(1)); + if (!machInst.vm) ss << ", v0.t"; + return ss.str(); +} + +std::string VlIndexMicroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' + << registerName(destRegIdx(0)) << "[" << uint16_t(vdElemIdx) << "], " + << '(' << registerName(srcRegIdx(0)) << "), " + << registerName(srcRegIdx(1)) << "[" << uint16_t(vs2ElemIdx) << "]"; + if (microIdx != 0 || machInst.vtype8.vma == 0 || machInst.vtype8.vta == 0) + ss << ", " << registerName(srcRegIdx(2)); + if (!machInst.vm) ss << ", v0.t"; + return ss.str(); +} + +std::string VsIndexMacroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(srcRegIdx(2)) << ", " + << '(' << registerName(srcRegIdx(0)) << ")," + << registerName(srcRegIdx(1)); + if (!machInst.vm) ss << ", v0.t"; + return ss.str(); +} + +std::string VsIndexMicroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' + << registerName(srcRegIdx(2)) << "[" << uint16_t(vs3ElemIdx) << "], " + << '(' << registerName(srcRegIdx(0)) << "), " + << registerName(srcRegIdx(1)) << "[" << uint16_t(vs2ElemIdx) << "]"; + if (!machInst.vm) ss << ", v0.t"; + return ss.str(); +} + +std::string +VMvWholeMacroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", " << + registerName(srcRegIdx(1)); + return ss.str(); +} + +std::string +VMvWholeMicroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", " << + registerName(srcRegIdx(1)); + return ss.str(); +} + +VMaskMergeMicroInst::VMaskMergeMicroInst(ExtMachInst extMachInst, + uint8_t _dstReg, uint8_t _numSrcs, uint32_t _vlen, size_t _elemSize) + : VectorArithMicroInst("vmask_mv_micro", extMachInst, + VectorIntegerArithOp, 0, 0), + vlen(_vlen), + elemSize(_elemSize) +{ + setRegIdxArrays( + reinterpret_cast( + &std::remove_pointer_t::srcRegIdxArr), + reinterpret_cast( + &std::remove_pointer_t::destRegIdxArr)); + + _numSrcRegs = 0; + _numDestRegs = 0; + + setDestRegIdx(_numDestRegs++, vecRegClass[_dstReg]); + _numTypedDestRegs[VecRegClass]++; + for (uint8_t i=0; i<_numSrcs; i++) { + setSrcRegIdx(_numSrcRegs++, vecRegClass[VecMemInternalReg0 + i]); + } +} + +Fault +VMaskMergeMicroInst::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + vreg_t& tmp_d0 = *(vreg_t *)xc->getWritableRegOperand(this, 0); + PCStateBase *pc_ptr = xc->tcBase()->pcState().clone(); + auto Vd = tmp_d0.as(); + uint32_t vlenb = pc_ptr->as().vlenb(); + const uint32_t elems_per_vreg = vlenb / elemSize; + size_t bit_cnt = elems_per_vreg; + vreg_t tmp_s; + xc->getRegOperand(this, 0, &tmp_s); + auto s = tmp_s.as(); + // cp the first result and tail + memcpy(Vd, s, vlenb); + for (uint8_t i = 1; i < this->_numSrcRegs; i++) { + xc->getRegOperand(this, i, &tmp_s); + s = tmp_s.as(); + if (elems_per_vreg < 8) { + const uint32_t m = (1 << elems_per_vreg) - 1; + const uint32_t mask = m << (i * elems_per_vreg % 8); + // clr & ext bits + Vd[bit_cnt/8] ^= Vd[bit_cnt/8] & mask; + Vd[bit_cnt/8] |= s[bit_cnt/8] & mask; + bit_cnt += elems_per_vreg; + } else { + const uint32_t byte_offset = elems_per_vreg / 8; + memcpy(Vd + i * byte_offset, s + i * byte_offset, byte_offset); + } + } + if (traceData) + traceData->setData(vecRegClass, &tmp_d0); + return NoFault; +} + +std::string +VMaskMergeMicroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)); + for (uint8_t i = 0; i < this->_numSrcRegs; i++) { + ss << ", " << registerName(srcRegIdx(i)); + } + unsigned vlenb = vlen >> 3; + ss << ", offset:" << vlenb / elemSize; + return ss.str(); +} + +Fault +VxsatMicroInst::execute(ExecContext* xc, trace::InstRecord* traceData) const +{ + xc->setMiscReg(MISCREG_VXSAT, *vxsat); + auto vcsr = xc->readMiscReg(MISCREG_VCSR); + xc->setMiscReg(MISCREG_VCSR, ((vcsr&~1)|*vxsat)); + return NoFault; +} + +std::string +VxsatMicroInst::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << "VXSAT" << ", " << (*vxsat ? "0x1" : "0x0"); + return ss.str(); +} + +} // namespace RiscvISA +} // namespace gem5 diff --git a/src/arch/riscv/insts/vector.hh b/src/arch/riscv/insts/vector.hh new file mode 100644 index 0000000000..fb36f0809d --- /dev/null +++ b/src/arch/riscv/insts/vector.hh @@ -0,0 +1,579 @@ +/* + * Copyright (c) 2022 PLCT Lab + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ARCH_RISCV_INSTS_VECTOR_HH__ +#define __ARCH_RISCV_INSTS_VECTOR_HH__ + +#include + +#include "arch/riscv/insts/static_inst.hh" +#include "arch/riscv/isa.hh" +#include "arch/riscv/regs/misc.hh" +#include "arch/riscv/utility.hh" +#include "cpu/exec_context.hh" +#include "cpu/static_inst.hh" + +namespace gem5 +{ + +namespace RiscvISA +{ + +float +getVflmul(uint32_t vlmul_encoding); + +inline uint32_t +getSew(uint32_t vsew) +{ + assert(vsew <= 3); + return (8 << vsew); +} + +uint32_t +getVlmax(VTYPE vtype, uint32_t vlen); + +/** + * Base class for Vector Config operations + */ +class VConfOp : public RiscvStaticInst +{ + protected: + uint64_t bit30; + uint64_t bit31; + uint64_t zimm10; + uint64_t zimm11; + uint64_t uimm; + uint32_t elen; + VConfOp(const char *mnem, ExtMachInst _extMachInst, + uint32_t _elen, OpClass __opClass) + : RiscvStaticInst(mnem, _extMachInst, __opClass), + bit30(_extMachInst.bit30), bit31(_extMachInst.bit31), + zimm10(_extMachInst.zimm_vsetivli), + zimm11(_extMachInst.zimm_vsetvli), + uimm(_extMachInst.uimm_vsetivli), + elen(_elen) + { + this->flags[IsVector] = true; + } + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; + + std::string generateZimmDisassembly() const; +}; + +inline uint8_t checked_vtype(bool vill, uint8_t vtype) { + panic_if(vill, "vill has been set"); + const uint8_t vsew = bits(vtype, 5, 3); + panic_if(vsew >= 0b100, "vsew: %#x not supported", vsew); + const uint8_t vlmul = bits(vtype, 2, 0); + panic_if(vlmul == 0b100, "vlmul: %#x not supported", vlmul); + return vtype; +} + +class VectorNonSplitInst : public RiscvStaticInst +{ + protected: + uint32_t vl; + uint8_t vtype; + VectorNonSplitInst(const char* mnem, ExtMachInst _machInst, + OpClass __opClass) + : RiscvStaticInst(mnem, _machInst, __opClass), + vl(_machInst.vl), + vtype(_machInst.vtype8) + { + this->flags[IsVector] = true; + } + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VectorMacroInst : public RiscvMacroInst +{ + protected: + uint32_t vl; + uint8_t vtype; + uint32_t vlen; + + VectorMacroInst(const char* mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _vlen = 256) + : RiscvMacroInst(mnem, _machInst, __opClass), + vl(_machInst.vl), + vtype(_machInst.vtype8), + vlen(_vlen) + { + this->flags[IsVector] = true; + } +}; + +class VectorMicroInst : public RiscvMicroInst +{ +protected: + uint32_t vlen; + uint32_t microVl; + uint32_t microIdx; + uint8_t vtype; + VectorMicroInst(const char *mnem, ExtMachInst _machInst, OpClass __opClass, + uint32_t _microVl, uint32_t _microIdx, uint32_t _vlen = 256) + : RiscvMicroInst(mnem, _machInst, __opClass), + vlen(_vlen), + microVl(_microVl), + microIdx(_microIdx), + vtype(_machInst.vtype8) + { + this->flags[IsVector] = true; + } +}; + +class VectorNopMicroInst : public RiscvMicroInst +{ +public: + VectorNopMicroInst(ExtMachInst _machInst) + : RiscvMicroInst("vnop", _machInst, No_OpClass) + {} + + Fault execute(ExecContext* xc, trace::InstRecord* traceData) + const override + { + return NoFault; + } + + std::string generateDisassembly(Addr pc, const loader::SymbolTable *symtab) + const override + { + std::stringstream ss; + ss << mnemonic; + return ss.str(); + } +}; + +class VectorArithMicroInst : public VectorMicroInst +{ +protected: + VectorArithMicroInst(const char *mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _microVl, + uint32_t _microIdx) + : VectorMicroInst(mnem, _machInst, __opClass, _microVl, _microIdx) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VectorArithMacroInst : public VectorMacroInst +{ + protected: + VectorArithMacroInst(const char* mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _vlen = 256) + : VectorMacroInst(mnem, _machInst, __opClass, _vlen) + { + this->flags[IsVector] = true; + } + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VectorVMUNARY0MicroInst : public VectorMicroInst +{ +protected: + VectorVMUNARY0MicroInst(const char *mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _microVl, + uint32_t _microIdx) + : VectorMicroInst(mnem, _machInst, __opClass, _microVl, _microIdx) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VectorVMUNARY0MacroInst : public VectorMacroInst +{ + protected: + VectorVMUNARY0MacroInst(const char* mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _vlen) + : VectorMacroInst(mnem, _machInst, __opClass, _vlen) + { + this->flags[IsVector] = true; + } + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VectorSlideMacroInst : public VectorMacroInst +{ + protected: + VectorSlideMacroInst(const char* mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _vlen = 256) + : VectorMacroInst(mnem, _machInst, __opClass, _vlen) + { + this->flags[IsVector] = true; + } + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VectorSlideMicroInst : public VectorMicroInst +{ + protected: + uint32_t vdIdx; + uint32_t vs2Idx; + VectorSlideMicroInst(const char *mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _microVl, + uint32_t _microIdx, uint32_t _vdIdx, uint32_t _vs2Idx) + : VectorMicroInst(mnem, _machInst, __opClass, _microVl, _microIdx) + , vdIdx(_vdIdx), vs2Idx(_vs2Idx) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VectorMemMicroInst : public VectorMicroInst +{ + protected: + uint32_t offset; // Used to calculate EA. + Request::Flags memAccessFlags; + + VectorMemMicroInst(const char* mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _microVl, + uint32_t _microIdx, uint32_t _offset) + : VectorMicroInst(mnem, _machInst, __opClass, _microVl, _microIdx) + , offset(_offset) + , memAccessFlags(0) + {} +}; + +class VectorMemMacroInst : public VectorMacroInst +{ + protected: + VectorMemMacroInst(const char* mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _vlen = 256) + : VectorMacroInst(mnem, _machInst, __opClass, _vlen) + {} +}; + +class VleMacroInst : public VectorMemMacroInst +{ + protected: + VleMacroInst(const char* mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _vlen) + : VectorMemMacroInst(mnem, _machInst, __opClass, _vlen) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VseMacroInst : public VectorMemMacroInst +{ + protected: + VseMacroInst(const char* mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _vlen) + : VectorMemMacroInst(mnem, _machInst, __opClass, _vlen) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VleMicroInst : public VectorMicroInst +{ + protected: + Request::Flags memAccessFlags; + + VleMicroInst(const char *mnem, ExtMachInst _machInst,OpClass __opClass, + uint32_t _microVl, uint32_t _microIdx, uint32_t _vlen) + : VectorMicroInst(mnem, _machInst, __opClass, _microVl, + _microIdx, _vlen) + { + this->flags[IsLoad] = true; + } + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VseMicroInst : public VectorMicroInst +{ + protected: + Request::Flags memAccessFlags; + + VseMicroInst(const char *mnem, ExtMachInst _machInst, OpClass __opClass, + uint32_t _microVl, uint32_t _microIdx, uint32_t _vlen) + : VectorMicroInst(mnem, _machInst, __opClass, _microVl, + _microIdx, _vlen) + { + this->flags[IsStore] = true; + } + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VlWholeMacroInst : public VectorMemMacroInst +{ + protected: + VlWholeMacroInst(const char *mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _vlen) + : VectorMemMacroInst(mnem, _machInst, __opClass, _vlen) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VlWholeMicroInst : public VectorMicroInst +{ + protected: + Request::Flags memAccessFlags; + + VlWholeMicroInst(const char *mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _microVl, uint32_t _microIdx, + uint32_t _vlen) + : VectorMicroInst(mnem, _machInst, __opClass, _microVl, + _microIdx, _vlen) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VsWholeMacroInst : public VectorMemMacroInst +{ + protected: + VsWholeMacroInst(const char *mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _vlen) + : VectorMemMacroInst(mnem, _machInst, __opClass, _vlen) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VsWholeMicroInst : public VectorMicroInst +{ + protected: + Request::Flags memAccessFlags; + + VsWholeMicroInst(const char *mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _microVl, + uint32_t _microIdx, uint32_t _vlen) + : VectorMicroInst(mnem, _machInst, __opClass , _microVl, + _microIdx, _vlen) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VlStrideMacroInst : public VectorMemMacroInst +{ + protected: + VlStrideMacroInst(const char* mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _vlen) + : VectorMemMacroInst(mnem, _machInst, __opClass, _vlen) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VlStrideMicroInst : public VectorMemMicroInst +{ + protected: + uint32_t regIdx; + VlStrideMicroInst(const char *mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _regIdx, + uint32_t _microIdx, uint32_t _microVl) + : VectorMemMicroInst(mnem, _machInst, __opClass, _microVl, + _microIdx, 0) + , regIdx(_regIdx) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VsStrideMacroInst : public VectorMemMacroInst +{ + protected: + VsStrideMacroInst(const char* mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _vlen) + : VectorMemMacroInst(mnem, _machInst, __opClass, _vlen) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VsStrideMicroInst : public VectorMemMicroInst +{ + protected: + uint32_t regIdx; + VsStrideMicroInst(const char *mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _regIdx, + uint32_t _microIdx, uint32_t _microVl) + : VectorMemMicroInst(mnem, _machInst, __opClass, _microVl, + _microIdx, 0) + , regIdx(_regIdx) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VlIndexMacroInst : public VectorMemMacroInst +{ + protected: + VlIndexMacroInst(const char* mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _vlen) + : VectorMemMacroInst(mnem, _machInst, __opClass, _vlen) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VlIndexMicroInst : public VectorMemMicroInst +{ + protected: + uint32_t vdRegIdx; + uint32_t vdElemIdx; + uint32_t vs2RegIdx; + uint32_t vs2ElemIdx; + VlIndexMicroInst(const char *mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _vdRegIdx, uint32_t _vdElemIdx, + uint32_t _vs2RegIdx, uint32_t _vs2ElemIdx) + : VectorMemMicroInst(mnem, _machInst, __opClass, 1, + 0, 0) + , vdRegIdx(_vdRegIdx), vdElemIdx(_vdElemIdx) + , vs2RegIdx(_vs2RegIdx), vs2ElemIdx(_vs2ElemIdx) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VsIndexMacroInst : public VectorMemMacroInst +{ + protected: + VsIndexMacroInst(const char* mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _vlen) + : VectorMemMacroInst(mnem, _machInst, __opClass, _vlen) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VsIndexMicroInst : public VectorMemMicroInst +{ + protected: + uint32_t vs3RegIdx; + uint32_t vs3ElemIdx; + uint32_t vs2RegIdx; + uint32_t vs2ElemIdx; + VsIndexMicroInst(const char *mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _vs3RegIdx, + uint32_t _vs3ElemIdx, uint32_t _vs2RegIdx, + uint32_t _vs2ElemIdx) + : VectorMemMicroInst(mnem, _machInst, __opClass, 1, 0, 0), + vs3RegIdx(_vs3RegIdx), vs3ElemIdx(_vs3ElemIdx), + vs2RegIdx(_vs2RegIdx), vs2ElemIdx(_vs2ElemIdx) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VMvWholeMacroInst : public VectorArithMacroInst +{ + protected: + VMvWholeMacroInst(const char* mnem, ExtMachInst _machInst, + OpClass __opClass) + : VectorArithMacroInst(mnem, _machInst, __opClass) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class VMvWholeMicroInst : public VectorArithMicroInst +{ + protected: + VMvWholeMicroInst(const char *mnem, ExtMachInst _machInst, + OpClass __opClass, uint32_t _microVl, + uint32_t _microIdx) + : VectorArithMicroInst(mnem, _machInst, __opClass, _microVl, _microIdx) + {} + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + + +class VMaskMergeMicroInst : public VectorArithMicroInst +{ + private: + RegId srcRegIdxArr[NumVecInternalRegs]; + RegId destRegIdxArr[1]; + + public: + uint32_t vlen; + size_t elemSize; + VMaskMergeMicroInst(ExtMachInst extMachInst, + uint8_t _dstReg, uint8_t _numSrcs, uint32_t _vlen, size_t _elemSize); + Fault execute(ExecContext *, trace::InstRecord *) const override; + std::string generateDisassembly(Addr, + const loader::SymbolTable *) const override; +}; + +class VxsatMicroInst : public VectorArithMicroInst +{ + private: + bool* vxsat; + public: + VxsatMicroInst(bool* Vxsat, ExtMachInst extMachInst) + : VectorArithMicroInst("vxsat_micro", extMachInst, + VectorIntegerArithOp, 0, 0) + { + vxsat = Vxsat; + } + Fault execute(ExecContext *, trace::InstRecord *) const override; + std::string generateDisassembly(Addr, const loader::SymbolTable *) + const override; +}; + +} // namespace RiscvISA +} // namespace gem5 + + +#endif // __ARCH_RISCV_INSTS_VECTOR_HH__ diff --git a/src/arch/riscv/interrupts.hh b/src/arch/riscv/interrupts.hh index a1ee396cd4..b003b59426 100644 --- a/src/arch/riscv/interrupts.hh +++ b/src/arch/riscv/interrupts.hh @@ -69,21 +69,41 @@ class Interrupts : public BaseInterrupts { INTERRUPT mask = 0; STATUS status = tc->readMiscReg(MISCREG_STATUS); - INTERRUPT mideleg = tc->readMiscReg(MISCREG_MIDELEG); - INTERRUPT sideleg = tc->readMiscReg(MISCREG_SIDELEG); + MISA misa = tc->readMiscRegNoEffect(MISCREG_ISA); + INTERRUPT mideleg = 0; + if (misa.rvs || misa.rvn) { + mideleg = tc->readMiscReg(MISCREG_MIDELEG); + } + INTERRUPT sideleg = 0; + if (misa.rvs && misa.rvn) { + sideleg = tc->readMiscReg(MISCREG_SIDELEG); + } PrivilegeMode prv = (PrivilegeMode)tc->readMiscReg(MISCREG_PRV); switch (prv) { case PRV_U: - mask.mei = (!sideleg.mei) | (sideleg.mei & status.uie); - mask.mti = (!sideleg.mti) | (sideleg.mti & status.uie); - mask.msi = (!sideleg.msi) | (sideleg.msi & status.uie); - mask.sei = (!sideleg.sei) | (sideleg.sei & status.uie); - mask.sti = (!sideleg.sti) | (sideleg.sti & status.uie); - mask.ssi = (!sideleg.ssi) | (sideleg.ssi & status.uie); + // status.uie is always 0 if misa.rvn is disabled + if (misa.rvs) { + mask.mei = (!sideleg.mei) | (sideleg.mei & status.uie); + mask.mti = (!sideleg.mti) | (sideleg.mti & status.uie); + mask.msi = (!sideleg.msi) | (sideleg.msi & status.uie); + mask.sei = (!sideleg.sei) | (sideleg.sei & status.uie); + mask.sti = (!sideleg.sti) | (sideleg.sti & status.uie); + mask.ssi = (!sideleg.ssi) | (sideleg.ssi & status.uie); + } else { + // According to the RISC-V privilege spec v1.10, if the + // S privilege mode is not implemented and user-trap + // support, setting mideleg/medeleg bits will delegate the + // trap to U-mode trap handler + mask.mei = (!mideleg.mei) | (mideleg.mei & status.uie); + mask.mti = (!mideleg.mti) | (mideleg.mti & status.uie); + mask.msi = (!mideleg.msi) | (mideleg.msi & status.uie); + mask.sei = mask.sti = mask.ssi = 0; + } if (status.uie) mask.uei = mask.uti = mask.usi = 1; break; case PRV_S: + // status.sie is always 0 if misa.rvn is disabled mask.mei = (!mideleg.mei) | (mideleg.mei & status.sie); mask.mti = (!mideleg.mti) | (mideleg.mti & status.sie); mask.msi = (!mideleg.msi) | (mideleg.msi & status.sie); @@ -112,13 +132,13 @@ class Interrupts : public BaseInterrupts } bool checkInterrupt(int num) const { return ip[num] && ie[num]; } - bool checkInterrupts() const + bool checkInterrupts() const override { return checkNonMaskableInterrupt() || (ip & ie & globalMask()).any(); } Fault - getInterrupt() + getInterrupt() override { assert(checkInterrupts()); if (checkNonMaskableInterrupt()) @@ -135,10 +155,10 @@ class Interrupts : public BaseInterrupts return NoFault; } - void updateIntrInfo() {} + void updateIntrInfo() override {} void - post(int int_num, int index) + post(int int_num, int index) override { DPRINTF(Interrupt, "Interrupt %d:%d posted\n", int_num, index); if (int_num != INT_NMI) { @@ -149,7 +169,7 @@ class Interrupts : public BaseInterrupts } void - clear(int int_num, int index) + clear(int int_num, int index) override { DPRINTF(Interrupt, "Interrupt %d:%d cleared\n", int_num, index); if (int_num != INT_NMI) { @@ -163,7 +183,7 @@ class Interrupts : public BaseInterrupts void clearNMI() { tc->setMiscReg(MISCREG_NMIP, 0); } void - clearAll() + clearAll() override { DPRINTF(Interrupt, "All interrupts cleared\n"); ip = 0; @@ -176,7 +196,7 @@ class Interrupts : public BaseInterrupts void setIE(const uint64_t& val) { ie = val; } void - serialize(CheckpointOut &cp) const + serialize(CheckpointOut &cp) const override { unsigned long ip_ulong = ip.to_ulong(); unsigned long ie_ulong = ie.to_ulong(); @@ -185,7 +205,7 @@ class Interrupts : public BaseInterrupts } void - unserialize(CheckpointIn &cp) + unserialize(CheckpointIn &cp) override { unsigned long ip_ulong; unsigned long ie_ulong; diff --git a/src/arch/riscv/isa.cc b/src/arch/riscv/isa.cc index 94a8239bac..18e9b5fce2 100644 --- a/src/arch/riscv/isa.cc +++ b/src/arch/riscv/isa.cc @@ -36,13 +36,16 @@ #include #include "arch/riscv/faults.hh" +#include "arch/riscv/insts/static_inst.hh" #include "arch/riscv/interrupts.hh" #include "arch/riscv/mmu.hh" #include "arch/riscv/pagetable.hh" #include "arch/riscv/pmp.hh" +#include "arch/riscv/pcstate.hh" #include "arch/riscv/regs/float.hh" #include "arch/riscv/regs/int.hh" #include "arch/riscv/regs/misc.hh" +#include "arch/riscv/regs/vector.hh" #include "base/bitfield.hh" #include "base/compiler.hh" #include "base/logging.hh" @@ -52,6 +55,7 @@ #include "debug/LLSC.hh" #include "debug/MatRegs.hh" #include "debug/RiscvMisc.hh" +#include "debug/VecRegs.hh" #include "mem/packet.hh" #include "mem/request.hh" #include "params/RiscvISA.hh" @@ -189,6 +193,14 @@ namespace RiscvISA [MISCREG_FFLAGS] = "FFLAGS", [MISCREG_FRM] = "FRM", + [MISCREG_VSTART] = "VSTART", + [MISCREG_VXSAT] = "VXSAT", + [MISCREG_VXRM] = "VXRM", + [MISCREG_VCSR] = "VCSR", + [MISCREG_VL] = "VL", + [MISCREG_VTYPE] = "VTYPE", + [MISCREG_VLENB] = "VLENB", + [MISCREG_NMIVEC] = "NMIVEC", [MISCREG_NMIE] = "NMIE", [MISCREG_NMIP] = "NMIP", @@ -234,17 +246,18 @@ namespace { /* Not applicable to RISCV */ -RegClass vecRegClass(VecRegClass, VecRegClassName, 1, debug::IntRegs); -RegClass vecElemClass(VecElemClass, VecElemClassName, 2, debug::IntRegs); -RegClass vecPredRegClass(VecPredRegClass, VecPredRegClassName, 1, +RegClass vecElemClass(VecElemClass, VecElemClassName, 0, debug::IntRegs); +RegClass vecPredRegClass(VecPredRegClass, VecPredRegClassName, 0, debug::IntRegs); -RegClass matRegClass(MatRegClass, MatRegClassName, 1, debug::MatRegs); +RegClass matRegClass(MatRegClass, MatRegClassName, 0, debug::MatRegs); RegClass ccRegClass(CCRegClass, CCRegClassName, 0, debug::IntRegs); } // anonymous namespace -ISA::ISA(const Params &p) : - BaseISA(p), rv_type(p.riscv_type), checkAlignment(p.check_alignment) +ISA::ISA(const Params &p) : BaseISA(p), + _rvType(p.riscv_type), checkAlignment(p.check_alignment), + enableRvv(p.enable_rvv), vlen(p.vlen), elen(p.elen), + _privilegeModeSet(p.privilege_mode_set) { _regClasses.push_back(&intRegClass); _regClasses.push_back(&floatRegClass); @@ -255,6 +268,14 @@ ISA::ISA(const Params &p) : _regClasses.push_back(&ccRegClass); _regClasses.push_back(&miscRegClass); + fatal_if( p.vlen < p.elen, + "VLEN should be greater or equal", + "than ELEN. Ch. 2RISC-V vector spec."); + + inform("RVV enabled, VLEN = %d bits, ELEN = %d bits", + p.vlen, p.elen); + + miscRegFile.resize(NUM_MISCREGS); clear(); } @@ -275,6 +296,17 @@ ISA::copyRegsFrom(ThreadContext *src) for (auto &id: floatRegClass) tc->setReg(id, src->getReg(id)); + // Third loop through the vector registers. + RiscvISA::VecRegContainer vc; + for (auto &id: vecRegClass) { + src->getReg(id, &vc); + tc->setReg(id, &vc); + } + + // Copying Misc Regs + for (int i = 0; i < NUM_MISCREGS; i++) + tc->setMiscRegNoEffect(i, src->readMiscRegNoEffect(i)); + // Lastly copy PC/NPC tc->pcState(src->pcState()); } @@ -293,23 +325,44 @@ void ISA::clear() // default config arch isa string is rv64(32)imafdc misa.rvi = misa.rvm = misa.rva = misa.rvf = misa.rvd = misa.rvc = 1; - // default privlege modes if MSU - misa.rvs = misa.rvu = 1; + + switch (getPrivilegeModeSet()) { + case enums::M: + break; + case enums::MU: + misa.rvu = 1; + break; + case enums::MNU: + misa.rvu = misa.rvn = 1; + break; + case enums::MSU: + misa.rvs = misa.rvu = 1; + break; + case enums::MNSU: + misa.rvs = misa.rvu = misa.rvn = 1; + break; + default: + panic("Privilege mode set config should not reach here"); + } // mark FS is initial status.fs = INITIAL; - // rv_type dependent init. - switch (rv_type) { + // _rvType dependent init. + switch (_rvType) { case RV32: misa.rv32_mxl = 1; break; case RV64: misa.rv64_mxl = 2; status.uxl = status.sxl = 2; + if (getEnableRvv()) { + status.vs = VPUStatus::INITIAL; + misa.rvv = 1; + } break; default: - panic("%s: Unknown rv_type: %d", name(), (int)rv_type); + panic("%s: Unknown _rvType: %d", name(), (int)_rvType); } miscRegFile[MISCREG_ISA] = misa; @@ -465,7 +518,7 @@ ISA::readMiscReg(RegIndex idx) (status.xs == 3) || (status.fs == 3) || (status.vs == 3); // For RV32, the SD bit is at index 31 // For RV64, the SD bit is at index 63. - switch (rv_type) { + switch (_rvType) { case RV32: status.rv32_sd = sd_bit; break; @@ -473,12 +526,51 @@ ISA::readMiscReg(RegIndex idx) status.rv64_sd = sd_bit; break; default: - panic("%s: Unknown rv_type: %d", name(), (int)rv_type); + panic("%s: Unknown _rvType: %d", name(), (int)_rvType); } + // Check status.mpp + MISA misa = readMiscRegNoEffect(MISCREG_ISA); + switch(status.mpp) { + case PRV_U: + status.mpp = (misa.rvu) ? PRV_U : PRV_M; + break; + case PRV_S: + if (misa.rvs) + status.mpp = PRV_S; + else + status.mpp = (misa.rvu) ? PRV_U : PRV_M; + break; + case PRV_M: + break; + default: + status.mpp = (misa.rvu) ? PRV_U : PRV_M; + } + setMiscRegNoEffect(idx, status); return readMiscRegNoEffect(idx); } + case MISCREG_VLENB: + { + auto rpc = tc->pcState().as(); + return rpc.vlenb(); + } + case MISCREG_VTYPE: + { + auto rpc = tc->pcState().as(); + return rpc.vtype(); + } + case MISCREG_VL: + { + auto rpc = tc->pcState().as(); + return (RegVal)rpc.vl(); + } + case MISCREG_VCSR: + { + return readMiscRegNoEffect(MISCREG_VXSAT) & + (readMiscRegNoEffect(MISCREG_VXRM) << 1); + } + break; default: // Try reading HPM counters // As a placeholder, all HPM counters are just cycle counters @@ -541,7 +633,7 @@ ISA::setMiscReg(RegIndex idx, RegVal val) assert(readMiscRegNoEffect(MISCREG_PRV) == PRV_M); int regSize = 0; - switch (rv_type) { + switch (_rvType) { case RV32: regSize = 4; break; @@ -549,7 +641,7 @@ ISA::setMiscReg(RegIndex idx, RegVal val) regSize = 8; break; default: - panic("%s: Unknown rv_type: %d", name(), (int)rv_type); + panic("%s: Unknown _rvType: %d", name(), (int)_rvType); } // Specs do not seem to mention what should be @@ -638,20 +730,46 @@ ISA::setMiscReg(RegIndex idx, RegVal val) 2, 0) != 0) { new_misa.rvc = new_misa.rvc | cur_misa.rvc; } + if (!getEnableRvv()) { + new_misa.rvv = 0; + } + new_misa.rvn = cur_misa.rvn; + new_misa.rvs = cur_misa.rvs; + new_misa.rvu = cur_misa.rvu; setMiscRegNoEffect(idx, new_misa); } break; case MISCREG_STATUS: { - if (rv_type != RV32) { + if (_rvType != RV32) { // SXL and UXL are hard-wired to 64 bit auto cur = readMiscRegNoEffect(idx); val &= ~(STATUS_SXL_MASK | STATUS_UXL_MASK); val |= cur & (STATUS_SXL_MASK | STATUS_UXL_MASK); } + if (!getEnableRvv()) { + // Always OFF is rvv is disabled. + val &= ~STATUS_VS_MASK; + } setMiscRegNoEffect(idx, val); } break; + case MISCREG_VXSAT: + { + setMiscRegNoEffect(idx, val & 0x1); + } + break; + case MISCREG_VXRM: + { + setMiscRegNoEffect(idx, val & 0x3); + } + break; + case MISCREG_VCSR: + { + setMiscRegNoEffect(MISCREG_VXSAT, val & 0x1); + setMiscRegNoEffect(MISCREG_VXRM, (val & 0x6) >> 1); + } + break; default: setMiscRegNoEffect(idx, val); } @@ -758,6 +876,16 @@ ISA::resetThread() Reset().invoke(tc); } +Addr +ISA::getFaultHandlerAddr(RegIndex idx, uint64_t cause, bool intr) const +{ + auto vec = tc->readMiscRegNoEffect(idx); + Addr addr = mbits(vec, 63, 2); + if (intr && bits(vec, 1, 0) == 1) + addr += 4 * cause; + return addr; +} + } // namespace RiscvISA } // namespace gem5 diff --git a/src/arch/riscv/isa.hh b/src/arch/riscv/isa.hh index 7ef5c526f5..9a24a76745 100644 --- a/src/arch/riscv/isa.hh +++ b/src/arch/riscv/isa.hh @@ -67,12 +67,15 @@ enum FPUStatus DIRTY = 3, }; +using VPUStatus = FPUStatus; + class ISA : public BaseISA { protected: - RiscvType rv_type; + RiscvType _rvType; std::vector miscRegFile; bool checkAlignment; + bool enableRvv; bool hpmCounterEnabled(int counter) const; @@ -81,6 +84,21 @@ class ISA : public BaseISA const Addr INVALID_RESERVATION_ADDR = (Addr)-1; std::unordered_map load_reservation_addrs; + /** Length of each vector register in bits. + * VLEN in Ch. 2 of RISC-V vector spec + */ + unsigned vlen; + + /** Length of each vector element in bits. + * ELEN in Ch. 2 of RISC-V vector spec + */ + unsigned elen; + + /** The combination of privilege modes + * in Privilege Levels section of RISC-V privileged spec + */ + PrivilegeModeSet _privilegeModeSet; + public: using Params = RiscvISAParams; @@ -89,14 +107,8 @@ class ISA : public BaseISA PCStateBase* newPCState(Addr new_inst_addr=0) const override { - return new PCState(new_inst_addr, rv_type); - } - - void - clearLoadReservation(ContextID cid) override - { - Addr& load_reservation_addr = load_reservation_addrs[cid]; - load_reservation_addr = INVALID_RESERVATION_ADDR; + unsigned vlenb = vlen >> 3; + return new PCState(new_inst_addr, _rvType, vlenb); } public: @@ -117,7 +129,7 @@ class ISA : public BaseISA virtual const std::unordered_map& getCSRMaskMap() const { - return CSRMasks[rv_type]; + return CSRMasks[_rvType][_privilegeModeSet]; } bool alignmentCheckEnabled() const { return checkAlignment; } @@ -141,7 +153,26 @@ class ISA : public BaseISA void resetThread() override; - RiscvType rvType() const { return rv_type; } + RiscvType rvType() const { return _rvType; } + + bool getEnableRvv() const { return enableRvv; } + + void + clearLoadReservation(ContextID cid) + { + Addr& load_reservation_addr = load_reservation_addrs[cid]; + load_reservation_addr = INVALID_RESERVATION_ADDR; + } + + /** Methods for getting VLEN, VLENB and ELEN values */ + unsigned getVecLenInBits() { return vlen; } + unsigned getVecLenInBytes() { return vlen >> 3; } + unsigned getVecElemLenInBits() { return elen; } + + PrivilegeModeSet getPrivilegeModeSet() { return _privilegeModeSet; } + + virtual Addr getFaultHandlerAddr( + RegIndex idx, uint64_t cause, bool intr) const; }; } // namespace RiscvISA diff --git a/src/arch/riscv/isa/bitfields.isa b/src/arch/riscv/isa/bitfields.isa index 8589269949..66ce74afe3 100644 --- a/src/arch/riscv/isa/bitfields.isa +++ b/src/arch/riscv/isa/bitfields.isa @@ -98,7 +98,9 @@ def bitfield RL <25>; // Compressed def bitfield COPCODE <15:13>; +def bitfield CFUNCT6LOW3 <12:10>; def bitfield CFUNCT1 <12>; +def bitfield CFUNCT1BIT6 <6>; def bitfield CFUNCT2HIGH <11:10>; def bitfield CFUNCT2LOW <6:5>; def bitfield RC1 <11:7>; @@ -133,3 +135,27 @@ def bitfield BIT25 <25>; def bitfield RNUM <23:20>; def bitfield KFUNCT5 <29:25>; def bitfield BS <31:30>; + +// Vector instructions +def bitfield VFUNCT6 vfunct6; +def bitfield VFUNCT5 vfunct5; +def bitfield VFUNCT3 vfunct3; +def bitfield VFUNCT2 vfunct2; + +def bitfield VS3 vs3; +def bitfield VS2 vs2; +def bitfield VS1 vs1; +def bitfield VD vd; + +def bitfield NF nf; +def bitfield MEW mew; +def bitfield MOP mop; +def bitfield VM vm; +def bitfield LUMOP lumop; +def bitfield SUMOP sumop; +def bitfield WIDTH width; + +def bitfield BIT31 bit31; +def bitfield BIT30 bit30; +def bitfield SIMM5 uimm_vsetivli; +def bitfield SIMM3 simm3; diff --git a/src/arch/riscv/isa/decoder.isa b/src/arch/riscv/isa/decoder.isa index 2dcd118225..be4b621a37 100644 --- a/src/arch/riscv/isa/decoder.isa +++ b/src/arch/riscv/isa/decoder.isa @@ -61,6 +61,11 @@ decode QUADRANT default Unknown::unknown() { return std::make_shared("FPU is off", machInst); + // Mutating any floating point register changes the FS bit + // of the STATUS CSR. + status.fs = FPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + Fp2_bits = Mem; }}, {{ EA = rvZext(Rp1 + offset); @@ -85,6 +90,9 @@ decode QUADRANT default Unknown::unknown() { return std::make_shared("FPU is off", machInst); + status.fs = FPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + freg_t fd = freg(f32(Mem_uw)); Fp2_bits = fd.v; }}, {{ @@ -99,6 +107,49 @@ decode QUADRANT default Unknown::unknown() { }}); } } + 0x4: decode CFUNCT6LOW3 { + format CompressedLoad { + 0x0: c_lbu({{ + offset = (CIMM2<0:0> << 1) | CIMM2<1:1>; + }}, {{ + Rp2 = Mem_ub; + }}, {{ + EA = rvZext(Rp1 + offset); + }}); + 0x1: decode CFUNCT1BIT6 { + 0x0: c_lhu({{ + offset = CIMM2<0:0> << 1; + }}, {{ + Rp2 = Mem_uh; + }}, {{ + EA = rvZext(Rp1 + offset); + }}); + 0x1: c_lh({{ + offset = CIMM2<0:0> << 1; + }}, {{ + Rp2_sd = Mem_sh; + }}, {{ + EA = rvZext(Rp1 + offset); + }}); + } + } + format CompressedStore { + 0x2: c_sb({{ + offset = (CIMM2<0:0> << 1) | CIMM2<1:1>; + }}, {{ + Mem_ub = Rp2_ub; + }}, ea_code={{ + EA = rvZext(Rp1 + offset); + }}); + 0x3: c_sh({{ + offset = (CIMM2<0:0> << 1); + }}, {{ + Mem_uh = Rp2_uh; + }}, ea_code={{ + EA = rvZext(Rp1 + offset); + }}); + } + } format CompressedStore { 0x5: c_fsd({{ offset = CIMM3 << 3 | CIMM2 << 6; @@ -256,15 +307,42 @@ decode QUADRANT default Unknown::unknown() { Rp1 = rvSext(Rp1 & Rp2); }}); } - 0x1: decode RVTYPE { - 0x1: decode CFUNCT2LOW { - 0x0: c_subw({{ + 0x1: decode CFUNCT2LOW { + 0x0: decode RVTYPE { + 0x1: c_subw({{ Rp1_sd = (int32_t)Rp1_sd - Rp2_sw; }}); + } + 0x1: decode RVTYPE { 0x1: c_addw({{ Rp1_sd = (int32_t)Rp1_sd + Rp2_sw; }}); } + 0x2: c_mul({{ + Rp1_sd = rvSext(Rp1_sd * Rp2_sd); + }}, IntMultOp); + 0x3: decode RP2 { + 0x0: c_zext_b({{ + Rp1 = Rp1 & 0xFFULL; + }}); + 0x1: c_sext_b({{ + Rp1 = sext<8>(Rp1 & 0xFFULL); + }}); + 0x2: c_zext_h({{ + Rp1 = Rp1 & 0xFFFFULL; + }}); + 0x3: c_sext_h({{ + Rp1 = sext<16>(Rp1 & 0xFFFFULL); + }}); + 0x4: decode RVTYPE { + 0x1: c_zext_w({{ + Rp1 = bits(Rp1, 31, 0); + }}); + } + 0x5: c_not({{ + Rp1 = ~Rp1; + }}); + } } } } @@ -312,6 +390,9 @@ decode QUADRANT default Unknown::unknown() { return std::make_shared("FPU is off", machInst); + status.fs = FPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + Fc1_bits = Mem; }}, {{ EA = rvZext(sp + offset); @@ -340,6 +421,9 @@ decode QUADRANT default Unknown::unknown() { return std::make_shared("FPU is off", machInst); + status.fs = FPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + freg_t fd; fd = freg(f32(Mem_uw)); Fd_bits = fd.v; @@ -477,6 +561,10 @@ decode QUADRANT default Unknown::unknown() { if (status.fs == FPUStatus::OFF) return std::make_shared( "FPU is off", machInst); + + status.fs = FPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + freg_t fd; fd = freg(f16(Mem_uh)); Fd_bits = fd.v; @@ -486,6 +574,10 @@ decode QUADRANT default Unknown::unknown() { if (status.fs == FPUStatus::OFF) return std::make_shared( "FPU is off", machInst); + + status.fs = FPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + freg_t fd; fd = freg(f32(Mem_uw)); Fd_bits = fd.v; @@ -495,11 +587,183 @@ decode QUADRANT default Unknown::unknown() { if (status.fs == FPUStatus::OFF) return std::make_shared( "FPU is off", machInst); + + status.fs = FPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + freg_t fd; fd = freg(f64(Mem)); Fd_bits = fd.v; }}, inst_flags=FloatMemReadOp); } + + 0x0: decode MOP { + 0x0: decode LUMOP { + 0x00: VleOp::vle8_v({{ + if ((machInst.vm || elem_mask(v0, ei)) && + i < this->microVl) { + Vd_ub[i] = Mem_vc.as()[i]; + } else { + Vd_ub[i] = Vs2_ub[i]; + } + }}, inst_flags=VectorUnitStrideLoadOp); + 0x08: decode NF { + format VlWholeOp { + 0x0: vl1re8_v({{ + Vd_ub[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + 0x1: vl2re8_v({{ + Vd_ub[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + 0x3: vl4re8_v({{ + Vd_ub[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + 0x7: vl8re8_v({{ + Vd_ub[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + } + } + 0x0b: VlmOp::vlm_v({{ + Vd_ub[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorUnitStrideMaskLoadOp); + } + 0x1: VlIndexOp::vluxei8_v({{ + Vd_vu[vdElemIdx] = Mem_vc.as()[0]; + }}, {{ + EA = Rs1 + Vs2_ub[vs2ElemIdx]; + }}, inst_flags=VectorIndexedLoadOp); + 0x2: VlStrideOp::vlse8_v({{ + Vd_ub[microIdx] = Mem_vc.as()[0]; + }}, inst_flags=VectorStridedLoadOp); + 0x3: VlIndexOp::vloxei8_v({{ + Vd_vu[vdElemIdx] = Mem_vc.as()[0]; + }}, {{ + EA = Rs1 + Vs2_ub[vs2ElemIdx]; + }}, inst_flags=VectorIndexedLoadOp); + } + 0x5: decode MOP { + 0x0: decode LUMOP { + 0x00: VleOp::vle16_v({{ + if ((machInst.vm || elem_mask(v0, ei)) && + i < this->microVl) { + Vd_uh[i] = Mem_vc.as()[i]; + } else { + Vd_uh[i] = Vs2_uh[i]; + } + }}, inst_flags=VectorUnitStrideLoadOp); + 0x08: decode NF { + format VlWholeOp { + 0x0: vl1re16_v({{ + Vd_uh[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + 0x1: vl2re16_v({{ + Vd_uh[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + 0x3: vl4re16_v({{ + Vd_uh[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + 0x7: vl8re16_v({{ + Vd_uh[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + } + } + } + 0x1: VlIndexOp::vluxei16_v({{ + Vd_vu[vdElemIdx] = Mem_vc.as()[0]; + }}, {{ + EA = Rs1 + Vs2_uh[vs2ElemIdx]; + }}, inst_flags=VectorIndexedLoadOp); + 0x2: VlStrideOp::vlse16_v({{ + Vd_uh[microIdx] = Mem_vc.as()[0]; + }}, inst_flags=VectorStridedLoadOp); + 0x3: VlIndexOp::vloxei16_v({{ + Vd_vu[vdElemIdx] = Mem_vc.as()[0]; + }}, {{ + EA = Rs1 + Vs2_uh[vs2ElemIdx]; + }}, inst_flags=VectorIndexedLoadOp); + } + 0x6: decode MOP { + 0x0: decode LUMOP { + 0x00: VleOp::vle32_v({{ + if ((machInst.vm || elem_mask(v0, ei)) && + i < this->microVl) { + Vd_uw[i] = Mem_vc.as()[i]; + } else { + Vd_uw[i] = Vs2_uw[i]; + } + }}, inst_flags=VectorUnitStrideLoadOp); + 0x08: decode NF { + format VlWholeOp { + 0x0: vl1re32_v({{ + Vd_uw[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + 0x1: vl2re32_v({{ + Vd_uw[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + 0x3: vl4re32_v({{ + Vd_uw[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + 0x7: vl8re32_v({{ + Vd_uw[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + } + } + } + 0x1: VlIndexOp::vluxei32_v({{ + Vd_vu[vdElemIdx] = Mem_vc.as()[0]; + }}, {{ + EA = Rs1 + Vs2_uw[vs2ElemIdx]; + }}, inst_flags=VectorIndexedLoadOp); + 0x2: VlStrideOp::vlse32_v({{ + Vd_uw[microIdx] = Mem_vc.as()[0]; + }}, inst_flags=VectorStridedLoadOp); + 0x3: VlIndexOp::vloxei32_v({{ + Vd_vu[vdElemIdx] = Mem_vc.as()[0]; + }}, {{ + EA = Rs1 + Vs2_uw[vs2ElemIdx]; + }}, inst_flags=VectorIndexedLoadOp); + } + 0x7: decode MOP { + 0x0: decode LUMOP { + 0x00: VleOp::vle64_v({{ + if ((machInst.vm || elem_mask(v0, ei)) && + i < this->microVl) { + Vd_ud[i] = Mem_vc.as()[i]; + } else { + Vd_ud[i] = Vs2_ud[i]; + } + }}, inst_flags=VectorUnitStrideLoadOp); + 0x08: decode NF { + format VlWholeOp { + 0x0: vl1re64_v({{ + Vd_ud[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + 0x1: vl2re64_v({{ + Vd_ud[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + 0x3: vl4re64_v({{ + Vd_ud[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + 0x7: vl8re64_v({{ + Vd_ud[i] = Mem_vc.as()[i]; + }}, inst_flags=VectorWholeRegisterLoadOp); + } + } + } + 0x1: VlIndexOp::vluxei64_v({{ + Vd_vu[vdElemIdx] = Mem_vc.as()[0]; + }}, {{ + EA = Rs1 + Vs2_ud[vs2ElemIdx]; + }}, inst_flags=VectorIndexedLoadOp); + 0x2: VlStrideOp::vlse64_v({{ + Vd_ud[microIdx] = Mem_vc.as()[0]; + }}, inst_flags=VectorStridedLoadOp); + 0x3: VlIndexOp::vloxei64_v({{ + Vd_vu[vdElemIdx] = Mem_vc.as()[0]; + }}, {{ + EA = Rs1 + Vs2_ud[vs2ElemIdx]; + }}, inst_flags=VectorIndexedLoadOp); + } } 0x03: decode FUNCT3 { @@ -509,6 +773,23 @@ decode QUADRANT default Unknown::unknown() { 0x1: fence_i({{ }}, uint64_t, IsNonSpeculative, IsSerializeAfter, No_OpClass); } + + 0x2: decode FUNCT12 { + format CBMOp { + 0x0: cbo_inval({{ + Mem = 0; + }}, mem_flags=[INVALIDATE, DST_POC]); + 0x1: cbo_clean({{ + Mem = 0; + }}, mem_flags=[CLEAN, DST_POC]); + 0x2: cbo_flush({{ + Mem = 0; + }}, mem_flags=[CLEAN, INVALIDATE, DST_POC]); + 0x4: cbo_zero({{ + Mem = 0; + }}, mem_flags=[CACHE_BLOCK_ZERO]); + } + } } 0x04: decode FUNCT3 { @@ -806,6 +1087,106 @@ decode QUADRANT default Unknown::unknown() { Mem_ud = Fs2_bits; }}, inst_flags=FloatMemWriteOp); } + + 0x0: decode MOP { + 0x0: decode SUMOP { + 0x00: VseOp::vse8_v({{ + Mem_vc.as()[i] = Vs3_ub[i]; + }}, inst_flags=VectorUnitStrideStoreOp); + format VsWholeOp { + 0x8: decode NF { + 0x0: vs1r_v({{ + Mem_vc.as()[i] = Vs3_ub[i]; + }}, inst_flags=VectorWholeRegisterStoreOp); + 0x1: vs2r_v({{ + Mem_vc.as()[i] = Vs3_ub[i]; + }}, inst_flags=VectorWholeRegisterStoreOp); + 0x3: vs4r_v({{ + Mem_vc.as()[i] = Vs3_ub[i]; + }}, inst_flags=VectorWholeRegisterStoreOp); + 0x7: vs8r_v({{ + Mem_vc.as()[i] = Vs3_ub[i]; + }}, inst_flags=VectorWholeRegisterStoreOp); + } + } + 0x0b: VsmOp::vsm_v({{ + Mem_vc.as()[i] = Vs3_ub[i]; + }}, inst_flags=VectorUnitStrideMaskStoreOp); + } + 0x1: VsIndexOp::vsuxei8_v({{ + Mem_vc.as()[0] = Vs3_vu[vs3ElemIdx]; + }}, {{ + EA = Rs1 + Vs2_ub[vs2ElemIdx]; + }}, inst_flags=VectorIndexedStoreOp); + 0x2: VsStrideOp::vsse8_v({{ + Mem_vc.as()[0] = Vs3_ub[microIdx]; + }}, inst_flags=VectorStridedStoreOp); + 0x3: VsIndexOp::vsoxei8_v({{ + Mem_vc.as()[0] = Vs3_vu[vs3ElemIdx]; + }}, {{ + EA = Rs1 + Vs2_ub[vs2ElemIdx]; + }}, inst_flags=VectorIndexedStoreOp); + } + 0x5: decode MOP { + 0x0: decode SUMOP { + 0x00: VseOp::vse16_v({{ + Mem_vc.as()[i] = Vs3_uh[i]; + }}, inst_flags=VectorUnitStrideStoreOp); + } + 0x1: VsIndexOp::vsuxei16_v({{ + Mem_vc.as()[0] = Vs3_vu[vs3ElemIdx]; + }}, {{ + EA = Rs1 + Vs2_uh[vs2ElemIdx]; + }}, inst_flags=VectorIndexedStoreOp); + 0x2: VsStrideOp::vsse16_v({{ + Mem_vc.as()[0] = Vs3_uh[microIdx]; + }}, inst_flags=VectorStridedStoreOp); + 0x3: VsIndexOp::vsoxei16_v({{ + Mem_vc.as()[0] = Vs3_vu[vs3ElemIdx]; + }}, {{ + EA = Rs1 + Vs2_uh[vs2ElemIdx]; + }}, inst_flags=VectorIndexedStoreOp); + } + 0x6: decode MOP { + 0x0: decode SUMOP { + 0x00: VseOp::vse32_v({{ + Mem_vc.as()[i] = Vs3_uw[i]; + }}, inst_flags=VectorUnitStrideStoreOp); + } + 0x1: VsIndexOp::vsuxei32_v({{ + Mem_vc.as()[0] = Vs3_vu[vs3ElemIdx]; + }}, {{ + EA = Rs1 + Vs2_uw[vs2ElemIdx]; + }}, inst_flags=VectorIndexedStoreOp); + 0x2: VsStrideOp::vsse32_v({{ + Mem_vc.as()[0] = Vs3_uw[microIdx]; + }}, inst_flags=VectorStridedStoreOp); + 0x3: VsIndexOp::vsoxei32_v({{ + Mem_vc.as()[0] = Vs3_vu[vs3ElemIdx]; + }}, {{ + EA = Rs1 + Vs2_uw[vs2ElemIdx]; + }}, inst_flags=VectorIndexedStoreOp); + } + 0x7: decode MOP { + 0x0: decode SUMOP { + 0x00: VseOp::vse64_v({{ + Mem_vc.as()[i] = Vs3_ud[i]; + }}, inst_flags=VectorUnitStrideStoreOp); + } + 0x1: VsIndexOp::vsuxei64_v({{ + Mem_vc.as()[0] = Vs3_vu[vs3ElemIdx]; + }}, {{ + EA = Rs1 + Vs2_ud[vs2ElemIdx]; + }}, inst_flags=VectorIndexedStoreOp); + 0x2: VsStrideOp::vsse64_v({{ + Mem_vc.as()[0] = Vs3_ud[microIdx]; + }}, inst_flags=VectorStridedStoreOp); + 0x3: VsIndexOp::vsoxei64_v({{ + Mem_vc.as()[0] = Vs3_vu[vs3ElemIdx]; + }}, {{ + EA = Rs1 + Vs2_ud[vs2ElemIdx]; + }}, inst_flags=VectorIndexedStoreOp); + } } 0x0b: decode FUNCT3 { @@ -1615,93 +1996,80 @@ decode QUADRANT default Unknown::unknown() { } 0x14: decode ROUND_MODE { 0x0: fmin_s({{ - bool less = f32_lt_quiet(f32(freg(Fs1_bits)), - f32(freg(Fs2_bits))) || - (f32_eq(f32(freg(Fs1_bits)), - f32(freg(Fs2_bits))) && - bits(f32(freg(Fs1_bits)).v, 31)); + float32_t fs1 = f32(freg(Fs1_bits)); + float32_t fs2 = f32(freg(Fs2_bits)); + float32_t fd; + bool less = f32_lt_quiet(fs1, fs2) || + (f32_eq(fs1, fs2) && bits(fs1.v, 31)); - Fd_bits = less || - isNaNF32UI(f32(freg(Fs2_bits)).v) ? - freg(Fs1_bits).v : freg(Fs2_bits).v; - if (isNaNF32UI(f32(freg(Fs1_bits)).v) && - isNaNF32UI(f32(freg(Fs2_bits)).v)) - Fd_bits = f32(defaultNaNF32UI).v; + fd = less || isNaNF32UI(fs2.v) ? fs1 : fs2; + if (isNaNF32UI(fs1.v) && isNaNF32UI(fs2.v)) + fd = f32(defaultNaNF32UI); + Fd_bits = freg(fd).v; }}, FloatCmpOp); 0x1: fmax_s({{ - bool greater = f32_lt_quiet(f32(freg(Fs2_bits)), - f32(freg(Fs1_bits))) || - (f32_eq(f32(freg(Fs2_bits)), - f32(freg(Fs1_bits))) && - bits(f32(freg(Fs2_bits)).v, 31)); + float32_t fs1 = f32(freg(Fs1_bits)); + float32_t fs2 = f32(freg(Fs2_bits)); + float32_t fd; + bool greater = f32_lt_quiet(fs2, fs1) || + (f32_eq(fs2, fs1) && bits(fs2.v, 31)); - Fd_bits = greater || - isNaNF32UI(f32(freg(Fs2_bits)).v) ? - freg(Fs1_bits).v : freg(Fs2_bits).v; - if (isNaNF32UI(f32(freg(Fs1_bits)).v) && - isNaNF32UI(f32(freg(Fs2_bits)).v)) - Fd_bits = f32(defaultNaNF32UI).v; + fd = greater || isNaNF32UI(fs2.v) ? fs1: fs2; + if (isNaNF32UI(fs1.v) && isNaNF32UI(fs2.v)) + fd = f32(defaultNaNF32UI); + Fd_bits = freg(fd).v; }}, FloatCmpOp); } 0x15: decode ROUND_MODE { 0x0: fmin_d({{ - bool less = f64_lt_quiet(f64(freg(Fs1_bits)), - f64(freg(Fs2_bits))) || - (f64_eq(f64(freg(Fs1_bits)), - f64(freg(Fs2_bits))) && - bits(f64(freg(Fs1_bits)).v, 63)); + float64_t fs1 = f64(freg(Fs1_bits)); + float64_t fs2 = f64(freg(Fs2_bits)); + float64_t fd; + bool less = f64_lt_quiet(fs1, fs2) || + (f64_eq(fs1, fs2) && bits(fs1.v, 63)); - Fd_bits = less || - isNaNF64UI(f64(freg(Fs2_bits)).v) ? - freg(Fs1_bits).v : freg(Fs2_bits).v; - if (isNaNF64UI(f64(freg(Fs1_bits)).v) && - isNaNF64UI(f64(freg(Fs2_bits)).v)) - Fd_bits = f64(defaultNaNF64UI).v; + fd = less || isNaNF64UI(fs2.v) ? fs1 : fs2; + if (isNaNF64UI(fs1.v) && isNaNF64UI(fs2.v)) + fd = f64(defaultNaNF64UI); + Fd_bits = freg(fd).v; }}, FloatCmpOp); 0x1: fmax_d({{ - bool greater = - f64_lt_quiet(f64(freg(Fs2_bits)), - f64(freg(Fs1_bits))) || - (f64_eq(f64(freg(Fs2_bits)), - f64(freg(Fs1_bits))) && - bits(f64(freg(Fs2_bits)).v, 63)); + float64_t fs1 = f64(freg(Fs1_bits)); + float64_t fs2 = f64(freg(Fs2_bits)); + float64_t fd; + bool greater = f64_lt_quiet(fs2, fs1) || + (f64_eq(fs2, fs1) && bits(fs2.v, 63)); - Fd_bits = greater || - isNaNF64UI(f64(freg(Fs2_bits)).v) ? - freg(Fs1_bits).v : freg(Fs2_bits).v; - if (isNaNF64UI(f64(freg(Fs1_bits)).v) && - isNaNF64UI(f64(Fs2_bits).v)) - Fd_bits = f64(defaultNaNF64UI).v; + fd = greater || isNaNF64UI(fs2.v) ? fs1 : fs2; + if (isNaNF64UI(fs1.v) && isNaNF64UI(fs2.v)) + fd = f64(defaultNaNF64UI); + Fd_bits = freg(fd).v; }}, FloatCmpOp); } 0x16: decode ROUND_MODE { 0x0: fmin_h({{ - bool less = f16_lt_quiet(f16(freg(Fs1_bits)), - f16(freg(Fs2_bits))) || - (f16_eq(f16(freg(Fs1_bits)), - f16(freg(Fs2_bits))) && - bits(f16(freg(Fs1_bits)).v, 15)); + float16_t fs1 = f16(freg(Fs1_bits)); + float16_t fs2 = f16(freg(Fs2_bits)); + float16_t fd; + bool less = f16_lt_quiet(fs1, fs2) || + (f16_eq(fs1, fs2) && bits(fs1.v, 15)); - Fd_bits = less || - isNaNF16UI(f16(freg(Fs2_bits)).v) ? - freg(Fs1_bits).v : freg(Fs2_bits).v; - if (isNaNF16UI(f16(freg(Fs1_bits)).v) && - isNaNF16UI(f16(freg(Fs2_bits)).v)) - Fd_bits = f16(defaultNaNF16UI).v; + fd = less || isNaNF16UI(fs2.v) ? fs1 : fs2; + if (isNaNF16UI(fs1.v) && isNaNF16UI(fs2.v)) + fd = f16(defaultNaNF16UI); + Fd_bits = freg(fd).v; }}, FloatCmpOp); 0x1: fmax_h({{ - bool greater = f16_lt_quiet(f16(freg(Fs2_bits)), - f16(freg(Fs1_bits))) || - (f16_eq(f16(freg(Fs2_bits)), - f16(freg(Fs1_bits))) && - bits(f16(freg(Fs2_bits)).v, 15)); + float16_t fs1 = f16(freg(Fs1_bits)); + float16_t fs2 = f16(freg(Fs2_bits)); + float16_t fd; + bool greater = f16_lt_quiet(fs2, fs1) || + (f16_eq(fs2, fs1) && bits(fs2.v, 15)); - Fd_bits = greater || - isNaNF16UI(f16(freg(Fs2_bits)).v) ? - freg(Fs1_bits).v : freg(Fs2_bits).v; - if (isNaNF16UI(f16(freg(Fs1_bits)).v) && - isNaNF16UI(f16(freg(Fs2_bits)).v)) - Fd_bits = f16(defaultNaNF16UI).v; + fd = greater || isNaNF16UI(fs2.v) ? fs1 : fs2; + if (isNaNF16UI(fs1.v) && isNaNF16UI(fs2.v)) + fd = f16(defaultNaNF16UI); + Fd_bits = freg(fd).v; }}, FloatCmpOp); } 0x20: decode CONV_SGN { @@ -2025,6 +2393,2135 @@ decode QUADRANT default Unknown::unknown() { } } + 0x15: decode FUNCT3 { + // OPIVV + 0x0: decode VFUNCT6 { + format VectorIntFormat { + 0x0: vadd_vv({{ + Vd_vu[i] = Vs2_vu[i] + Vs1_vu[i]; + }}, OPIVV, VectorIntegerArithOp); + 0x2: vsub_vv({{ + Vd_vu[i] = Vs2_vu[i] - Vs1_vu[i]; + }}, OPIVV, VectorIntegerArithOp); + 0x4: vminu_vv({{ + Vd_vu[i] = Vs2_vu[i] < Vs1_vu[i] ? + Vs2_vu[i] : Vs1_vu[i]; + }}, OPIVV, VectorIntegerArithOp); + 0x5: vmin_vv({{ + Vd_vi[i] = Vs2_vi[i] < Vs1_vi[i] ? + Vs2_vi[i] : Vs1_vi[i]; + }}, OPIVV, VectorIntegerArithOp); + 0x6: vmaxu_vv({{ + Vd_vu[i] = Vs2_vu[i] > Vs1_vu[i] ? + Vs2_vu[i] : Vs1_vu[i]; + }}, OPIVV, VectorIntegerArithOp); + 0x7: vmax_vv({{ + Vd_vi[i] = Vs2_vi[i] > Vs1_vi[i] ? + Vs2_vi[i] : Vs1_vi[i]; + }}, OPIVV, VectorIntegerArithOp); + 0x9: vand_vv({{ + Vd_vu[i] = Vs2_vu[i] & Vs1_vu[i]; + }}, OPIVV, VectorIntegerArithOp); + 0xa: vor_vv({{ + Vd_vu[i] = Vs2_vu[i] | Vs1_vu[i]; + }}, OPIVV, VectorIntegerArithOp); + 0xb: vxor_vv({{ + Vd_vu[i] = Vs2_vu[i] ^ Vs1_vu[i]; + }}, OPIVV, VectorIntegerArithOp); + } + 0x0c: VectorGatherFormat::vrgather_vv({{ + for (uint32_t i = 0; i < microVl; i++) { + uint32_t ei = i + vs1_idx * vs1_elems + vs1_bias; + if (this->vm || elem_mask(v0, ei)) { + const uint64_t idx = Vs1_vu[i] + - vs2_elems * vs2_idx; + auto res = (Vs1_vu[i] >= vlmax) ? 0 + : (idx < vs2_elems) ? Vs2_vu[idx] + : Vs3_vu[i]; + Vd_vu[i] = res; + } + } + }}, OPIVV, VectorMiscOp); + 0x0e: VectorGatherFormat::vrgatherei16_vv({{ + for (uint32_t i = 0; i < microVl; i++) { + uint32_t ei = i + vs1_idx * vs1_elems + vs1_bias; + if (this->vm || elem_mask(v0, ei)) { + const uint32_t idx = Vs1_uh[i + vs1_bias] + - vs2_elems * vs2_idx; + auto res = (Vs1_uh[i + vs1_bias] >= vlmax) ? 0 + : (idx < vs2_elems) ? Vs2_vu[idx] + : Vs3_vu[i + vd_bias]; + Vd_vu[i + vd_bias] = res; + } + } + }}, OPIVV, VectorMiscOp); + format VectorIntFormat { + 0x10: decode VM { + 0x0: vadc_vvm({{ + Vd_vi[i] = Vs2_vi[i] + Vs1_vi[i] + + elem_mask(v0, ei); + }}, OPIVV, VectorIntegerArithOp); + // the unmasked versions (vm=1) are reserved + } + 0x12: decode VM { + 0x0: vsbc_vvm({{ + Vd_vi[i] = Vs2_vi[i] - Vs1_vi[i] + - elem_mask(v0, ei); + }}, OPIVV, VectorIntegerArithOp); + // the unmasked versions (vm=1) are reserved + } + 0x17: decode VM { + 0x0: vmerge_vvm({{ + Vd_vu[i] = elem_mask(v0, ei) + ? Vs1_vu[i] + : Vs2_vu[i]; + }}, OPIVV, VectorIntegerArithOp); + 0x1: decode VS2 { + 0x0: vmv_v_v({{ + Vd_vu[i] = Vs1_vu[i]; + }}, OPIVV, VectorIntegerArithOp); + } + } + } + format VectorIntVxsatFormat{ + 0x20: vsaddu_vv({{ + Vd_vu[i] = sat_addu(Vs2_vu[i], Vs1_vu[i], + vxsatptr); + }}, OPIVV, VectorIntegerArithOp); + 0x21: vsadd_vv({{ + Vd_vu[i] = sat_add(Vs2_vu[i], Vs1_vu[i], + vxsatptr); + }}, OPIVV, VectorIntegerArithOp); + 0x22: vssubu_vv({{ + Vd_vu[i] = sat_subu(Vs2_vu[i], Vs1_vu[i], + vxsatptr); + }}, OPIVV, VectorIntegerArithOp); + 0x23: vssub_vv({{ + Vd_vu[i] = sat_sub(Vs2_vu[i], Vs1_vu[i], + vxsatptr); + }}, OPIVV, VectorIntegerArithOp); + 0x27: vsmul_vv({{ + vi max = std::numeric_limits::max(); + vi min = std::numeric_limits::min(); + bool overflow = Vs1_vi[i] == Vs2_vi[i] && + Vs1_vi[i] == min; + __int128_t result = (__int128_t)Vs1_vi[i] * + (__int128_t)Vs2_vi[i]; + result = int_rounding<__int128_t>( + result, 0 /* TODO */, sew - 1); + result = result >> (sew - 1); + if (overflow) { + result = max; + *vxsatptr = true; + } + + Vd_vi[i] = (vi)result; + }}, OPIVV, VectorIntegerArithOp); + } + format VectorIntFormat { + 0x25: vsll_vv({{ + Vd_vu[i] = Vs2_vu[i] << (Vs1_vu[i] & (sew - 1)); + }}, OPIVV, VectorIntegerArithOp); + 0x28: vsrl_vv({{ + Vd_vu[i] = Vs2_vu[i] >> (Vs1_vu[i] & (sew - 1)); + }}, OPIVV, VectorIntegerArithOp); + 0x29: vsra_vv({{ + Vd_vi[i] = Vs2_vi[i] >> (Vs1_vu[i] & (sew - 1)); + }}, OPIVV, VectorIntegerArithOp); + 0x2a: vssrl_vv({{ + int sh = Vs1_vu[i] & (sew - 1); + __uint128_t val = Vs2_vu[i]; + + val = int_rounding<__uint128_t>(val, + xc->readMiscReg(MISCREG_VXRM), sh); + Vd_vu[i] = val >> sh; + }}, OPIVV, VectorIntegerArithOp); + 0x2b: vssra_vv({{ + int sh = Vs1_vi[i] & (sew - 1); + __int128_t val = Vs2_vi[i]; + + val = int_rounding<__int128_t>(val, + xc->readMiscReg(MISCREG_VXRM), sh); + Vd_vi[i] = val >> sh; + }}, OPIVV, VectorIntegerArithOp); + } + format VectorReduceIntWideningFormat { + 0x30: vwredsumu_vs({{ + Vd_vwu[0] = reduce_loop(std::plus(), + Vs1_vwu, Vs2_vu); + }}, OPIVV, VectorIntegerReduceOp); + 0x31: vwredsum_vs({{ + Vd_vwu[0] = reduce_loop(std::plus(), + Vs1_vwi, Vs2_vi); + }}, OPIVV, VectorIntegerReduceOp); + } + format VectorIntMaskFormat { + 0x11: decode VM { + 0x0: vmadc_vvm({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + carry_out(Vs2_vu[i], Vs1_vu[i], + elem_mask(v0, ei))); + }}, OPIVV, VectorIntegerArithOp); + 0x1: vmadc_vv({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + carry_out(Vs2_vu[i], Vs1_vu[i])); + }}, OPIVV, VectorIntegerArithOp); + } + 0x13: decode VM { + 0x0: vmsbc_vvm({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + borrow_out(Vs2_vi[i], Vs1_vi[i], + elem_mask(v0, ei))); + }}, OPIVV, VectorIntegerArithOp); + 0x1: vmsbc_vv({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + borrow_out(Vs2_vi[i], Vs1_vi[i])); + }}, OPIVV, VectorIntegerArithOp); + } + 0x18: vmseq_vv({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vu[i] == Vs1_vu[i])); + }}, OPIVV, VectorIntegerArithOp); + 0x19: vmsne_vv({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vu[i] != Vs1_vu[i])); + }}, OPIVV, VectorIntegerArithOp); + 0x1a: vmsltu_vv({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vu[i] < Vs1_vu[i])); + }}, OPIVV, VectorIntegerArithOp); + 0x1b: vmslt_vv({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vi[i] < Vs1_vi[i])); + }}, OPIVV, VectorIntegerArithOp); + 0x1c: vmsleu_vv({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vu[i] <= Vs1_vu[i])); + }}, OPIVV, VectorIntegerArithOp); + 0x1d: vmsle_vv({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vi[i] <= Vs1_vi[i])); + }}, OPIVV, VectorIntegerArithOp); + } + format VectorIntNarrowingFormat { + 0x2c: vnsrl_wv({{ + Vd_vu[i + offset] = (vu)(Vs2_vwu[i] >> + ((vwu)Vs1_vu[i + offset] & (sew * 2 - 1))); + }}, OPIVV, VectorIntegerArithOp); + 0x2d: vnsra_wv({{ + Vd_vi[i + offset] = (vi)(Vs2_vwi[i] >> + ((vwu)Vs1_vu[i + offset] & (sew * 2 - 1))); + }}, OPIVV, VectorIntegerArithOp); + 0x2e: vnclipu_wv({{ + vu max = std::numeric_limits::max(); + uint64_t sign_mask = + std::numeric_limits::max() << sew; + __uint128_t res = Vs2_vwu[i]; + unsigned shift = Vs1_vu[i + offset] & ((sew * 2) - 1); + + res = int_rounding<__uint128_t>( + res, 0 /* TODO */, shift) >> shift; + + if (res & sign_mask) { + res = max; + // TODO: vxsat + } + + Vd_vu[i + offset] = (vu)res; + }}, OPIVV, VectorIntegerArithOp); + 0x2f: vnclip_wv({{ + vi max = std::numeric_limits::max(); + vi min = std::numeric_limits::min(); + __int128_t res = Vs2_vwi[i]; + unsigned shift = Vs1_vi[i + offset] & ((sew * 2) - 1); + + res = int_rounding<__int128_t>( + res, 0 /* TODO */, shift) >> shift; + + if (res < min) { + res = min; + // TODO: vxsat + } else if (res > max) { + res = max; + // TODO: vxsat + } + + Vd_vi[i + offset] = (vi)res; + }}, OPIVV, VectorIntegerArithOp); + } + } + // OPFVV + 0x1: decode VFUNCT6 { + 0x00: VectorFloatFormat::vfadd_vv({{ + auto fd = fadd(ftype(Vs2_vu[i]), + ftype(Vs1_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x01: VectorReduceFloatFormat::vfredusum_vs({{ + Vd_vu[0] = reduce_loop([](const vu& src1, const vu& src2) { + return fadd(ftype(src1), ftype(src2)); + }, Vs1_vu, Vs2_vu); + }}, OPFVV, VectorFloatReduceOp); + 0x02: VectorFloatFormat::vfsub_vv({{ + auto fd = fsub(ftype(Vs2_vu[i]), + ftype(Vs1_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x03: VectorReduceFloatFormat::vfredosum_vs({{ + Vd_vu[0] = reduce_loop([](const vu& src1, const vu& src2) { + return fadd(ftype(src1), ftype(src2)); + }, Vs1_vu, Vs2_vu); + }}, OPFVV, VectorFloatReduceOp); + 0x04: VectorFloatFormat::vfmin_vv({{ + auto fd = fmin(ftype(Vs2_vu[i]), + ftype(Vs1_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x05: VectorReduceFloatFormat::vfredmin_vs({{ + Vd_vu[0] = reduce_loop([](const vu& src1, const vu& src2) { + return fmin(ftype(src1), ftype(src2)); + }, Vs1_vu, Vs2_vu); + }}, OPFVV, VectorFloatReduceOp); + 0x06: VectorFloatFormat::vfmax_vv({{ + auto fd = fmax(ftype(Vs2_vu[i]), + ftype(Vs1_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x07: VectorReduceFloatFormat::vfredmax_vs({{ + Vd_vu[0] = reduce_loop([](const vu& src1, const vu& src2) { + return fmax(ftype(src1), ftype(src2)); + }, Vs1_vu, Vs2_vu); + }}, OPFVV, VectorFloatReduceOp); + 0x08: VectorFloatFormat::vfsgnj_vv({{ + Vd_vu[i] = fsgnj(ftype(Vs2_vu[i]), + ftype(Vs1_vu[i]), + false, false).v; + }}, OPFVV, VectorFloatArithOp); + 0x09: VectorFloatFormat::vfsgnjn_vv({{ + Vd_vu[i] = fsgnj(ftype(Vs2_vu[i]), + ftype(Vs1_vu[i]), + true, false).v; + }}, OPFVV, VectorFloatArithOp); + 0x0a: VectorFloatFormat::vfsgnjx_vv({{ + Vd_vu[i] = fsgnj(ftype(Vs2_vu[i]), + ftype(Vs1_vu[i]), + false, true).v; + }}, OPFVV, VectorFloatArithOp); + // VWFUNARY0 + 0x10: decode VS1 { + 0x00: decode VM { + // The encodings corresponding to the masked versions + // (vm=0) of vfmv.f.s are reserved + 0x1: VectorNonSplitFormat::vfmv_f_s({{ + freg_t fd = freg(Vs2_vu[0]); + Fd_bits = fd.v; + }}, OPFVV, VectorMiscOp); + } + } + 0x12: decode VS1 { + format VectorFloatCvtFormat { + 0x00: vfcvt_xu_f_v({{ + Vd_vu[i] = f_to_ui(ftype(Vs2_vu[i]), + softfloat_roundingMode); + }}, OPFVV, VectorFloatConvertOp); + 0x01: vfcvt_x_f_v({{ + Vd_vu[i] = f_to_i(ftype(Vs2_vu[i]), + softfloat_roundingMode); + }}, OPFVV, VectorFloatConvertOp); + 0x02: vfcvt_f_xu_v({{ + auto fd = ui_to_f(Vs2_vu[i]); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatConvertOp); + 0x03: vfcvt_f_x_v({{ + auto fd = i_to_f(Vs2_vu[i]); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatConvertOp); + 0x06: vfcvt_rtz_xu_f_v({{ + Vd_vu[i] = f_to_ui(ftype(Vs2_vu[i]), + softfloat_round_minMag); + }}, OPFVV, VectorFloatConvertOp); + 0x07: vfcvt_rtz_x_f_v({{ + Vd_vu[i] = f_to_i(ftype(Vs2_vu[i]), + softfloat_round_minMag); + }}, OPFVV, VectorFloatConvertOp); + } + format VectorFloatWideningCvtFormat { + 0x08: vfwcvt_xu_f_v({{ + Vd_vwu[i] = f_to_wui( + ftype(Vs2_vu[i + offset]), + softfloat_roundingMode); + }}, OPFVV, VectorFloatConvertOp); + 0x09: vfwcvt_x_f_v({{ + Vd_vwu[i] = f_to_wi( + ftype(Vs2_vu[i + offset]), + softfloat_roundingMode); + }}, OPFVV, VectorFloatConvertOp); + 0x0a: vfwcvt_f_xu_v({{ + auto fd = ui_to_wf(Vs2_vu[i + offset]); + Vd_vwu[i] = fd.v; + }}, OPFVV, VectorFloatConvertOp); + 0x0b: vfwcvt_f_x_v({{ + auto fd = i_to_wf(Vs2_vu[i + offset]); + Vd_vwu[i] = fd.v; + }}, OPFVV, VectorFloatConvertOp); + 0x0c: vfwcvt_f_f_v({{ + auto fd = f_to_wf( + ftype(Vs2_vu[i + offset])); + Vd_vwu[i] = fd.v; + }}, OPFVV, VectorFloatConvertOp); + 0x0e: vfwcvt_rtz_xu_f_v({{ + Vd_vwu[i] = f_to_wui( + ftype(Vs2_vu[i + offset]), + softfloat_round_minMag); + }}, OPFVV, VectorFloatConvertOp); + 0x0f: vfwcvt_rtz_x_f_v({{ + Vd_vwu[i] = f_to_wi( + ftype(Vs2_vu[i + offset]), + softfloat_round_minMag); + }}, OPFVV, VectorFloatConvertOp); + } + format VectorFloatNarrowingCvtFormat { + 0x10: vfncvt_xu_f_w({{ + Vd_vu[i + offset] = f_to_nui( + ftype(Vs2_vwu[i]), + softfloat_roundingMode); + }}, OPFVV, VectorFloatConvertOp); + 0x11: vfncvt_x_f_w({{ + Vd_vu[i + offset] = f_to_ni( + ftype(Vs2_vwu[i]), + softfloat_roundingMode); + }}, OPFVV, VectorFloatConvertOp); + 0x12: vfncvt_f_xu_w({{ + auto fd = ui_to_nf(Vs2_vwu[i]); + Vd_vu[i + offset] = fd.v; + }}, OPFVV, VectorFloatConvertOp); + 0x13: vfncvt_f_x_w({{ + auto fd = i_to_nf(Vs2_vwu[i]); + Vd_vu[i + offset] = fd.v; + }}, OPFVV, VectorFloatConvertOp); + 0x14: vfncvt_f_f_w({{ + auto fd = f_to_nf(ftype(Vs2_vwu[i])); + Vd_vu[i + offset] = fd.v; + }}, OPFVV, VectorFloatConvertOp); + 0x15: vfncvt_rod_f_f_w({{ + softfloat_roundingMode = softfloat_round_odd; + auto fd = f_to_nf(ftype(Vs2_vwu[i])); + Vd_vu[i + offset] = fd.v; + }}, OPFVV, VectorFloatConvertOp); + 0x16: vfncvt_rtz_xu_f_w({{ + Vd_vu[i + offset] = f_to_nui( + ftype(Vs2_vwu[i]), + softfloat_round_minMag); + }}, OPFVV, VectorFloatConvertOp); + 0x17: vfncvt_rtz_x_f_w({{ + Vd_vu[i + offset] = f_to_ni( + ftype(Vs2_vwu[i]), + softfloat_round_minMag); + }}, OPFVV, VectorFloatConvertOp); + } + } + 0x13: decode VS1 { + format VectorFloatCvtFormat { + 0x00: vfsqrt_v({{ + auto fd = fsqrt(ftype(Vs2_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x04: vfrsqrt7_v({{ + auto fd = frsqrte7(ftype(Vs2_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x05: vfrec7_v({{ + auto fd = frecip7(ftype(Vs2_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x10: vfclass_v({{ + auto fd = fclassify(ftype(Vs2_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + } + } + + format VectorFloatMaskFormat { + 0x18: vmfeq_vv({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + feq(ftype(Vs2_vu[i]), + ftype(Vs1_vu[i]))); + }}, OPFVV, VectorFloatArithOp); + 0x19: vmfle_vv({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + fle(ftype(Vs2_vu[i]), + ftype(Vs1_vu[i]))); + }}, OPFVV, VectorFloatArithOp); + 0x1b: vmflt_vv({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + flt(ftype(Vs2_vu[i]), + ftype(Vs1_vu[i]))); + }}, OPFVV, VectorFloatArithOp); + 0x1c: vmfne_vv({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + !feq(ftype(Vs2_vu[i]), + ftype(Vs1_vu[i]))); + }}, OPFVV, VectorFloatArithOp); + } + format VectorFloatFormat { + 0x20: vfdiv_vv({{ + auto fd = fdiv(ftype(Vs2_vu[i]), + ftype(Vs1_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x24: vfmul_vv({{ + auto fd = fmul(ftype(Vs2_vu[i]), + ftype(Vs1_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x28: vfmadd_vv({{ + auto fd = fmadd(ftype(Vs3_vu[i]), + ftype(Vs1_vu[i]), + ftype(Vs2_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x29: vfnmadd_vv({{ + auto fd = fmadd(fneg(ftype(Vs3_vu[i])), + ftype(Vs1_vu[i]), + fneg(ftype(Vs2_vu[i]))); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x2a: vfmsub_vv({{ + auto fd = fmadd(ftype(Vs3_vu[i]), + ftype(Vs1_vu[i]), + fneg(ftype(Vs2_vu[i]))); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x2b: vfnmsub_vv({{ + auto fd = fmadd(fneg(ftype(Vs3_vu[i])), + ftype(Vs1_vu[i]), + ftype(Vs2_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x2c: vfmacc_vv({{ + auto fd = fmadd(ftype(Vs1_vu[i]), + ftype(Vs2_vu[i]), + ftype(Vs3_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x2d: vfnmacc_vv({{ + auto fd = fmadd(fneg(ftype(Vs1_vu[i])), + ftype(Vs2_vu[i]), + fneg(ftype(Vs3_vu[i]))); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x2e: vfmsac_vv({{ + auto fd = fmadd(ftype(Vs1_vu[i]), + ftype(Vs2_vu[i]), + fneg(ftype(Vs3_vu[i]))); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x2f: vfnmsac_vv({{ + auto fd = fmadd(fneg(ftype(Vs1_vu[i])), + ftype(Vs2_vu[i]), + ftype(Vs3_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x31: VectorReduceFloatWideningFormat::vfwredusum_vs({{ + Vd_vwu[0] = reduce_loop( + [](const vwu& src1, const vu& src2) { + return fadd( + ftype(src1), + f_to_wf(ftype(src2)) + ); + }, Vs1_vwu, Vs2_vu); + }}, OPFVV, VectorFloatReduceOp); + 0x33: VectorReduceFloatWideningFormat::vfwredosum_vs({{ + Vd_vwu[0] = reduce_loop( + [](const vwu& src1, const vu& src2) { + return fadd( + ftype(src1), + f_to_wf(ftype(src2)) + ); + }, Vs1_vwu, Vs2_vu); + }}, OPFVV, VectorFloatReduceOp); + } + format VectorFloatWideningFormat { + 0x30: vfwadd_vv({{ + auto fd = fadd( + fwiden(ftype(Vs2_vu[i + offset])), + fwiden(ftype(Vs1_vu[i + offset]))); + Vd_vwu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x32: vfwsub_vv({{ + auto fd = fsub( + fwiden(ftype(Vs2_vu[i + offset])), + fwiden(ftype(Vs1_vu[i + offset]))); + Vd_vwu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x34: vfwadd_wv({{ + auto fd = fadd( + ftype(Vs2_vwu[i]), + fwiden(ftype(Vs1_vu[i + offset]))); + Vd_vwu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x36: vfwsub_wv({{ + auto fd = fsub( + ftype(Vs2_vwu[i]), + fwiden(ftype(Vs1_vu[i + offset]))); + Vd_vwu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x38: vfwmul_vv({{ + auto fd = fmul( + fwiden(ftype(Vs2_vu[i + offset])), + fwiden(ftype(Vs1_vu[i + offset]))); + Vd_vwu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x3c: vfwmacc_vv({{ + auto fd = fmadd( + fwiden(ftype(Vs1_vu[i + offset])), + fwiden(ftype(Vs2_vu[i + offset])), + ftype(Vs3_vwu[i])); + Vd_vwu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x3d: vfwnmacc_vv({{ + auto fd = fmadd( + fwiden(fneg(ftype(Vs1_vu[i + offset]))), + fwiden(ftype(Vs2_vu[i + offset])), + fneg(ftype(Vs3_vwu[i]))); + Vd_vwu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x3e: vfwmsac_vv({{ + auto fd = fmadd( + fwiden(ftype(Vs1_vu[i + offset])), + fwiden(ftype(Vs2_vu[i + offset])), + fneg(ftype(Vs3_vwu[i]))); + Vd_vwu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + 0x3f: vfwnmsac_vv({{ + auto fd = fmadd( + fwiden(fneg(ftype(Vs1_vu[i + offset]))), + fwiden(ftype(Vs2_vu[i + offset])), + ftype(Vs3_vwu[i])); + Vd_vwu[i] = fd.v; + }}, OPFVV, VectorFloatArithOp); + } + } + // OPMVV + 0x2: decode VFUNCT6 { + format VectorReduceIntFormat { + 0x0: vredsum_vs({{ + Vd_vi[0] = + reduce_loop(std::plus(), Vs1_vi, Vs2_vi); + }}, OPMVV, VectorIntegerReduceOp); + 0x1: vredand_vs({{ + Vd_vi[0] = + reduce_loop(std::bit_and(), Vs1_vi, Vs2_vi); + }}, OPMVV, VectorIntegerReduceOp); + 0x2: vredor_vs({{ + Vd_vi[0] = + reduce_loop(std::bit_or(), Vs1_vi, Vs2_vi); + }}, OPMVV, VectorIntegerReduceOp); + 0x3: vredxor_vs({{ + Vd_vi[0] = + reduce_loop(std::bit_xor(), Vs1_vi, Vs2_vi); + }}, OPMVV, VectorIntegerReduceOp); + 0x4: vredminu_vs({{ + Vd_vu[0] = + reduce_loop([](const vu& src1, const vu& src2) { + return std::min(src1, src2); + }, Vs1_vu, Vs2_vu); + }}, OPMVV, VectorIntegerReduceOp); + 0x5: vredmin_vs({{ + Vd_vi[0] = + reduce_loop([](const vi& src1, const vi& src2) { + return std::min(src1, src2); + }, Vs1_vi, Vs2_vi); + }}, OPMVV, VectorIntegerReduceOp); + 0x6: vredmaxu_vs({{ + Vd_vu[0] = + reduce_loop([](const vu& src1, const vu& src2) { + return std::max(src1, src2); + }, Vs1_vu, Vs2_vu); + }}, OPMVV, VectorIntegerReduceOp); + 0x7: vredmax_vs({{ + Vd_vi[0] = + reduce_loop([](const vi& src1, const vi& src2) { + return std::max(src1, src2); + }, Vs1_vi, Vs2_vi); + }}, OPMVV, VectorIntegerReduceOp); + } + format VectorIntFormat { + 0x8: vaaddu_vv({{ + __uint128_t res = (__uint128_t)Vs2_vu[i] + Vs1_vu[i]; + res = int_rounding<__uint128_t>(res, 0 /* TODO */, 1); + Vd_vu[i] = res >> 1; + }}, OPMVV, VectorIntegerArithOp); + 0x9: vaadd_vv({{ + __uint128_t res = (__uint128_t)Vs2_vi[i] + Vs1_vi[i]; + res = int_rounding<__uint128_t>(res, 0 /* TODO */, 1); + Vd_vi[i] = res >> 1; + }}, OPMVV, VectorIntegerArithOp); + 0xa: vasubu_vv({{ + __uint128_t res = (__uint128_t)Vs2_vu[i] - Vs1_vu[i]; + res = int_rounding<__uint128_t>(res, 0 /* TODO */, 1); + Vd_vu[i] = res >> 1; + }}, OPMVV, VectorIntegerArithOp); + 0xb: vasub_vv({{ + __uint128_t res = (__uint128_t)Vs2_vi[i] - Vs1_vi[i]; + res = int_rounding<__uint128_t>(res, 0 /* TODO */, 1); + Vd_vi[i] = res >> 1; + }}, OPMVV, VectorIntegerArithOp); + } + // VWXUNARY0 + 0x10: decode VS1 { + 0x00: decode VM { + // The encodings corresponding to the masked versions + // (vm=0) of vmv.x.s are reserved. + 0x1: VectorNonSplitFormat::vmv_x_s({{ + Rd_ud = Vs2_vi[0]; + }}, OPMVV, VectorMiscOp); + } + 0x10: Vector1Vs1RdMaskFormat::vcpop_m({{ + uint64_t popcount = 0; + for (uint32_t i = 0; i < (uint32_t)machInst.vl; i++) { + bool vs2_lsb = elem_mask(Vs2_vu, i); + if(this->vm){ + popcount += vs2_lsb; + }else{ + bool do_mask = elem_mask(v0, i); + popcount += (vs2_lsb && do_mask); + } + } + Rd_vu = popcount; + }}, OPMVV, VectorMiscOp); + 0x11: Vector1Vs1RdMaskFormat::vfirst_m({{ + int64_t pos = -1; + for (uint32_t i = 0; i < (uint32_t)machInst.vl; i++) { + if(this->vm == 0){ + if(elem_mask(v0, i)==0){ + continue; + } + } + bool vs2_lsb = elem_mask(Vs2_vu, i); + if (vs2_lsb) { + pos = i; + break; + } + } + Rd_vu = pos; + }}, OPMVV, VectorMiscOp); + } + 0x12: decode VS1 { + format VectorIntExtFormat { + 0x02: vzext_vf8({{ + auto offset = (vlen / SEW) * index; + + Vd_vu[i] = Vs2_vextu[i + offset]; + }}, OPMVV, VectorIntegerExtensionOp); + 0x03: vsext_vf8({{ + auto offset = (vlen / SEW) * index; + + Vd_vi[i] = Vs2_vext[i + offset]; + }}, OPMVV, VectorIntegerExtensionOp); + 0x04: vzext_vf4({{ + auto offset = (vlen / SEW) * index; + + Vd_vu[i] = Vs2_vextu[i + offset]; + }}, OPMVV, VectorIntegerExtensionOp); + 0x05: vsext_vf4({{ + auto offset = (vlen / SEW) * index; + + Vd_vi[i] = Vs2_vext[i + offset]; + }}, OPMVV, VectorIntegerExtensionOp); + 0x06: vzext_vf2({{ + auto offset = (vlen / SEW) * index; + + Vd_vu[i] = Vs2_vextu[i + offset]; + }}, OPMVV, VectorIntegerExtensionOp); + 0x07: vsext_vf2({{ + auto offset = (vlen / SEW) * index; + + Vd_vi[i] = Vs2_vext[i + offset]; + }}, OPMVV, VectorIntegerExtensionOp); + } + } + 0x14: decode VS1 { + 0x01: Vector1Vs1VdMaskFormat::vmsbf_m({{ + bool has_one = false; + for (uint32_t i = 0; i < (uint32_t)machInst.vl; i++) { + bool vs2_lsb = elem_mask(Vs2_vu, i); + bool do_mask = elem_mask(v0, i); + if(this->vm||(this->vm == 0&&do_mask)){ + uint64_t res = 0; + if (!has_one && !vs2_lsb) { + res = 1; + } else if(!has_one && vs2_lsb) { + has_one = true; + } + Vd_ub[i/8] = ASSIGN_VD_BIT(i, res); + } + } + }}, OPMVV, VectorMiscOp); + 0x02: Vector1Vs1VdMaskFormat::vmsof_m({{ + bool has_one = false; + for (uint32_t i = 0; i < (uint32_t)machInst.vl; i++) { + bool vs2_lsb = elem_mask(Vs2_vu, i); + bool do_mask = elem_mask(v0, i); + if(this->vm||(this->vm == 0&&do_mask)){ + uint64_t res = 0; + if(!has_one && vs2_lsb) { + has_one = true; + res = 1; + } + Vd_ub[i/8] = ASSIGN_VD_BIT(i, res); + } + } + }}, OPMVV, VectorMiscOp); + 0x03: Vector1Vs1VdMaskFormat::vmsif_m({{ + bool has_one = false; + for (uint32_t i = 0; i < (uint32_t)machInst.vl; i++) { + bool vs2_lsb = elem_mask(Vs2_vu, i); + bool do_mask = elem_mask(v0, i); + if(this->vm||(this->vm == 0&&do_mask)){ + uint64_t res = 0; + if (!has_one && !vs2_lsb) { + res = 1; + } else if(!has_one && vs2_lsb) { + has_one = true; + res = 1; + } + Vd_ub[i/8] = ASSIGN_VD_BIT(i, res); + } + } + }}, OPMVV, VectorMiscOp); + 0x10: ViotaFormat::viota_m({{ + RiscvISAInst::VecRegContainer tmp_s2; + xc->getRegOperand(this, 2, + &tmp_s2); + auto Vs2bit = tmp_s2.as(); + for (uint32_t i = 0; i < this->microVl; i++) { + uint32_t ei = i + + vtype_VLMAX(vtype, vlen, true) * + this->microIdx; + bool vs2_lsb = elem_mask(Vs2bit, ei); + bool do_mask = elem_mask(v0, ei); + bool has_one = false; + if (this->vm || (do_mask && !this->vm)) { + if (vs2_lsb) { + has_one = true; + } + } + bool use_ori = (!this->vm) && !do_mask; + if(use_ori == false){ + Vd_vu[i] = *cnt; + } + if (has_one) { + *cnt = *cnt+1; + } + } + }}, OPMVV, VectorMiscOp); + 0x11: VectorIntFormat::vid_v({{ + Vd_vu[i] = ei; + }}, OPMVV, VectorMiscOp); + } + format VectorMaskFormat { + 0x18: vmandn_mm({{ + Vd_ub[i/8] = ASSIGN_VD_BIT(i, + elem_mask(Vs2_vu, i) & !elem_mask(Vs1_vu, i)); + }}, OPMVV, VectorMiscOp); + 0x19: vmand_mm({{ + Vd_ub[i/8] = ASSIGN_VD_BIT(i, + elem_mask(Vs2_vu, i) & elem_mask(Vs1_vu, i)); + }}, OPMVV, VectorMiscOp); + 0x1a: vmor_mm({{ + Vd_ub[i/8] = ASSIGN_VD_BIT(i, + elem_mask(Vs2_vu, i) | elem_mask(Vs1_vu, i)); + }}, OPMVV, VectorMiscOp); + 0x1b: vmxor_mm({{ + Vd_ub[i/8] = ASSIGN_VD_BIT(i, + elem_mask(Vs2_vu, i) ^ elem_mask(Vs1_vu, i)); + }}, OPMVV, VectorMiscOp); + 0x1c: vmorn_mm({{ + Vd_ub[i/8] = ASSIGN_VD_BIT(i, + elem_mask(Vs2_vu, i) | !elem_mask(Vs1_vu, i)); + }}, OPMVV, VectorMiscOp); + 0x1d: vmnand_mm({{ + Vd_ub[i/8] = ASSIGN_VD_BIT(i, + !(elem_mask(Vs2_vu, i) & elem_mask(Vs1_vu, i))); + }}, OPMVV, VectorMiscOp); + 0x1e: vmnor_mm({{ + Vd_ub[i/8] = ASSIGN_VD_BIT(i, + !(elem_mask(Vs2_vu, i) | elem_mask(Vs1_vu, i))); + }}, OPMVV, VectorMiscOp); + 0x1f: vmxnor_mm({{ + Vd_ub[i/8] = ASSIGN_VD_BIT(i, + !(elem_mask(Vs2_vu, i) ^ elem_mask(Vs1_vu, i))); + }}, OPMVV, VectorMiscOp); + } + format VectorIntFormat { + 0x20: vdivu_vv({{ + if (Vs1_vu[i] == 0) + Vd_vu[i] = (vu)-1; + else + Vd_vu[i] = Vs2_vu[i] / Vs1_vu[i]; + }}, OPMVV, VectorIntegerArithOp); + 0x21: vdiv_vv({{ + if (Vs1_vi[i] == 0) + Vd_vi[i] = -1; + else if (Vs2_vi[i] == std::numeric_limits::min() + && Vs1_vi[i] == -1) + Vd_vi[i] = Vs2_vi[i]; + else + Vd_vi[i] = Vs2_vi[i] / Vs1_vi[i]; + }}, OPMVV, VectorIntegerArithOp); + 0x22: vremu_vv({{ + if (Vs1_vu[i] == 0) { + Vd_vu[i] = Vs2_vu[i]; + } else { + Vd_vu[i] = Vs2_vu[i] % Vs1_vu[i]; + } + }}, OPMVV, VectorIntegerArithOp); + 0x23: vrem_vv({{ + if (Vs1_vi[i] == 0) { + Vd_vi[i] = Vs2_vi[i]; + } else if (Vs2_vi[i] == std::numeric_limits::min() + && Vs1_vi[i] == -1) { + Vd_vi[i] = 0; + } else { + Vd_vi[i] = Vs2_vi[i] % Vs1_vi[i]; + } + }}, OPMVV, VectorIntegerArithOp); + 0x24: vmulhu_vv({{ + if (sew < 64) { + Vd_vu[i] = ((uint64_t)Vs2_vu[i] * Vs1_vu[i]) + >> sew; + } else { + Vd_vu[i] = mulhu_64(Vs2_vu[i], Vs1_vu[i]); + } + }}, OPMVV, VectorIntegerArithOp); + 0x25: vmul_vv({{ + Vd_vi[i] = Vs2_vi[i] * Vs1_vi[i]; + }}, OPMVV, VectorIntegerArithOp); + 0x26: vmulhsu_vv({{ + if (sew < 64) { + Vd_vi[i] = ((int64_t)Vs2_vi[i] * + (uint64_t)Vs1_vu[i]) + >> sew; + } else { + Vd_vi[i] = mulhsu_64(Vs2_vi[i], Vs1_vu[i]); + } + }}, OPMVV, VectorIntegerArithOp); + 0x27: vmulh_vv({{ + if (sew < 64) { + Vd_vi[i] = ((int64_t)Vs2_vi[i] * Vs1_vi[i]) + >> sew; + } else { + Vd_vi[i] = mulh_64(Vs2_vi[i], Vs1_vi[i]); + } + }}, OPMVV, VectorIntegerArithOp); + 0x29: vmadd_vv({{ + Vd_vi[i] = Vs3_vi[i] * Vs1_vi[i] + Vs2_vi[i]; + }}, OPMVV, VectorIntegerArithOp); + 0x2b: vnmsub_vv({{ + Vd_vi[i] = -(Vs3_vi[i] * Vs1_vi[i]) + Vs2_vi[i]; + }}, OPMVV, VectorIntegerArithOp); + 0x2d: vmacc_vv({{ + Vd_vi[i] = Vs2_vi[i] * Vs1_vi[i] + Vs3_vi[i]; + }}, OPMVV, VectorIntegerArithOp); + 0x2f: vnmsac_vv({{ + Vd_vi[i] = -(Vs2_vi[i] * Vs1_vi[i]) + Vs3_vi[i]; + }}, OPMVV, VectorIntegerArithOp); + } + format VectorIntWideningFormat { + 0x30: vwaddu_vv({{ + Vd_vwu[i] = vwu(Vs2_vu[i + offset]) + + vwu(Vs1_vu[i + offset]); + }}, OPMVV, VectorIntegerArithOp); + 0x31: vwadd_vv({{ + Vd_vwi[i] = vwi(Vs2_vi[i + offset]) + + vwi(Vs1_vi[i + offset]); + }}, OPMVV, VectorIntegerArithOp); + 0x32: vwsubu_vv({{ + Vd_vwu[i] = vwu(Vs2_vu[i + offset]) + - vwu(Vs1_vu[i + offset]); + }}, OPMVV, VectorIntegerArithOp); + 0x33: vwsub_vv({{ + Vd_vwi[i] = vwi(Vs2_vi[i + offset]) + - vwi(Vs1_vi[i + offset]); + }}, OPMVV, VectorIntegerArithOp); + 0x34: vwaddu_wv({{ + Vd_vwu[i] = Vs2_vwu[i] + vwu(Vs1_vu[i + offset]); + }}, OPMVV, VectorIntegerArithOp); + 0x35: vwadd_wv({{ + Vd_vwi[i] = Vs2_vwi[i] + vwi(Vs1_vi[i + offset]); + }}, OPMVV, VectorIntegerArithOp); + 0x36: vwsubu_wv({{ + Vd_vwu[i] = Vs2_vwu[i] - vwu(Vs1_vu[i + offset]); + }}, OPMVV, VectorIntegerArithOp); + 0x37: vwsub_wv({{ + Vd_vwi[i] = Vs2_vwi[i] - vwi(Vs1_vi[i + offset]); + }}, OPMVV, VectorIntegerArithOp); + 0x38: vwmulu_vv({{ + Vd_vwu[i] = vwu(Vs2_vu[i + offset]) + * vwu(Vs1_vu[i + offset]); + }}, OPMVV, VectorIntegerArithOp); + 0x3a: vwmulsu_vv({{ + Vd_vwi[i] = vwi(Vs2_vi[i + offset]) + * vwu(Vs1_vu[i + offset]); + }}, OPMVV, VectorIntegerArithOp); + 0x3b: vwmul_vv({{ + Vd_vwi[i] = vwi(Vs2_vi[i + offset]) + * vwi(Vs1_vi[i + offset]); + }}, OPMVV, VectorIntegerArithOp); + 0x3c: vwmaccu_vv({{ + Vd_vwu[i] = vwu(Vs1_vu[i + offset]) + * vwu(Vs2_vu[i + offset]) + + Vs3_vwu[i]; + }}, OPMVV, VectorIntegerArithOp); + 0x3d: vwmacc_vv({{ + Vd_vwi[i] = vwi(Vs1_vi[i + offset]) + * vwi(Vs2_vi[i + offset]) + + Vs3_vwi[i]; + }}, OPMVV, VectorIntegerArithOp); + 0x3f: vwmaccsu_vv({{ + Vd_vwi[i] = vwi(Vs1_vi[i + offset]) + * vwu(Vs2_vu[i + offset]) + + Vs3_vwi[i]; + }}, OPMVV, VectorIntegerArithOp); + } + } + // OPIVI + 0x3: decode VFUNCT6 { + format VectorIntFormat { + 0x00: vadd_vi({{ + Vd_vi[i] = Vs2_vi[i] + (vi)sext<5>(SIMM5); + }}, OPIVI, VectorIntegerArithOp); + 0x03: vrsub_vi({{ + Vd_vi[i] = (vi)sext<5>(SIMM5) - Vs2_vi[i]; + }}, OPIVI, VectorIntegerArithOp); + 0x09: vand_vi({{ + Vd_vi[i] = Vs2_vi[i] & (vi)sext<5>(SIMM5); + }}, OPIVI, VectorIntegerArithOp); + 0x0a: vor_vi({{ + Vd_vi[i] = Vs2_vi[i] | (vi)sext<5>(SIMM5); + }}, OPIVI, VectorIntegerArithOp); + 0x0b: vxor_vi({{ + Vd_vi[i] = Vs2_vi[i] ^ (vi)sext<5>(SIMM5); + }}, OPIVI, VectorIntegerArithOp); + } + 0x0c: VectorGatherFormat::vrgather_vi({{ + for (uint32_t i = 0; i < microVl; i++) { + uint32_t ei = i + vs1_idx * vs1_elems + vs1_bias; + if (this->vm || elem_mask(v0, ei)) { + const uint64_t idx = + (uint64_t)sext<5>(SIMM5) - vs2_elems * vs2_idx; + Vd_vu[i] = ((uint64_t)sext<5>(SIMM5) >= vlmax) ? 0 + : (idx < vs2_elems) ? Vs2_vu[idx] + : Vs3_vu[i]; + } + } + }}, OPIVI, VectorMiscOp); + 0x0e: VectorSlideUpFormat::vslideup_vi({{ + const int offset = (int)(uint64_t)(SIMM5); + const int microVlmax = vtype_VLMAX(machInst.vtype8, + vlen, true); + const int vregOffset = vdIdx - vs2Idx; + const int offsetInVreg = offset - vregOffset * microVlmax; + if (std::abs(offsetInVreg) < uint32_t(microVlmax)) { + const int upperBound = (offsetInVreg >= 0) + ? microVlmax - offsetInVreg + : microVlmax + offsetInVreg; + const int vdOffset = (offsetInVreg >= 0) + ? offsetInVreg + : 0; + const int vs2Offset = (offsetInVreg >= 0) + ? 0 + : -offsetInVreg; + const int elemOffset = vdOffset + vdIdx * microVlmax; + for (int i = 0; + i < upperBound && i + vdOffset < microVl; + i++) { + if (this->vm || elem_mask(v0, i + elemOffset)) { + Vd_vu[i + vdOffset] = Vs2_vu[i + vs2Offset]; + } + } + } + }}, OPIVI, VectorMiscOp); + 0x0f: VectorSlideDownFormat::vslidedown_vi({{ + const int offset = (int)(uint64_t)(SIMM5); + const int microVlmax = vtype_VLMAX(machInst.vtype8, + vlen, true); + const int vregOffset = vs2Idx - vdIdx; + const int offsetInVreg = offset - vregOffset * microVlmax; + const int numVs2s = vtype_regs_per_group(vtype); + if (std::abs(offsetInVreg) < uint32_t(microVlmax)) { + const bool needZeroTail = numVs2s == vs2Idx + 1; + const int upperBound = (offsetInVreg >= 0) + ? microVlmax - offsetInVreg + : microVlmax + offsetInVreg; + const int vdOffset = (offsetInVreg >= 0) + ? 0 + : -offsetInVreg; + const int vs2Offset = (offsetInVreg >= 0) + ? offsetInVreg + : 0; + const int elemIdxBase = vdIdx * microVlmax; + vreg_t resVreg; + auto res = resVreg.as(); + for (int i = 0; + i < upperBound && i + vdOffset < microVl; + i++) { + res[i + vdOffset] = Vs2_vu[i + vs2Offset]; + } + if (needZeroTail) { + for (int i = upperBound + vdOffset; + i < microVlmax; i++) { + res[i] = 0; + } + } + for (int i = vdOffset; i < microVl ; i++) { + if (vm || elem_mask(v0, i + elemIdxBase)) { + Vd_vu[i] = res[i]; + } + } + } + }}, OPIVI, VectorMiscOp); + format VectorIntFormat { + 0x10: decode VM { + 0x0: vadc_vim({{ + Vd_vi[i] = Vs2_vi[i] + + (vi)sext<5>(SIMM5) + elem_mask(v0, ei); + }}, OPIVI, VectorIntegerArithOp); + // the unmasked versions (vm=1) are reserved + } + 0x17: decode VM { + 0x0: vmerge_vim({{ + Vd_vi[i] = elem_mask(v0, ei) + ? (vi)sext<5>(SIMM5) + : Vs2_vi[i]; + }}, OPIVI, VectorIntegerArithOp); + 0x1: vmv_v_i({{ + Vd_vi[i] = (vi)sext<5>(SIMM5); + }}, OPIVI, VectorIntegerArithOp); + } + } + format VectorIntVxsatFormat{ + 0x20: vsaddu_vi({{ + Vd_vu[i] = sat_addu(Vs2_vu[i], (vu)SIMM5, + vxsatptr); + }}, OPIVI, VectorIntegerArithOp); + 0x21: vsadd_vi({{ + Vd_vu[i] = sat_add(Vs2_vu[i], (vu)SIMM5, + vxsatptr); + }}, OPIVI, VectorIntegerArithOp); + } + format VectorIntFormat { + 0x25: vsll_vi({{ + Vd_vu[i] = Vs2_vu[i] << ((vu)SIMM5 & (sew - 1) & 0x1f); + }}, OPIVI, VectorIntegerArithOp); + 0x28: vsrl_vi({{ + Vd_vu[i] = Vs2_vu[i] >> ((vu)SIMM5 & (sew - 1) & 0x1f); + }}, OPIVI, VectorIntegerArithOp); + 0x2a: vssrl_vi({{ + int sh = SIMM5 & (vtype_SEW(vtype) - 1); + __uint128_t res = Vs2_vu[i]; + + res = int_rounding<__uint128_t>( + res, 0 /* TODO */, sh) >> sh; + + Vd_vu[i] = res; + }}, OPIVI, VectorIntegerArithOp); + 0x29: vsra_vi({{ + Vd_vi[i] = Vs2_vi[i] >> ((vu)SIMM5 & (sew - 1) & 0x1f); + }}, OPIVI, VectorIntegerArithOp); + 0x2b: vssra_vi({{ + int sh = SIMM5 & (sew - 1); + __int128_t val = Vs2_vi[i]; + + val = int_rounding<__int128_t>(val, + xc->readMiscReg(MISCREG_VXRM), sh); + Vd_vi[i] = val >> sh; + }}, OPIVI, VectorIntegerArithOp); + } + // According to Spec Section 16.6, + // vm must be 1 (unmasked) in vmvr.v instructions. + 0x27: decode VM { 0x1: decode SIMM3 { + format VMvWholeFormat { + 0x0: vmv1r_v({{ + Vd_ud[i] = Vs2_ud[i]; + }}, OPIVI, VectorMiscOp); + 0x1: vmv2r_v({{ + Vd_ud[i] = Vs2_ud[i]; + }}, OPIVI, VectorMiscOp); + 0x3: vmv4r_v({{ + Vd_ud[i] = Vs2_ud[i]; + }}, OPIVI, VectorMiscOp); + 0x7: vmv8r_v({{ + Vd_ud[i] = Vs2_ud[i]; + }}, OPIVI, VectorMiscOp); + } + }} + format VectorIntMaskFormat { + 0x11: decode VM { + 0x0: vmadc_vim({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + carry_out(Vs2_vi[i], (vi)sext<5>(SIMM5), + elem_mask(v0, ei))); + }}, OPIVI, VectorIntegerArithOp); + 0x1: vmadc_vi({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + carry_out(Vs2_vi[i], (vi)sext<5>(SIMM5))); + }}, OPIVI, VectorIntegerArithOp); + } + 0x18: vmseq_vi({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vi[i] == (vi)sext<5>(SIMM5))); + }}, OPIVI, VectorIntegerArithOp); + 0x19: vmsne_vi({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vi[i] != (vi)sext<5>(SIMM5))); + }}, OPIVI, VectorIntegerArithOp); + 0x1c: vmsleu_vi({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vu[i] <= (vu)sext<5>(SIMM5))); + }}, OPIVI, VectorIntegerArithOp); + 0x1d: vmsle_vi({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vi[i] <= (vi)sext<5>(SIMM5))); + }}, OPIVI, VectorIntegerArithOp); + 0x1e: vmsgtu_vi({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vu[i] > (vu)sext<5>(SIMM5))); + }}, OPIVI, VectorIntegerArithOp); + 0x1f: vmsgt_vi({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vi[i] > (vi)sext<5>(SIMM5))); + }}, OPIVI, VectorIntegerArithOp); + } + format VectorIntNarrowingFormat { + 0x2c: vnsrl_wi({{ + Vd_vu[i + offset] = (vu)(Vs2_vwu[i] >> + ((vwu)SIMM5 & (sew * 2 - 1))); + }}, OPIVI, VectorIntegerArithOp); + 0x2d: vnsra_wi({{ + Vd_vi[i + offset] = (vi)(Vs2_vwi[i] >> + ((vwu)SIMM5 & (sew * 2 - 1))); + }}, OPIVI, VectorIntegerArithOp); + 0x2e: vnclipu_wi({{ + vu max = std::numeric_limits::max(); + uint64_t sign_mask = + std::numeric_limits::max() << sew; + __uint128_t res = Vs2_vwu[i]; + unsigned shift = VS1 & ((sew * 2) - 1); + + res = int_rounding<__uint128_t>( + res, 0 /* TODO */, shift) >> shift; + + if (res & sign_mask) { + // TODO: vxsat + res = max; + } + + Vd_vu[i + offset] = (vu)res; + }}, OPIVI, VectorIntegerArithOp); + 0x2f: vnclip_wi({{ + vi max = std::numeric_limits::max(); + vi min = std::numeric_limits::min(); + __int128_t res = Vs2_vwi[i]; + unsigned shift = VS1 & ((sew * 2) - 1); + + res = int_rounding<__int128_t>( + res, 0 /* TODO */, shift) >> shift; + + if (res < min) { + res = min; + // TODO: vxsat + } else if (res > max) { + res = max; + // TODO: vxsat + } + + Vd_vi[i + offset] = (vi)res; + }}, OPIVI, VectorIntegerArithOp); + } + } + // OPIVX + 0x4: decode VFUNCT6 { + format VectorIntFormat { + 0x0: vadd_vx({{ + Vd_vu[i] = Vs2_vu[i] + Rs1_vu; + }}, OPIVX, VectorIntegerArithOp); + 0x2: vsub_vx({{ + Vd_vu[i] = Vs2_vu[i] - Rs1_vu; + }}, OPIVX, VectorIntegerArithOp); + 0x3: vrsub_vx({{ + Vd_vu[i] = Rs1_vu - Vs2_vu[i]; + }}, OPIVX, VectorIntegerArithOp); + 0x4: vminu_vx({{ + Vd_vu[i] = std::min(Vs2_vu[i], Rs1_vu); + }}, OPIVX, VectorIntegerArithOp); + 0x5: vmin_vx({{ + Vd_vi[i] = std::min(Vs2_vi[i], Rs1_vi); + }}, OPIVX, VectorIntegerArithOp); + 0x6: vmaxu_vx({{ + Vd_vu[i] = std::max(Vs2_vu[i], Rs1_vu); + }}, OPIVX, VectorIntegerArithOp); + 0x7: vmax_vx({{ + Vd_vi[i] = std::max(Vs2_vi[i], Rs1_vi); + }}, OPIVX, VectorIntegerArithOp); + 0x9: vand_vx({{ + Vd_vu[i] = Vs2_vu[i] & Rs1_vu; + }}, OPIVX, VectorIntegerArithOp); + 0xa: vor_vx({{ + Vd_vu[i] = Vs2_vu[i] | Rs1_vu; + }}, OPIVX, VectorIntegerArithOp); + 0xb: vxor_vx({{ + Vd_vu[i] = Vs2_vu[i] ^ Rs1_vu; + }}, OPIVX, VectorIntegerArithOp); + } + 0x0e: VectorSlideUpFormat::vslideup_vx({{ + const int offset = (int)Rs1_vu; + const int microVlmax = vtype_VLMAX(machInst.vtype8, + vlen, true); + const int vregOffset = vdIdx - vs2Idx; + const int offsetInVreg = offset - vregOffset * microVlmax; + if (std::abs(offsetInVreg) < uint32_t(microVlmax)) { + const int upperBound = (offsetInVreg >= 0) + ? microVlmax - offsetInVreg + : microVlmax + offsetInVreg; + const int vdOffset = (offsetInVreg >= 0) + ? offsetInVreg + : 0; + const int vs2Offset = (offsetInVreg >= 0) + ? 0 + : -offsetInVreg; + const int elemOffset = vdOffset + vdIdx * microVlmax; + for (int i = 0; + i < upperBound && i + vdOffset < microVl; + i++) { + if (this->vm || elem_mask(v0, i + elemOffset)) { + Vd_vu[i + vdOffset] = Vs2_vu[i + vs2Offset]; + } + } + } + }}, OPIVX, VectorMiscOp); + 0x0f: VectorSlideDownFormat::vslidedown_vx({{ + const int offset = (int)Rs1_vu; + const int microVlmax = vtype_VLMAX(machInst.vtype8, + vlen, true); + const int vregOffset = vs2Idx - vdIdx; + const int offsetInVreg = offset - vregOffset * microVlmax; + const int numVs2s = vtype_regs_per_group(vtype); + if (std::abs(offsetInVreg) < uint32_t(microVlmax)) { + const bool needZeroTail = numVs2s == vs2Idx + 1; + const int upperBound = (offsetInVreg >= 0) + ? microVlmax - offsetInVreg + : microVlmax + offsetInVreg; + const int vdOffset = (offsetInVreg >= 0) + ? 0 + : -offsetInVreg; + const int vs2Offset = (offsetInVreg >= 0) + ? offsetInVreg + : 0; + const int elemIdxBase = vdIdx * microVlmax; + vreg_t resVreg; + auto res = resVreg.as(); + for (int i = 0; + i < upperBound && i + vdOffset < microVl; + i++) { + res[i + vdOffset] = Vs2_vu[i + vs2Offset]; + } + if (needZeroTail) { + for (int i = upperBound + vdOffset; + i < microVlmax; i++) { + res[i] = 0; + } + } + for (int i = vdOffset; i < microVl ; i++) { + if (vm || elem_mask(v0, i + elemIdxBase)) { + Vd_vu[i] = res[i]; + } + } + } + }}, OPIVX, VectorMiscOp); + 0x0c: VectorGatherFormat::vrgather_vx({{ + for (uint32_t i = 0; i < microVl; i++) { + uint32_t ei = i + vs1_idx * vs1_elems + vs1_bias; + if (this->vm || elem_mask(v0, ei)) { + const uint64_t idx = Rs1_vu - vs2_elems * vs2_idx; + Vd_vu[i] = (Rs1_vu >= vlmax) ? 0 + : (idx < vs2_elems) ? Vs2_vu[idx] + : Vs3_vu[i]; + } + } + }}, OPIVX, VectorMiscOp); + format VectorIntFormat { + 0x10: decode VM { + 0x0: vadc_vxm({{ + Vd_vi[i] = Vs2_vi[i] + Rs1_vi + elem_mask(v0, ei); + }}, OPIVX, VectorIntegerArithOp); + // the unmasked versions (vm=1) are reserved + } + 0x12: decode VM { + 0x0: vsbc_vxm({{ + Vd_vi[i] = Vs2_vi[i] - Rs1_vi - elem_mask(v0, ei); + }}, OPIVX, VectorIntegerArithOp); + // the unmasked versions (vm=1) are reserved + } + 0x17: decode VM { + 0x0: vmerge_vxm({{ + Vd_vu[i] = elem_mask(v0, ei) ? Rs1_vu : Vs2_vu[i]; + }}, OPIVX, VectorIntegerArithOp); + 0x1: decode VS2 { + 0x0: vmv_v_x({{ + Vd_vu[i] = Rs1_vu; + }}, OPIVX, VectorIntegerArithOp); + } + } + } + format VectorIntVxsatFormat{ + 0x20: vsaddu_vx({{ + Vd_vu[i] = sat_addu(Vs2_vu[i], Rs1_vu, + vxsatptr); + }}, OPIVX, VectorIntegerArithOp); + 0x21: vsadd_vx({{ + Vd_vu[i] = sat_add(Vs2_vu[i], Rs1_vu, + vxsatptr); + }}, OPIVX, VectorIntegerArithOp); + 0x22: vssubu_vx({{ + Vd_vu[i] = sat_subu(Vs2_vu[i], Rs1_vu, + vxsatptr); + }}, OPIVX, VectorIntegerArithOp); + 0x23: vssub_vx({{ + Vd_vu[i] = sat_sub(Vs2_vu[i], Rs1_vu, + vxsatptr); + }}, OPIVX, VectorIntegerArithOp); + 0x27: vsmul_vx({{ + vi max = std::numeric_limits::max(); + vi min = std::numeric_limits::min(); + bool overflow = Rs1_vi == Vs2_vi[i] && Rs1_vi == min; + __int128_t result = + (__int128_t)Rs1_vi * (__int128_t)Vs2_vi[i]; + result = int_rounding<__uint128_t>( + result, 0 /* TODO */, sew - 1); + result = result >> (sew - 1); + if (overflow) { + result = max; + *vxsatptr = true; + } + + Vd_vi[i] = (vi)result; + }}, OPIVX, VectorIntegerArithOp); + } + format VectorIntFormat { + 0x25: vsll_vx({{ + Vd_vu[i] = Vs2_vu[i] << (Rs1_vu & (sew - 1)); + }}, OPIVX, VectorIntegerArithOp); + 0x28: vsrl_vx({{ + Vd_vu[i] = Vs2_vu[i] >> (Rs1_vu & (sew - 1)); + }}, OPIVX, VectorIntegerArithOp); + 0x29: vsra_vx({{ + Vd_vi[i] = Vs2_vi[i] >> (Rs1_vu & (sew - 1)); + }}, OPIVX, VectorIntegerArithOp); + 0x2a: vssrl_vx({{ + int sh = Rs1_vu & (sew - 1); + __uint128_t val = Vs2_vu[i]; + + val = int_rounding<__uint128_t>(val, + xc->readMiscReg(MISCREG_VXRM), sh); + Vd_vu[i] = val >> sh; + }}, OPIVX, VectorIntegerArithOp); + 0x2b: vssra_vx({{ + int sh = Rs1_vu & (sew - 1); + __int128_t val = Vs2_vi[i]; + + val = int_rounding<__int128_t>(val, + xc->readMiscReg(MISCREG_VXRM), sh); + Vd_vi[i] = val >> sh; + }}, OPIVX, VectorIntegerArithOp); + } + format VectorIntNarrowingFormat { + 0x2c: vnsrl_wx({{ + Vd_vu[i + offset] = (vu)(Vs2_vwu[i] >> + ((vwu)Rs1_vu & (sew * 2 - 1))); + }}, OPIVX, VectorIntegerArithOp); + 0x2d: vnsra_wx({{ + Vd_vi[i + offset] = (vi)(Vs2_vwi[i] >> + ((vwu)Rs1_vu & (sew * 2 - 1))); + }}, OPIVX, VectorIntegerArithOp); + 0x2e: vnclipu_wx({{ + vu max = std::numeric_limits::max(); + uint64_t sign_mask = + std::numeric_limits::max() << sew; + __uint128_t res = Vs2_vwu[i]; + unsigned shift = Rs1_vu & ((sew * 2) - 1); + + res = int_rounding<__uint128_t>( + res, 0 /* TODO */, shift) >> shift; + + if (res & sign_mask) { + // TODO: vxsat + res = max; + } + + Vd_vu[i + offset] = (vu)res; + }}, OPIVX, VectorIntegerArithOp); + 0x2f: vnclip_wx({{ + vi max = std::numeric_limits::max(); + vi min = std::numeric_limits::min(); + __int128_t res = Vs2_vwi[i]; + unsigned shift = Rs1_vi & ((sew * 2) - 1); + + res = int_rounding<__int128_t>( + res, 0 /* TODO */, shift) >> shift; + + if (res < min) { + res = min; + // TODO: vxsat + } else if (res > max) { + res = max; + // TODO: vxsat + } + + Vd_vi[i + offset] = (vi)res; + }}, OPIVX, VectorIntegerArithOp); + } + + format VectorIntMaskFormat { + 0x11: decode VM { + 0x0: vmadc_vxm({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + carry_out(Vs2_vi[i], Rs1_vi, + elem_mask(v0, ei))); + }}, OPIVX, VectorIntegerArithOp); + 0x1: vmadc_vx({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + carry_out(Vs2_vi[i], Rs1_vi)); + }}, OPIVX, VectorIntegerArithOp); + } + 0x13: decode VM { + 0x0: vmsbc_vxm({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + borrow_out(Vs2_vi[i], Rs1_vi, + elem_mask(v0, ei))); + }}, OPIVX, VectorIntegerArithOp); + 0x1: vmsbc_vx({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + borrow_out(Vs2_vi[i], Rs1_vi)); + }}, OPIVX, VectorIntegerArithOp); + } + 0x18: vmseq_vx({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vu[i] == Rs1_vu)); + }}, OPIVX, VectorIntegerArithOp); + 0x19: vmsne_vx({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vu[i] != Rs1_vu)); + }}, OPIVX, VectorIntegerArithOp); + 0x1a: vmsltu_vx({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vu[i] < Rs1_vu)); + }}, OPIVX, VectorIntegerArithOp); + 0x1b: vmslt_vx({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vi[i] < Rs1_vi)); + }}, OPIVX, VectorIntegerArithOp); + 0x1c: vmsleu_vx({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vu[i] <= Rs1_vu)); + }}, OPIVX, VectorIntegerArithOp); + 0x1d: vmsle_vx({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vi[i] <= Rs1_vi)); + }}, OPIVX, VectorIntegerArithOp); + 0x1e: vmsgtu_vx({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vu[i] > Rs1_vu)); + }}, OPIVX, VectorIntegerArithOp); + 0x1f: vmsgt_vx({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + (Vs2_vi[i] > Rs1_vi)); + }}, OPIVX, VectorIntegerArithOp); + } + } + // OPFVF + 0x5: decode VFUNCT6 { + format VectorFloatFormat{ + 0x00: vfadd_vf({{ + auto fd = fadd(ftype(Vs2_vu[i]), + ftype_freg(freg(Fs1_bits))); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x02: vfsub_vf({{ + auto fd = fsub(ftype(Vs2_vu[i]), + ftype_freg(freg(Fs1_bits))); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x04: vfmin_vf({{ + auto fd = fmin(ftype(Vs2_vu[i]), + ftype_freg(freg(Fs1_bits))); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x06: vfmax_vf({{ + auto fd = fmax(ftype(Vs2_vu[i]), + ftype_freg(freg(Fs1_bits))); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x08: vfsgnj_vf({{ + Vd_vu[i] = fsgnj(ftype(Vs2_vu[i]), + ftype_freg(freg(Fs1_bits)), + false, false).v; + }}, OPFVF, VectorFloatArithOp); + 0x09: vfsgnjn_vf({{ + Vd_vu[i] = fsgnj(ftype(Vs2_vu[i]), + ftype_freg(freg(Fs1_bits)), + true, false).v; + }}, OPFVF, VectorFloatArithOp); + 0x0a: vfsgnjx_vf({{ + Vd_vu[i] = fsgnj(ftype(Vs2_vu[i]), + ftype_freg(freg(Fs1_bits)), + false, true).v; + }}, OPFVF, VectorFloatArithOp); + } + 0x0e: VectorFloatSlideUpFormat::vfslide1up_vf({{ + const int offset = 1; + const int microVlmax = vtype_VLMAX(machInst.vtype8, + vlen, true); + const int vregOffset = vdIdx - vs2Idx; + const int offsetInVreg = offset - vregOffset * microVlmax; + if (std::abs(offsetInVreg) < uint32_t(microVlmax)) { + const int upperBound = (offsetInVreg >= 0) + ? microVlmax - offsetInVreg + : microVlmax + offsetInVreg; + const int vdOffset = (offsetInVreg >= 0) + ? offsetInVreg + : 0; + const int vs2Offset = (offsetInVreg >= 0) + ? 0 + : -offsetInVreg; + const int elemOffset = vdOffset + vdIdx * microVlmax; + for (int i = 0; + i < upperBound && i + vdOffset < microVl; + i++) { + if (this->vm || elem_mask(v0, i + elemOffset)) { + Vd_vu[i + vdOffset] = Vs2_vu[i + vs2Offset]; + } + } + // TODO: dirty code + if (vdIdx == 0 && vs2Idx == 0 && + (this->vm || elem_mask(v0, 0))) { + tmp_d0.as()[0] = Rs1_vu; + } + } + }}, OPFVF, VectorMiscOp); + 0x0f: VectorFloatSlideDownFormat::vfslide1down_vf({{ + const int offset = 1; + const int microVlmax = vtype_VLMAX(machInst.vtype8, + vlen, true); + const int vregOffset = vs2Idx - vdIdx; + const int offsetInVreg = offset - vregOffset * microVlmax; + const int numVs2s = vtype_regs_per_group(vtype); + if (std::abs(offsetInVreg) < uint32_t(microVlmax)) { + const bool needZeroTail = numVs2s == vs2Idx + 1; + const int upperBound = (offsetInVreg >= 0) + ? microVlmax - offsetInVreg + : microVlmax + offsetInVreg; + const int vdOffset = (offsetInVreg >= 0) + ? 0 + : -offsetInVreg; + const int vs2Offset = (offsetInVreg >= 0) + ? offsetInVreg + : 0; + const int elemIdxBase = vdIdx * microVlmax; + vreg_t resVreg; + auto res = resVreg.as(); + for (int i = 0; + i < upperBound && i + vdOffset < microVl; + i++) { + res[i + vdOffset] = Vs2_vu[i + vs2Offset]; + } + if (needZeroTail) { + for (int i = upperBound + vdOffset; + i < microVlmax; i++) { + res[i] = 0; + } + } + for (int i = vdOffset; i < microVl ; i++) { + if (vm || elem_mask(v0, i + elemIdxBase)) { + Vd_vu[i] = (i + elemIdxBase != machInst.vl - 1) + ? res[i] + : Rs1_vu; + } + } + } + }}, OPFVF, VectorMiscOp); + // VRFUNARY0 + 0x10: decode VS2 { + 0x00: decode VM { + // The encodings corresponding to the masked versions + // (vm=0) of vfmv.s.f are reserved + 0x1: VectorNonSplitFormat::vfmv_s_f({{ + auto fd = ftype_freg(freg(Fs1_bits)); + Vd_vu[0] = fd.v; + }}, OPFVV, VectorMiscOp); + } + } + format VectorFloatFormat{ + 0x17: decode VM { + 0x0: vfmerge_vfm({{ + Vd_vu[i] = elem_mask(v0, ei) + ? ftype_freg(freg(Fs1_bits)).v + : Vs2_vu[i]; + }}, OPFVF, VectorFloatArithOp); + 0x1: vfmv_v_f({{ + auto fd = ftype_freg(freg(Fs1_bits)); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + } + } + format VectorFloatMaskFormat { + 0x18: vmfeq_vf({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + feq(ftype(Vs2_vu[i]), + ftype_freg(freg(Fs1_bits)))); + }}, OPFVF, VectorFloatArithOp); + 0x19: vmfle_vf({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + fle(ftype(Vs2_vu[i]), + ftype_freg(freg(Fs1_bits)))); + }}, OPFVF, VectorFloatArithOp); + 0x1b: vmflt_vf({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + flt(ftype(Vs2_vu[i]), + ftype_freg(freg(Fs1_bits)))); + }}, OPFVF, VectorFloatArithOp); + 0x1c: vmfne_vf({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + !feq(ftype(Vs2_vu[i]), + ftype_freg(freg(Fs1_bits)))); + }}, OPFVF, VectorFloatArithOp); + 0x1d: vmfgt_vf({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + flt(ftype_freg(freg(Fs1_bits)), + ftype(Vs2_vu[i]))); + }}, OPFVF, VectorFloatArithOp); + 0x1f: vmfge_vf({{ + Vd_ub[(i + offset)/8] = ASSIGN_VD_BIT(i + offset, + fle(ftype_freg(freg(Fs1_bits)), + ftype(Vs2_vu[i]))); + }}, OPFVF, VectorFloatArithOp); + } + format VectorFloatFormat{ + 0x20: vfdiv_vf({{ + auto fd = fdiv(ftype(Vs2_vu[i]), + ftype_freg(freg(Fs1_bits))); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x21: vfrdiv_vf({{ + auto fd = fdiv(ftype_freg(freg(Fs1_bits)), + ftype(Vs2_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x24: vfmul_vf({{ + auto fd = fmul(ftype(Vs2_vu[i]), + ftype_freg(freg(Fs1_bits))); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x27: vfrsub_vf({{ + auto fd = fsub(ftype_freg(freg(Fs1_bits)), + ftype(Vs2_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x28: vfmadd_vf({{ + auto fd = fmadd(ftype(Vs3_vu[i]), + ftype_freg(freg(Fs1_bits)), + ftype(Vs2_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x29: vfnmadd_vf({{ + auto fd = fmadd(fneg(ftype(Vs3_vu[i])), + ftype_freg(freg(Fs1_bits)), + fneg(ftype(Vs2_vu[i]))); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x2a: vfmsub_vf({{ + auto fd = fmadd(ftype(Vs3_vu[i]), + ftype_freg(freg(Fs1_bits)), + fneg(ftype(Vs2_vu[i]))); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x2b: vfnmsub_vf({{ + auto fd = fmadd(fneg(ftype(Vs3_vu[i])), + ftype_freg(freg(Fs1_bits)), + ftype(Vs2_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x2c: vfmacc_vf({{ + auto fd = fmadd(ftype_freg(freg(Fs1_bits)), + ftype(Vs2_vu[i]), + ftype(Vs3_vu[i])); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x2d: vfnmacc_vf({{ + auto fd = fmadd( + fneg(ftype_freg(freg(Fs1_bits))), + ftype(Vs2_vu[i]), + fneg(ftype(Vs3_vu[i])) + ); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x2e: vfmsac_vf({{ + auto fd = fmadd(ftype_freg(freg(Fs1_bits)), + ftype(Vs2_vu[i]), + fneg(ftype(Vs3_vu[i]))); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x2f: vfnmsac_vf({{ + auto fd = fmadd( + fneg(ftype_freg(freg(Fs1_bits))), + ftype(Vs2_vu[i]), + ftype(Vs3_vu[i]) + ); + Vd_vu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + } + format VectorFloatWideningFormat { + 0x30: vfwadd_vf({{ + auto fd = fadd( + fwiden(ftype(Vs2_vu[i + offset])), + fwiden(ftype_freg(freg(Fs1_bits)))); + Vd_vwu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x32: vfwsub_vf({{ + auto fd = fsub( + fwiden(ftype(Vs2_vu[i + offset])), + fwiden(ftype_freg(freg(Fs1_bits)))); + Vd_vwu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x34: vfwadd_wf({{ + auto fd = fadd( + ftype(Vs2_vwu[i]), + fwiden(ftype_freg(freg(Fs1_bits)))); + Vd_vwu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x36: vfwsub_wf({{ + auto fd = fsub( + ftype(Vs2_vwu[i]), + fwiden(ftype_freg(freg(Fs1_bits)))); + Vd_vwu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x38: vfwmul_vf({{ + auto fd = fmul( + fwiden(ftype(Vs2_vu[i + offset])), + fwiden(ftype_freg(freg(Fs1_bits)))); + Vd_vwu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x3c: vfwmacc_vf({{ + auto fd = fmadd( + fwiden(ftype_freg(freg(Fs1_bits))), + fwiden(ftype(Vs2_vu[i + offset])), + ftype(Vs3_vwu[i])); + Vd_vwu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x3d: vfwnmacc_vf({{ + auto fd = fmadd( + fwiden(fneg(ftype_freg(freg(Fs1_bits)))), + fwiden(ftype(Vs2_vu[i + offset])), + fneg(ftype(Vs3_vwu[i]))); + Vd_vwu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x3e: vfwmsac_vf({{ + auto fd = fmadd( + fwiden(ftype_freg(freg(Fs1_bits))), + fwiden(ftype(Vs2_vu[i + offset])), + fneg(ftype(Vs3_vwu[i]))); + Vd_vwu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + 0x3f: vfwnmsac_vf({{ + auto fd = fmadd( + fwiden(fneg(ftype_freg(freg(Fs1_bits)))), + fwiden(ftype(Vs2_vu[i + offset])), + ftype(Vs3_vwu[i])); + Vd_vwu[i] = fd.v; + }}, OPFVF, VectorFloatArithOp); + } + } + // OPMVX + 0x6: decode VFUNCT6 { + format VectorIntFormat { + 0x08: vaaddu_vx({{ + __uint128_t res = (__uint128_t)Vs2_vu[i] + Rs1_vu; + res = int_rounding<__uint128_t>(res, 0 /* TODO */, 1); + Vd_vu[i] = res >> 1; + }}, OPMVX, VectorIntegerArithOp); + 0x09: vaadd_vx({{ + __uint128_t res = (__uint128_t)Vs2_vi[i] + Rs1_vi; + res = int_rounding<__uint128_t>(res, 0 /* TODO */, 1); + Vd_vi[i] = res >> 1; + }}, OPMVX, VectorIntegerArithOp); + } + 0x0e: VectorSlideUpFormat::vslide1up_vx({{ + const int offset = 1; + const int microVlmax = vtype_VLMAX(machInst.vtype8, + vlen, true); + const int vregOffset = vdIdx - vs2Idx; + const int offsetInVreg = offset - vregOffset * microVlmax; + if (std::abs(offsetInVreg) < uint32_t(microVlmax)) { + const int upperBound = (offsetInVreg >= 0) + ? microVlmax - offsetInVreg + : microVlmax + offsetInVreg; + const int vdOffset = (offsetInVreg >= 0) + ? offsetInVreg + : 0; + const int vs2Offset = (offsetInVreg >= 0) + ? 0 + : -offsetInVreg; + const int elemOffset = vdOffset + vdIdx * microVlmax; + for (int i = 0; + i < upperBound && i + vdOffset < microVl; + i++) { + if (this->vm || elem_mask(v0, i + elemOffset)) { + Vd_vu[i + vdOffset] = Vs2_vu[i + vs2Offset]; + } + } + // TODO: dirty code + if (vdIdx == 0 && vs2Idx == 0 && + (this->vm || elem_mask(v0, 0))) { + tmp_d0.as()[0] = Rs1_vu; + } + } + }}, OPIVX, VectorMiscOp); + 0x0f: VectorSlideDownFormat::vslide1down_vx({{ + const int offset = 1; + const int microVlmax = vtype_VLMAX(machInst.vtype8, + vlen, true); + const int vregOffset = vs2Idx - vdIdx; + const int offsetInVreg = offset - vregOffset * microVlmax; + const int numVs2s = vtype_regs_per_group(vtype); + if (std::abs(offsetInVreg) < uint32_t(microVlmax)) { + const bool needZeroTail = numVs2s == vs2Idx + 1; + const int upperBound = (offsetInVreg >= 0) + ? microVlmax - offsetInVreg + : microVlmax + offsetInVreg; + const int vdOffset = (offsetInVreg >= 0) + ? 0 + : -offsetInVreg; + const int vs2Offset = (offsetInVreg >= 0) + ? offsetInVreg + : 0; + const int elemIdxBase = vdIdx * microVlmax; + vreg_t resVreg; + auto res = resVreg.as(); + for (int i = 0; + i < upperBound && i + vdOffset < microVl; + i++) { + res[i + vdOffset] = Vs2_vu[i + vs2Offset]; + } + if (needZeroTail) { + for (int i = upperBound + vdOffset; + i < microVlmax; i++) { + res[i] = 0; + } + } + for (int i = vdOffset; i < microVl ; i++) { + if (vm || elem_mask(v0, i + elemIdxBase)) { + Vd_vu[i] = (i + elemIdxBase != machInst.vl - 1) + ? res[i] + : Rs1_vu; + } + } + } + }}, OPIVX, VectorMiscOp); + // VRXUNARY0 + 0x10: decode VS2 { + 0x00: decode VM { + // The encodings corresponding to the masked versions + // (vm=0) of vmv.s.x are reserved. + 0x1: VectorNonSplitFormat::vmv_s_x({{ + Vd_vu[0] = Rs1_vu; + }}, OPMVX, VectorMiscOp); + } + } + format VectorIntFormat { + 0x0a: vasubu_vx({{ + __uint128_t res = (__uint128_t)Vs2_vu[i] - Rs1_vu; + res = int_rounding<__uint128_t>(res, 0 /* TODO */, 1); + Vd_vu[i] = res >> 1; + }}, OPMVX, VectorIntegerArithOp); + 0x0b: vasub_vx({{ + __uint128_t res = (__uint128_t)Vs2_vi[i] - Rs1_vi; + res = int_rounding<__uint128_t>(res, 0 /* TODO */, 1); + Vd_vi[i] = res >> 1; + }}, OPMVX, VectorIntegerArithOp); + 0x20: vdivu_vx({{ + if (Rs1_vu == 0) + Vd_vu[i] = (vu)-1; + else + Vd_vu[i] = Vs2_vu[i] / Rs1_vu; + }}, OPMVX, VectorIntegerArithOp); + 0x21: vdiv_vx({{ + if (Rs1_vi == 0) + Vd_vi[i] = -1; + else if (Vs2_vi[i] == std::numeric_limits::min() + && Rs1_vi == -1) + Vd_vi[i] = Vs2_vi[i]; + else + Vd_vi[i] = Vs2_vi[i] / Rs1_vi; + }}, OPMVX, VectorIntegerArithOp); + 0x22: vremu_vx({{ + if (Rs1_vu == 0) + Vd_vu[i] = Vs2_vu[i]; + else + Vd_vu[i] = Vs2_vu[i] % Rs1_vu; + }}, OPMVX, VectorIntegerArithOp); + 0x23: vrem_vx({{ + if (Rs1_vi == 0) + Vd_vi[i] = Vs2_vi[i]; + else if (Vs2_vi[i] == std::numeric_limits::min() + && Rs1_vi == -1) + Vd_vi[i] = 0; + else + Vd_vi[i] = Vs2_vi[i] % Rs1_vi; + }}, OPMVX, VectorIntegerArithOp); + 0x24: vmulhu_vx({{ + if (sew < 64) + Vd_vu[i] = ((uint64_t)Vs2_vu[i] * Rs1_vu) + >> sew; + else + Vd_vu[i] = mulhu_64(Vs2_vu[i], Rs1_vu); + }}, OPMVX, VectorIntegerArithOp); + 0x25: vmul_vx({{ + Vd_vi[i] = Vs2_vi[i] * Rs1_vi; + }}, OPMVX, VectorIntegerArithOp); + 0x26: vmulhsu_vx({{ + if (sew < 64) + Vd_vi[i] = ((int64_t)Vs2_vi[i] * + (uint64_t)Rs1_vu) + >> sew; + else + Vd_vi[i] = mulhsu_64(Vs2_vi[i], Rs1_vu); + }}, OPMVX, VectorIntegerArithOp); + 0x27: vmulh_vx({{ + if (sew < 64) + Vd_vi[i] = ((int64_t)Vs2_vi[i] * Rs1_vi) + >> sew; + else + Vd_vi[i] = mulh_64(Vs2_vi[i], Rs1_vi); + }}, OPMVX, VectorIntegerArithOp); + 0x29: vmadd_vx({{ + Vd_vi[i] = Vs3_vi[i] * Rs1_vi + Vs2_vi[i]; + }}, OPMVX, VectorIntegerArithOp); + 0x2b: vnmsub_vx({{ + Vd_vi[i] = -(Vs3_vi[i] * Rs1_vi) + Vs2_vi[i]; + }}, OPMVX, VectorIntegerArithOp); + 0x2d: vmacc_vx({{ + Vd_vi[i] = Vs2_vi[i] * Rs1_vi + Vs3_vi[i]; + }}, OPMVX, VectorIntegerArithOp); + 0x2f: vnmsac_vx({{ + Vd_vi[i] = -(Vs2_vi[i] * Rs1_vi) + Vs3_vi[i]; + }}, OPMVX, VectorIntegerArithOp); + } + format VectorIntWideningFormat { + 0x30: vwaddu_vx({{ + Vd_vwu[i] = vwu(Vs2_vu[i + offset]) + vwu(Rs1_vu); + }}, OPMVX, VectorIntegerArithOp); + 0x31: vwadd_vx({{ + Vd_vwi[i] = vwi(Vs2_vi[i + offset]) + vwi(Rs1_vi); + }}, OPMVX, VectorIntegerArithOp); + 0x32: vwsubu_vx({{ + Vd_vwu[i] = vwu(Vs2_vu[i + offset]) - vwu(Rs1_vu); + }}, OPMVX, VectorIntegerArithOp); + 0x33: vwsub_vx({{ + Vd_vwi[i] = vwi(Vs2_vi[i + offset]) - vwi(Rs1_vi); + }}, OPMVX, VectorIntegerArithOp); + 0x34: vwaddu_wx({{ + Vd_vwu[i] = Vs2_vwu[i] + vwu(Rs1_vu); + }}, OPMVX, VectorIntegerArithOp); + 0x35: vwadd_wx({{ + Vd_vwi[i] = Vs2_vwi[i] + vwi(Rs1_vi); + }}, OPMVX, VectorIntegerArithOp); + 0x36: vwsubu_wx({{ + Vd_vwu[i] = Vs2_vwu[i] - vwu(Rs1_vu); + }}, OPMVX, VectorIntegerArithOp); + 0x37: vwsub_wx({{ + Vd_vwi[i] = Vs2_vwi[i] - vwi(Rs1_vi); + }}, OPMVX, VectorIntegerArithOp); + 0x38: vwmulu_vx({{ + Vd_vwu[i] = vwu(Vs2_vu[i + offset]) * vwu(Rs1_vu); + }}, OPMVX, VectorIntegerArithOp); + 0x3a: vwmulsu_vx({{ + Vd_vwi[i] = vwi(Vs2_vi[i + offset]) * vwu(Rs1_vu); + }}, OPMVX, VectorIntegerArithOp); + 0x3b: vwmul_vx({{ + Vd_vwi[i] = vwi(Vs2_vi[i + offset]) * vwi(Rs1_vi); + }}, OPMVX, VectorIntegerArithOp); + 0x3c: vwmaccu_vx({{ + Vd_vwu[i] = vwu(Rs1_vu) * vwu(Vs2_vu[i + offset]) + + Vs3_vwu[i]; + }}, OPMVX, VectorIntegerArithOp); + 0x3d: vwmacc_vx({{ + Vd_vwi[i] = vwi(Rs1_vi) * vwi(Vs2_vi[i + offset]) + + Vs3_vwi[i]; + }}, OPMVX, VectorIntegerArithOp); + 0x3e: vwmaccus_vx({{ + Vd_vwi[i] = vwu(Rs1_vu) * vwi(Vs2_vi[i + offset]) + + Vs3_vwi[i]; + }}, OPMVX, VectorIntegerArithOp); + 0x3f: vwmaccsu_vx({{ + Vd_vwi[i] = vwi(Rs1_vi) * vwu(Vs2_vu[i + offset]) + + Vs3_vwi[i]; + }}, OPMVX, VectorIntegerArithOp); + } + } + 0x7: decode BIT31 { + format VConfOp { + 0x0: vsetvli({{ + uint64_t rd_bits = RD; + uint64_t rs1_bits = RS1; + uint64_t requested_vl = Rs1_ud; + uint64_t requested_vtype = zimm11; + uint32_t vlen = VlenbBits * 8; + uint32_t vlmax = getVlmax(Vtype, vlen); + uint32_t current_vl = VL; + }}, {{ + Rd_ud = new_vl; + VL = new_vl; + Vtype = new_vtype; + }}, VSetVlDeclare, VSetVliBranchTarget + , VectorConfigOp, IsUncondControl + , IsIndirectControl); + 0x1: decode BIT30 { + 0x0: vsetvl({{ + uint64_t rd_bits = RD; + uint64_t rs1_bits = RS1; + uint64_t requested_vl = Rs1_ud; + uint64_t requested_vtype = Rs2_ud; + uint32_t vlen = VlenbBits * 8; + uint32_t vlmax = getVlmax(Vtype, vlen); + uint32_t current_vl = VL; + }}, {{ + Rd_ud = new_vl; + VL = new_vl; + Vtype = new_vtype; + }}, VSetVlDeclare, VSetVlBranchTarget + , VectorConfigOp, IsUncondControl + , IsIndirectControl); + 0x1: vsetivli({{ + uint64_t rd_bits = RD; + uint64_t rs1_bits = -1; + uint64_t requested_vl = uimm; + uint64_t requested_vtype = zimm10; + uint32_t vlen = VlenbBits * 8; + uint32_t vlmax = getVlmax(Vtype, vlen); + uint32_t current_vl = VL; + }}, {{ + Rd_ud = new_vl; + VL = new_vl; + Vtype = new_vtype; + }}, VSetiVliDeclare, VSetiVliBranchTarget + , VectorConfigOp, IsUncondControl + , IsDirectControl); + } + } + } + } + 0x18: decode FUNCT3 { format BOp { 0x0: beq({{ @@ -2098,6 +4595,12 @@ decode QUADRANT default Unknown::unknown() { xc->pcState()); }}, IsSerializeAfter, IsNonSpeculative, No_OpClass); 0x2: uret({{ + MISA misa = xc->readMiscReg(MISCREG_ISA); + if (!misa.rvn) { + return std::make_shared( + "sret can't execute without N systems", + machInst); + } STATUS status = xc->readMiscReg(MISCREG_STATUS); status.uie = status.upie; status.upie = 1; @@ -2107,6 +4610,12 @@ decode QUADRANT default Unknown::unknown() { } 0x8: decode RS2 { 0x2: sret({{ + MISA misa = xc->readMiscReg(MISCREG_ISA); + if (!misa.rvs) { + return std::make_shared( + "sret can't execute without RVS", + machInst); + } STATUS status = xc->readMiscReg(MISCREG_STATUS); auto pm = (PrivilegeMode)xc->readMiscReg( MISCREG_PRV); @@ -2126,11 +4635,12 @@ decode QUADRANT default Unknown::unknown() { } }}, IsSerializeAfter, IsNonSpeculative, IsReturn); 0x5: wfi({{ + MISA misa = xc->readMiscReg(MISCREG_ISA); STATUS status = xc->readMiscReg(MISCREG_STATUS); auto pm = (PrivilegeMode)xc->readMiscReg( MISCREG_PRV); - if (pm == PRV_U || - (pm == PRV_S && status.tw == 1)) { + if (misa.rvs && (pm == PRV_U || + (pm == PRV_S && status.tw == 1))) { return std::make_shared( "wfi in user mode or TW enabled", machInst); @@ -2147,9 +4657,15 @@ decode QUADRANT default Unknown::unknown() { tc->quiesce(); } }}, IsNonSpeculative, IsQuiesce, - IsSerializeAfter, No_OpClass); + IsSerializeAfter, No_OpClass, IsSquashAfter); } 0x9: sfence_vma({{ + MISA misa = xc->readMiscReg(MISCREG_ISA); + if (!misa.rvs) { + return std::make_shared( + "sfence_vma can't execute without RVS", + machInst); + } STATUS status = xc->readMiscReg(MISCREG_STATUS); auto pm = (PrivilegeMode)xc->readMiscReg(MISCREG_PRV); if (pm == PRV_U || (pm == PRV_S && status.tvm == 1)) { diff --git a/src/arch/riscv/isa/formats/compressed.isa b/src/arch/riscv/isa/formats/compressed.isa index 3d89ec38a6..7a9fd634c8 100644 --- a/src/arch/riscv/isa/formats/compressed.isa +++ b/src/arch/riscv/isa/formats/compressed.isa @@ -150,8 +150,9 @@ def template CBasicExecute {{ std::vector indices = {%(regs)s}; std::stringstream ss; ss << mnemonic << ' '; - ss << registerName(indices[0]) << ", "; - ss << registerName(indices[1]); + ss << registerName(indices[0]); + if (_numSrcRegs >= 2) + ss << ", " << registerName(indices[1]); return ss.str(); } }}; diff --git a/src/arch/riscv/isa/formats/formats.isa b/src/arch/riscv/isa/formats/formats.isa index 19749438a8..0102df17d7 100644 --- a/src/arch/riscv/isa/formats/formats.isa +++ b/src/arch/riscv/isa/formats/formats.isa @@ -37,6 +37,9 @@ ##include "fp.isa" ##include "amo.isa" ##include "bs.isa" +##include "vector_conf.isa" +##include "vector_arith.isa" +##include "vector_mem.isa" // Include formats for nonstandard extensions ##include "compressed.isa" diff --git a/src/arch/riscv/isa/formats/mem.isa b/src/arch/riscv/isa/formats/mem.isa index 0d80260a25..53de4af8b4 100644 --- a/src/arch/riscv/isa/formats/mem.isa +++ b/src/arch/riscv/isa/formats/mem.isa @@ -228,6 +228,69 @@ def template StoreCompleteAcc {{ } }}; +def template CacheBlockBasedStoreExecute {{ + Fault + %(class_name)s::execute(ExecContext *xc, + trace::InstRecord *traceData) const + { + Addr EA; + + %(op_decl)s; + %(op_rd)s; + %(ea_code)s; + + Addr cacheBlockSize = xc->tcBase()->getCpuPtr()->cacheLineSize(); + uint64_t numOffsetBits = floorLog2(cacheBlockSize); + EA = (EA >> numOffsetBits) << numOffsetBits; + + { + Fault fault = + writeMemAtomic(xc, nullptr, EA, cacheBlockSize, memAccessFlags, + nullptr, std::vector(cacheBlockSize, true)); + if (fault != NoFault) + return fault; + } + + return NoFault; + } +}}; + +def template CacheBlockBasedStoreInitiateAcc {{ + Fault + %(class_name)s::initiateAcc(ExecContext *xc, + trace::InstRecord *traceData) const + { + Addr EA; + + %(op_decl)s; + %(op_rd)s; + %(ea_code)s; + + Addr cacheBlockSize = xc->tcBase()->getCpuPtr()->cacheLineSize(); + uint64_t numOffsetBits = floorLog2(cacheBlockSize); + EA = (EA >> numOffsetBits) << numOffsetBits; + + { + Fault fault = + writeMemTiming(xc, nullptr, EA, cacheBlockSize, memAccessFlags, + nullptr, std::vector(cacheBlockSize, true)); + if (fault != NoFault) + return fault; + } + + return NoFault; + } +}}; + +def template CacheBlockBasedStoreCompleteAcc {{ + Fault + %(class_name)s::completeAcc(PacketPtr pkt, ExecContext *xc, + trace::InstRecord *traceData) const + { + return NoFault; + } +}}; + def format Load(memacc_code, ea_code = {{EA = rvZext(Rs1 + offset);}}, offset_code={{offset = sext<12>(IMM12);}}, mem_flags=[], inst_flags=[]) {{ @@ -243,3 +306,10 @@ def format Store(memacc_code, ea_code={{EA = rvZext(Rs1 + offset);}}, LoadStoreBase(name, Name, offset_code, ea_code, memacc_code, mem_flags, inst_flags, 'Store', exec_template_base='Store') }}; + +def format CBMOp(memacc_code, ea_code={{EA = rvZext(Rs1);}}, + offset_code={{;}}, mem_flags=[], inst_flags=[]) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, offset_code, ea_code, memacc_code, mem_flags, + inst_flags, 'Store', exec_template_base='CacheBlockBasedStore') +}}; diff --git a/src/arch/riscv/isa/formats/standard.isa b/src/arch/riscv/isa/formats/standard.isa index bb500f5f49..051c526377 100644 --- a/src/arch/riscv/isa/formats/standard.isa +++ b/src/arch/riscv/isa/formats/standard.isa @@ -267,7 +267,7 @@ def template JumpConstructor {{ flags[IsCall] = true; // Handle "Jalr" instruction - if (FUNCT3 == 0x0) { + if (FUNCT3 == 0x0 && OPCODE5 == 0x19) { // If RD is not link and RS1 is link, then pop RAS if (!rd_link && rs1_link) flags[IsReturn] = true; else if (rd_link) { @@ -334,6 +334,7 @@ def template CSRExecute {{ auto isa = static_cast(xc->tcBase()->getIsaPtr()); auto& csr_data = isa->getCSRDataMap(); auto& csr_masks = isa->getCSRMaskMap(); + MISA misa = isa->readMiscRegNoEffect(MISCREG_ISA); auto csr_data_it = csr_data.find(csr); if (csr_data_it == csr_data.end()) { @@ -351,6 +352,14 @@ def template CSRExecute {{ machInst); } + MISA csr_exts = csr_data_it->second.isaExts; + if ((csr_exts & misa) != csr_exts) { + return std::make_shared( + csprintf("%s is not support in the isa spec %d\n", + csrName), + machInst); + } + auto mask_it = csr_masks.find(csr); RegVal maskVal = (mask_it == csr_masks.end()) ? mask(64) : mask_it->second; diff --git a/src/arch/riscv/isa/formats/vector_arith.isa b/src/arch/riscv/isa/formats/vector_arith.isa new file mode 100644 index 0000000000..1ddf323f04 --- /dev/null +++ b/src/arch/riscv/isa/formats/vector_arith.isa @@ -0,0 +1,1557 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2022 PLCT Lab +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +let {{ + def setVlen(): + return "uint32_t vlen = VlenbBits * 8;\n" + def setVlenb(): + return "uint32_t vlenb = VlenbBits;\n" + def setDestWrapper(destRegId): + return "setDestRegIdx(_numDestRegs++, " + destRegId + ");\n" + \ + "_numTypedDestRegs[VecRegClass]++;\n" + def setSrcWrapper(srcRegId): + return "setSrcRegIdx(_numSrcRegs++, " + srcRegId + ");\n" + def setSrcVm(): + return "if (!this->vm)\n" + \ + " setSrcRegIdx(_numSrcRegs++, vecRegClass[0]);" + def vmDeclAndReadData(): + return ''' + [[maybe_unused]] RiscvISA::vreg_t tmp_v0; + [[maybe_unused]] uint8_t* v0; + if(!machInst.vm) { + xc->getRegOperand(this, _numSrcRegs-1, &tmp_v0); + v0 = tmp_v0.as(); + } + ''' + def copyOldVd(vd_idx): + return 'COPY_OLD_VD(%d);' % vd_idx + def loopWrapper(code, micro_inst = True): + if micro_inst: + upper_bound = "this->microVl" + else: + upper_bound = "(uint32_t)machInst.vl" + return ''' + for (uint32_t i = 0; i < %s; i++) { + %s + } + ''' % (upper_bound, code) + def maskCondWrapper(code): + return "if (this->vm || elem_mask(v0, ei)) {\n" + \ + code + "}\n" + def eiDeclarePrefix(code, widening = False): + if widening: + return ''' + uint32_t ei = i + micro_vlmax * this->microIdx; + ''' + code + else: + return ''' + uint32_t ei = i + vtype_VLMAX(vtype, vlen, true) * this->microIdx; + ''' + code + + def wideningOpRegisterConstraintChecks(code): + return ''' + const uint32_t num_microops = 1 << std::max(0, vtype_vlmul(machInst.vtype8) + 1); + if ((machInst.vd % alignToPowerOfTwo(num_microops)) != 0) { + std::string error = + csprintf("Unaligned Vd group in Widening op"); + return std::make_shared(error, machInst); + } + if ((machInst.vs2 <= machInst.vd) && (machInst.vd < (machInst.vs2 + num_microops - 1))) { + // A destination vector register group can overlap a source vector + // register group if The destination EEW is greater than the source + // EEW, the source EMUL is at least 1, and the overlap is in the + // highest- numbered part of the destination register group. + std::string error = + csprintf("Unsupported overlap in Vs2 and Vd for Widening op"); + return std::make_shared(error, machInst); + } + ''' + code + + def narrowingOpRegisterConstraintChecks(code): + return ''' + const uint32_t num_microops = 1 << std::max(0, vtype_vlmul(machInst.vtype8) + 1); + if ((machInst.vs2 % alignToPowerOfTwo(num_microops)) != 0) { + std::string error = + csprintf("Unaligned VS2 group in Narrowing op"); + return std::make_shared(error, machInst); + } + if ((machInst.vs2 < machInst.vd) && (machInst.vd <= (VS2 + num_microops - 1))) { + // A destination vector register group can overlap a source vector + // register group The destination EEW is smaller than the source EEW + // and the overlap is in the lowest-numbered part of the source + // register group + std::string error = + csprintf("Unsupported overlap in Vs2 and Vd for Narrowing op"); + return std::make_shared(error, machInst); + } + ''' + code + + def fflags_wrapper(code): + return ''' + RegVal FFLAGS = xc->readMiscReg(MISCREG_FFLAGS); + std::feclearexcept(FE_ALL_EXCEPT); + ''' + code + ''' + FFLAGS |= softfloat_exceptionFlags; + softfloat_exceptionFlags = 0; + xc->setMiscReg(MISCREG_FFLAGS, FFLAGS); + ''' + + def declareVArithTemplate( + class_name, type_name='uint', min_size=8, max_size=64): + sizes = [8, 16, 32, 64] + code = '' + for size in sizes: + if size < min_size or size > max_size: + continue + code += f'template class {class_name}<{type_name}{size}_t>;\n' + return code + + def declareGatherTemplate(class_name, index_type): + sizes = [8, 16, 32, 64] + code = '' + for size in sizes: + if index_type == 'elem_type': + idx_type = f'uint{size}_t' + else: + idx_type = index_type + code += ('template class' + f' {class_name};\n') + return code +}}; + + +def format VectorIntFormat(code, category, *flags) {{ + macroop_class_name = 'VectorArithMacroInst' + microop_class_name = 'VectorArithMicroInst' + + if name == "vid_v" : + macroop_class_name = 'VectorVMUNARY0MacroInst' + microp_class_name = 'VectorVMUNARY0MicroInst' + + iop = InstObjParams( + name, + Name, + macroop_class_name, + {'code': code, + 'declare_varith_template': declareVArithTemplate(Name)}, + flags + ) + inst_name, inst_suffix = name.split("_", maxsplit=1) + v0_required = inst_name not in ["vmv"] + mask_cond = v0_required and (inst_suffix not in ['vvm', 'vxm', 'vim']) + need_elem_idx = mask_cond or code.find("ei") != -1 + + dest_reg_id = "vecRegClass[_machInst.vd + _microIdx]" + + num_src_regs = 0 + + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx]" + num_src_regs += 1 + + src1_reg_id = "" + if category in ["OPIVV", "OPMVV"]: + src1_reg_id = "vecRegClass[_machInst.vs1 + _microIdx]" + num_src_regs += 1 + elif category in ["OPIVX", "OPMVX"]: + src1_reg_id = "intRegClass[_machInst.rs1]" + num_src_regs += 1 + elif category == "OPIVI": + pass + else: + error("not supported category for VectorIntFormat: %s" % category) + + old_vd_idx = num_src_regs + src3_reg_id = "vecRegClass[_machInst.vd + _microIdx]" + + set_dest_reg_idx = setDestWrapper(dest_reg_id) + + set_src_reg_idx = "" + if category != "OPIVI": + set_src_reg_idx += setSrcWrapper(src1_reg_id) + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(src3_reg_id) + if v0_required: + set_src_reg_idx += setSrcVm() + + # code + if mask_cond: + code = maskCondWrapper(code) + if need_elem_idx: + code = eiDeclarePrefix(code) + code = loopWrapper(code) + + vm_decl_rd = "" + if v0_required: + vm_decl_rd = vmDeclAndReadData() + + set_vlenb = setVlenb() + + microiop = InstObjParams(name + "_micro", + Name + "Micro", + microop_class_name, + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb' : set_vlenb, + 'vm_decl_rd': vm_decl_rd, + 'copy_old_vd': copyOldVd(old_vd_idx), + 'declare_varith_template': declareVArithTemplate(Name + "Micro")}, + flags) + + header_output = \ + VectorIntMicroDeclare.subst(microiop) + \ + VectorIntMacroDeclare.subst(iop) + decoder_output = \ + VectorIntMicroConstructor.subst(microiop) + \ + VectorIntMacroConstructor.subst(iop) + exec_output = VectorIntMicroExecute.subst(microiop) + decode_block = VectorIntDecodeBlock.subst(iop) +}}; + + +def format VectorIntExtFormat(code, category, *flags) {{ + iop = InstObjParams( + name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': declareVArithTemplate(Name)}, + flags + ) + inst_name, inst_suffix = name.split("_", maxsplit=1) + ext_div = int(inst_suffix[-1]) + + old_vd_idx = 1 + dest_reg_id = "vecRegClass[_machInst.vd + _microIdx]" + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx / " + \ + str(ext_div) + "]" + src3_reg_id = "vecRegClass[_machInst.vs3 + _microIdx]" + + set_dest_reg_idx = setDestWrapper(dest_reg_id) + + set_src_reg_idx = "" + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(src3_reg_id) + set_src_reg_idx += setSrcVm() + + code = maskCondWrapper(code) + code = eiDeclarePrefix(code) + code = loopWrapper(code) + vm_decl_rd = vmDeclAndReadData() + + set_vlenb = setVlenb(); + set_vlen = setVlen(); + + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'set_vlen': set_vlen, + 'vm_decl_rd': vm_decl_rd, + 'copy_old_vd': copyOldVd(old_vd_idx), + 'ext_div': ext_div, + 'declare_varith_template': declareVArithTemplate(Name + "Micro")}, + flags) + + header_output = \ + VectorIntExtMicroDeclare.subst(microiop) + \ + VectorIntExtMacroDeclare.subst(iop) + decoder_output = \ + VectorIntMicroConstructor.subst(microiop) + \ + VectorIntMacroConstructor.subst(iop) + exec_output = \ + VectorIntExtMicroExecute.subst(microiop) + \ + VectorIntExtMacroExecute.subst(iop) + decode_block = VectorIntDecodeBlock.subst(iop) +}}; + +def format VectorIntWideningFormat(code, category, *flags) {{ + iop = InstObjParams( + name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': declareVArithTemplate(Name, max_size=32)}, + flags + ) + inst_name, inst_suffix = name.split("_", maxsplit=1) + v0_required = True + mask_cond = v0_required + need_elem_idx = mask_cond or code.find("ei") != -1 + old_vd_idx = 2 + dest_reg_id = "vecRegClass[_machInst.vd + _microIdx]" + src1_reg_id = "" + if category in ["OPIVV", "OPMVV"]: + src1_reg_id = "vecRegClass[_machInst.vs1 + _microIdx / 2]" + elif category in ["OPIVX", "OPMVX"]: + src1_reg_id = "intRegClass[_machInst.rs1]" + else: + error("not supported category for VectorIntFormat: %s" % category) + src2_reg_id = "" + if inst_suffix in ["vv", "vx"]: + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx / 2]" + elif inst_suffix in ["wv", "wx"]: + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx]" + src3_reg_id = "vecRegClass[_machInst.vs3 + _microIdx]" + + set_dest_reg_idx = setDestWrapper(dest_reg_id) + + set_src_reg_idx = "" + set_src_reg_idx += setSrcWrapper(src1_reg_id) + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(src3_reg_id) + if v0_required: + set_src_reg_idx += setSrcVm() + + # code + if mask_cond: + code = maskCondWrapper(code) + if need_elem_idx: + code = eiDeclarePrefix(code, widening=True) + code = loopWrapper(code) + + code = wideningOpRegisterConstraintChecks(code) + + vm_decl_rd = "" + if v0_required: + vm_decl_rd = vmDeclAndReadData() + + set_vlenb = setVlenb(); + set_vlen = setVlen(); + + varith_micro_declare = declareVArithTemplate(Name + "Micro", max_size=32) + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'set_vlen': set_vlen, + 'vm_decl_rd': vm_decl_rd, + 'copy_old_vd': copyOldVd(old_vd_idx), + 'declare_varith_template': varith_micro_declare}, + flags) + + header_output = \ + VectorIntWideningMicroDeclare.subst(microiop) + \ + VectorIntWideningMacroDeclare.subst(iop) + decoder_output = \ + VectorIntWideningMicroConstructor.subst(microiop) + \ + VectorIntWideningMacroConstructor.subst(iop) + exec_output = VectorIntWideningMicroExecute.subst(microiop) + decode_block = VectorIntWideningDecodeBlock.subst(iop) +}}; + +def format VectorIntNarrowingFormat(code, category, *flags) {{ + iop = InstObjParams( + name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': declareVArithTemplate(Name, max_size=32)}, + flags + ) + mask_cond = True + need_elem_idx = True + + old_vd_idx = 2 + dest_reg_id = "vecRegClass[_machInst.vd + _microIdx / 2]" + if category in ["OPIVV"]: + src1_reg_id = "vecRegClass[_machInst.vs1 + _microIdx / 2]" + elif category in ["OPIVX"]: + src1_reg_id = "intRegClass[_machInst.rs1]" + elif category == "OPIVI": + old_vd_idx = 1 + else: + error("not supported category for VectorIntFormat: %s" % category) + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx]" + old_dest_reg_id = "vecRegClass[_machInst.vs3 + _microIdx / 2]" + + set_dest_reg_idx = setDestWrapper(dest_reg_id) + set_src_reg_idx = "" + if category != "OPIVI": + set_src_reg_idx += setSrcWrapper(src1_reg_id) + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(old_dest_reg_id) + set_src_reg_idx += setSrcVm() + # code + code = maskCondWrapper(code) + code = eiDeclarePrefix(code, widening=True) + code = loopWrapper(code) + code = narrowingOpRegisterConstraintChecks(code) + vm_decl_rd = vmDeclAndReadData() + + set_vlenb = setVlenb(); + set_vlen = setVlen(); + + varith_micro_declare = declareVArithTemplate(Name + "Micro", max_size=32) + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'set_vlen': set_vlen, + 'vm_decl_rd': vm_decl_rd, + 'copy_old_vd': copyOldVd(old_vd_idx), + 'declare_varith_template': varith_micro_declare + }, + flags) + + header_output = \ + VectorIntWideningMicroDeclare.subst(microiop) + \ + VectorIntWideningMacroDeclare.subst(iop) + decoder_output = \ + VectorIntWideningMicroConstructor.subst(microiop) + \ + VectorIntWideningMacroConstructor.subst(iop) + exec_output = VectorIntNarrowingMicroExecute.subst(microiop) + decode_block = VectorIntWideningDecodeBlock.subst(iop) +}}; + +def format VectorIntMaskFormat(code, category, *flags) {{ + iop = InstObjParams(name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': declareVArithTemplate(Name)}, + flags) + inst_name, inst_suffix = name.split("_", maxsplit=1) + v0_required = not (inst_name in ["vmadc", "vmsbc"] \ + and inst_suffix in ["vv", "vx", "vi"]) + mask_cond = inst_name not in ['vmadc', 'vmsbc'] + need_elem_idx = mask_cond or code.find("ei") != -1 + + old_vd_idx = 2 + dest_reg_id = "vecRegClass[VecMemInternalReg0 + _microIdx]" + src1_reg_id = "" + if category == "OPIVV": + src1_reg_id = "vecRegClass[_machInst.vs1 + _microIdx]" + elif category == "OPIVX": + src1_reg_id = "intRegClass[_machInst.rs1]" + elif category == "OPIVI": + old_vd_idx = 1 + else: + error("not supported category for VectorIntFormat: %s" % category) + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx]" + old_dest_reg_id = "vecRegClass[_machInst.vd]" + set_dest_reg_idx = setDestWrapper(dest_reg_id) + set_src_reg_idx = "" + if category != "OPIVI": + set_src_reg_idx += setSrcWrapper(src1_reg_id) + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(old_dest_reg_id) + if v0_required: + set_src_reg_idx += setSrcVm() + + #code + if mask_cond: + code = maskCondWrapper(code) + if need_elem_idx: + code = eiDeclarePrefix(code) + code = loopWrapper(code) + + vm_decl_rd = "" + if v0_required: + vm_decl_rd = vmDeclAndReadData() + + set_vlenb = setVlenb() + + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'vm_decl_rd': vm_decl_rd, + 'copy_old_vd': copyOldVd(old_vd_idx), + 'declare_varith_template': declareVArithTemplate(Name + "Micro")}, + flags) + + header_output = \ + VectorIntMaskMicroDeclare.subst(microiop) + \ + VectorIntMaskMacroDeclare.subst(iop) + decoder_output = \ + VectorIntMaskMicroConstructor.subst(microiop) + \ + VectorIntMaskMacroConstructor.subst(iop) + exec_output = VectorIntMaskMicroExecute.subst(microiop) + decode_block = VectorIntDecodeBlock.subst(iop) +}}; + +def format VectorGatherFormat(code, category, *flags) {{ + inst_name, inst_suffix = name.split("_", maxsplit=1) + if inst_name == "vrgatherei16": + idx_type = "uint16_t" + else: + idx_type = "elem_type" + iop = InstObjParams(name, Name, 'VectorArithMacroInst', + {'idx_type': idx_type, + 'code': code, + 'declare_varith_template': declareGatherTemplate(Name, idx_type)}, + flags) + old_vd_idx = 2 + dest_reg_id = "vecRegClass[_machInst.vd + vd_idx]" + src1_reg_id = "" + if category in ["OPIVV"]: + src1_reg_id = "vecRegClass[_machInst.vs1 + vs1_idx]" + elif category in ["OPIVX"]: + src1_reg_id = "intRegClass[_machInst.rs1]" + elif category == "OPIVI": + old_vd_idx = 1 + else: + error("not supported category for VectorIntFormat: %s" % category) + src2_reg_id = "vecRegClass[_machInst.vs2 + vs2_idx]" + src3_reg_id = "vecRegClass[_machInst.vs3 + vd_idx]" + + set_dest_reg_idx = setDestWrapper(dest_reg_id) + + set_src_reg_idx = "" + if category != "OPIVI": + set_src_reg_idx += setSrcWrapper(src1_reg_id) + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(src3_reg_id) + set_src_reg_idx += setSrcVm() + + # code + + vm_decl_rd = vmDeclAndReadData() + + set_vlenb = setVlenb(); + set_vlen = setVlen(); + + varith_micro_declare = declareGatherTemplate(Name + "Micro", idx_type) + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'set_vlen': set_vlen, + 'vm_decl_rd': vm_decl_rd, + 'copy_old_vd': copyOldVd(old_vd_idx), + 'idx_type': idx_type, + 'declare_varith_template': varith_micro_declare}, + flags) + + header_output = \ + VectorGatherMicroDeclare.subst(microiop) + \ + VectorGatherMacroDeclare.subst(iop) + decoder_output = \ + VectorGatherMicroConstructor.subst(microiop) + \ + VectorGatherMacroConstructor.subst(iop) + exec_output = VectorGatherMicroExecute.subst(microiop) + decode_block = VectorGatherDecodeBlock.subst(iop) + +}}; + +def format VectorFloatFormat(code, category, *flags) {{ + iop = InstObjParams( + name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': declareVArithTemplate(Name, 'float', 32)}, + flags + ) + inst_name, inst_suffix = name.split("_", maxsplit=1) + v0_required = inst_name not in ["vfmv"] + mask_cond = v0_required and (inst_suffix not in ['vvm', 'vfm']) + need_elem_idx = mask_cond or code.find("ei") != -1 + + dest_reg_id = "vecRegClass[_machInst.vd + _microIdx]" + src1_reg_id = "" + if category == "OPFVV": + src1_reg_id = "vecRegClass[_machInst.vs1 + _microIdx]" + elif category == "OPFVF": + src1_reg_id = "floatRegClass[_machInst.rs1]" + else: + error("not supported category for VectorFloatFormat: %s" % category) + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx]" + src3_reg_id = "vecRegClass[_machInst.vs3 + _microIdx]" + + set_dest_reg_idx = setDestWrapper(dest_reg_id) + + set_src_reg_idx = "" + set_src_reg_idx += setSrcWrapper(src1_reg_id) + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(src3_reg_id) + if v0_required: + set_src_reg_idx += setSrcVm() + # code + if mask_cond: + code = maskCondWrapper(code) + if need_elem_idx: + code = eiDeclarePrefix(code) + code = loopWrapper(code) + code = fflags_wrapper(code) + + vm_decl_rd = "" + if v0_required: + vm_decl_rd = vmDeclAndReadData() + + set_vlenb = setVlenb(); + + varith_micro_declare = declareVArithTemplate(Name + "Micro", 'float', 32) + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'vm_decl_rd': vm_decl_rd, + 'copy_old_vd': copyOldVd(2), + 'declare_varith_template': varith_micro_declare}, + flags) + + header_output = \ + VectorFloatMicroDeclare.subst(microiop) + \ + VectorFloatMacroDeclare.subst(iop) + decoder_output = \ + VectorFloatMicroConstructor.subst(microiop) + \ + VectorFloatMacroConstructor.subst(iop) + exec_output = VectorFloatMicroExecute.subst(microiop) + decode_block = VectorFloatDecodeBlock.subst(iop) +}}; + +def format VectorFloatCvtFormat(code, category, *flags) {{ + iop = InstObjParams( + name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': declareVArithTemplate(Name, 'float', 32)}, + flags + ) + + old_vd_idx = 1 + dest_reg_id = "vecRegClass[_machInst.vd + _microIdx]" + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx]" + src3_reg_id = "vecRegClass[_machInst.vs3 + _microIdx]" + + set_dest_reg_idx = setDestWrapper(dest_reg_id) + + set_src_reg_idx = "" + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(src3_reg_id) + set_src_reg_idx += setSrcVm() + code = maskCondWrapper(code) + code = eiDeclarePrefix(code) + code = loopWrapper(code) + code = fflags_wrapper(code) + + vm_decl_rd = vmDeclAndReadData() + + set_vlenb = setVlenb(); + + varith_micro_declare = declareVArithTemplate(Name + "Micro", 'float', 32) + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'vm_decl_rd': vm_decl_rd, + 'copy_old_vd': copyOldVd(old_vd_idx), + 'declare_varith_template': varith_micro_declare}, + flags) + + header_output = \ + VectorFloatCvtMicroDeclare.subst(microiop) + \ + VectorFloatCvtMacroDeclare.subst(iop) + decoder_output = \ + VectorFloatMicroConstructor.subst(microiop) + \ + VectorFloatMacroConstructor.subst(iop) + exec_output = VectorFloatMicroExecute.subst(microiop) + decode_block = VectorFloatDecodeBlock.subst(iop) +}}; + +def format VectorFloatWideningFormat(code, category, *flags) {{ + varith_macro_declare = declareVArithTemplate(Name, 'float', 32, 32) + iop = InstObjParams( + name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': varith_macro_declare}, + flags + ) + inst_name, inst_suffix = name.split("_", maxsplit=1) + v0_required = True + mask_cond = v0_required + need_elem_idx = mask_cond or code.find("ei") != -1 + + dest_reg_id = "vecRegClass[_machInst.vd + _microIdx]" + src1_reg_id = "" + if category in ["OPFVV"]: + src1_reg_id = "vecRegClass[_machInst.vs1 + _microIdx / 2]" + elif category in ["OPFVF"]: + src1_reg_id = "floatRegClass[_machInst.rs1]" + else: + error("not supported category for VectorFloatFormat: %s" % category) + src2_reg_id = "" + if inst_suffix in ["vv", "vf"]: + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx / 2]" + elif inst_suffix in ["wv", "wf"]: + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx]" + src3_reg_id = "vecRegClass[_machInst.vs3 + _microIdx]" + + set_dest_reg_idx = setDestWrapper(dest_reg_id) + + set_src_reg_idx = "" + set_src_reg_idx += setSrcWrapper(src1_reg_id) + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(src3_reg_id) + if v0_required: + set_src_reg_idx += setSrcVm() + + # code + if mask_cond: + code = maskCondWrapper(code) + if need_elem_idx: + code = eiDeclarePrefix(code, widening=True) + code = loopWrapper(code) + code = fflags_wrapper(code) + + code = wideningOpRegisterConstraintChecks(code) + + vm_decl_rd = "" + if v0_required: + vm_decl_rd = vmDeclAndReadData() + + set_vlenb = setVlenb(); + set_vlen = setVlen(); + + varith_micro_declare = declareVArithTemplate( + Name + "Micro", 'float', 32, 32) + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'set_vlen': set_vlen, + 'vm_decl_rd': vm_decl_rd, + 'copy_old_vd': copyOldVd(2), + 'declare_varith_template': varith_micro_declare}, + flags) + + header_output = \ + VectorIntWideningMicroDeclare.subst(microiop) + \ + VectorIntWideningMacroDeclare.subst(iop) + decoder_output = \ + VectorIntWideningMicroConstructor.subst(microiop) + \ + VectorIntWideningMacroConstructor.subst(iop) + exec_output = VectorFloatWideningMicroExecute.subst(microiop) + decode_block = VectorFloatWideningDecodeBlock.subst(iop) +}}; + +def format VectorFloatWideningCvtFormat(code, category, *flags) {{ + varith_macro_declare = declareVArithTemplate(Name, 'float', 32, 32) + iop = InstObjParams( + name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': varith_macro_declare}, + flags + ) + + old_vd_idx = 1 + dest_reg_id = "vecRegClass[_machInst.vd + _microIdx]" + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx / 2]" + src3_reg_id = "vecRegClass[_machInst.vs3 + _microIdx]" + + set_dest_reg_idx = setDestWrapper(dest_reg_id) + + set_src_reg_idx = "" + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(src3_reg_id) + set_src_reg_idx += setSrcVm() + code = maskCondWrapper(code) + code = eiDeclarePrefix(code) + code = loopWrapper(code) + code = fflags_wrapper(code) + + vm_decl_rd = vmDeclAndReadData() + + set_vlenb = setVlenb(); + set_vlen = setVlen(); + + varith_micro_declare = declareVArithTemplate( + Name + "Micro", 'float', 32, 32) + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'set_vlen': set_vlen, + 'vm_decl_rd': vm_decl_rd, + 'copy_old_vd': copyOldVd(old_vd_idx), + 'declare_varith_template': varith_micro_declare}, + flags) + + header_output = \ + VectorFloatCvtMicroDeclare.subst(microiop) + \ + VectorFloatCvtMacroDeclare.subst(iop) + decoder_output = \ + VectorFloatMicroConstructor.subst(microiop) + \ + VectorIntWideningMacroConstructor.subst(iop) + exec_output = VectorFloatWideningMicroExecute.subst(microiop) + decode_block = VectorFloatWideningDecodeBlock.subst(iop) +}}; + +def format VectorFloatNarrowingCvtFormat(code, category, *flags) {{ + varith_macro_declare = declareVArithTemplate(Name, 'float', 32, 32) + iop = InstObjParams( + name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': varith_macro_declare}, + flags + ) + + old_vd_idx = 1 + dest_reg_id = "vecRegClass[_machInst.vd + _microIdx / 2]" + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx]" + src3_reg_id = "vecRegClass[_machInst.vs3 + _microIdx / 2]" + + set_dest_reg_idx = setDestWrapper(dest_reg_id) + + set_src_reg_idx = "" + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(src3_reg_id) + set_src_reg_idx += setSrcVm() + code = maskCondWrapper(code) + code = eiDeclarePrefix(code) + code = loopWrapper(code) + code = fflags_wrapper(code) + code = narrowingOpRegisterConstraintChecks(code) + + vm_decl_rd = vmDeclAndReadData() + + set_vlenb = setVlenb(); + set_vlen = setVlen(); + + varith_micro_declare = declareVArithTemplate( + Name + "Micro", 'float', 32, 32) + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'set_vlen': set_vlen, + 'vm_decl_rd': vm_decl_rd, + 'copy_old_vd': copyOldVd(old_vd_idx), + 'declare_varith_template': varith_micro_declare}, + flags) + + header_output = \ + VectorFloatCvtMicroDeclare.subst(microiop) + \ + VectorFloatCvtMacroDeclare.subst(iop) + decoder_output = \ + VectorFloatMicroConstructor.subst(microiop) + \ + VectorIntWideningMacroConstructor.subst(iop) + exec_output = VectorFloatNarrowingMicroExecute.subst(microiop) + decode_block = VectorFloatWideningDecodeBlock.subst(iop) +}}; + +def format VectorFloatMaskFormat(code, category, *flags) {{ + iop = InstObjParams(name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': declareVArithTemplate(Name, 'float', 32)}, + flags + ) + dest_reg_id = "vecRegClass[VecMemInternalReg0 + _microIdx]" + src1_reg_id = "" + if category == "OPFVV": + src1_reg_id = "vecRegClass[_machInst.vs1 + _microIdx]" + elif category == "OPFVF": + src1_reg_id = "floatRegClass[_machInst.rs1]" + else: + error("not supported category for VectorFloatFormat: %s" % category) + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx]" + old_dest_reg_id = "vecRegClass[_machInst.vd]" + set_dest_reg_idx = setDestWrapper(dest_reg_id) + set_src_reg_idx = "" + set_src_reg_idx += setSrcWrapper(src1_reg_id) + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(old_dest_reg_id) + set_src_reg_idx += setSrcVm() + vm_decl_rd = vmDeclAndReadData() + set_vlenb = setVlenb() + + code = maskCondWrapper(code) + code = eiDeclarePrefix(code) + code = loopWrapper(code) + code = fflags_wrapper(code) + + varith_micro_declare = declareVArithTemplate(Name + "Micro", 'float', 32) + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'vm_decl_rd': vm_decl_rd, + 'copy_old_vd': copyOldVd(2), + 'declare_varith_template': varith_micro_declare}, + flags) + + header_output = \ + VectorFloatMaskMicroDeclare.subst(microiop) + \ + VectorFloatMaskMacroDeclare.subst(iop) + decoder_output = \ + VectorFloatMaskMicroConstructor.subst(microiop) + \ + VectorFloatMaskMacroConstructor.subst(iop) + exec_output = VectorFloatMaskMicroExecute.subst(microiop) + decode_block = VectorFloatDecodeBlock.subst(iop) +}}; + +def format VMvWholeFormat(code, category, *flags) {{ + iop = InstObjParams(name, Name, 'VMvWholeMacroInst', {'code': code}, flags) + + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VMvWholeMicroInst', + {'code': code, + 'set_vlen': setVlen()}, + flags) + + header_output = \ + VMvWholeMacroDeclare.subst(iop) + \ + VMvWholeMicroDeclare.subst(microiop) + decoder_output = \ + VMvWholeMacroConstructor.subst(iop) + \ + VMvWholeMicroConstructor.subst(microiop) + exec_output = VMvWholeMicroExecute.subst(microiop) + decode_block = BasicDecode.subst(iop) +}}; + +def format ViotaFormat(code, category, *flags){{ + iop = InstObjParams( + name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': declareVArithTemplate(Name)}, + flags + ) + + inst_name, inst_suffix = name.split("_", maxsplit=1) + dest_reg_id = "vecRegClass[_machInst.vd + _microIdx]" + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx]" + # The tail of vector mask inst should be treated as tail-agnostic. + # We treat it with tail-undisturbed policy, since + # the test suits only support undisturbed policy. + old_dest_reg_id = "vecRegClass[_machInst.vd + _microIdx]" + + set_src_reg_idx = "" + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(old_dest_reg_id) + set_dest_reg_idx = setDestWrapper(dest_reg_id) + vm_decl_rd = vmDeclAndReadData() + set_vm_idx = setSrcVm() + set_vlenb = setVlenb() + + microiop = InstObjParams(name+"_micro", + Name+"Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'vm_decl_rd': vm_decl_rd, + 'set_vm_idx': set_vm_idx, + 'copy_old_vd': copyOldVd(1), + 'declare_varith_template': declareVArithTemplate(Name + "Micro")}, + flags) + + header_output = \ + ViotaMicroDeclare.subst(microiop) + \ + ViotaMacroDeclare.subst(iop) + decoder_output = \ + ViotaMicroConstructor.subst(microiop) + \ + ViotaMacroConstructor.subst(iop) + exec_output = ViotaMicroExecute.subst(microiop) + decode_block = VectorIntDecodeBlock.subst(iop) + +}}; + +def format Vector1Vs1VdMaskFormat(code, category, *flags){{ + inst_name, inst_suffix = name.split("_", maxsplit=1) + dest_reg_id = "vecRegClass[_machInst.vd]" + src2_reg_id = "vecRegClass[_machInst.vs2]" + # The tail of vector mask inst should be treated as tail-agnostic. + # We treat it with tail-undisturbed policy, since + # the test suits only support undisturbed policy. + old_dest_reg_id = "vecRegClass[_machInst.vd]" + set_src_reg_idx = "" + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(old_dest_reg_id) + set_dest_reg_idx = setDestWrapper(dest_reg_id) + vm_decl_rd = vmDeclAndReadData() + set_vm_idx = setSrcVm() + set_vlenb = setVlenb() + iop = InstObjParams(name, + Name, + 'VectorNonSplitInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'vm_decl_rd': vm_decl_rd, + 'set_vm_idx': set_vm_idx, + 'copy_old_vd': copyOldVd(1), + 'declare_varith_template': declareVArithTemplate(Name, 'uint', 8, 8), + }, + flags) + + header_output = Vector1Vs1RdMaskDeclare.subst(iop) + decoder_output = Vector1Vs1VdMaskConstructor.subst(iop) + exec_output = Vector1Vs1VdMaskExecute.subst(iop) + decode_block = VectorMaskDecodeBlock.subst(iop) +}}; + +def format Vector1Vs1RdMaskFormat(code, category, *flags){{ + inst_name, inst_suffix = name.split("_", maxsplit=1) + vm_decl_rd = vmDeclAndReadData() + set_vm_idx = setSrcVm() + iop = InstObjParams(name, + Name, + 'VectorNonSplitInst', + {'code': code, + 'vm_decl_rd': vm_decl_rd, + 'set_vm_idx': set_vm_idx, + 'declare_varith_template': declareVArithTemplate(Name, 'uint', 8, 8) + }, + flags) + + header_output = Vector1Vs1RdMaskDeclare.subst(iop) + decoder_output = Vector1Vs1RdMaskConstructor.subst(iop) + exec_output = Vector1Vs1RdMaskExecute.subst(iop) + decode_block = VectorMaskDecodeBlock.subst(iop) +}}; + +def format VectorNonSplitFormat(code, category, *flags) {{ + inst_name, inst_suffix = name.split("_", maxsplit=1) + vm_decl_rd = "" + + set_vm_idx = "" + + if inst_name == "vfmv" : + code = fflags_wrapper(code) + + if inst_name == "vfmv" : + varith_template = declareVArithTemplate(Name, 'float', 32) + iop = InstObjParams(name, + Name, + 'VectorNonSplitInst', + {'code': code, + 'vm_decl_rd': vm_decl_rd, + 'set_vm_idx': set_vm_idx, + 'declare_varith_template': varith_template}, + flags) + header_output = VectorNonSplitDeclare.subst(iop) + decoder_output = VectorNonSplitConstructor.subst(iop) + exec_output = VectorFloatNonSplitExecute.subst(iop) + decode_block = VectorFloatNonSplitDecodeBlock.subst(iop) + elif inst_name == "vmv" : + iop = InstObjParams(name, + Name, + 'VectorNonSplitInst', + {'code': code, + 'vm_decl_rd': vm_decl_rd, + 'set_vm_idx': set_vm_idx, + 'declare_varith_template': declareVArithTemplate(Name)}, + flags) + header_output = VectorNonSplitDeclare.subst(iop) + decoder_output = VectorNonSplitConstructor.subst(iop) + exec_output = VectorIntNonSplitExecute.subst(iop) + decode_block = VectorIntNonSplitDecodeBlock.subst(iop) + else : + error("Unsupported inst for VectorNonSplitFormat: %s" % inst_name) + +}}; + +def format VectorMaskFormat(code, category, *flags) {{ + inst_name, inst_suffix = name.split("_", maxsplit=1) + old_vd_idx = 2 + if category not in ["OPMVV"]: + error("not supported category for VectorIntFormat: %s" % category) + dest_reg_id = "vecRegClass[_machInst.vd]" + src1_reg_id = "vecRegClass[_machInst.vs1]" + src2_reg_id = "vecRegClass[_machInst.vs2]" + + # The tail of vector mask inst should be treated as tail-agnostic. + # We treat it with tail-undisturbed policy, since + # the test suits only support undisturbed policy. + # TODO: remove it + old_dest_reg_id = "vecRegClass[_machInst.vd]" + + set_src_reg_idx = "" + set_src_reg_idx += setSrcWrapper(src1_reg_id) + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(old_dest_reg_id) + + set_dest_reg_idx = setDestWrapper(dest_reg_id) + + set_vlenb = setVlenb() + + code = loopWrapper(code, micro_inst = False) + + iop = InstObjParams(name, + Name, + 'VectorNonSplitInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'copy_old_vd': copyOldVd(old_vd_idx), + 'declare_varith_template': declareVArithTemplate(Name, 'uint', 8, 8) + }, + flags) + + header_output = VectorMaskDeclare.subst(iop) + decoder_output = VectorMaskConstructor.subst(iop) + exec_output = VectorMaskExecute.subst(iop) + decode_block = VectorMaskDecodeBlock.subst(iop) +}}; + +def format VectorReduceIntFormat(code, category, *flags) {{ + iop = InstObjParams( + name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': declareVArithTemplate(Name)}, + flags + ) + inst_name, inst_suffix = name.split("_", maxsplit=1) + dest_reg_id = "vecRegClass[_machInst.vd]" + src1_reg_id = "vecRegClass[_machInst.vs1]" + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx]" + old_dest_reg_id = "vecRegClass[_machInst.vd]" + set_dest_reg_idx = setDestWrapper(dest_reg_id) + set_src_reg_idx = setSrcWrapper(src1_reg_id) + set_src_reg_idx += setSrcWrapper(src2_reg_id) + # Treat tail undisturbed/agnostic as the same + # We always need old rd as src vreg + set_src_reg_idx += setSrcWrapper(old_dest_reg_id) + set_src_reg_idx += setSrcVm() + vm_decl_rd = vmDeclAndReadData() + set_vlenb = setVlenb() + set_vlen = setVlen() + + type_def = ''' + using vu [[maybe_unused]] = std::make_unsigned_t; + using vi [[maybe_unused]] = std::make_signed_t; + ''' + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb' : set_vlenb, + 'set_vlen' : set_vlen, + 'vm_decl_rd': vm_decl_rd, + 'type_def': type_def, + 'copy_old_vd': copyOldVd(2), + 'declare_varith_template': declareVArithTemplate(Name + "Micro")}, + flags) + + header_output = \ + VectorReduceMicroDeclare.subst(microiop) + \ + VectorReduceMacroDeclare.subst(iop) + decoder_output = \ + VectorReduceMicroConstructor.subst(microiop) + \ + VectorReduceMacroConstructor.subst(iop) + exec_output = VectorReduceIntMicroExecute.subst(microiop) + decode_block = VectorIntDecodeBlock.subst(iop) +}}; + +def format VectorReduceFloatFormat(code, category, *flags) {{ + iop = InstObjParams( + name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': declareVArithTemplate(Name, 'float', 32)}, + flags + ) + inst_name, inst_suffix = name.split("_", maxsplit=1) + dest_reg_id = "vecRegClass[_machInst.vd]" + src1_reg_id = "vecRegClass[_machInst.vs1]" + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx]" + old_dest_reg_id = "vecRegClass[_machInst.vd]" + set_dest_reg_idx = setDestWrapper(dest_reg_id) + set_src_reg_idx = setSrcWrapper(src1_reg_id) + set_src_reg_idx += setSrcWrapper(src2_reg_id) + # Treat tail undisturbed/agnostic as the same + # We always need old rd as src vreg + set_src_reg_idx += setSrcWrapper(old_dest_reg_id) + set_src_reg_idx += setSrcVm() + vm_decl_rd = vmDeclAndReadData() + set_vlenb = setVlenb() + set_vlen = setVlen() + + type_def = ''' + using et = ElemType; + using vu = decltype(et::v); + ''' + + code = fflags_wrapper(code) + + varith_micro_declare = declareVArithTemplate(Name + "Micro", 'float', 32) + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'set_vlen': set_vlen, + 'vm_decl_rd': vm_decl_rd, + 'type_def': type_def, + 'copy_old_vd': copyOldVd(2), + 'declare_varith_template': varith_micro_declare}, + flags) + + header_output = \ + VectorReduceMicroDeclare.subst(microiop) + \ + VectorReduceMacroDeclare.subst(iop) + decoder_output = \ + VectorReduceMicroConstructor.subst(microiop) + \ + VectorReduceMacroConstructor.subst(iop) + exec_output = VectorReduceFloatMicroExecute.subst(microiop) + decode_block = VectorFloatDecodeBlock.subst(iop) +}}; + +def format VectorReduceFloatWideningFormat(code, category, *flags) {{ + varith_macro_declare = declareVArithTemplate(Name, 'float', 32, 32) + iop = InstObjParams( + name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': varith_macro_declare}, + flags + ) + inst_name, inst_suffix = name.split("_", maxsplit=1) + dest_reg_id = "vecRegClass[_machInst.vd]" + src1_reg_id = "vecRegClass[_machInst.vs1]" + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx]" + old_dest_reg_id = "vecRegClass[_machInst.vd]" + set_dest_reg_idx = setDestWrapper(dest_reg_id) + set_src_reg_idx = setSrcWrapper(src1_reg_id) + set_src_reg_idx += setSrcWrapper(src2_reg_id) + # Treat tail undisturbed/agnostic as the same + # We always need old rd as src vreg + set_src_reg_idx += setSrcWrapper(old_dest_reg_id) + set_src_reg_idx += setSrcVm() + vm_decl_rd = vmDeclAndReadData() + set_vlenb = setVlenb() + set_vlen = setVlen() + type_def = ''' + using et = ElemType; + using vu [[maybe_unused]] = decltype(et::v); + using ewt = typename double_width::type; + using vwu = decltype(ewt::v); + ''' + + varith_micro_declare = declareVArithTemplate( + Name + "Micro", 'float', 32, 32) + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'set_vlen': set_vlen, + 'vm_decl_rd': vm_decl_rd, + 'type_def': type_def, + 'copy_old_vd': copyOldVd(2), + 'declare_varith_template': varith_micro_declare}, + flags) + + header_output = \ + VectorReduceMicroDeclare.subst(microiop) + \ + VectorReduceMacroDeclare.subst(iop) + decoder_output = \ + VectorReduceMicroConstructor.subst(microiop) + \ + VectorReduceMacroConstructor.subst(iop) + exec_output = VectorReduceFloatWideningMicroExecute.subst(microiop) + decode_block = VectorFloatWideningDecodeBlock.subst(iop) +}}; + +def format VectorIntVxsatFormat(code, category, *flags) {{ + iop = InstObjParams( + name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': declareVArithTemplate(Name)}, + flags + ) + inst_name, inst_suffix = name.split("_", maxsplit=1) + old_vd_idx = 2 + dest_reg_id = "vecRegClass[_machInst.vd + _microIdx]" + src1_reg_id = "" + if category in ["OPIVV"]: + src1_reg_id = "vecRegClass[_machInst.vs1 + _microIdx]" + elif category in ["OPIVX"]: + src1_reg_id = "intRegClass[_machInst.rs1]" + elif category == "OPIVI": + old_vd_idx = 1 + else: + error("not supported category for VectorIntVxsatFormat: %s" % category) + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx]" + src3_reg_id = "vecRegClass[_machInst.vs3 + _microIdx]" + set_dest_reg_idx = setDestWrapper(dest_reg_id) + + set_src_reg_idx = "" + if category != "OPIVI": + set_src_reg_idx += setSrcWrapper(src1_reg_id) + set_src_reg_idx += setSrcWrapper(src2_reg_id) + set_src_reg_idx += setSrcWrapper(src3_reg_id) + set_src_reg_idx += setSrcVm() + vm_decl_rd = vmDeclAndReadData() + + set_vlenb = setVlenb() + + code = maskCondWrapper(code) + code = eiDeclarePrefix(code) + code = loopWrapper(code) + + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'vm_decl_rd': vm_decl_rd, + 'copy_old_vd': copyOldVd(old_vd_idx), + 'declare_varith_template': declareVArithTemplate(Name + "Micro")}, + flags) + + header_output = \ + VectorIntVxsatMicroDeclare.subst(microiop) + \ + VectorIntVxsatMacroDeclare.subst(iop) + decoder_output = \ + VectorIntVxsatMicroConstructor.subst(microiop) + \ + VectorIntVxsatMacroConstructor.subst(iop) + exec_output = VectorIntMicroExecute.subst(microiop) + decode_block = VectorIntDecodeBlock.subst(iop) +}}; + +def format VectorReduceIntWideningFormat(code, category, *flags) {{ + iop = InstObjParams( + name, + Name, + 'VectorArithMacroInst', + {'code': code, + 'declare_varith_template': declareVArithTemplate(Name, max_size=32)}, + flags + ) + inst_name, inst_suffix = name.split("_", maxsplit=1) + dest_reg_id = "vecRegClass[_machInst.vd]" + src1_reg_id = "vecRegClass[_machInst.vs1]" + src2_reg_id = "vecRegClass[_machInst.vs2 + _microIdx]" + old_dest_reg_id = "vecRegClass[_machInst.vd]" + set_dest_reg_idx = setDestWrapper(dest_reg_id) + set_src_reg_idx = setSrcWrapper(src1_reg_id) + set_src_reg_idx += setSrcWrapper(src2_reg_id) + # Treat tail undisturbed/agnostic as the same + # We always need old rd as src vreg + set_src_reg_idx += setSrcWrapper(old_dest_reg_id) + set_src_reg_idx += setSrcVm() + vm_decl_rd = vmDeclAndReadData() + set_vlenb = setVlenb() + set_vlen = setVlen() + + varith_micro_declare = declareVArithTemplate(Name + "Micro", max_size=32) + microiop = InstObjParams(name + "_micro", + Name + "Micro", + 'VectorArithMicroInst', + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'set_vlen': set_vlen, + 'vm_decl_rd': vm_decl_rd, + 'copy_old_vd': copyOldVd(2), + 'declare_varith_template': varith_micro_declare}, + flags) + + header_output = \ + VectorReduceMicroDeclare.subst(microiop) + \ + VectorReduceMacroDeclare.subst(iop) + decoder_output = \ + VectorReduceMicroConstructor.subst(microiop) + \ + VectorReduceMacroConstructor.subst(iop) + exec_output = VectorReduceIntWideningMicroExecute.subst(microiop) + decode_block = VectorIntWideningDecodeBlock.subst(iop) +}}; + +let {{ + +def VectorSlideBase(name, Name, category, code, flags, macro_construtor, + decode_template, micro_execute_template): + macroop_class_name = 'VectorSlideMacroInst' + microop_class_name = 'VectorSlideMicroInst' + # Make sure flags are in lists (convert to lists if not). + flags = makeList(flags) + + if decode_template is VectorIntDecodeBlock: + varith_macro_declare = declareVArithTemplate(Name) + elif decode_template is VectorFloatDecodeBlock: + varith_macro_declare = declareVArithTemplate(Name, 'float', 32) + + iop = InstObjParams( + name, + Name, + macroop_class_name, + {'code': code, + 'declare_varith_template': varith_macro_declare}, + flags + ) + inst_name, inst_suffix = name.split("_", maxsplit=1) + dest_reg_id = "vecRegClass[_machInst.vd + vdIdx]" + src2_reg_id = "vecRegClass[_machInst.vs2 + vs2Idx]" + src1_ireg_id = "intRegClass[_machInst.rs1]" + src1_freg_id = "floatRegClass[_machInst.rs1]" + + # The tail of vector mask inst should be treated as tail-agnostic. + # We treat it with tail-undisturbed policy, since + # the test suits only support undisturbed policy. + num_src_regs = 0 + + old_dest_reg_id = "vecRegClass[_machInst.vd + vdIdx]" + set_src_reg_idx = "" + if category in ["OPIVX", "OPMVX"]: + set_src_reg_idx += setSrcWrapper(src1_ireg_id) + num_src_regs += 1 + elif category in ["OPFVF"]: + set_src_reg_idx += setSrcWrapper(src1_freg_id) + num_src_regs += 1 + set_src_reg_idx += setSrcWrapper(src2_reg_id) + num_src_regs += 1 + old_vd_idx = num_src_regs + set_src_reg_idx += setSrcWrapper(old_dest_reg_id) + set_dest_reg_idx = setDestWrapper(dest_reg_id) + vm_decl_rd = vmDeclAndReadData() + set_src_reg_idx += setSrcVm() + set_vlenb = setVlenb() + set_vlen = setVlen() + + if decode_template is VectorIntDecodeBlock: + varith_micro_declare = declareVArithTemplate(Name + "Micro") + elif decode_template is VectorFloatDecodeBlock: + varith_micro_declare = declareVArithTemplate( + Name + "Micro", 'float', 32) + + microiop = InstObjParams(name + "_micro", + Name + "Micro", + microop_class_name, + {'code': code, + 'set_dest_reg_idx': set_dest_reg_idx, + 'set_src_reg_idx': set_src_reg_idx, + 'set_vlenb': set_vlenb, + 'set_vlen': set_vlen, + 'vm_decl_rd': vm_decl_rd, + 'copy_old_vd': copyOldVd(old_vd_idx), + 'declare_varith_template': varith_micro_declare}, + flags) + + header_output = \ + VectorSlideMicroDeclare.subst(microiop) + \ + VectorSlideMacroDeclare.subst(iop) + decoder_output = \ + VectorSlideMicroConstructor.subst(microiop) + \ + macro_construtor.subst(iop) + exec_output = micro_execute_template.subst(microiop) + decode_block = decode_template.subst(iop) + return (header_output, decoder_output, decode_block, exec_output) + +}}; + +def format VectorSlideUpFormat(code, category, *flags) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + VectorSlideBase(name, Name, category, code, + flags, + macro_construtor = VectorSlideUpMacroConstructor, + decode_template = VectorIntDecodeBlock, + micro_execute_template = VectorSlideMicroExecute) +}}; + +def format VectorSlideDownFormat(code, category, *flags) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + VectorSlideBase(name, Name, category, code, + flags, + macro_construtor = VectorSlideDownMacroConstructor, + decode_template = VectorIntDecodeBlock, + micro_execute_template = VectorSlideMicroExecute) +}}; + +def format VectorFloatSlideUpFormat(code, category, *flags) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + VectorSlideBase(name, Name, category, code, + flags, + macro_construtor = VectorSlideUpMacroConstructor, + decode_template = VectorFloatDecodeBlock, + micro_execute_template = VectorFloatSlideMicroExecute) +}}; + +def format VectorFloatSlideDownFormat(code, category, *flags) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + VectorSlideBase(name, Name, category, code, + flags, + macro_construtor = VectorSlideDownMacroConstructor, + decode_template = VectorFloatDecodeBlock, + micro_execute_template = VectorFloatSlideMicroExecute) +}}; diff --git a/src/arch/riscv/isa/formats/vector_conf.isa b/src/arch/riscv/isa/formats/vector_conf.isa new file mode 100644 index 0000000000..6280e5679b --- /dev/null +++ b/src/arch/riscv/isa/formats/vector_conf.isa @@ -0,0 +1,261 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2022 PLCT Lab +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +def format VConfOp(code, write_code, declare_class, branch_class, *flags) {{ + iop = InstObjParams( + name, + Name, + 'VConfOp', + { + 'code': code, + 'write_code': write_code, + }, + flags + ) + declareTemplate = eval(declare_class) + branchTargetTemplate = eval(branch_class) + + header_output = declareTemplate.subst(iop) + decoder_output = VConfConstructor.subst(iop) + decode_block = VConfDecodeBlock.subst(iop) + exec_output = VConfExecute.subst(iop) + branchTargetTemplate.subst(iop) +}}; + +def template VSetVlDeclare {{ + // + // Static instruction class for "%(mnemonic)s". + // + class %(class_name)s : public %(base_class)s + { + private: + %(reg_idx_arr_decl)s; + VTYPE getNewVtype(VTYPE, VTYPE, uint32_t) const; + uint32_t getNewVL( + uint32_t, uint32_t, uint32_t, uint64_t, uint64_t) const; + + public: + /// Constructor. + %(class_name)s(ExtMachInst machInst, uint32_t elen); + Fault execute(ExecContext *, trace::InstRecord *) const override; + std::unique_ptr branchTarget( + ThreadContext *tc) const override; + + using StaticInst::branchTarget; + using %(base_class)s::generateDisassembly; + + }; +}}; + +def template VSetiVliDeclare {{ + // + // Static instruction class for "%(mnemonic)s". + // + class %(class_name)s : public %(base_class)s + { + private: + %(reg_idx_arr_decl)s; + VTYPE getNewVtype(VTYPE, VTYPE, uint32_t) const; + uint32_t getNewVL( + uint32_t, uint32_t, uint32_t, uint64_t, uint64_t) const; + + public: + /// Constructor. + %(class_name)s(ExtMachInst machInst, uint32_t elen); + Fault execute(ExecContext *, trace::InstRecord *) const override; + std::unique_ptr branchTarget( + const PCStateBase &branch_pc) const override; + + using StaticInst::branchTarget; + using %(base_class)s::generateDisassembly; + + }; +}}; + +def template VConfConstructor {{ +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _elen) + : %(base_class)s("%(mnemonic)s", _machInst, _elen, %(op_class)s) + { + %(set_reg_idx_arr)s; + %(constructor)s; + } +}}; + +def template VConfDecodeBlock {{ + return new %(class_name)s(machInst,elen); +}}; + +def template VConfExecute {{ + VTYPE + %(class_name)s::getNewVtype( + VTYPE oldVtype, VTYPE reqVtype, uint32_t vlen) const + { + VTYPE newVtype = oldVtype; + if (oldVtype != reqVtype) { + newVtype = reqVtype; + + float vflmul = getVflmul(newVtype.vlmul); + + uint32_t sew = getSew(newVtype.vsew); + + uint32_t newVill = + !(vflmul >= 0.125 && vflmul <= 8) || + sew > std::min(vflmul, 1.0f) * elen || + bits(reqVtype, 62, 8) != 0; + if (newVill) { + newVtype = 0; + newVtype.vill = 1; + } + } + return newVtype; + } + + uint32_t + %(class_name)s::getNewVL(uint32_t currentVl, uint32_t reqVl, + uint32_t vlmax, uint64_t rdBits, uint64_t rs1Bits) const + { + uint32_t newVl = 0; + if (vlmax == 0) { + newVl = 0; + } else if (rdBits == 0 && rs1Bits == 0) { + newVl = currentVl > vlmax ? vlmax : currentVl; + } else if (rdBits != 0 && rs1Bits == 0) { + newVl = vlmax; + } else if (rs1Bits != 0) { + newVl = reqVl > vlmax ? vlmax : reqVl; + } + return newVl; + } + + Fault + %(class_name)s::execute(ExecContext *xc, + trace::InstRecord *traceData) const + { + auto tc = xc->tcBase(); + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + %(op_decl)s; + %(op_rd)s; + %(code)s; + + tc->setMiscReg(MISCREG_VSTART, 0); + + VTYPE new_vtype = getNewVtype(Vtype, requested_vtype, + vlen); + vlmax = new_vtype.vill ? 0 : getVlmax(new_vtype, vlen); + uint32_t new_vl = getNewVL( + current_vl, requested_vl, vlmax, rd_bits, rs1_bits); + + + + %(write_code)s; + + %(op_wb)s; + return NoFault; + } +}}; + +def template VSetiVliBranchTarget {{ + std::unique_ptr + %(class_name)s::branchTarget(const PCStateBase &branch_pc) const + { + auto &rpc = branch_pc.as(); + + uint64_t rd_bits = machInst.rd; + uint64_t rs1_bits = -1; + uint64_t requested_vl = uimm; + uint64_t requested_vtype = zimm10; + + uint32_t vlen = rpc.vlenb() * 8; + + VTYPE new_vtype = getNewVtype(rpc.vtype(), requested_vtype, vlen); + uint32_t vlmax = new_vtype.vill ? 0 : getVlmax(new_vtype, vlen); + uint32_t new_vl = getNewVL( + rpc.vl(), requested_vl, vlmax, rd_bits, rs1_bits); + + std::unique_ptr npc(dynamic_cast(rpc.clone())); + npc->vtype(new_vtype); + npc->vl(new_vl); + return npc; + } +}}; + +def template VSetVliBranchTarget {{ + std::unique_ptr + %(class_name)s::branchTarget(ThreadContext *tc) const + { + PCStateBase *pc_ptr = tc->pcState().clone(); + + uint64_t rd_bits = machInst.rd; + uint64_t rs1_bits = machInst.rs1; + uint64_t requested_vl = tc->getReg(srcRegIdx(0)); + uint64_t requested_vtype = zimm11; + + uint32_t vlen = pc_ptr->as().vlenb() * 8; + + VTYPE new_vtype = getNewVtype( + pc_ptr->as().vtype(), requested_vtype, vlen); + uint32_t vlmax = new_vtype.vill ? 0 : getVlmax(new_vtype, vlen); + uint32_t new_vl = getNewVL( + pc_ptr->as().vl(), requested_vl, vlmax, rd_bits, rs1_bits); + + pc_ptr->as().vtype(new_vtype); + pc_ptr->as().vl(new_vl); + return std::unique_ptr{pc_ptr}; + } +}}; + +def template VSetVlBranchTarget {{ + std::unique_ptr + %(class_name)s::branchTarget(ThreadContext *tc) const + { + PCStateBase *pc_ptr = tc->pcState().clone(); + + uint64_t rd_bits = machInst.rd; + uint64_t rs1_bits = machInst.rs1; + uint64_t requested_vl = tc->getReg(srcRegIdx(0)); + uint64_t requested_vtype = tc->getReg(srcRegIdx(1)); + + uint32_t vlen = pc_ptr->as().vlenb() * 8; + + VTYPE new_vtype = getNewVtype( + pc_ptr->as().vtype(), requested_vtype, vlen); + uint32_t vlmax = new_vtype.vill ? 0 : getVlmax(new_vtype, vlen); + uint32_t new_vl = getNewVL( + pc_ptr->as().vl(), requested_vl, vlmax, rd_bits, rs1_bits); + + pc_ptr->as().vtype(new_vtype); + pc_ptr->as().vl(new_vl); + return std::unique_ptr{pc_ptr}; + } +}}; diff --git a/src/arch/riscv/isa/formats/vector_mem.isa b/src/arch/riscv/isa/formats/vector_mem.isa new file mode 100644 index 0000000000..3b3309797c --- /dev/null +++ b/src/arch/riscv/isa/formats/vector_mem.isa @@ -0,0 +1,230 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2022 PLCT Lab +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +let {{ + +def setVlen(): + return "uint32_t vlen = VlenbBits * 8;\n" +def setVlenb(): + return "uint32_t vlenb = VlenbBits;\n" + +def declareVMemTemplate(class_name): + return f''' + template class {class_name}; + template class {class_name}; + template class {class_name}; + template class {class_name}; + ''' + +def VMemBase(name, Name, ea_code, memacc_code, mem_flags, + inst_flags, base_class, postacc_code='', + declare_template_base=VMemMacroDeclare, + decode_template=VMemBaseDecodeBlock, exec_template_base='', + # If it's a macroop, the corresponding microops will be + # generated. + is_macroop=True): + # Make sure flags are in lists (convert to lists if not). + mem_flags = makeList(mem_flags) + inst_flags = makeList(inst_flags) + iop = InstObjParams(name, Name, base_class, + {'ea_code': ea_code, + 'memacc_code': memacc_code, + 'postacc_code': postacc_code, + 'declare_vmem_template': declareVMemTemplate(Name)}, + inst_flags) + + constructTemplate = eval(exec_template_base + 'Constructor') + + header_output = declare_template_base.subst(iop) + decoder_output = constructTemplate.subst(iop) + decode_block = decode_template.subst(iop) + exec_output = '' + if not is_macroop: + return (header_output, decoder_output, decode_block, exec_output) + + micro_class_name = exec_template_base + 'MicroInst' + microiop = InstObjParams(name + '_micro', + Name + 'Micro', + exec_template_base + 'MicroInst', + {'ea_code': ea_code, + 'memacc_code': memacc_code, + 'postacc_code': postacc_code, + 'set_vlenb': setVlenb(), + 'set_vlen': setVlen(), + 'declare_vmem_template': declareVMemTemplate(Name + 'Micro')}, + inst_flags) + + if mem_flags: + mem_flags = [ 'Request::%s' % flag for flag in mem_flags ] + s = '\n\tmemAccessFlags = ' + '|'.join(mem_flags) + ';' + microiop.constructor += s + + microDeclTemplate = eval(exec_template_base + 'Micro' + 'Declare') + microConsTemplate = eval(exec_template_base + 'Micro' + 'Constructor') + microExecTemplate = eval(exec_template_base + 'Micro' + 'Execute') + microInitTemplate = eval(exec_template_base + 'Micro' + 'InitiateAcc') + microCompTemplate = eval(exec_template_base + 'Micro' + 'CompleteAcc') + header_output = microDeclTemplate.subst(microiop) + header_output + decoder_output = microConsTemplate.subst(microiop) + decoder_output + micro_exec_output = (microExecTemplate.subst(microiop) + + microInitTemplate.subst(microiop) + + microCompTemplate.subst(microiop)) + exec_output += micro_exec_output + + return (header_output, decoder_output, decode_block, exec_output) + +}}; + +def format VleOp( + memacc_code, + ea_code={{ + EA = Rs1 + vlenb * microIdx; + }}, + mem_flags=[], + inst_flags=[] +) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + VMemBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + 'VleMacroInst', exec_template_base='Vle') +}}; + +def format VseOp( + memacc_code, + ea_code={{ + EA = Rs1 + vlenb * microIdx; + }}, + mem_flags=[], + inst_flags=[] +) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + VMemBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + 'VseMacroInst', exec_template_base='Vse') +}}; + +def format VlmOp( + memacc_code, + ea_code={{ EA = Rs1; }}, + mem_flags=[], + inst_flags=[] +) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + VMemBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + 'VleMacroInst', exec_template_base='Vlm', is_macroop=False) +}}; + +def format VsmOp( + memacc_code, + ea_code={{ EA = Rs1; }}, + mem_flags=[], + inst_flags=[] +) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + VMemBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + 'VseMacroInst', exec_template_base='Vsm', is_macroop=False) +}}; + +def format VlWholeOp( + memacc_code, + ea_code={{ + EA = Rs1 + vlenb * microIdx; + }}, + mem_flags=[], + inst_flags=[] +) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + VMemBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + 'VlWholeMacroInst', exec_template_base='VlWhole') +}}; + +def format VsWholeOp( + memacc_code, + ea_code={{ + EA = Rs1 + vlenb * microIdx; + }}, + mem_flags=[], + inst_flags=[] +) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + VMemBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + 'VsWholeMacroInst', exec_template_base='VsWhole') +}}; + +def format VlStrideOp( + memacc_code, + ea_code={{ + EA = Rs1 + Rs2 * (regIdx * vlenb / elem_size + microIdx); + }}, + mem_flags=[], + inst_flags=[] +) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + VMemBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + 'VlStrideMacroInst', exec_template_base='VlStride') +}}; + +def format VsStrideOp( + memacc_code, + ea_code={{ + EA = Rs1 + Rs2 * (regIdx * vlenb / elem_size + microIdx); + }}, + mem_flags=[], + inst_flags=[] +) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + VMemBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + 'VsStrideMacroInst', exec_template_base='VsStride') +}}; + +def format VlIndexOp( + memacc_code, + ea_code, + mem_flags=[], + inst_flags=[] +) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + VMemBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + 'VlIndexMacroInst', exec_template_base='VlIndex', + declare_template_base=VMemTemplateMacroDeclare, + decode_template=VMemSplitTemplateDecodeBlock + ) +}}; + +def format VsIndexOp( + memacc_code, + ea_code, + mem_flags=[], + inst_flags=[] +) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + VMemBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + 'VsIndexMacroInst', exec_template_base='VsIndex', + declare_template_base=VMemTemplateMacroDeclare, + decode_template=VMemSplitTemplateDecodeBlock + ) +}}; diff --git a/src/arch/riscv/isa/includes.isa b/src/arch/riscv/isa/includes.isa index 8dddc2fb59..b37e62bca8 100644 --- a/src/arch/riscv/isa/includes.isa +++ b/src/arch/riscv/isa/includes.isa @@ -34,6 +34,7 @@ // output header {{ +#include #include #include #include @@ -53,6 +54,7 @@ output header {{ #include "arch/riscv/insts/standard.hh" #include "arch/riscv/insts/static_inst.hh" #include "arch/riscv/insts/unknown.hh" +#include "arch/riscv/insts/vector.hh" #include "arch/riscv/interrupts.hh" #include "cpu/static_inst.hh" #include "mem/packet.hh" @@ -66,9 +68,15 @@ output decoder {{ #include #include +/* riscv softfloat library */ +#include +#include +#include + #include "arch/riscv/decoder.hh" #include "arch/riscv/faults.hh" #include "arch/riscv/mmu.hh" +#include "arch/riscv/regs/float.hh" #include "base/cprintf.hh" #include "base/loader/symtab.hh" #include "cpu/thread_context.hh" @@ -95,6 +103,7 @@ output exec {{ #include "arch/riscv/reg_abi.hh" #include "arch/riscv/regs/float.hh" #include "arch/riscv/regs/misc.hh" +#include "arch/riscv/regs/vector.hh" #include "arch/riscv/utility.hh" #include "base/condcodes.hh" #include "cpu/base.hh" diff --git a/src/arch/riscv/isa/main.isa b/src/arch/riscv/isa/main.isa index 24f366b00c..2923a965da 100644 --- a/src/arch/riscv/isa/main.isa +++ b/src/arch/riscv/isa/main.isa @@ -50,6 +50,9 @@ namespace RiscvISA; //Include the operand_types and operand definitions ##include "operands.isa" +//Include the definitions for the instruction templates +##include "templates/templates.isa" + //Include the definitions for the instruction formats ##include "formats/formats.isa" diff --git a/src/arch/riscv/isa/operands.isa b/src/arch/riscv/isa/operands.isa index 72d8f81bca..3a16e0994c 100644 --- a/src/arch/riscv/isa/operands.isa +++ b/src/arch/riscv/isa/operands.isa @@ -38,7 +38,15 @@ def operand_types {{ 'sd' : 'int64_t', 'ud' : 'uint64_t', 'sf' : 'float', - 'df' : 'double' + 'df' : 'double', + + 'vi' : 'vi', + 'vu' : 'vu', + 'vwi' : 'vwi', + 'vwu' : 'vwu', + 'vext' : 'vext', + 'vextu' : 'vextu', + 'vc' : 'RiscvISA::VecRegContainer' }}; let {{ @@ -79,10 +87,21 @@ def operands {{ 'Fp2': FloatRegOp('df', 'FP2 + 8', 'IsFloating', 2), 'Fp2_bits': FloatRegOp('ud', 'FP2 + 8', 'IsFloating', 2), + 'Vd': VecRegOp('vc', 'VD', 'IsVector', 1), + 'Vs1': VecRegOp('vc', 'VS1', 'IsVector', 2), + 'Vs2': VecRegOp('vc', 'VS2', 'IsVector', 3), + 'Vs3': VecRegOp('vc', 'VS3', 'IsVector', 4), + #Memory Operand 'Mem': MemOp('ud', None, (None, 'IsLoad', 'IsStore'), 5), #Program Counter Operands 'PC': PCStateOp('ud', 'pc', (None, None, 'IsControl'), 7), 'NPC': PCStateOp('ud', 'npc', (None, None, 'IsControl'), 8), + +# VL and VTYPE + 'Vtype': PCStateOp('ud', 'vtype', (None, None, 'IsControl'), 10), + 'VL': PCStateOp('uw', 'vl', (None, None, 'IsControl'), 11), +#VLENB, actually the CSR is read only. + 'VlenbBits': PCStateOp('ud', 'vlenb', (None, None, 'IsControl'), 12), }}; diff --git a/src/arch/riscv/isa/templates/templates.isa b/src/arch/riscv/isa/templates/templates.isa new file mode 100644 index 0000000000..ed3f5287c0 --- /dev/null +++ b/src/arch/riscv/isa/templates/templates.isa @@ -0,0 +1,32 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2022 PLCT Lab +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Include +##include "vector_mem.isa" +##include "vector_arith.isa" diff --git a/src/arch/riscv/isa/templates/vector_arith.isa b/src/arch/riscv/isa/templates/vector_arith.isa new file mode 100644 index 0000000000..bce4e2c55e --- /dev/null +++ b/src/arch/riscv/isa/templates/vector_arith.isa @@ -0,0 +1,2410 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2022 PLCT Lab +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +output header {{ + +#define ASSIGN_VD_BIT(idx, bit) \ + ((Vd[(idx)/8] & ~(1 << (idx)%8)) | ((bit) << (idx)%8)) + +#define COPY_OLD_VD(idx) \ + [[maybe_unused]] RiscvISA::vreg_t old_vd; \ + [[maybe_unused]] decltype(Vd) old_Vd = nullptr; \ + xc->getRegOperand(this, (idx), &old_vd); \ + old_Vd = old_vd.as >(); \ + memcpy(Vd, old_Vd, vlenb); + +#define VRM_REQUIRED \ + uint_fast8_t frm = xc->readMiscReg(MISCREG_FRM); \ + if (frm > 4) \ + return std::make_shared("RM fault", machInst); \ + softfloat_roundingMode = frm; + +template +bool inline +carry_out(Type a, Type b, bool carry_in = false) { + using TypeU = std::make_unsigned_t; + TypeU s = *reinterpret_cast(&a) + + *reinterpret_cast(&b) + carry_in; + return carry_in + ? (s <= *reinterpret_cast(&a)) + : (s < *reinterpret_cast(&a)); +} + +template +bool inline +borrow_out(Type a, Type b, bool borrow_in = false) { + using TypeU = std::make_unsigned_t; + return borrow_in + ? (*reinterpret_cast(&a) <= *reinterpret_cast(&b)) + : (*reinterpret_cast(&a) < *reinterpret_cast(&b)); +} + +}}; + +def template VectorIntMacroDeclare {{ + +template +class %(class_name)s : public %(base_class)s { +private: + %(reg_idx_arr_decl)s; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _vlen); + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorIntMacroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + const uint32_t num_microops = vtype_regs_per_group(vtype); + int32_t tmp_vl = this->vl; + const int32_t micro_vlmax = vtype_VLMAX(_machInst.vtype8, vlen, true); + int32_t micro_vl = std::min(tmp_vl, micro_vlmax); + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + for (int i = 0; i < num_microops && micro_vl > 0; ++i) { + microop = new %(class_name)sMicro(_machInst, micro_vl, i); + microop->setDelayedCommit(); + this->microops.push_back(microop); + micro_vl = std::min(tmp_vl -= micro_vlmax, micro_vlmax); + } + + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +%(declare_varith_template)s; + +}}; + +def template VectorIntMicroDeclare {{ + +template +class %(class_name)s : public %(base_class)s +{ +private: + // vs1, vs2, vs3(old_vd), vm for *.vv, *.vx + // vs2, (old_vd), vm for *.vi + RegId srcRegIdxArr[4]; + RegId destRegIdxArr[1]; + bool vm; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _microVl, + uint32_t _microIdx); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorIntMicroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx) + : %(base_class)s("%(mnemonic)s", _machInst, + %(op_class)s, _microVl, _microIdx) +{ + this->vm = _machInst.vm; + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + %(set_dest_reg_idx)s; + %(set_src_reg_idx)s; +} + +%(declare_varith_template)s; + +}}; + +def template VectorIntMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using vu [[maybe_unused]] = std::make_unsigned_t; + using vi [[maybe_unused]] = std::make_signed_t; + [[maybe_unused]] constexpr size_t sew = sizeof(vu) * 8; + + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(vm_decl_rd)s; + %(copy_old_vd)s; + %(code)s; + %(op_wb)s; + + return NoFault; +} + +%(declare_varith_template)s; + +}}; + +def template VectorIntExtMacroDeclare {{ + +template +class %(class_name)s : public %(base_class)s { +private: + %(reg_idx_arr_decl)s; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _vlen); + std::string generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const override; +}; + +}}; + +def template VectorIntExtMicroDeclare {{ + +template +class %(class_name)s : public %(base_class)s +{ +private: + RegId srcRegIdxArr[3]; + RegId destRegIdxArr[1]; + bool vm; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _microVl, + uint32_t _microIdx); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + std::string generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const override; +}; + +}}; + +def template VectorIntExtMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using vu [[maybe_unused]] = std::make_unsigned_t; + using vi [[maybe_unused]] = std::make_signed_t; + + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + auto SEW = vtype_SEW(vtype); + auto index = (microIdx % %(ext_div)d); + + switch (SEW / %(ext_div)d) { + case 8: { + using vext [[maybe_unused]] = int8_t; + using vextu [[maybe_unused]] = uint8_t; + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + %(vm_decl_rd)s; + %(copy_old_vd)s; + %(code)s; + %(op_wb)s; + break; + } + case 16: { + using vext [[maybe_unused]] = int16_t; + using vextu [[maybe_unused]] = uint16_t; + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + %(vm_decl_rd)s; + %(copy_old_vd)s; + %(code)s; + %(op_wb)s; + break; + } + case 32: { + using vext [[maybe_unused]] = int32_t; + using vextu [[maybe_unused]] = uint32_t; + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + %(vm_decl_rd)s; + %(copy_old_vd)s; + %(code)s; + %(op_wb)s; + break; + } + default: break; + } + + return NoFault; +} + +template +std::string +%(class_name)s::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", " + << registerName(srcRegIdx(0)); + if (machInst.vm == 0) ss << ", v0.t"; + return ss.str(); +} + +%(declare_varith_template)s; + +}}; + +def template VectorIntExtMacroExecute {{ + +template +std::string +%(class_name)s::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", " + << registerName(srcRegIdx(0)); + if (machInst.vm == 0) ss << ", v0.t"; + return ss.str(); +} + +%(declare_varith_template)s; + +}}; + +def template VectorIntDecodeBlock {{ + +switch(machInst.vtype8.vsew) { +case 0b000: return new %(class_name)s(machInst, vlen); +case 0b001: return new %(class_name)s(machInst, vlen); +case 0b010: return new %(class_name)s(machInst, vlen); +case 0b011: return new %(class_name)s(machInst, vlen); +default: GEM5_UNREACHABLE; +} + +}}; + +def template VectorIntWideningMacroDeclare {{ + +template +class %(class_name)s : public %(base_class)s { +private: + %(reg_idx_arr_decl)s; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _vlen); + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorIntWideningMacroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + const int64_t vlmul = vtype_vlmul(_machInst.vtype8); + // Todo: move to Decode template + panic_if(vlmul == 3, "LMUL=8 is illegal for widening inst"); + // when LMUL setted as m1, need to split to 2 micro insts + const uint32_t num_microops = 1 << std::max(0, vlmul + 1); + + int32_t tmp_vl = this->vl; + const int32_t t_micro_vlmax = vtype_VLMAX(_machInst.vtype8, vlen, true); + const int32_t micro_vlmax = vlmul < 0 ? t_micro_vlmax : t_micro_vlmax / 2; + int32_t micro_vl = std::min(tmp_vl, micro_vlmax); + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + for (int i = 0; i < num_microops && micro_vl > 0; ++i) { + microop = new %(class_name)sMicro(_machInst, micro_vl, i); + microop->setDelayedCommit(); + this->microops.push_back(microop); + micro_vl = std::min(tmp_vl -= micro_vlmax, micro_vlmax); + } + + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +%(declare_varith_template)s; + +}}; + +def template VectorIntWideningMicroDeclare {{ + +template +class %(class_name)s : public %(base_class)s +{ +private: + // vs1, vs2, vs3(old_vd), vm for *.vv, *.vx + RegId srcRegIdxArr[4]; + RegId destRegIdxArr[1]; + bool vm; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _microVl, + uint32_t _microIdx); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorIntWideningMicroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx) + : %(base_class)s("%(mnemonic)s", _machInst, + %(op_class)s, _microVl, _microIdx) +{ + this->vm = _machInst.vm; + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + %(set_dest_reg_idx)s; + %(set_src_reg_idx)s; +} + +%(declare_varith_template)s; + +}}; + +def template VectorIntWideningMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using vu [[maybe_unused]] = std::make_unsigned_t; + using vi [[maybe_unused]] = std::make_signed_t; + using vwu [[maybe_unused]] = typename double_width::type; + using vwi [[maybe_unused]] = typename double_width::type; + [[maybe_unused]] constexpr size_t sew = sizeof(vu) * 8; + + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + const int64_t vlmul = vtype_vlmul(machInst.vtype8); + const int32_t t_micro_vlmax = vtype_VLMAX(machInst.vtype8, vlen, true); + const int32_t micro_vlmax = vlmul < 0 ? t_micro_vlmax : t_micro_vlmax / 2; + [[maybe_unused]] const size_t offset = + (this->microIdx % 2 == 0) ? 0 : micro_vlmax; + + %(vm_decl_rd)s; + %(copy_old_vd)s; + %(code)s; + %(op_wb)s; + return NoFault; +} + +%(declare_varith_template)s; + +}}; + +def template VectorIntNarrowingMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using vu [[maybe_unused]] = std::make_unsigned_t; + using vi [[maybe_unused]] = std::make_signed_t; + using vwu [[maybe_unused]] = typename double_width::type; + using vwi [[maybe_unused]] = typename double_width::type; + [[maybe_unused]] constexpr size_t sew = sizeof(vu) * 8; + + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + const int64_t vlmul = vtype_vlmul(machInst.vtype8); + const int32_t t_micro_vlmax = vtype_VLMAX(machInst.vtype8, vlen, true); + const int32_t micro_vlmax = vlmul < 0 ? t_micro_vlmax : t_micro_vlmax / 2; + [[maybe_unused]] const size_t offset = + (this->microIdx % 2 == 0) ? 0 : micro_vlmax; + + %(vm_decl_rd)s; + %(copy_old_vd)s; + %(code)s; + %(op_wb)s; + return NoFault; +} + +%(declare_varith_template)s; + +}}; + +def template VectorIntWideningDecodeBlock {{ + +switch(machInst.vtype8.vsew) { +case 0b000: return new %(class_name)s(machInst, vlen); +case 0b001: return new %(class_name)s(machInst, vlen); +case 0b010: return new %(class_name)s(machInst, vlen); +default: GEM5_UNREACHABLE; +} + +}}; + +def template VectorFloatMacroDeclare {{ + +template +class %(class_name)s : public %(base_class)s { +private: + %(reg_idx_arr_decl)s; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _vlen); + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorFloatMacroConstructor {{ +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + const uint32_t num_microops = vtype_regs_per_group(vtype); + int32_t tmp_vl = this->vl; + const int32_t micro_vlmax = vtype_VLMAX(_machInst.vtype8, vlen, true); + int32_t micro_vl = std::min(tmp_vl, micro_vlmax); + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + for (int i = 0; i < num_microops && micro_vl > 0; ++i) { + microop = new %(class_name)sMicro(_machInst, micro_vl, i); + microop->setDelayedCommit(); + this->microops.push_back(microop); + micro_vl = std::min(tmp_vl -= micro_vlmax, micro_vlmax); + } + + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +%(declare_varith_template)s; + +}}; + +def template VectorFloatMicroDeclare {{ + +template +class %(class_name)s : public %(base_class)s +{ +private: + // vs1, vs2, vs3(old_vd), vm + RegId srcRegIdxArr[4]; + RegId destRegIdxArr[1]; + bool vm; +public: + %(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorFloatMicroConstructor {{ +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx) + : %(base_class)s("%(mnemonic)s", _machInst, + %(op_class)s, _microVl, _microIdx) +{ + this->vm = _machInst.vm; + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + %(set_dest_reg_idx)s; + %(set_src_reg_idx)s; +} + +%(declare_varith_template)s; + +}}; + +def template VectorFloatMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using et = ElemType; + using vu = decltype(et::v); + + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + VRM_REQUIRED; + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(vm_decl_rd)s; + %(copy_old_vd)s; + %(code)s; + %(op_wb)s; + + return NoFault; +} + +%(declare_varith_template)s; + +}}; + +def template VectorFloatDecodeBlock {{ + +switch(machInst.vtype8.vsew) { +case 0b010: return new %(class_name)s(machInst, vlen); +case 0b011: return new %(class_name)s(machInst, vlen); +default: GEM5_UNREACHABLE; +} + +}}; + +def template VectorFloatCvtMacroDeclare {{ + +template +class %(class_name)s : public %(base_class)s { +private: + %(reg_idx_arr_decl)s; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _vlen); + std::string generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const override + { + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", " + << registerName(srcRegIdx(0)); + if (machInst.vm == 0) ss << ", v0.t"; + return ss.str(); + } +}; + +}}; + +def template VectorFloatCvtMicroDeclare {{ + +template +class %(class_name)s : public %(base_class)s +{ +private: + RegId srcRegIdxArr[3]; + RegId destRegIdxArr[1]; + bool vm; +public: + %(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + std::string generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const override + { + std::stringstream ss; + ss << mnemonic << ' ' << registerName(destRegIdx(0)) << ", " + << registerName(srcRegIdx(0)); + if (machInst.vm == 0) ss << ", v0.t"; + return ss.str(); + } +}; + +}}; + + +def template VectorFloatWideningMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using et = ElemType; + using vu [[maybe_unused]] = decltype(et::v); + using ewt = typename double_width::type; + using vwu = decltype(ewt::v); + + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + VRM_REQUIRED; + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + + const int64_t vlmul = vtype_vlmul(machInst.vtype8); + const int32_t t_micro_vlmax = vtype_VLMAX(machInst.vtype8, vlen, true); + const int32_t micro_vlmax = vlmul < 0 ? t_micro_vlmax : t_micro_vlmax / 2; + [[maybe_unused]] const size_t offset = + (this->microIdx % 2 == 0) ? 0 : micro_vlmax; + + %(vm_decl_rd)s; + %(copy_old_vd)s; + %(code)s; + %(op_wb)s; + return NoFault; +} + +%(declare_varith_template)s; + +}}; + +def template VectorFloatNarrowingMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using et = ElemType; + using vu [[maybe_unused]] = decltype(et::v); + using ewt = typename double_width::type; + using vwu = decltype(ewt::v); + + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + VRM_REQUIRED; + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + + const int64_t vlmul = vtype_vlmul(machInst.vtype8); + const int32_t t_micro_vlmax = vtype_VLMAX(machInst.vtype8, vlen, true); + const int32_t micro_vlmax = vlmul < 0 ? t_micro_vlmax : t_micro_vlmax / 2; + [[maybe_unused]] const size_t offset = + (this->microIdx % 2 == 0) ? 0 : micro_vlmax; + + %(vm_decl_rd)s; + %(copy_old_vd)s; + %(code)s; + %(op_wb)s; + return NoFault; +} + +%(declare_varith_template)s; + +}}; + +def template VectorFloatWideningDecodeBlock {{ + +switch(machInst.vtype8.vsew) { +case 0b010: return new %(class_name)s(machInst, vlen); +default: GEM5_UNREACHABLE; +} + +}}; + +def template ViotaMacroDeclare {{ + +template +class %(class_name)s : public %(base_class)s { +private: + int cnt = 0; + %(reg_idx_arr_decl)s; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _vlen); + using %(base_class)s::generateDisassembly; +}; + +}}; + + +def template ViotaMacroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + const uint32_t num_microops = vtype_regs_per_group(vtype); + int32_t tmp_vl = this->vl; + const int32_t micro_vlmax = vtype_VLMAX(_machInst.vtype8, vlen, true); + int32_t micro_vl = std::min(tmp_vl, micro_vlmax); + + StaticInstPtr microop; + + // Allow one empty micro op to hold IsLastMicroop flag + for (int i = 0; i < num_microops && micro_vl >= 0; ++i) { + microop = new %(class_name)sMicro(_machInst, micro_vl, i, + &cnt); + microop->setDelayedCommit(); + this->microops.push_back(microop); + micro_vl = std::min(tmp_vl -= micro_vlmax, micro_vlmax); + } + + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +%(declare_varith_template)s; + +}}; + +def template ViotaMicroDeclare {{ + +template +class %(class_name)s : public %(base_class)s +{ +private: + RegId srcRegIdxArr[4]; + RegId destRegIdxArr[1]; + bool vm; + int* cnt; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _microVl, + uint32_t _microIdx, int* cnt); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template ViotaMicroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx, int* cnt) + : %(base_class)s("%(mnemonic)s", _machInst, + %(op_class)s, _microVl, _microIdx) +{ + this->vm = _machInst.vm; + this->cnt = cnt; + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + %(set_dest_reg_idx)s; + %(set_src_reg_idx)s; + setSrcRegIdx(_numSrcRegs++, vecRegClass[_machInst.vs2]); +} + +%(declare_varith_template)s; + +}}; + +def template ViotaMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using vu [[maybe_unused]] = std::make_unsigned_t; + using vi [[maybe_unused]] = std::make_signed_t; + + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(vm_decl_rd)s; + %(copy_old_vd)s; + %(code)s; + %(op_wb)s; + return NoFault; +} + +%(declare_varith_template)s; + +}}; + + +def template Vector1Vs1VdMaskConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s) +{ + this->vm = _machInst.vm; + %(set_reg_idx_arr)s; + %(set_dest_reg_idx)s; + %(set_src_reg_idx)s; + %(set_vm_idx)s; +} + +%(declare_varith_template)s; + +}}; + +def template Vector1Vs1VdMaskExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using vu = uint8_t; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(vm_decl_rd)s; + %(copy_old_vd)s; + %(code)s; + %(op_wb)s; + return NoFault; +}; + +%(declare_varith_template)s; + +}}; + +def template Vector1Vs1RdMaskDeclare {{ + +template +class %(class_name)s : public %(base_class)s { +private: + RegId srcRegIdxArr[2]; + RegId destRegIdxArr[1]; + bool vm; +public: + %(class_name)s(ExtMachInst _machInst); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template Vector1Vs1RdMaskConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s) +{ + this->vm = _machInst.vm; + %(set_reg_idx_arr)s; + %(constructor)s; + %(set_vm_idx)s; +} + +%(declare_varith_template)s; + +}}; + +def template Vector1Vs1RdMaskExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using vu [[maybe_unused]] = std::make_unsigned_t; + using vi [[maybe_unused]] = std::make_signed_t; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_rd)s; + uint64_t Rd = 0; + %(vm_decl_rd)s; + %(code)s; + %(op_wb)s; + return NoFault; +}; + +%(declare_varith_template)s; + +}}; + +def template VectorIntMaskMacroDeclare {{ + +template +class %(class_name)s : public %(base_class)s { +private: + %(reg_idx_arr_decl)s; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _vlen); + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorIntMaskMacroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + const uint32_t num_microops = vtype_regs_per_group(vtype); + int32_t tmp_vl = this->vl; + const int32_t micro_vlmax = vtype_VLMAX(_machInst.vtype8, vlen, true); + int32_t micro_vl = std::min(tmp_vl, micro_vlmax); + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + for (int i = 0; i < num_microops && micro_vl > 0; ++i) { + microop = new %(class_name)sMicro(_machInst, micro_vl, i); + microop->setDelayedCommit(); + this->microops.push_back(microop); + micro_vl = std::min(tmp_vl -= micro_vlmax, micro_vlmax); + } + microop = new VMaskMergeMicroInst(_machInst, _machInst.vd, + this->microops.size(), _vlen, sizeof(ElemType)); + this->microops.push_back(microop); + + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +%(declare_varith_template)s; + +}}; + +def template VectorIntMaskMicroDeclare {{ + +template +class %(class_name)s : public %(base_class)s +{ +private: + // vs1(rs1), vs2, old_vd, v0 for *.vv[m] or *.vx[m] + // vs2, old_vd, v0 for *.vi[m] + RegId srcRegIdxArr[4]; + RegId destRegIdxArr[1]; + bool vm; +public: + %(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorIntMaskMicroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx) +: %(base_class)s("%(mnemonic)s", _machInst, + %(op_class)s, _microVl, _microIdx) +{ + this->vm = _machInst.vm; + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + %(set_dest_reg_idx)s; + %(set_src_reg_idx)s; +} + +%(declare_varith_template)s; + +}}; + +def template VectorIntMaskMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using vu [[maybe_unused]] = std::make_unsigned_t; + using vi [[maybe_unused]] = std::make_signed_t; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(vm_decl_rd)s; + %(copy_old_vd)s; + + const uint32_t bit_offset = vlenb / sizeof(ElemType); + const uint32_t offset = bit_offset * microIdx; + + %(code)s; + %(op_wb)s; + return NoFault; +} + +%(declare_varith_template)s; + +}}; + +def template VectorFloatMaskMacroDeclare {{ + +template +class %(class_name)s : public %(base_class)s { +private: + %(reg_idx_arr_decl)s; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _vlen); + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorFloatMaskMacroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + const uint32_t num_microops = vtype_regs_per_group(vtype); + int32_t tmp_vl = this->vl; + const int32_t micro_vlmax = vtype_VLMAX(_machInst.vtype8, vlen, true); + int32_t micro_vl = std::min(tmp_vl, micro_vlmax); + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + for (int i = 0; i < num_microops && micro_vl > 0; ++i) { + microop = new %(class_name)sMicro(_machInst, micro_vl, i); + microop->setDelayedCommit(); + this->microops.push_back(microop); + micro_vl = std::min(tmp_vl -= micro_vlmax, micro_vlmax); + } + microop = new VMaskMergeMicroInst(_machInst, _machInst.vd, + this->microops.size(), _vlen, sizeof(ElemType)); + this->microops.push_back(microop); + + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +%(declare_varith_template)s; + +}}; + +def template VectorFloatMaskMicroDeclare {{ + +template +class %(class_name)s : public %(base_class)s +{ +private: + // vs1(rs1), vs2, old_vd, v0 for *.vv or *.vf + RegId srcRegIdxArr[4]; + RegId destRegIdxArr[1]; + bool vm; +public: + %(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorFloatMaskMicroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx) +: %(base_class)s("%(mnemonic)s", _machInst, + %(op_class)s, _microVl, _microIdx) +{ + this->vm = _machInst.vm; + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + %(set_dest_reg_idx)s; + %(set_src_reg_idx)s; +} + +%(declare_varith_template)s; + +}}; + +def template VectorFloatMaskMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using et = ElemType; + using vu = decltype(et::v); + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(vm_decl_rd)s; + %(copy_old_vd)s; + + const uint32_t bit_offset = vlenb / sizeof(ElemType); + const uint32_t offset = bit_offset * microIdx; + + %(code)s; + %(op_wb)s; + return NoFault; +} + +%(declare_varith_template)s; + +}}; + +def template VMvWholeMacroDeclare {{ + +class %(class_name)s : public %(base_class)s { +private: + %(reg_idx_arr_decl)s; +public: + %(class_name)s(ExtMachInst _machInst); + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VMvWholeMacroConstructor {{ + +%(class_name)s::%(class_name)s(ExtMachInst _machInst) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + const uint32_t num_microops = _machInst.simm3 + 1; + StaticInstPtr microop; + + for (int i = 0; i < num_microops; ++i) { + microop = new %(class_name)sMicro(_machInst, 0, i); + microop->setDelayedCommit(); + this->microops.push_back(microop); + } + + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +}}; + +def template VMvWholeMicroDeclare {{ + +class %(class_name)s : public %(base_class)s +{ +private: + RegId srcRegIdxArr[1]; + RegId destRegIdxArr[1]; + bool vm; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _microVl, + uint32_t _microIdx); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VMvWholeMicroConstructor {{ + +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx) + : %(base_class)s("%(mnemonic)s", _machInst, + %(op_class)s, _microVl, _microIdx) +{ + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + setDestRegIdx(_numDestRegs++, vecRegClass[_machInst.vd + _microIdx]); + _numTypedDestRegs[VecRegClass]++; + setSrcRegIdx(_numSrcRegs++, vecRegClass[_machInst.vs2 + _microIdx]); +} + +}}; + +def template VMvWholeMicroExecute {{ + +Fault +%(class_name)s::execute(ExecContext* xc, trace::InstRecord* traceData) const +{ + // TODO: Check register alignment. + // TODO: If vd is equal to vs2 the instruction is an architectural NOP. + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlen)s; + for (size_t i = 0; i < (vlen / 64); i++) { + %(code)s; + } + %(op_wb)s; + return NoFault; +} + +}}; + +def template VectorMaskDeclare {{ + +template +class %(class_name)s : public %(base_class)s { +private: + RegId srcRegIdxArr[3]; + RegId destRegIdxArr[1]; +public: + %(class_name)s(ExtMachInst _machInst); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorMaskConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s) +{ + %(set_reg_idx_arr)s; + %(set_dest_reg_idx)s; + %(set_src_reg_idx)s; +} + +%(declare_varith_template)s; + +}}; + +def template VectorMaskExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using vu = uint8_t; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + // TODO: remove it + %(set_vlenb)s; + %(copy_old_vd)s; + %(code)s; + %(op_wb)s; + + return NoFault; +}; + +%(declare_varith_template)s; + +}}; + +def template VectorMaskDecodeBlock {{ + +return new %(class_name)s(machInst); + +}}; + +def template VectorNonSplitDeclare {{ + +template +class %(class_name)s : public %(base_class)s { +private: + RegId srcRegIdxArr[2]; + RegId destRegIdxArr[1]; +public: + %(class_name)s(ExtMachInst _machInst); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorNonSplitConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + %(set_vm_idx)s; +} + +%(declare_varith_template)s; + +}}; + +def template VectorIntNonSplitExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using vu [[maybe_unused]] = std::make_unsigned_t; + using vi [[maybe_unused]] = std::make_signed_t; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(vm_decl_rd)s; + %(code)s; + %(op_wb)s; + return NoFault; +} + +%(declare_varith_template)s; + +}}; + +def template VectorFloatNonSplitExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using et = ElemType; + using vu = decltype(et::v); + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(vm_decl_rd)s; + %(code)s; + %(op_wb)s; + return NoFault; +} + +%(declare_varith_template)s; + +}}; + +def template VectorFloatNonSplitDecodeBlock {{ + +switch(machInst.vtype8.vsew) { +case 0b010: return new %(class_name)s(machInst); +case 0b011: return new %(class_name)s(machInst); +default: GEM5_UNREACHABLE; +} + +}}; + +def template VectorIntNonSplitDecodeBlock {{ + +switch(machInst.vtype8.vsew) { +case 0b000: return new %(class_name)s(machInst); +case 0b001: return new %(class_name)s(machInst); +case 0b010: return new %(class_name)s(machInst); +case 0b011: return new %(class_name)s(machInst); +default: GEM5_UNREACHABLE; +} + +}}; + +def template VectorReduceMacroDeclare {{ + +template +class %(class_name)s : public %(base_class)s { +private: + %(reg_idx_arr_decl)s; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _vlen); + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorReduceMacroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + const uint32_t num_microops = vtype_regs_per_group(vtype); + int32_t tmp_vl = this->vl; + const int32_t micro_vlmax = vtype_VLMAX(_machInst.vtype8, vlen, true); + int32_t micro_vl = std::min(tmp_vl, micro_vlmax); + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + for (int i = 0; i < num_microops && micro_vl > 0; ++i) { + microop = new %(class_name)sMicro(_machInst, micro_vl, i); + microop->setDelayedCommit(); + this->microops.push_back(microop); + micro_vl = std::min(tmp_vl -= micro_vlmax, micro_vlmax); + } + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +%(declare_varith_template)s; + +}}; + +def template VectorReduceMicroDeclare {{ + +template +class %(class_name)s : public %(base_class)s +{ +private: + // vs2, vs1, vd, vm + RegId srcRegIdxArr[4]; + RegId destRegIdxArr[1]; + bool vm; +public: + %(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorReduceMicroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx) +: %(base_class)s("%(mnemonic)s", _machInst, + %(op_class)s, _microVl, _microIdx) +{ + this->vm = _machInst.vm; + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + %(set_dest_reg_idx)s; + %(set_src_reg_idx)s; +} + +%(declare_varith_template)s; + +}}; + +def template VectorReduceIntMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + %(type_def)s; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + %(vm_decl_rd)s; + %(copy_old_vd)s; + + auto reduce_loop = + [&, this](const auto& f, const auto* _, const auto* vs2) { + ElemType microop_result = this->microIdx != 0 ? old_Vd[0] : Vs1[0]; + for (uint32_t i = 0; i < this->microVl; i++) { + uint32_t ei = i + vtype_VLMAX(vtype, vlen, true) * + this->microIdx; + if (this->vm || elem_mask(v0, ei)) { + microop_result = f(microop_result, Vs2[i]); + } + } + return microop_result; + }; + + %(code)s; + %(op_wb)s; + return NoFault; +} + +%(declare_varith_template)s; + +}}; + +def template VectorReduceFloatMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + %(type_def)s; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + %(vm_decl_rd)s; + %(copy_old_vd)s; + + Vd[0] = this->microIdx != 0 ? old_Vd[0] : Vs1[0]; + + auto reduce_loop = + [&, this](const auto& f, const auto* _, const auto* vs2) { + vu tmp_val = Vd[0]; + for (uint32_t i = 0; i < this->microVl; i++) { + uint32_t ei = i + vtype_VLMAX(vtype, vlen, true) * + this->microIdx; + if (this->vm || elem_mask(v0, ei)) { + tmp_val = f(tmp_val, Vs2[i]).v; + } + } + return tmp_val; + }; + + %(code)s; + %(op_wb)s; + return NoFault; +} + +%(declare_varith_template)s; + +}}; + +def template VectorReduceFloatWideningMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + %(type_def)s; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + %(vm_decl_rd)s; + %(copy_old_vd)s; + + Vd[0] = this->microIdx != 0 ? old_Vd[0] : Vs1[0]; + + auto reduce_loop = + [&, this](const auto& f, const auto* _, const auto* vs2) { + vwu tmp_val = Vd[0]; + for (uint32_t i = 0; i < this->microVl; i++) { + uint32_t ei = i + vtype_VLMAX(vtype, vlen, true) * + this->microIdx; + if (this->vm || elem_mask(v0, ei)) { + tmp_val = f(tmp_val, Vs2[i]).v; + } + } + return tmp_val; + }; + + %(code)s; + %(op_wb)s; + return NoFault; +} + +%(declare_varith_template)s; + +}}; + +def template VectorGatherMacroDeclare {{ + +template +class %(class_name)s : public %(base_class)s{ +private: + %(reg_idx_arr_decl)s; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _vlen); + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorGatherMacroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + constexpr uint32_t vd_eewb = sizeof(ElemType); + constexpr uint32_t vs2_eewb = sizeof(ElemType); + constexpr uint32_t vs1_eewb = sizeof(IndexType); + constexpr bool vs1_split = vd_eewb > vs1_eewb; + const int8_t lmul = vtype_vlmul(vtype); + const int8_t vs1_emul = lmul + + (vs1_split ? -(vs2_eewb / vs1_eewb) : vs1_eewb / vs2_eewb); + const uint8_t vs2_vregs = lmul < 0 ? 1 : 1 << lmul; + const uint8_t vs1_vregs = vs1_emul < 0 ? 1 : 1 << vs1_emul; + const uint8_t vd_vregs = vs2_vregs; + uint32_t vlenb = vlen >> 3; + const int32_t micro_vlmax = vlenb / std::max(vd_eewb, vs1_eewb); + int32_t remaining_vl = this->vl; + int32_t micro_vl = std::min(remaining_vl, micro_vlmax); + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + for (uint32_t i = 0; i < std::max(vs1_vregs, vd_vregs) && micro_vl > 0; + i++) { + for (uint8_t j = 0; j < vs2_vregs; j++) { + microop = new %(class_name)sMicro( + _machInst, micro_vl, i * vs2_vregs + j); + microop->setDelayedCommit(); + this->microops.push_back(microop); + } + micro_vl = std::min(remaining_vl -= micro_vlmax, micro_vlmax); + } + + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +%(declare_varith_template)s; + +}}; + +def template VectorGatherMicroDeclare {{ + +template +class %(class_name)s : public %(base_class)s +{ +private: + // vs2, vs1, vd, vm + RegId srcRegIdxArr[4]; + RegId destRegIdxArr[1]; + bool vm; +public: + %(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorGatherMicroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx) +: %(base_class)s("%(mnemonic)s", _machInst, + %(op_class)s, _microVl, _microIdx) +{ + this->vm = _machInst.vm; + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + [[maybe_unused]] constexpr uint32_t vd_eewb = sizeof(ElemType); + [[maybe_unused]] constexpr uint32_t vs2_eewb = sizeof(ElemType); + [[maybe_unused]] constexpr uint32_t vs1_eewb = sizeof(IndexType); + constexpr uint32_t vs1_split_num = (vd_eewb + vs1_eewb - 1) / vs1_eewb; + constexpr uint32_t vd_split_num = (vs1_eewb + vd_eewb - 1) / vd_eewb; + const int8_t lmul = vtype_vlmul(vtype); + const uint8_t vs2_vregs = lmul < 0 ? 1 : 1 << lmul; + [[maybe_unused]] const uint32_t vs2_idx = _microIdx % vs2_vregs; + [[maybe_unused]] const uint32_t vs1_idx = + _microIdx / vs2_vregs / vs1_split_num; + [[maybe_unused]] const uint32_t vd_idx = + _microIdx / vs2_vregs / vd_split_num; + %(set_dest_reg_idx)s; + %(set_src_reg_idx)s; +} + +%(declare_varith_template)s; + +}}; + +def template VectorGatherMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using vu [[maybe_unused]] = std::make_unsigned_t; + [[maybe_unused]] constexpr size_t sew = sizeof(vu) * 8; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + %(vm_decl_rd)s; + %(copy_old_vd)s; + const uint32_t vlmax = vtype_VLMAX(vtype,vlen); + constexpr uint32_t vd_eewb = sizeof(ElemType); + constexpr uint32_t vs1_eewb = sizeof(IndexType); + constexpr uint32_t vs2_eewb = sizeof(ElemType); + constexpr uint32_t vs1_split_num = (vd_eewb + vs1_eewb - 1) / vs1_eewb; + constexpr uint32_t vd_split_num = (vs1_eewb + vd_eewb - 1) / vd_eewb; + [[maybe_unused]] const uint32_t vd_elems = vlenb / vd_eewb; + [[maybe_unused]] const uint32_t vs1_elems = vlenb / vs1_eewb; + [[maybe_unused]] const uint32_t vs2_elems = vlenb / vs2_eewb; + [[maybe_unused]] const int8_t lmul = vtype_vlmul(vtype); + [[maybe_unused]] const uint8_t vs2_vregs = lmul < 0 ? 1 : 1 << lmul; + [[maybe_unused]] const uint32_t vs2_idx = microIdx % vs2_vregs; + [[maybe_unused]] const uint32_t vs1_idx = + microIdx / vs2_vregs / vs1_split_num; + [[maybe_unused]] const uint32_t vd_idx = + microIdx / vs2_vregs / vd_split_num; + [[maybe_unused]] const uint32_t vs1_bias = + vs1_elems * (vd_idx % vs1_split_num) / vs1_split_num; + [[maybe_unused]] const uint32_t vd_bias = + vd_elems * (vs1_idx % vd_split_num) / vd_split_num; + + %(code)s; + %(op_wb)s; + + return NoFault; +} + +%(declare_varith_template)s; + +}}; + +def template VectorGatherDecodeBlock {{ + +switch(machInst.vtype8.vsew) { + case 0b000: { + using elem_type [[maybe_unused]] = uint8_t; + return new %(class_name)s(machInst, vlen); + } + case 0b001: { + using elem_type [[maybe_unused]] = uint16_t; + return new %(class_name)s(machInst, vlen); + } + case 0b010: { + using elem_type [[maybe_unused]] = uint32_t; + return new %(class_name)s(machInst, vlen); + } + case 0b011: { + using elem_type [[maybe_unused]] = uint64_t; + return new %(class_name)s(machInst, vlen); + } + default: GEM5_UNREACHABLE; +} + +}}; + +def template VectorIntVxsatMacroDeclare {{ + +template +class %(class_name)s : public %(base_class)s{ +private: + %(reg_idx_arr_decl)s; + bool vxsat = false; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _vlen); + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorIntVxsatMacroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + const uint32_t num_microops = vtype_regs_per_group(vtype); + int32_t tmp_vl = this->vl; + const int32_t micro_vlmax = vtype_VLMAX(_machInst.vtype8, vlen, true); + int32_t micro_vl = std::min(tmp_vl, micro_vlmax); + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + for (int i = 0; i < num_microops && micro_vl > 0; ++i) { + microop = new %(class_name)sMicro(_machInst, + micro_vl, i, &vxsat); + microop->setDelayedCommit(); + this->microops.push_back(microop); + micro_vl = std::min(tmp_vl -= micro_vlmax, micro_vlmax); + } + + microop = new VxsatMicroInst(&vxsat, _machInst); + microop->setFlag(StaticInst::IsSerializeAfter); + microop->setFlag(StaticInst::IsNonSpeculative); + this->microops.push_back(microop); + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +%(declare_varith_template)s; + +}}; + +def template VectorIntVxsatMicroDeclare {{ + +template +class %(class_name)s : public %(base_class)s +{ +private: + RegId srcRegIdxArr[4]; + RegId destRegIdxArr[1]; + bool vm; + bool* vxsatptr; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _microVl, + uint32_t _microIdx, bool* vxsatptr); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorIntVxsatMicroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx, bool* vxsatptr) + : %(base_class)s("%(mnemonic)s", _machInst, + %(op_class)s, _microVl, _microIdx) +{ + this->vm = _machInst.vm; + this->vxsatptr = vxsatptr; + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + %(set_dest_reg_idx)s; + %(set_src_reg_idx)s; +} + +%(declare_varith_template)s; + +}}; + +def template VectorReduceIntWideningMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using vu [[maybe_unused]] = std::make_unsigned_t; + using vi [[maybe_unused]] = std::make_signed_t; + using vwu [[maybe_unused]] = typename double_width::type; + using vwi [[maybe_unused]] = typename double_width::type; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + %(vm_decl_rd)s; + %(copy_old_vd)s; + + Vd[0] = this->microIdx != 0 ? old_Vd[0] : Vs1[0]; + + auto reduce_loop = + [&, this](const auto& f, const auto* _, const auto* vs2) { + vwu tmp_val = Vd[0]; + for (uint32_t i = 0; i < this->microVl; i++) { + uint32_t ei = i + vtype_VLMAX(vtype, vlen, true) * + this->microIdx; + if (this->vm || elem_mask(v0, ei)) { + tmp_val = f(tmp_val, Vs2[i]); + } + } + return tmp_val; + }; + + %(code)s; + %(op_wb)s; + return NoFault; +} + +%(declare_varith_template)s; + +}}; + +def template VectorSlideMacroDeclare {{ + +template +class %(class_name)s : public %(base_class)s { +private: + %(reg_idx_arr_decl)s; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _vlen); + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorSlideUpMacroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + const uint32_t num_microops = vtype_regs_per_group(vtype); + int32_t tmp_vl = this->vl; + const int32_t micro_vlmax = vtype_VLMAX(_machInst.vtype8, vlen, true); + int32_t micro_vl = std::min(tmp_vl, micro_vlmax); + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + // Todo static filter out useless uop + int micro_idx = 0; + for (int i = 0; i < num_microops && micro_vl > 0; ++i) { + for (int j = 0; j <= i; ++j) { + microop = new %(class_name)sMicro( + _machInst, micro_vl, micro_idx++, i, j); + microop->setDelayedCommit(); + this->microops.push_back(microop); + } + micro_vl = std::min(tmp_vl -= micro_vlmax, micro_vlmax); + } + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +%(declare_varith_template)s; + +}}; + +def template VectorSlideDownMacroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + const uint32_t num_microops = vtype_regs_per_group(vtype); + int32_t tmp_vl = this->vl; + const int32_t micro_vlmax = vtype_VLMAX(_machInst.vtype8, vlen, true); + int32_t micro_vl = std::min(tmp_vl, micro_vlmax); + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + // Todo static filter out useless uop + int micro_idx = 0; + for (int i = 0; i < num_microops && micro_vl > 0; ++i) { + for (int j = i; j < num_microops; ++j) { + microop = new %(class_name)sMicro( + _machInst, micro_vl, micro_idx++, i, j); + microop->setDelayedCommit(); + this->microops.push_back(microop); + } + micro_vl = std::min(tmp_vl -= micro_vlmax, micro_vlmax); + } + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +%(declare_varith_template)s; + +}}; + +def template VectorSlideMicroDeclare {{ + +template +class %(class_name)s : public %(base_class)s +{ +private: + // vs2, vs1, vs3(old_vd), vm for *.vv, *.vx + // vs2, (old_vd), vm for *.vi + RegId srcRegIdxArr[4]; + RegId destRegIdxArr[1]; + bool vm; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _microVl, + uint32_t _microIdx, uint32_t _vdIdx, uint32_t _vs2Idx); + Fault execute(ExecContext* xc, trace::InstRecord* traceData)const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VectorSlideMicroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx, uint32_t _vdIdx, + uint32_t _vs2Idx) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _microVl, + _microIdx, _vdIdx, _vs2Idx) +{ + this->vm = _machInst.vm; + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + %(set_dest_reg_idx)s; + %(set_src_reg_idx)s; +} + +%(declare_varith_template)s; + +}}; + +def template VectorSlideMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using vu [[maybe_unused]] = std::make_unsigned_t; + using vi [[maybe_unused]] = std::make_signed_t; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + + [[maybe_unused]]const uint32_t vlmax = vtype_VLMAX(vtype, vlen); + + %(vm_decl_rd)s; + %(copy_old_vd)s; + %(code)s; + %(op_wb)s; + + return NoFault; +}; + +%(declare_varith_template)s; + +}}; + +def template VectorFloatSlideMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using et = ElemType; + using vu = decltype(et::v); + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + + [[maybe_unused]]const uint32_t vlmax = vtype_VLMAX(vtype, vlen); + + %(vm_decl_rd)s; + %(copy_old_vd)s; + %(code)s; + %(op_wb)s; + + return NoFault; +}; + +%(declare_varith_template)s; + +}}; diff --git a/src/arch/riscv/isa/templates/vector_mem.isa b/src/arch/riscv/isa/templates/vector_mem.isa new file mode 100644 index 0000000000..1510c106c7 --- /dev/null +++ b/src/arch/riscv/isa/templates/vector_mem.isa @@ -0,0 +1,1671 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2022 PLCT Lab +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer; +// redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution; +// neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +def template VMemMacroDeclare {{ + +class %(class_name)s : public %(base_class)s +{ +private: + %(reg_idx_arr_decl)s; +public: + %(class_name)s(ExtMachInst _machInst); + %(class_name)s(ExtMachInst _machInst, uint32_t _vlen); + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VMemTemplateMacroDeclare {{ + +template +class %(class_name)s : public %(base_class)s +{ +private: + %(reg_idx_arr_decl)s; +public: + %(class_name)s(ExtMachInst _machInst); + %(class_name)s(ExtMachInst _machInst, uint32_t _vlen); + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VleConstructor {{ + +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + + const int32_t micro_vlmax = vlen / width_EEW(_machInst.width); + const uint32_t num_microops = ceil((float) this->vl / (micro_vlmax)); + int32_t remaining_vl = this->vl; + int32_t micro_vl = std::min(remaining_vl, micro_vlmax); + + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + for (int i = 0; i < num_microops && micro_vl > 0; ++i) { + microop = new %(class_name)sMicro(_machInst, micro_vl, i, vlen); + microop->setDelayedCommit(); + microop->setFlag(IsLoad); + this->microops.push_back(microop); + micro_vl = std::min(remaining_vl -= micro_vlmax, micro_vlmax); + } + + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +}}; + +def template VleMicroDeclare {{ + +class %(class_name)s : public %(base_class)s +{ +private: + RegId srcRegIdxArr[3]; + RegId destRegIdxArr[1]; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _microVl, + uint32_t _microIdx, uint32_t _vlen); + + Fault execute(ExecContext *, trace::InstRecord *) const override; + Fault initiateAcc(ExecContext *, trace::InstRecord *) const override; + Fault completeAcc(PacketPtr, ExecContext *, + trace::InstRecord *) const override; + using %(base_class)s::generateDisassembly; + +}; + +}}; + +def template VleMicroConstructor {{ + +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _microVl, + uint32_t _microIdx, uint32_t _vlen) + : %(base_class)s( + "%(mnemonic)s", _machInst, %(op_class)s, _microVl, _microIdx, _vlen) +{ + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + setDestRegIdx(_numDestRegs++, vecRegClass[_machInst.vd + _microIdx]); + _numTypedDestRegs[VecRegClass]++; + setSrcRegIdx(_numSrcRegs++, intRegClass[_machInst.rs1]); + setSrcRegIdx(_numSrcRegs++, vecRegClass[_machInst.vd + _microIdx]); + if (!_machInst.vm) { + setSrcRegIdx(_numSrcRegs++, vecRegClass[0]); + } +} + +}}; + +def template VleMicroExecute {{ + +Fault +%(class_name)s::execute(ExecContext *xc, trace::InstRecord *traceData) const +{ + Addr EA; + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + %(ea_code)s; + + RiscvISA::vreg_t tmp_v0; + uint8_t *v0; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + if(!machInst.vm) { + xc->getRegOperand(this, _numSrcRegs - 1, &tmp_v0); + v0 = tmp_v0.as(); + } + + uint32_t mem_size = width_EEW(machInst.width) / 8 * this->microVl; + + const std::vector byte_enable(mem_size, true); + Fault fault = xc->readMem(EA, Mem.as(), mem_size, memAccessFlags, + byte_enable); + if (fault != NoFault) + return fault; + + const size_t micro_vlmax = vtype_VLMAX(machInst.vtype8, vlen, true); + const size_t micro_elems = vlen / width_EEW(machInst.width); + + size_t ei; + + for (size_t i = 0; i < micro_elems; i++) { + ei = i + micro_vlmax * microIdx; + %(memacc_code)s; + } + + %(op_wb)s; + return fault; +} + +}}; + +def template VleMicroInitiateAcc {{ + +Fault +%(class_name)s::initiateAcc(ExecContext* xc, + trace::InstRecord* traceData) const +{ + + Addr EA; + + %(op_src_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(ea_code)s; + + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + uint32_t mem_size = width_EEW(this->machInst.width) / 8 * this->microVl; + + const std::vector byte_enable(mem_size, true); + Fault fault = initiateMemRead(xc, EA, mem_size, memAccessFlags, + byte_enable); + return fault; +} + +}}; + +def template VleMicroCompleteAcc {{ + +Fault +%(class_name)s::completeAcc(PacketPtr pkt, ExecContext *xc, + trace::InstRecord *traceData) const +{ + %(op_decl)s; + %(op_rd)s; + %(set_vlen)s; + + STATUS status = xc->readMiscReg(MISCREG_STATUS); + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + RiscvISA::vreg_t tmp_v0; + uint8_t *v0; + if(!machInst.vm) { + xc->getRegOperand(this, _numSrcRegs - 1, &tmp_v0); + v0 = tmp_v0.as(); + } + + memcpy(Mem.as(), pkt->getPtr(), pkt->getSize()); + + const size_t micro_vlmax = vtype_VLMAX(machInst.vtype8, vlen, true); + const size_t micro_elems = vlen / width_EEW(machInst.width); + + size_t ei; + for (size_t i = 0; i < micro_elems; i++) { + ei = i + micro_vlmax * microIdx; + %(memacc_code)s; + } + + %(op_wb)s; + return NoFault; +} + +}}; + +def template VseConstructor {{ + +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + + const int32_t micro_vlmax = vlen / width_EEW(_machInst.width); + const uint32_t num_microops = ceil((float) this->vl / (micro_vlmax)); + int32_t remaining_vl = this->vl; + int32_t micro_vl = std::min(remaining_vl, micro_vlmax); + + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + for (int i = 0; i < num_microops && micro_vl > 0; ++i) { + microop = new %(class_name)sMicro(_machInst, micro_vl, i, vlen); + microop->setDelayedCommit(); + microop->setFlag(IsStore); + this->microops.push_back(microop); + micro_vl = std::min(remaining_vl -= micro_vlmax, micro_vlmax); + } + + this->microops.front()->setFlag(IsFirstMicroop); + this->microops.back()->setFlag(IsLastMicroop); +} + +}}; + +def template VseMicroDeclare {{ + +class %(class_name)s : public %(base_class)s +{ +private: + RegId srcRegIdxArr[3]; + RegId destRegIdxArr[0]; +public: + %(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx, uint32_t _vlen); + + Fault execute(ExecContext *, trace::InstRecord *) const override; + Fault initiateAcc(ExecContext *, trace::InstRecord *) const override; + Fault completeAcc(PacketPtr, ExecContext *, + trace::InstRecord *) const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VseMicroConstructor {{ + +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx, uint32_t _vlen) + : %(base_class)s( + "%(mnemonic)s", _machInst, %(op_class)s, _microVl, _microIdx, _vlen) +{ + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + setSrcRegIdx(_numSrcRegs++, intRegClass[_machInst.rs1]); + setSrcRegIdx(_numSrcRegs++, vecRegClass[_machInst.vs3 + _microIdx]); + if (!_machInst.vm) { + setSrcRegIdx(_numSrcRegs++, vecRegClass[0]); + } + this->flags[IsVector] = true; + this->flags[IsStore] = true; +} + +}}; + +def template VseMicroExecute {{ + +Fault +%(class_name)s::execute(ExecContext *xc, trace::InstRecord *traceData) const +{ + Addr EA; + + RiscvISA::vreg_t tmp_v0; + uint8_t *v0; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + if(!machInst.vm) { + xc->getRegOperand(this, _numSrcRegs - 1, &tmp_v0); + v0 = tmp_v0.as(); + } + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + %(ea_code)s; + + const size_t micro_vlmax = vtype_VLMAX(machInst.vtype8, vlen, true); + const size_t eewb = width_EEW(machInst.width) / 8; + const size_t mem_size = eewb * microVl; + std::vector byte_enable(mem_size, false); + size_t ei; + for (size_t i = 0; i < microVl; i++) { + ei = i + micro_vlmax * microIdx; + if (machInst.vm || elem_mask(v0, ei)) { + %(memacc_code)s; + auto it = byte_enable.begin() + i * eewb; + std::fill(it, it + eewb, true); + } + } + + Fault fault; + fault = xc->writeMem(Mem.as(), mem_size, EA, memAccessFlags, + nullptr, byte_enable); + return fault; +} + +}}; + +def template VseMicroInitiateAcc {{ + +Fault +%(class_name)s::initiateAcc(ExecContext* xc, + trace::InstRecord* traceData) const +{ + Addr EA; + + RiscvISA::vreg_t tmp_v0; + uint8_t *v0; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + if(!machInst.vm) { + xc->getRegOperand(this, _numSrcRegs - 1, &tmp_v0); + v0 = tmp_v0.as(); + } + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + %(ea_code)s; + + const size_t micro_vlmax = vtype_VLMAX(machInst.vtype8, vlen, true); + const size_t eewb = width_EEW(machInst.width) / 8; + const size_t mem_size = eewb * microVl; + std::vector byte_enable(mem_size, false); + size_t ei; + for (size_t i = 0; i < microVl; i++) { + ei = i + micro_vlmax * microIdx; + if (machInst.vm || elem_mask(v0, ei)) { + %(memacc_code)s; + auto it = byte_enable.begin() + i * eewb; + std::fill(it, it + eewb, true); + } + } + + Fault fault; + fault = xc->writeMem(Mem.as(), mem_size, EA, memAccessFlags, + nullptr, byte_enable); + return fault; +} + +}}; + +def template VseMicroCompleteAcc {{ + +Fault +%(class_name)s::completeAcc(PacketPtr pkt, ExecContext* xc, + trace::InstRecord* traceData) const +{ + return NoFault; +} + +}}; + +def template VlmConstructor {{ + +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + + const uint32_t micro_vlmax = vlen / width_EEW(_machInst.width); + int32_t micro_vl = (std::min(this->vl, micro_vlmax) + 7) / 8; + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + } else { + microop = new Vle8_vMicro(_machInst, micro_vl, 0, vlen); + microop->setDelayedCommit(); + microop->setFlag(IsLoad); + } + this->microops.push_back(microop); + + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +}}; + +def template VsmConstructor {{ + +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + + const uint32_t micro_vlmax = vlen / width_EEW(_machInst.width); + int32_t micro_vl = (std::min(this->vl, micro_vlmax) + 7) / 8; + + StaticInstPtr microop; + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + } else { + microop = new Vse8_vMicro(_machInst, micro_vl, 0, vlen); + microop->setDelayedCommit(); + microop->setFlag(IsStore); + } + this->microops.push_back(microop); + + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +}}; + +def template VsWholeConstructor {{ + +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + + size_t NFIELDS = machInst.nf + 1; + const int32_t micro_vlmax = vlen / width_EEW(_machInst.width); + + StaticInstPtr microop; + for (int i = 0; i < NFIELDS; ++i) { + microop = new %(class_name)sMicro(_machInst, micro_vlmax, i, vlen); + microop->setDelayedCommit(); + microop->setFlag(IsStore); + this->microops.push_back(microop); + } + + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +}}; + +def template VsWholeMicroDeclare {{ + +class %(class_name)s: public %(base_class)s +{ +private: + RegId destRegIdxArr[0]; + RegId srcRegIdxArr[2]; +public: + %(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx, uint32_t _vlen); + + Fault execute(ExecContext *, trace::InstRecord *) const override; + Fault initiateAcc(ExecContext *, trace::InstRecord *) const override; + Fault completeAcc(PacketPtr, ExecContext *, + trace::InstRecord *) const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VsWholeMicroConstructor {{ + +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx, uint32_t _vlen) + : %(base_class)s( + "%(mnemonic)s", _machInst, %(op_class)s, _microVl, _microIdx, _vlen) +{ + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + setSrcRegIdx(_numSrcRegs++, intRegClass[_machInst.rs1]); + setSrcRegIdx(_numSrcRegs++, vecRegClass[_machInst.vs3 + _microIdx]); + this->flags[IsVector] = true; + this->flags[IsStore] = true; +} + +}}; + +def template VsWholeMicroExecute {{ + +Fault +%(class_name)s::execute(ExecContext *xc, trace::InstRecord *traceData) const +{ + Addr EA; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(ea_code)s; + + + for (size_t i = 0; i < vlenb; i++) { + %(memacc_code)s; + } + + Fault fault = writeMemAtomicLE(xc, traceData, *(vreg_t::Container*)(&Mem), + vlenb, EA, memAccessFlags, nullptr); + return fault; +} + +}}; + +def template VsWholeMicroInitiateAcc {{ + +Fault +%(class_name)s::initiateAcc(ExecContext* xc, + trace::InstRecord* traceData) const +{ + Addr EA; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(ea_code)s; + + + for (size_t i = 0; i < vlenb; i++) { + %(memacc_code)s; + } + + Fault fault = writeMemTimingLE(xc, traceData, *(vreg_t::Container*)(&Mem), + EA, vlenb, memAccessFlags, nullptr); + return fault; +} + +}}; + +def template VsWholeMicroCompleteAcc {{ + +Fault +%(class_name)s::completeAcc(PacketPtr pkt, ExecContext* xc, + trace::InstRecord* traceData) const +{ + return NoFault; +} + +}}; + +def template VlWholeConstructor {{ + +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + + size_t NFIELDS = machInst.nf + 1; + + const int32_t micro_vlmax = vlen / width_EEW(_machInst.width); + + StaticInstPtr microop; + for (int i = 0; i < NFIELDS; ++i) { + microop = new %(class_name)sMicro(_machInst, micro_vlmax, i, vlen); + microop->setDelayedCommit(); + microop->setFlag(IsLoad); + this->microops.push_back(microop); + } + + this->microops.front()->setFirstMicroop(); + this->microops.back()->setLastMicroop(); +} + +}}; + +def template VlWholeMicroDeclare {{ + +class %(class_name)s: public %(base_class)s +{ +private: + RegId destRegIdxArr[1]; + RegId srcRegIdxArr[1]; +public: + %(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx, uint32_t _vlen); + + Fault execute(ExecContext *, trace::InstRecord *) const override; + Fault initiateAcc(ExecContext *, trace::InstRecord *) const override; + Fault completeAcc(PacketPtr, ExecContext *, + trace::InstRecord *) const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VlWholeMicroConstructor {{ + +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _microVl, uint32_t _microIdx, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s_micro", _machInst, %(op_class)s, _microVl, + _microIdx, _vlen) +{ + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + setDestRegIdx(_numDestRegs++, vecRegClass[_machInst.vd + _microIdx]); + _numTypedDestRegs[VecRegClass]++; + setSrcRegIdx(_numSrcRegs++, intRegClass[_machInst.rs1]); + this->flags[IsVector] = true; + this->flags[IsLoad] = true; +} + +}}; + +def template VlWholeMicroExecute {{ + +Fault +%(class_name)s::execute(ExecContext *xc, trace::InstRecord *traceData) const +{ + Addr EA; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(set_vlen)s; + %(ea_code)s; + + Fault fault = readMemAtomicLE(xc, traceData, EA, + *(vreg_t::Container*)(&Mem), vlenb, + memAccessFlags); + if (fault != NoFault) + return fault; + + size_t elem_per_reg = vlen / width_EEW(machInst.width); + for (size_t i = 0; i < elem_per_reg; i++) { + %(memacc_code)s; + } + + %(op_wb)s; + return NoFault; +} + +}}; + +def template VlWholeMicroInitiateAcc {{ + +Fault +%(class_name)s::initiateAcc(ExecContext* xc, + trace::InstRecord* traceData) const +{ + Addr EA; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + %(op_src_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(ea_code)s; + + const std::vector byte_enable(vlenb, true); + Fault fault = initiateMemRead(xc, EA, vlenb, memAccessFlags, byte_enable); + return fault; +} + +}}; + +def template VlWholeMicroCompleteAcc {{ + +Fault +%(class_name)s::completeAcc(PacketPtr pkt, ExecContext* xc, + trace::InstRecord* traceData) const +{ + %(op_decl)s; + %(op_rd)s; + %(set_vlen)s; + + STATUS status = xc->readMiscReg(MISCREG_STATUS); + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + memcpy(Mem.as(), pkt->getPtr(), pkt->getSize()); + + size_t elem_per_reg = vlen / width_EEW(machInst.width); + for (size_t i = 0; i < elem_per_reg; ++i) { + %(memacc_code)s; + } + + %(op_wb)s; + return NoFault; +} + +}}; + +def template VlStrideConstructor {{ + +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + + const int32_t num_elems_per_vreg = vlen / width_EEW(_machInst.width); + int32_t remaining_vl = this->vl; + // Num of elems in one vreg + int32_t micro_vl = std::min(remaining_vl, num_elems_per_vreg); + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + for (int i = 0; micro_vl > 0; ++i) { + for (int j = 0; j < micro_vl; ++j) { + microop = new %(class_name)sMicro(machInst, i, j, micro_vl); + microop->setFlag(IsDelayedCommit); + microop->setFlag(IsLoad); + this->microops.push_back(microop); + } + remaining_vl -= num_elems_per_vreg; + micro_vl = std::min(remaining_vl, num_elems_per_vreg); + } + + this->microops.front()->setFlag(IsFirstMicroop); + this->microops.back()->setFlag(IsLastMicroop); + this->flags[IsVector] = true; +} + +}}; + +def template VlStrideMicroDeclare {{ + +class %(class_name)s : public %(base_class)s +{ +private: + // rs1, rs2, vd, vm + RegId srcRegIdxArr[4]; + RegId destRegIdxArr[1]; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _regIdx, uint32_t _microIdx, + uint32_t _microVl); + + Fault execute(ExecContext *, trace::InstRecord *) const override; + Fault initiateAcc(ExecContext *, trace::InstRecord *) const override; + Fault completeAcc(PacketPtr, ExecContext *, + trace::InstRecord *) const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VlStrideMicroConstructor {{ + +%(class_name)s::%(class_name)s( + ExtMachInst _machInst, uint32_t _regIdx, uint32_t _microIdx, + uint32_t _microVl) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, + _regIdx, _microIdx, _microVl) +{ + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + setDestRegIdx(_numDestRegs++, vecRegClass[_machInst.vd + _regIdx]); + _numTypedDestRegs[VecRegClass]++; + setSrcRegIdx(_numSrcRegs++, intRegClass[_machInst.rs1]); + setSrcRegIdx(_numSrcRegs++, intRegClass[_machInst.rs2]); + // We treat agnostic as undistrubed + setSrcRegIdx(_numSrcRegs++, vecRegClass[_machInst.vd + _regIdx]); + if (!_machInst.vm) { + setSrcRegIdx(_numSrcRegs++, vecRegClass[0]); + } + this->flags[IsLoad] = true; +} + +}}; + +def template VlStrideMicroExecute {{ + +Fault +%(class_name)s::execute(ExecContext *xc, trace::InstRecord *traceData) const +{ + Fault fault = NoFault; + Addr EA; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + constexpr uint8_t elem_size = sizeof(Vd[0]); + %(ea_code)s; // ea_code depends on elem_size + + RiscvISA::vreg_t tmp_v0; + uint8_t *v0; + if (!machInst.vm) { + xc->getRegOperand(this, _numSrcRegs-1, &tmp_v0); + v0 = tmp_v0.as(); + } + + uint32_t mem_size = elem_size; + const std::vector byte_enable(mem_size, true); + + size_t ei = this->regIdx * vlenb / elem_size + this->microIdx; + if (machInst.vm || elem_mask(v0, ei)) { + fault = xc->readMem(EA, Mem.as(), mem_size, + memAccessFlags, byte_enable); + if (fault != NoFault) + return fault; + %(memacc_code)s; /* Vd[this->microIdx] = Mem[0]; */ + } + + %(op_wb)s; + return fault; +} + +}}; + +def template VlStrideMicroInitiateAcc {{ + +Fault +%(class_name)s::initiateAcc(ExecContext* xc, + trace::InstRecord* traceData) const +{ + Fault fault = NoFault; + Addr EA; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + %(op_src_decl)s; + %(op_rd)s; + %(set_vlenb)s; + constexpr uint8_t elem_size = sizeof(Vd[0]); + %(ea_code)s; // ea_code depends on elem_size + + RiscvISA::vreg_t tmp_v0; + uint8_t *v0; + if (!machInst.vm) { + xc->getRegOperand(this, _numSrcRegs-1, &tmp_v0); + v0 = tmp_v0.as(); + } + + uint32_t mem_size = elem_size; + size_t ei = this->regIdx * vlenb / elem_size + this->microIdx; + bool need_load = machInst.vm || elem_mask(v0, ei); + const std::vector byte_enable(mem_size, need_load); + fault = initiateMemRead(xc, EA, mem_size, memAccessFlags, byte_enable); + return fault; +} + +}}; + +def template VlStrideMicroCompleteAcc {{ + +Fault +%(class_name)s::completeAcc(PacketPtr pkt, ExecContext *xc, + trace::InstRecord *traceData) const +{ + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + + STATUS status = xc->readMiscReg(MISCREG_STATUS); + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + constexpr uint8_t elem_size = sizeof(Vd[0]); + + RiscvISA::vreg_t old_vd; + decltype(Vd) old_Vd = nullptr; + // We treat agnostic as undistrubed + xc->getRegOperand(this, 2, &old_vd); + old_Vd = old_vd.as >(); + + RiscvISA::vreg_t tmp_v0; + uint8_t *v0; + if (!machInst.vm) { + xc->getRegOperand(this, _numSrcRegs-1, &tmp_v0); + v0 = tmp_v0.as(); + } + + if (microIdx == 0) { + // treat vma as vmu + // if (machInst.vtype8.vma == 0) + memcpy(Vd, old_Vd, microVl * elem_size); + // treat vta as vtu + // if (machInst.vtype8.vta == 0) + memcpy(Vd + microVl, old_Vd + microVl, vlenb - microVl * elem_size); + } else { + memcpy(Vd, old_Vd, vlenb); + } + + size_t ei = this->regIdx * vlenb / sizeof(Vd[0]) + this->microIdx; + if (machInst.vm || elem_mask(v0, ei)) { + memcpy(Mem.as(), pkt->getPtr(), pkt->getSize()); + %(memacc_code)s; /* Vd[this->microIdx] = Mem[0]; */ + } + + %(op_wb)s; + return NoFault; +} + +}}; + +def template VsStrideConstructor {{ + +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + + const int32_t num_elems_per_vreg = vlen / width_EEW(_machInst.width); + int32_t remaining_vl = this->vl; + // Num of elems in one vreg + int32_t micro_vl = std::min(remaining_vl, num_elems_per_vreg); + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + for (int i = 0; micro_vl > 0; ++i) { + for (int j = 0; j < micro_vl; ++j) { + microop = new %(class_name)sMicro(machInst, i, j, micro_vl); + microop->setFlag(IsDelayedCommit); + microop->setFlag(IsStore); + this->microops.push_back(microop); + } + remaining_vl -= num_elems_per_vreg; + micro_vl = std::min(remaining_vl, num_elems_per_vreg); + } + + this->microops.front()->setFlag(IsFirstMicroop); + this->microops.back()->setFlag(IsLastMicroop); + this->flags[IsVector] = true; +} + +}}; + +def template VsStrideMicroDeclare {{ + +class %(class_name)s : public %(base_class)s +{ +private: + // rs1, rs2, vs3, vm + RegId srcRegIdxArr[4]; + RegId destRegIdxArr[0]; +public: + %(class_name)s(ExtMachInst _machInst, uint32_t _regIdx, uint32_t _microIdx, + uint32_t _microVl); + + Fault execute(ExecContext *, trace::InstRecord *) const override; + Fault initiateAcc(ExecContext *, trace::InstRecord *) const override; + Fault completeAcc(PacketPtr, ExecContext *, + trace::InstRecord *) const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VsStrideMicroConstructor {{ + +%(class_name)s::%(class_name)s( + ExtMachInst _machInst, uint32_t _regIdx, uint32_t _microIdx, + uint32_t _microVl) + : %(base_class)s("%(mnemonic)s""_micro", _machInst, %(op_class)s, + _regIdx, _microIdx, _microVl) +{ + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + setSrcRegIdx(_numSrcRegs++, intRegClass[_machInst.rs1]); + setSrcRegIdx(_numSrcRegs++, intRegClass[_machInst.rs2]); + setSrcRegIdx(_numSrcRegs++, vecRegClass[_machInst.vs3 + _regIdx]); + if (!_machInst.vm) { + setSrcRegIdx(_numSrcRegs++, vecRegClass[0]); + } + this->flags[IsStore] = true; +} + +}}; + +def template VsStrideMicroExecute {{ + +Fault +%(class_name)s::execute(ExecContext *xc, trace::InstRecord *traceData) const +{ + Fault fault = NoFault; + Addr EA; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + constexpr uint8_t elem_size = sizeof(Vs3[0]); + %(ea_code)s; + + RiscvISA::vreg_t tmp_v0; + uint8_t *v0; + if(!machInst.vm) { + xc->getRegOperand(this, _numSrcRegs - 1, &tmp_v0); + v0 = tmp_v0.as(); + } + + uint32_t mem_size = elem_size; + const std::vector byte_enable(mem_size, true); + + size_t ei = this->regIdx * vlenb / elem_size + this->microIdx; + if (machInst.vm || elem_mask(v0, ei)) { + %(memacc_code)s; + fault = xc->writeMem(Mem.as(), mem_size, EA, + memAccessFlags, nullptr, byte_enable); + } + return fault; +} + +}}; + +def template VsStrideMicroInitiateAcc {{ + +Fault +%(class_name)s::initiateAcc(ExecContext* xc, + trace::InstRecord* traceData) const +{ + Fault fault = NoFault; + Addr EA; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + RiscvISA::vreg_t tmp_v0; + uint8_t *v0; + if(!machInst.vm) { + xc->getRegOperand(this, _numSrcRegs - 1, &tmp_v0); + v0 = tmp_v0.as(); + } + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + constexpr uint8_t elem_size = sizeof(Vs3[0]); + %(ea_code)s; + + uint32_t mem_size = elem_size; + + size_t ei = this->regIdx * vlenb / elem_size + this->microIdx; + bool need_store = machInst.vm || elem_mask(v0, ei); + if (need_store) { + const std::vector byte_enable(mem_size, need_store); + %(memacc_code)s; + fault = xc->writeMem(Mem.as(), mem_size, EA, + memAccessFlags, nullptr, byte_enable); + } + return fault; +} + +}}; + +def template VsStrideMicroCompleteAcc {{ + +Fault +%(class_name)s::completeAcc(PacketPtr pkt, ExecContext* xc, + trace::InstRecord* traceData) const +{ + return NoFault; +} + +}}; + +def template VlIndexConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + + const uint32_t vd_eewb = sizeof(ElemType); + const uint32_t vs2_eewb = width_EEW(_machInst.width) / 8; + const uint8_t vs2_split_num = (vd_eewb + vs2_eewb - 1) / vs2_eewb; + const uint8_t vd_split_num = (vs2_eewb + vd_eewb - 1) / vd_eewb; + uint32_t vlenb = vlen >> 3; + const int32_t micro_vlmax = vlenb / std::max(vd_eewb, vs2_eewb); + int32_t remaining_vl = this->vl; + int32_t micro_vl = std::min(remaining_vl, micro_vlmax); + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + for (uint32_t i = 0; micro_vl > 0; i++) { + for (uint32_t j = 0; j < micro_vl; ++j) { + uint32_t vdRegIdx = i / vd_split_num; + uint32_t vs2RegIdx = i / vs2_split_num; + uint32_t vdElemIdx = j + micro_vlmax * (i % vd_split_num); + uint32_t vs2ElemIdx = j + micro_vlmax * (i % vs2_split_num); + microop = new %(class_name)sMicro(machInst, + vdRegIdx, vdElemIdx, vs2RegIdx, vs2ElemIdx); + microop->setFlag(IsDelayedCommit); + microop->setFlag(IsLoad); + this->microops.push_back(microop); + } + remaining_vl -= micro_vlmax; + micro_vl = std::min(remaining_vl, micro_vlmax); + } + + this->microops.front()->setFlag(IsFirstMicroop); + this->microops.back()->setFlag(IsLastMicroop); + this->flags[IsVector] = true; +} + +%(declare_vmem_template)s; + +}}; + +def template VlIndexMicroDeclare {{ + +template +class %(class_name)s : public %(base_class)s +{ +private: + // rs1, vs2, vd, vm + RegId srcRegIdxArr[4]; + RegId destRegIdxArr[1]; +public: + %(class_name)s(ExtMachInst _machInst, + uint32_t _vdRegIdx, uint32_t _vdElemIdx, + uint32_t _vs2RegIdx, uint32_t _vs2ElemIdx); + + Fault execute(ExecContext *, trace::InstRecord *) const override; + Fault initiateAcc(ExecContext *, trace::InstRecord *) const override; + Fault completeAcc(PacketPtr, ExecContext *, + trace::InstRecord *) const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VlIndexMicroConstructor {{ + +template +%(class_name)s::%(class_name)s( + ExtMachInst _machInst,uint32_t _vdRegIdx, uint32_t _vdElemIdx, + uint32_t _vs2RegIdx, uint32_t _vs2ElemIdx) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, + _vdRegIdx, _vdElemIdx, _vs2RegIdx, _vs2ElemIdx) +{ + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + setDestRegIdx(_numDestRegs++, vecRegClass[_machInst.vd + _vdRegIdx]); + _numTypedDestRegs[VecRegClass]++; + setSrcRegIdx(_numSrcRegs++, intRegClass[_machInst.rs1]); + setSrcRegIdx(_numSrcRegs++, vecRegClass[_machInst.vs2 + _vs2RegIdx]); + // We treat agnostic as undistrubed + setSrcRegIdx(_numSrcRegs++, vecRegClass[_machInst.vd + _vdRegIdx]); + if (!_machInst.vm) { + setSrcRegIdx(_numSrcRegs++, vecRegClass[0]); + } + this->flags[IsLoad] = true; +} + +%(declare_vmem_template)s; + +}}; + +def template VlIndexMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext *xc, + trace::InstRecord *traceData)const +{ + using vu = std::make_unsigned_t; + Fault fault = NoFault; + Addr EA; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(ea_code)s; + constexpr uint8_t elem_size = sizeof(Vd[0]); + RiscvISA::vreg_t tmp_v0; + uint8_t *v0; + if (!machInst.vm) { + xc->getRegOperand(this, _numSrcRegs-1, &tmp_v0); + v0 = tmp_v0.as(); + } + + uint32_t mem_size = elem_size; + const std::vector byte_enable(mem_size, true); + size_t ei = this->vdRegIdx * vlenb / elem_size + this->vdElemIdx; + if (machInst.vm || elem_mask(v0, ei)) { + fault = xc->readMem(EA, Mem.as(), mem_size, + memAccessFlags, byte_enable); + if (fault != NoFault) + return fault; + %(memacc_code)s; /* Vd[this->vdElemIdx] = Mem[0]; */ + } + + %(op_wb)s; + return fault; +} + +}}; + +def template VlIndexMicroInitiateAcc {{ + +template +Fault +%(class_name)s::initiateAcc(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using vu = std::make_unsigned_t; + Fault fault = NoFault; + Addr EA; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + %(op_src_decl)s; + %(op_rd)s; + %(set_vlenb)s; + constexpr uint8_t elem_size = sizeof(Vd[0]); + %(ea_code)s; // ea_code depends on elem_size + + RiscvISA::vreg_t tmp_v0; + uint8_t *v0; + if (!machInst.vm) { + xc->getRegOperand(this, _numSrcRegs-1, &tmp_v0); + v0 = tmp_v0.as(); + } + + uint32_t mem_size = elem_size; + + size_t ei = this->vdRegIdx * vlenb / elem_size + this->vdElemIdx; + bool need_load = machInst.vm || elem_mask(v0, ei); + const std::vector byte_enable(mem_size, need_load); + fault = initiateMemRead(xc, EA, mem_size, memAccessFlags, byte_enable); + return fault; +} + +}}; + +def template VlIndexMicroCompleteAcc {{ + +template +Fault +%(class_name)s::completeAcc(PacketPtr pkt, ExecContext *xc, + trace::InstRecord *traceData) const +{ + STATUS status = xc->readMiscReg(MISCREG_STATUS); + status.vs = VPUStatus::DIRTY; + xc->setMiscReg(MISCREG_STATUS, status); + + using vu = std::make_unsigned_t; + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + + constexpr uint8_t elem_size = sizeof(Vd[0]); + + RiscvISA::vreg_t old_vd;; + decltype(Vd) old_Vd = nullptr; + // We treat agnostic as undistrubed + xc->getRegOperand(this, 2, &old_vd); + old_Vd = old_vd.as >(); + + RiscvISA::vreg_t tmp_v0; + uint8_t *v0; + if (!machInst.vm) { + xc->getRegOperand(this, _numSrcRegs-1, &tmp_v0); + v0 = tmp_v0.as(); + } + + memcpy(Vd, old_Vd, vlenb); + + size_t ei = this->vdRegIdx * vlenb / elem_size + this->vdElemIdx; + if (machInst.vm || elem_mask(v0, ei)) { + memcpy(Mem.as(), pkt->getPtr(), pkt->getSize()); + %(memacc_code)s; /* Vd[this->microIdx] = Mem[0]; */ + } + + %(op_wb)s; + return NoFault; +} + +%(declare_vmem_template)s; + +}}; + +def template VsIndexConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, uint32_t _vlen) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, _vlen) +{ + %(set_reg_idx_arr)s; + %(constructor)s; + + const uint32_t vs3_eewb = sizeof(ElemType); + const uint32_t vs2_eewb = width_EEW(_machInst.width) / 8; + const uint8_t vs2_split_num = (vs3_eewb + vs2_eewb - 1) / vs2_eewb; + const uint8_t vs3_split_num = (vs2_eewb + vs3_eewb - 1) / vs3_eewb; + uint32_t vlenb = vlen >> 3; + const int32_t micro_vlmax = vlenb / std::max(vs3_eewb, vs2_eewb); + int32_t remaining_vl = this->vl; + int32_t micro_vl = std::min(remaining_vl, micro_vlmax); + StaticInstPtr microop; + + if (micro_vl == 0) { + microop = new VectorNopMicroInst(_machInst); + this->microops.push_back(microop); + } + for (uint32_t i = 0; micro_vl > 0; i++) { + for (uint32_t j = 0; j < micro_vl; ++j) { + uint32_t vs3RegIdx = i / vs3_split_num; + uint32_t vs2RegIdx = i / vs2_split_num; + uint32_t vs3ElemIdx = j + micro_vlmax * (i % vs3_split_num); + uint32_t vs2ElemIdx = j + micro_vlmax * (i % vs2_split_num); + microop = new %(class_name)sMicro(machInst, + vs3RegIdx, vs3ElemIdx, vs2RegIdx, vs2ElemIdx); + microop->setFlag(IsDelayedCommit); + microop->setFlag(IsStore); + this->microops.push_back(microop); + } + remaining_vl -= micro_vlmax; + micro_vl = std::min(remaining_vl, micro_vlmax); + } + + this->microops.front()->setFlag(IsFirstMicroop); + this->microops.back()->setFlag(IsLastMicroop); + this->flags[IsVector] = true; +} + +%(declare_vmem_template)s; + +}}; + +def template VsIndexMicroDeclare {{ + +template +class %(class_name)s : public %(base_class)s +{ +private: + // rs1, vs2, vs3, vm + RegId srcRegIdxArr[4]; + RegId destRegIdxArr[0]; +public: + %(class_name)s(ExtMachInst _machInst, + uint32_t _vs3RegIdx, uint32_t _vs3ElemIdx, + uint32_t _vs2RegIdx, uint32_t _vs2ElemIdx); + + Fault execute(ExecContext *, trace::InstRecord *) const override; + Fault initiateAcc(ExecContext *, trace::InstRecord *) const override; + Fault completeAcc(PacketPtr, ExecContext *, + trace::InstRecord *) const override; + using %(base_class)s::generateDisassembly; +}; + +}}; + +def template VsIndexMicroConstructor {{ + +template +%(class_name)s::%(class_name)s(ExtMachInst _machInst, + uint32_t _vs3RegIdx, uint32_t _vs3ElemIdx, + uint32_t _vs2RegIdx, uint32_t _vs2ElemIdx) + : %(base_class)s("%(mnemonic)s", _machInst, %(op_class)s, + _vs3RegIdx, _vs3ElemIdx, _vs2RegIdx, _vs2ElemIdx) +{ + %(set_reg_idx_arr)s; + _numSrcRegs = 0; + _numDestRegs = 0; + setSrcRegIdx(_numSrcRegs++, intRegClass[_machInst.rs1]); + setSrcRegIdx(_numSrcRegs++, vecRegClass[_machInst.vs2 + _vs2RegIdx]); + // We treat agnostic as undistrubed + setSrcRegIdx(_numSrcRegs++, vecRegClass[_machInst.vs3 + _vs3RegIdx]); + if (!_machInst.vm) { + setSrcRegIdx(_numSrcRegs++, vecRegClass[0]); + } + this->flags[IsStore] = true; +} + +%(declare_vmem_template)s; + +}}; + +def template VsIndexMicroExecute {{ + +template +Fault +%(class_name)s::execute(ExecContext *xc, + trace::InstRecord *traceData)const +{ + using vu = std::make_unsigned_t; + Fault fault = NoFault; + Addr EA; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(ea_code)s; + constexpr uint8_t elem_size = sizeof(Vs3[0]); + RiscvISA::vreg_t tmp_v0; + uint8_t *v0; + if (!machInst.vm) { + xc->getRegOperand(this, _numSrcRegs-1, &tmp_v0); + v0 = tmp_v0.as(); + } + + uint32_t mem_size = elem_size; + const std::vector byte_enable(mem_size, true); + + size_t ei = this->vs3RegIdx * vlenb / elem_size + this->vs3ElemIdx; + if (machInst.vm || elem_mask(v0, ei)) { + %(memacc_code)s; /* Mem[0] = Vs3[this->vs3ElemIdx] */ + fault = xc->writeMem(Mem.as(), mem_size, EA, + memAccessFlags, nullptr, byte_enable); + } + return fault; +} + +}}; + +def template VsIndexMicroInitiateAcc {{ + +template +Fault +%(class_name)s::initiateAcc(ExecContext* xc, + trace::InstRecord* traceData) const +{ + using vu = std::make_unsigned_t; + Fault fault = NoFault; + Addr EA; + MISA misa = xc->readMiscReg(MISCREG_ISA); + STATUS status = xc->readMiscReg(MISCREG_STATUS); + if (!misa.rvv || status.vs == VPUStatus::OFF) { + return std::make_shared( + "RVV is disabled or VPU is off", machInst); + } + if (machInst.vill) + return std::make_shared("VILL is set", machInst); + %(op_decl)s; + %(op_rd)s; + %(set_vlenb)s; + %(ea_code)s; + constexpr uint8_t elem_size = sizeof(Vs3[0]); + RiscvISA::vreg_t tmp_v0; + uint8_t *v0; + if (!machInst.vm) { + xc->getRegOperand(this, _numSrcRegs-1, &tmp_v0); + v0 = tmp_v0.as(); + } + + constexpr uint8_t mem_size = elem_size; + const std::vector byte_enable(mem_size, true); + + size_t ei = this->vs3RegIdx * vlenb / elem_size + this->vs3ElemIdx; + if (machInst.vm || elem_mask(v0, ei)) { + %(memacc_code)s; /* Mem[0] = Vs3[this->vs3ElemIdx] */ + fault = xc->writeMem(Mem.as(), mem_size, EA, + memAccessFlags, nullptr, byte_enable); + } + return fault; +} + +}}; + +def template VsIndexMicroCompleteAcc {{ + +template +Fault +%(class_name)s::completeAcc(PacketPtr pkt, ExecContext* xc, + trace::InstRecord* traceData) const +{ + return NoFault; +} + +%(declare_vmem_template)s; + +}}; + +def template VMemBaseDecodeBlock {{ + return new %(class_name)s(machInst, vlen); +}}; + +def template VMemTemplateDecodeBlock {{ + +switch(machInst.vtype8.vsew) { + case 0b000: { + return new %(class_name)s(machInst); + } + case 0b001: { + return new %(class_name)s(machInst); + } + case 0b010: { + return new %(class_name)s(machInst); + } + case 0b011: { + return new %(class_name)s(machInst); + } + default: GEM5_UNREACHABLE; +} + +}}; + +def template VMemSplitTemplateDecodeBlock {{ + +switch(machInst.vtype8.vsew) { + case 0b000: { + return new %(class_name)s(machInst, vlen); + } + case 0b001: { + return new %(class_name)s(machInst, vlen); + } + case 0b010: { + return new %(class_name)s(machInst, vlen); + } + case 0b011: { + return new %(class_name)s(machInst, vlen); + } + default: GEM5_UNREACHABLE; +} + +}}; diff --git a/src/arch/riscv/linux/fs_workload.cc b/src/arch/riscv/linux/fs_workload.cc index 4a4a3812ec..309d324bcb 100644 --- a/src/arch/riscv/linux/fs_workload.cc +++ b/src/arch/riscv/linux/fs_workload.cc @@ -32,7 +32,10 @@ #include "base/loader/dtb_file.hh" #include "base/loader/object_file.hh" #include "base/loader/symtab.hh" +#include "cpu/pc_event.hh" +#include "kern/linux/events.hh" #include "sim/kernel_workload.hh" +#include "sim/sim_exit.hh" #include "sim/system.hh" namespace gem5 @@ -75,5 +78,195 @@ FsLinux::initState() } } +void +FsLinux::startup() +{ + KernelWorkload::startup(); + + addExitOnKernelPanicEvent(); + addExitOnKernelOopsEvent(); +} + +void +FsLinux::addExitOnKernelPanicEvent() +{ + const std::string dmesg_output = name() + ".dmesg"; + if (params().exit_on_kernel_panic) { + kernelPanicPcEvent = addKernelFuncEvent( + "panic", "Kernel panic in simulated system.", + dmesg_output, params().on_panic + ); + warn_if(!kernelPanicPcEvent, "Failed to find kernel symbol 'panic'"); + } +} + +void +FsLinux::addExitOnKernelOopsEvent() +{ + const std::string dmesg_output = name() + ".dmesg"; + if (params().exit_on_kernel_oops) { + kernelOopsPcEvent = addKernelFuncEvent( + "oops_exit", "Kernel oops in simulated system.", + dmesg_output, params().on_oops + ); + warn_if(!kernelOopsPcEvent, + "Failed to find kernel symbol 'oops_exit'"); + } +} + +void +BootloaderKernelWorkload::loadBootloaderSymbolTable() +{ + if (params().bootloader_filename != "") { + Addr bootloader_paddr_offset = params().bootloader_addr; + bootloader = loader::createObjectFile(params().bootloader_filename); + bootloaderSymbolTable = bootloader->symtab(); + auto renamedBootloaderSymbolTable = \ + bootloaderSymbolTable.offset( + bootloader_paddr_offset + )->functionSymbols()->rename( + [](const std::string &name) { + return "bootloader." + name; + } + ); + loader::debugSymbolTable.insert(*renamedBootloaderSymbolTable); + } +} + +void +BootloaderKernelWorkload::loadKernelSymbolTable() +{ + if (params().object_file != "") { + kernel = loader::createObjectFile(params().object_file); + kernelSymbolTable = kernel->symtab(); + auto renamedKernelSymbolTable = \ + kernelSymbolTable.functionSymbols()->rename( + [](const std::string &name) { + return "kernel." + name; + } + ); + loader::debugSymbolTable.insert(*renamedKernelSymbolTable); + } +} + +void +BootloaderKernelWorkload::loadBootloader() +{ + if (params().bootloader_filename != "") { + Addr bootloader_addr_offset = params().bootloader_addr; + bootloader->buildImage().offset(bootloader_addr_offset).write( + system->physProxy + ); + delete bootloader; + + inform("Loaded bootloader \'%s\' at 0x%llx\n", + params().bootloader_filename, + bootloader_addr_offset); + } else { + inform("Bootloader is not specified.\n"); + } +} + +void +BootloaderKernelWorkload::loadKernel() +{ + if (params().object_file != "") { + Addr kernel_paddr_offset = params().kernel_addr; + kernel->buildImage().offset(kernel_paddr_offset).write( + system->physProxy + ); + delete kernel; + + inform("Loaded kernel \'%s\' at 0x%llx\n", + params().object_file, + kernel_paddr_offset); + } else { + inform("Kernel is not specified.\n"); + } +} + + +void +BootloaderKernelWorkload::loadDtb() +{ + if (params().dtb_filename != "") { + auto *dtb_file = new loader::DtbFile(params().dtb_filename); + + dtb_file->buildImage().offset(params().dtb_addr) + .write(system->physProxy); + delete dtb_file; + + inform("Loaded DTB \'%s\' at 0x%llx\n", + params().dtb_filename, + params().dtb_addr); + + for (auto *tc: system->threads) { + tc->setReg(int_reg::A1, params().dtb_addr); + } + } else { + inform("DTB file is not specified.\n"); + } +} + +void +BootloaderKernelWorkload::addExitOnKernelPanicEvent() +{ + const std::string dmesg_output = name() + ".dmesg"; + if (params().exit_on_kernel_panic) { + kernelPanicPcEvent = addFuncEvent( + kernelSymbolTable, "panic", "Kernel panic in simulated system.", + dmesg_output, params().on_panic + ); + } +} + +void +BootloaderKernelWorkload::addExitOnKernelOopsEvent() +{ + const std::string dmesg_output = name() + ".dmesg"; + if (params().exit_on_kernel_oops) { + kernelOopsPcEvent = addFuncEvent( + kernelSymbolTable, "oops_exit", "Kernel oops in simulated system.", + dmesg_output, params().on_oops + ); + } +} + +void +BootloaderKernelWorkload::initState() +{ + loadBootloader(); + loadKernel(); + loadDtb(); + + for (auto *tc: system->threads) { + RiscvISA::Reset().invoke(tc); + tc->activate(); + } +} + +void +BootloaderKernelWorkload::startup() +{ + Workload::startup(); + + addExitOnKernelPanicEvent(); + addExitOnKernelOopsEvent(); +} + +void +BootloaderKernelWorkload::serialize(CheckpointOut &checkpoint) const +{ + bootloaderSymbolTable.serialize("bootloader_symbol_table", checkpoint); + kernelSymbolTable.serialize("kernel_symbol_table", checkpoint); +} + +void +BootloaderKernelWorkload::unserialize(CheckpointIn &checkpoint) +{ + bootloaderSymbolTable.unserialize("bootloader_symbol_table", checkpoint); + kernelSymbolTable.unserialize("kernel_symbol_table", checkpoint); +} + } // namespace RiscvISA } // namespace gem5 diff --git a/src/arch/riscv/linux/fs_workload.hh b/src/arch/riscv/linux/fs_workload.hh index 1dc704d906..c5fcd70eb0 100644 --- a/src/arch/riscv/linux/fs_workload.hh +++ b/src/arch/riscv/linux/fs_workload.hh @@ -29,7 +29,10 @@ #ifndef __ARCH_RISCV_LINUX_SYSTEM_HH__ #define __ARCH_RISCV_LINUX_SYSTEM_HH__ +#include + #include "arch/riscv/remote_gdb.hh" +#include "params/RiscvBootloaderKernelWorkload.hh" #include "params/RiscvLinux.hh" #include "sim/kernel_workload.hh" @@ -41,11 +44,30 @@ namespace RiscvISA class FsLinux : public KernelWorkload { + private: + /** + * Event to halt the simulator if the kernel calls panic() or + * oops_exit() + **/ + PCEvent *kernelPanicPcEvent = nullptr; + PCEvent *kernelOopsPcEvent = nullptr; + void addExitOnKernelPanicEvent(); + void addExitOnKernelOopsEvent(); public: PARAMS(RiscvLinux); FsLinux(const Params &p) : KernelWorkload(p) {} + ~FsLinux() + { + if (kernelPanicPcEvent != nullptr) { + delete kernelPanicPcEvent; + } + if (kernelOopsPcEvent != nullptr) { + delete kernelOopsPcEvent; + } + } void initState() override; + void startup() override; void setSystem(System *sys) override @@ -58,6 +80,84 @@ class FsLinux : public KernelWorkload ByteOrder byteOrder() const override { return ByteOrder::little; } }; +class BootloaderKernelWorkload: public Workload +{ + private: + Addr entryPoint = 0; + loader::ObjectFile *kernel = nullptr; + loader::ObjectFile *bootloader = nullptr; + loader::SymbolTable kernelSymbolTable; + loader::SymbolTable bootloaderSymbolTable; + const std::string bootArgs; + + /** + * Event to halt the simulator if the kernel calls panic() or + * oops_exit() + **/ + PCEvent *kernelPanicPcEvent = nullptr; + PCEvent *kernelOopsPcEvent = nullptr; + + private: + void loadBootloaderSymbolTable(); + void loadKernelSymbolTable(); + void loadBootloader(); + void loadKernel(); + void loadDtb(); + void addExitOnKernelPanicEvent(); + void addExitOnKernelOopsEvent(); + + public: + PARAMS(RiscvBootloaderKernelWorkload); + BootloaderKernelWorkload(const Params &p) + : Workload(p), entryPoint(p.entry_point), bootArgs(p.command_line) + { + loadBootloaderSymbolTable(); + loadKernelSymbolTable(); + } + + ~BootloaderKernelWorkload() + { + if (kernelPanicPcEvent != nullptr) { + delete kernelPanicPcEvent; + } + if (kernelOopsPcEvent != nullptr) { + delete kernelOopsPcEvent; + } + } + + void initState() override; + void startup() override; + + void + setSystem(System *sys) override + { + Workload::setSystem(sys); + gdb = BaseRemoteGDB::build( + params().remote_gdb_port, system); + } + + Addr getEntry() const override { return entryPoint; } + + ByteOrder byteOrder() const override { return ByteOrder::little; } + + loader::Arch getArch() const override { return kernel->getArch(); } + + const loader::SymbolTable & + symtab(ThreadContext *tc) override + { + return kernelSymbolTable; + } + + bool + insertSymbol(const loader::Symbol &symbol) override + { + return kernelSymbolTable.insert(symbol); + } + + void serialize(CheckpointOut &checkpoint) const override; + void unserialize(CheckpointIn &checkpoint) override; +}; + } // namespace RiscvISA } // namespace gem5 diff --git a/src/arch/riscv/pcstate.hh b/src/arch/riscv/pcstate.hh index de07145dc3..62ecd9f960 100644 --- a/src/arch/riscv/pcstate.hh +++ b/src/arch/riscv/pcstate.hh @@ -43,6 +43,8 @@ #define __ARCH_RISCV_PCSTATE_HH__ #include "arch/generic/pcstate.hh" +#include "arch/riscv/regs/vector.hh" +#include "enums/PrivilegeModeSet.hh" #include "enums/RiscvType.hh" namespace gem5 @@ -54,17 +56,32 @@ using RiscvType = enums::RiscvType; constexpr enums::RiscvType RV32 = enums::RV32; constexpr enums::RiscvType RV64 = enums::RV64; +using PrivilegeModeSet = enums::PrivilegeModeSet; + class PCState : public GenericISA::UPCState<4> { - private: + protected: + typedef GenericISA::UPCState<4> Base; + bool _compressed = false; - RiscvType _rv_type = RV64; + RiscvType _rvType = RV64; + uint64_t _vlenb = 32; + VTYPE _vtype = (1ULL << 63); // vtype.vill = 1 at initial; + uint32_t _vl = 0; public: + PCState(const PCState &other) : Base(other), + _rvType(other._rvType), _vlenb(other._vlenb), + _vtype(other._vtype), _vl(other._vl) + {} + PCState &operator=(const PCState &other) = default; PCState() = default; - PCState(const PCState &other) = default; - PCState(Addr addr, RiscvType rv_type) : UPCState(addr), _rv_type(rv_type) + explicit PCState(Addr addr) { set(addr); } + explicit PCState(Addr addr, RiscvType rvType, uint64_t vlenb) { + set(addr); + _rvType = rvType; + _vlenb = vlenb; } PCStateBase *clone() const override { return new PCState(*this); } @@ -75,23 +92,65 @@ class PCState : public GenericISA::UPCState<4> Base::update(other); auto &pcstate = other.as(); _compressed = pcstate._compressed; - _rv_type = pcstate._rv_type; + _rvType = pcstate._rvType; + _vlenb = pcstate._vlenb; + _vtype = pcstate._vtype; + _vl = pcstate._vl; } void compressed(bool c) { _compressed = c; } bool compressed() const { return _compressed; } - void rvType(RiscvType rv_type) { _rv_type = rv_type; } - RiscvType rvType() const { return _rv_type; } + void rvType(RiscvType rvType) { _rvType = rvType; } + RiscvType rvType() const { return _rvType; } + + void vlenb(uint64_t v) { _vlenb = v; } + uint64_t vlenb() const { return _vlenb; } + + void vtype(VTYPE v) { _vtype = v; } + VTYPE vtype() const { return _vtype; } + + void vl(uint32_t v) { _vl = v; } + uint32_t vl() const { return _vl; } + + uint64_t size() const { return _compressed ? 2 : 4; } bool branching() const override { - if (_compressed) { - return npc() != pc() + 2 || nupc() != upc() + 1; - } else { - return npc() != pc() + 4 || nupc() != upc() + 1; - } + return npc() != pc() + size() || nupc() != upc() + 1; + } + + bool + equals(const PCStateBase &other) const override + { + auto &opc = other.as(); + return Base::equals(other) && + _vlenb == opc._vlenb && + _vtype == opc._vtype && + _vl == opc._vl; + } + + void + serialize(CheckpointOut &cp) const override + { + Base::serialize(cp); + SERIALIZE_SCALAR(_rvType); + SERIALIZE_SCALAR(_vlenb); + SERIALIZE_SCALAR(_vtype); + SERIALIZE_SCALAR(_vl); + SERIALIZE_SCALAR(_compressed); + } + + void + unserialize(CheckpointIn &cp) override + { + Base::unserialize(cp); + UNSERIALIZE_SCALAR(_rvType); + UNSERIALIZE_SCALAR(_vlenb); + UNSERIALIZE_SCALAR(_vtype); + UNSERIALIZE_SCALAR(_vl); + UNSERIALIZE_SCALAR(_compressed); } }; diff --git a/src/arch/riscv/process.cc b/src/arch/riscv/process.cc index cd00f5d63a..a9ad7b1d32 100644 --- a/src/arch/riscv/process.cc +++ b/src/arch/riscv/process.cc @@ -106,6 +106,10 @@ RiscvProcess64::initState() tc->setMiscRegNoEffect(MISCREG_PRV, PRV_U); auto *isa = dynamic_cast(tc->getIsaPtr()); fatal_if(isa->rvType() != RV64, "RISC V CPU should run in 64 bits mode"); + MISA misa = tc->readMiscRegNoEffect(MISCREG_ISA); + fatal_if(!(misa.rvu && misa.rvs), + "RISC V SE mode can't run without supervisor and user " + "privilege modes."); } } @@ -120,6 +124,10 @@ RiscvProcess32::initState() tc->setMiscRegNoEffect(MISCREG_PRV, PRV_U); auto *isa = dynamic_cast(tc->getIsaPtr()); fatal_if(isa->rvType() != RV32, "RISC V CPU should run in 32 bits mode"); + MISA misa = tc->readMiscRegNoEffect(MISCREG_ISA); + fatal_if(!(misa.rvu && misa.rvs), + "RISC V SE mode can't run without supervisor and user " + "privilege modes."); } } diff --git a/src/arch/riscv/regs/float.hh b/src/arch/riscv/regs/float.hh index 4809372070..cca9e1be2f 100644 --- a/src/arch/riscv/regs/float.hh +++ b/src/arch/riscv/regs/float.hh @@ -211,6 +211,20 @@ const std::vector RegNames = { } // namespace float_reg +inline float32_t +fsgnj32(float32_t a, float32_t b, bool n, bool x) { + if (n) b.v = ~b.v; + else if (x) b.v = a.v ^ b.v; + return f32(insertBits(b.v, 30, 0, a.v)); +} + +inline float64_t +fsgnj64(float64_t a, float64_t b, bool n, bool x) { + if (n) b.v = ~b.v; + else if (x) b.v = a.v ^ b.v; + return f64(insertBits(b.v, 62, 0, a.v)); +} + } // namespace RiscvISA } // namespace gem5 diff --git a/src/arch/riscv/regs/misc.hh b/src/arch/riscv/regs/misc.hh index 5ea3536141..96e88c7438 100644 --- a/src/arch/riscv/regs/misc.hh +++ b/src/arch/riscv/regs/misc.hh @@ -191,6 +191,14 @@ enum MiscRegIndex MISCREG_FFLAGS, MISCREG_FRM, + MISCREG_VSTART, + MISCREG_VXSAT, + MISCREG_VXRM, + MISCREG_VCSR, + MISCREG_VL, + MISCREG_VTYPE, + MISCREG_VLENB, + // These registers are not in the standard, hence does not exist in the // CSRData map. These are mainly used to provide a minimal implementation // for non-maskable-interrupt in our simple cpu. @@ -476,7 +484,15 @@ enum CSRIndex CSR_TDATA3 = 0x7A3, CSR_DCSR = 0x7B0, CSR_DPC = 0x7B1, - CSR_DSCRATCH = 0x7B2 + CSR_DSCRATCH = 0x7B2, + + CSR_VSTART = 0x008, + CSR_VXSAT = 0x009, + CSR_VXRM = 0x00A, + CSR_VCSR = 0x00F, + CSR_VL = 0xC20, + CSR_VTYPE = 0xC21, + CSR_VLENB = 0xC22 }; struct CSRMetadata @@ -484,6 +500,7 @@ struct CSRMetadata const std::string name; const int physIndex; const uint64_t rvTypes; + const uint64_t isaExts; }; template @@ -491,234 +508,660 @@ constexpr uint64_t rvTypeFlags(T... args) { return ((1 << args) | ...); } +template +constexpr uint64_t isaExtsFlags(T... isa_exts) { + return ((1ULL << (isa_exts - 'a')) | ...); +} + +constexpr uint64_t isaExtsFlags() { + return 0ULL; +} + const std::unordered_map CSRData = { - {CSR_USTATUS, {"ustatus", MISCREG_STATUS, rvTypeFlags(RV64, RV32)}}, - {CSR_UIE, {"uie", MISCREG_IE, rvTypeFlags(RV64, RV32)}}, - {CSR_UTVEC, {"utvec", MISCREG_UTVEC, rvTypeFlags(RV64, RV32)}}, - {CSR_USCRATCH, {"uscratch", MISCREG_USCRATCH, rvTypeFlags(RV64, RV32)}}, - {CSR_UEPC, {"uepc", MISCREG_UEPC, rvTypeFlags(RV64, RV32)}}, - {CSR_UCAUSE, {"ucause", MISCREG_UCAUSE, rvTypeFlags(RV64, RV32)}}, - {CSR_UTVAL, {"utval", MISCREG_UTVAL, rvTypeFlags(RV64, RV32)}}, - {CSR_UIP, {"uip", MISCREG_IP, rvTypeFlags(RV64, RV32)}}, - {CSR_FFLAGS, {"fflags", MISCREG_FFLAGS, rvTypeFlags(RV64, RV32)}}, - {CSR_FRM, {"frm", MISCREG_FRM, rvTypeFlags(RV64, RV32)}}, - {CSR_FCSR, {"fcsr", MISCREG_FFLAGS, rvTypeFlags(RV64, RV32)}}, // Actually FRM << 5 | FFLAGS - {CSR_CYCLE, {"cycle", MISCREG_CYCLE, rvTypeFlags(RV64, RV32)}}, - {CSR_TIME, {"time", MISCREG_TIME, rvTypeFlags(RV64, RV32)}}, - {CSR_INSTRET, {"instret", MISCREG_INSTRET, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER03, {"hpmcounter03", MISCREG_HPMCOUNTER03, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER04, {"hpmcounter04", MISCREG_HPMCOUNTER04, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER05, {"hpmcounter05", MISCREG_HPMCOUNTER05, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER06, {"hpmcounter06", MISCREG_HPMCOUNTER06, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER07, {"hpmcounter07", MISCREG_HPMCOUNTER07, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER08, {"hpmcounter08", MISCREG_HPMCOUNTER08, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER09, {"hpmcounter09", MISCREG_HPMCOUNTER09, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER10, {"hpmcounter10", MISCREG_HPMCOUNTER10, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER11, {"hpmcounter11", MISCREG_HPMCOUNTER11, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER12, {"hpmcounter12", MISCREG_HPMCOUNTER12, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER13, {"hpmcounter13", MISCREG_HPMCOUNTER13, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER14, {"hpmcounter14", MISCREG_HPMCOUNTER14, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER15, {"hpmcounter15", MISCREG_HPMCOUNTER15, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER16, {"hpmcounter16", MISCREG_HPMCOUNTER16, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER17, {"hpmcounter17", MISCREG_HPMCOUNTER17, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER18, {"hpmcounter18", MISCREG_HPMCOUNTER18, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER19, {"hpmcounter19", MISCREG_HPMCOUNTER19, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER20, {"hpmcounter20", MISCREG_HPMCOUNTER20, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER21, {"hpmcounter21", MISCREG_HPMCOUNTER21, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER22, {"hpmcounter22", MISCREG_HPMCOUNTER22, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER23, {"hpmcounter23", MISCREG_HPMCOUNTER23, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER24, {"hpmcounter24", MISCREG_HPMCOUNTER24, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER25, {"hpmcounter25", MISCREG_HPMCOUNTER25, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER26, {"hpmcounter26", MISCREG_HPMCOUNTER26, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER27, {"hpmcounter27", MISCREG_HPMCOUNTER27, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER28, {"hpmcounter28", MISCREG_HPMCOUNTER28, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER29, {"hpmcounter29", MISCREG_HPMCOUNTER29, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER30, {"hpmcounter30", MISCREG_HPMCOUNTER30, rvTypeFlags(RV64, RV32)}}, - {CSR_HPMCOUNTER31, {"hpmcounter31", MISCREG_HPMCOUNTER31, rvTypeFlags(RV64, RV32)}}, - {CSR_CYCLEH, {"cycleh", MISCREG_CYCLEH, rvTypeFlags(RV32)}}, - {CSR_TIMEH, {"timeh", MISCREG_TIMEH, rvTypeFlags(RV32)}}, - {CSR_INSTRETH, {"instreth", MISCREG_INSTRETH, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER03H, {"hpmcounter03h", MISCREG_HPMCOUNTER03H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER04H, {"hpmcounter04h", MISCREG_HPMCOUNTER04H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER05H, {"hpmcounter05h", MISCREG_HPMCOUNTER05H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER06H, {"hpmcounter06h", MISCREG_HPMCOUNTER06H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER07H, {"hpmcounter07h", MISCREG_HPMCOUNTER07H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER08H, {"hpmcounter08h", MISCREG_HPMCOUNTER08H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER09H, {"hpmcounter09h", MISCREG_HPMCOUNTER09H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER10H, {"hpmcounter10h", MISCREG_HPMCOUNTER10H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER11H, {"hpmcounter11h", MISCREG_HPMCOUNTER11H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER12H, {"hpmcounter12h", MISCREG_HPMCOUNTER12H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER13H, {"hpmcounter13h", MISCREG_HPMCOUNTER13H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER14H, {"hpmcounter14h", MISCREG_HPMCOUNTER14H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER15H, {"hpmcounter15h", MISCREG_HPMCOUNTER15H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER16H, {"hpmcounter16h", MISCREG_HPMCOUNTER16H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER17H, {"hpmcounter17h", MISCREG_HPMCOUNTER17H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER18H, {"hpmcounter18h", MISCREG_HPMCOUNTER18H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER19H, {"hpmcounter19h", MISCREG_HPMCOUNTER19H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER20H, {"hpmcounter20h", MISCREG_HPMCOUNTER20H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER21H, {"hpmcounter21h", MISCREG_HPMCOUNTER21H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER22H, {"hpmcounter22h", MISCREG_HPMCOUNTER22H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER23H, {"hpmcounter23h", MISCREG_HPMCOUNTER23H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER24H, {"hpmcounter24h", MISCREG_HPMCOUNTER24H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER25H, {"hpmcounter25h", MISCREG_HPMCOUNTER25H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER26H, {"hpmcounter26h", MISCREG_HPMCOUNTER26H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER27H, {"hpmcounter27h", MISCREG_HPMCOUNTER27H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER28H, {"hpmcounter28h", MISCREG_HPMCOUNTER28H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER29H, {"hpmcounter29h", MISCREG_HPMCOUNTER29H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER30H, {"hpmcounter30h", MISCREG_HPMCOUNTER30H, rvTypeFlags(RV32)}}, - {CSR_HPMCOUNTER31H, {"hpmcounter31h", MISCREG_HPMCOUNTER31H, rvTypeFlags(RV32)}}, + {CSR_USTATUS, + {"ustatus", MISCREG_STATUS, rvTypeFlags(RV64, RV32), + isaExtsFlags('n')}}, + {CSR_UIE, + {"uie", MISCREG_IE, rvTypeFlags(RV64, RV32), isaExtsFlags('n')}}, + {CSR_UTVEC, + {"utvec", MISCREG_UTVEC, rvTypeFlags(RV64, RV32), isaExtsFlags('n')}}, + {CSR_USCRATCH, + {"uscratch", MISCREG_USCRATCH, rvTypeFlags(RV64, RV32), + isaExtsFlags('n')}}, + {CSR_UEPC, + {"uepc", MISCREG_UEPC, rvTypeFlags(RV64, RV32), isaExtsFlags('n')}}, + {CSR_UCAUSE, + {"ucause", MISCREG_UCAUSE, rvTypeFlags(RV64, RV32), + isaExtsFlags('n')}}, + {CSR_UTVAL, + {"utval", MISCREG_UTVAL, rvTypeFlags(RV64, RV32), isaExtsFlags('n')}}, + {CSR_UIP, + {"uip", MISCREG_IP, rvTypeFlags(RV64, RV32), isaExtsFlags('n')}}, + {CSR_FFLAGS, + {"fflags", MISCREG_FFLAGS, rvTypeFlags(RV64, RV32), + isaExtsFlags('f')}}, + {CSR_FRM, + {"frm", MISCREG_FRM, rvTypeFlags(RV64, RV32), isaExtsFlags('f')}}, + // Actually FRM << 5 | FFLAGS + {CSR_FCSR, + {"fcsr", MISCREG_FFLAGS, rvTypeFlags(RV64, RV32), isaExtsFlags('f')}}, + {CSR_CYCLE, + {"cycle", MISCREG_CYCLE, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_TIME, + {"time", MISCREG_TIME, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_INSTRET, + {"instret", MISCREG_INSTRET, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_HPMCOUNTER03, + {"hpmcounter03", MISCREG_HPMCOUNTER03, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER04, + {"hpmcounter04", MISCREG_HPMCOUNTER04, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER05, + {"hpmcounter05", MISCREG_HPMCOUNTER05, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER06, + {"hpmcounter06", MISCREG_HPMCOUNTER06, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER07, + {"hpmcounter07", MISCREG_HPMCOUNTER07, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER08, + {"hpmcounter08", MISCREG_HPMCOUNTER08, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER09, + {"hpmcounter09", MISCREG_HPMCOUNTER09, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER10, + {"hpmcounter10", MISCREG_HPMCOUNTER10, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER11, + {"hpmcounter11", MISCREG_HPMCOUNTER11, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER12, + {"hpmcounter12", MISCREG_HPMCOUNTER12, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER13, + {"hpmcounter13", MISCREG_HPMCOUNTER13, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER14, + {"hpmcounter14", MISCREG_HPMCOUNTER14, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER15, + {"hpmcounter15", MISCREG_HPMCOUNTER15, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER16, + {"hpmcounter16", MISCREG_HPMCOUNTER16, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER17, + {"hpmcounter17", MISCREG_HPMCOUNTER17, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER18, + {"hpmcounter18", MISCREG_HPMCOUNTER18, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER19, + {"hpmcounter19", MISCREG_HPMCOUNTER19, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER20, + {"hpmcounter20", MISCREG_HPMCOUNTER20, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER21, + {"hpmcounter21", MISCREG_HPMCOUNTER21, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER22, + {"hpmcounter22", MISCREG_HPMCOUNTER22, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER23, + {"hpmcounter23", MISCREG_HPMCOUNTER23, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER24, + {"hpmcounter24", MISCREG_HPMCOUNTER24, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER25, + {"hpmcounter25", MISCREG_HPMCOUNTER25, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER26, + {"hpmcounter26", MISCREG_HPMCOUNTER26, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER27, + {"hpmcounter27", MISCREG_HPMCOUNTER27, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER28, + {"hpmcounter28", MISCREG_HPMCOUNTER28, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER29, + {"hpmcounter29", MISCREG_HPMCOUNTER29, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER30, + {"hpmcounter30", MISCREG_HPMCOUNTER30, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER31, + {"hpmcounter31", MISCREG_HPMCOUNTER31, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_CYCLEH, + {"cycleh", MISCREG_CYCLEH, rvTypeFlags(RV32), isaExtsFlags()}}, + {CSR_TIMEH, + {"timeh", MISCREG_TIMEH, rvTypeFlags(RV32), isaExtsFlags()}}, + {CSR_INSTRETH, + {"instreth", MISCREG_INSTRETH, rvTypeFlags(RV32), isaExtsFlags()}}, + {CSR_HPMCOUNTER03H, + {"hpmcounter03h", MISCREG_HPMCOUNTER03H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER04H, + {"hpmcounter04h", MISCREG_HPMCOUNTER04H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER05H, + {"hpmcounter05h", MISCREG_HPMCOUNTER05H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER06H, + {"hpmcounter06h", MISCREG_HPMCOUNTER06H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER07H, + {"hpmcounter07h", MISCREG_HPMCOUNTER07H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER08H, + {"hpmcounter08h", MISCREG_HPMCOUNTER08H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER09H, + {"hpmcounter09h", MISCREG_HPMCOUNTER09H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER10H, + {"hpmcounter10h", MISCREG_HPMCOUNTER10H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER11H, + {"hpmcounter11h", MISCREG_HPMCOUNTER11H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER12H, + {"hpmcounter12h", MISCREG_HPMCOUNTER12H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER13H, + {"hpmcounter13h", MISCREG_HPMCOUNTER13H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER14H, + {"hpmcounter14h", MISCREG_HPMCOUNTER14H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER15H, + {"hpmcounter15h", MISCREG_HPMCOUNTER15H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER16H, + {"hpmcounter16h", MISCREG_HPMCOUNTER16H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER17H, + {"hpmcounter17h", MISCREG_HPMCOUNTER17H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER18H, + {"hpmcounter18h", MISCREG_HPMCOUNTER18H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER19H, + {"hpmcounter19h", MISCREG_HPMCOUNTER19H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER20H, + {"hpmcounter20h", MISCREG_HPMCOUNTER20H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER21H, + {"hpmcounter21h", MISCREG_HPMCOUNTER21H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER22H, + {"hpmcounter22h", MISCREG_HPMCOUNTER22H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER23H, + {"hpmcounter23h", MISCREG_HPMCOUNTER23H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER24H, + {"hpmcounter24h", MISCREG_HPMCOUNTER24H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER25H, + {"hpmcounter25h", MISCREG_HPMCOUNTER25H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER26H, + {"hpmcounter26h", MISCREG_HPMCOUNTER26H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER27H, + {"hpmcounter27h", MISCREG_HPMCOUNTER27H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER28H, + {"hpmcounter28h", MISCREG_HPMCOUNTER28H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER29H, + {"hpmcounter29h", MISCREG_HPMCOUNTER29H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER30H, + {"hpmcounter30h", MISCREG_HPMCOUNTER30H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_HPMCOUNTER31H, + {"hpmcounter31h", MISCREG_HPMCOUNTER31H, rvTypeFlags(RV32), + isaExtsFlags()}}, - {CSR_SSTATUS, {"sstatus", MISCREG_STATUS, rvTypeFlags(RV64, RV32)}}, - {CSR_SEDELEG, {"sedeleg", MISCREG_SEDELEG, rvTypeFlags(RV64, RV32)}}, - {CSR_SIDELEG, {"sideleg", MISCREG_SIDELEG, rvTypeFlags(RV64, RV32)}}, - {CSR_SIE, {"sie", MISCREG_IE, rvTypeFlags(RV64, RV32)}}, - {CSR_STVEC, {"stvec", MISCREG_STVEC, rvTypeFlags(RV64, RV32)}}, - {CSR_SCOUNTEREN, {"scounteren", MISCREG_SCOUNTEREN, rvTypeFlags(RV64, RV32)}}, - {CSR_SSCRATCH, {"sscratch", MISCREG_SSCRATCH, rvTypeFlags(RV64, RV32)}}, - {CSR_SEPC, {"sepc", MISCREG_SEPC, rvTypeFlags(RV64, RV32)}}, - {CSR_SCAUSE, {"scause", MISCREG_SCAUSE, rvTypeFlags(RV64, RV32)}}, - {CSR_STVAL, {"stval", MISCREG_STVAL, rvTypeFlags(RV64, RV32)}}, - {CSR_SIP, {"sip", MISCREG_IP, rvTypeFlags(RV64, RV32)}}, - {CSR_SATP, {"satp", MISCREG_SATP, rvTypeFlags(RV64, RV32)}}, + {CSR_SSTATUS, + {"sstatus", MISCREG_STATUS, rvTypeFlags(RV64, RV32), + isaExtsFlags('s')}}, + {CSR_SEDELEG, + {"sedeleg", MISCREG_SEDELEG, rvTypeFlags(RV64, RV32), + isaExtsFlags('s')}}, + {CSR_SIDELEG, + {"sideleg", MISCREG_SIDELEG, rvTypeFlags(RV64, RV32), + isaExtsFlags('s')}}, + {CSR_SIE, + {"sie", MISCREG_IE, rvTypeFlags(RV64, RV32), isaExtsFlags('s')}}, + {CSR_STVEC, + {"stvec", MISCREG_STVEC, rvTypeFlags(RV64, RV32), isaExtsFlags('s')}}, + {CSR_SCOUNTEREN, + {"scounteren", MISCREG_SCOUNTEREN, rvTypeFlags(RV64, RV32), + isaExtsFlags('s')}}, + {CSR_SSCRATCH, + {"sscratch", MISCREG_SSCRATCH, rvTypeFlags(RV64, RV32), + isaExtsFlags('s')}}, + {CSR_SEPC, + {"sepc", MISCREG_SEPC, rvTypeFlags(RV64, RV32), isaExtsFlags('s')}}, + {CSR_SCAUSE, + {"scause", MISCREG_SCAUSE, rvTypeFlags(RV64, RV32), + isaExtsFlags('s')}}, + {CSR_STVAL, + {"stval", MISCREG_STVAL, rvTypeFlags(RV64, RV32), isaExtsFlags('s')}}, + {CSR_SIP, + {"sip", MISCREG_IP, rvTypeFlags(RV64, RV32), isaExtsFlags('s')}}, + {CSR_SATP, + {"satp", MISCREG_SATP, rvTypeFlags(RV64, RV32), isaExtsFlags('s')}}, - {CSR_MVENDORID, {"mvendorid", MISCREG_VENDORID, rvTypeFlags(RV64, RV32)}}, - {CSR_MARCHID, {"marchid", MISCREG_ARCHID, rvTypeFlags(RV64, RV32)}}, - {CSR_MIMPID, {"mimpid", MISCREG_IMPID, rvTypeFlags(RV64, RV32)}}, - {CSR_MHARTID, {"mhartid", MISCREG_HARTID, rvTypeFlags(RV64, RV32)}}, - {CSR_MSTATUS, {"mstatus", MISCREG_STATUS, rvTypeFlags(RV64, RV32)}}, - {CSR_MISA, {"misa", MISCREG_ISA, rvTypeFlags(RV64, RV32)}}, - {CSR_MEDELEG, {"medeleg", MISCREG_MEDELEG, rvTypeFlags(RV64, RV32)}}, - {CSR_MIDELEG, {"mideleg", MISCREG_MIDELEG, rvTypeFlags(RV64, RV32)}}, - {CSR_MIE, {"mie", MISCREG_IE, rvTypeFlags(RV64, RV32)}}, - {CSR_MTVEC, {"mtvec", MISCREG_MTVEC, rvTypeFlags(RV64, RV32)}}, - {CSR_MCOUNTEREN, {"mcounteren", MISCREG_MCOUNTEREN, rvTypeFlags(RV64, RV32)}}, - {CSR_MSTATUSH, {"mstatush", MISCREG_MSTATUSH, rvTypeFlags(RV32)}}, - {CSR_MSCRATCH, {"mscratch", MISCREG_MSCRATCH, rvTypeFlags(RV64, RV32)}}, - {CSR_MEPC, {"mepc", MISCREG_MEPC, rvTypeFlags(RV64, RV32)}}, - {CSR_MCAUSE, {"mcause", MISCREG_MCAUSE, rvTypeFlags(RV64, RV32)}}, - {CSR_MTVAL, {"mtval", MISCREG_MTVAL, rvTypeFlags(RV64, RV32)}}, - {CSR_MIP, {"mip", MISCREG_IP, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPCFG0, {"pmpcfg0", MISCREG_PMPCFG0, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPCFG1, {"pmpcfg1", MISCREG_PMPCFG1, rvTypeFlags(RV32)}}, // pmpcfg1 rv32 only - {CSR_PMPCFG2, {"pmpcfg2", MISCREG_PMPCFG2, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPCFG3, {"pmpcfg3", MISCREG_PMPCFG3, rvTypeFlags(RV32)}}, // pmpcfg3 rv32 only - {CSR_PMPADDR00, {"pmpaddr0", MISCREG_PMPADDR00, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPADDR01, {"pmpaddr1", MISCREG_PMPADDR01, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPADDR02, {"pmpaddr2", MISCREG_PMPADDR02, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPADDR03, {"pmpaddr3", MISCREG_PMPADDR03, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPADDR04, {"pmpaddr4", MISCREG_PMPADDR04, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPADDR05, {"pmpaddr5", MISCREG_PMPADDR05, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPADDR06, {"pmpaddr6", MISCREG_PMPADDR06, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPADDR07, {"pmpaddr7", MISCREG_PMPADDR07, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPADDR08, {"pmpaddr8", MISCREG_PMPADDR08, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPADDR09, {"pmpaddr9", MISCREG_PMPADDR09, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPADDR10, {"pmpaddr10", MISCREG_PMPADDR10, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPADDR11, {"pmpaddr11", MISCREG_PMPADDR11, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPADDR12, {"pmpaddr12", MISCREG_PMPADDR12, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPADDR13, {"pmpaddr13", MISCREG_PMPADDR13, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPADDR14, {"pmpaddr14", MISCREG_PMPADDR14, rvTypeFlags(RV64, RV32)}}, - {CSR_PMPADDR15, {"pmpaddr15", MISCREG_PMPADDR15, rvTypeFlags(RV64, RV32)}}, - {CSR_MCYCLE, {"mcycle", MISCREG_CYCLE, rvTypeFlags(RV64, RV32)}}, - {CSR_MINSTRET, {"minstret", MISCREG_INSTRET, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER03, {"mhpmcounter03", MISCREG_HPMCOUNTER03, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER04, {"mhpmcounter04", MISCREG_HPMCOUNTER04, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER05, {"mhpmcounter05", MISCREG_HPMCOUNTER05, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER06, {"mhpmcounter06", MISCREG_HPMCOUNTER06, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER07, {"mhpmcounter07", MISCREG_HPMCOUNTER07, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER08, {"mhpmcounter08", MISCREG_HPMCOUNTER08, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER09, {"mhpmcounter09", MISCREG_HPMCOUNTER09, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER10, {"mhpmcounter10", MISCREG_HPMCOUNTER10, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER11, {"mhpmcounter11", MISCREG_HPMCOUNTER11, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER12, {"mhpmcounter12", MISCREG_HPMCOUNTER12, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER13, {"mhpmcounter13", MISCREG_HPMCOUNTER13, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER14, {"mhpmcounter14", MISCREG_HPMCOUNTER14, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER15, {"mhpmcounter15", MISCREG_HPMCOUNTER15, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER16, {"mhpmcounter16", MISCREG_HPMCOUNTER16, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER17, {"mhpmcounter17", MISCREG_HPMCOUNTER17, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER18, {"mhpmcounter18", MISCREG_HPMCOUNTER18, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER19, {"mhpmcounter19", MISCREG_HPMCOUNTER19, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER20, {"mhpmcounter20", MISCREG_HPMCOUNTER20, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER21, {"mhpmcounter21", MISCREG_HPMCOUNTER21, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER22, {"mhpmcounter22", MISCREG_HPMCOUNTER22, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER23, {"mhpmcounter23", MISCREG_HPMCOUNTER23, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER24, {"mhpmcounter24", MISCREG_HPMCOUNTER24, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER25, {"mhpmcounter25", MISCREG_HPMCOUNTER25, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER26, {"mhpmcounter26", MISCREG_HPMCOUNTER26, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER27, {"mhpmcounter27", MISCREG_HPMCOUNTER27, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER28, {"mhpmcounter28", MISCREG_HPMCOUNTER28, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER29, {"mhpmcounter29", MISCREG_HPMCOUNTER29, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER30, {"mhpmcounter30", MISCREG_HPMCOUNTER30, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMCOUNTER31, {"mhpmcounter31", MISCREG_HPMCOUNTER31, rvTypeFlags(RV64, RV32)}}, + {CSR_MVENDORID, + {"mvendorid", MISCREG_VENDORID, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MARCHID, + {"marchid", MISCREG_ARCHID, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_MIMPID, + {"mimpid", MISCREG_IMPID, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_MHARTID, + {"mhartid", MISCREG_HARTID, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_MSTATUS, + {"mstatus", MISCREG_STATUS, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_MISA, + {"misa", MISCREG_ISA, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_MEDELEG, + {"medeleg", MISCREG_MEDELEG, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_MIDELEG, + {"mideleg", MISCREG_MIDELEG, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_MIE, + {"mie", MISCREG_IE, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_MTVEC, + {"mtvec", MISCREG_MTVEC, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_MCOUNTEREN, + {"mcounteren", MISCREG_MCOUNTEREN, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MSTATUSH, + {"mstatush", MISCREG_MSTATUSH, rvTypeFlags(RV32), isaExtsFlags()}}, + {CSR_MSCRATCH, + {"mscratch", MISCREG_MSCRATCH, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MEPC, + {"mepc", MISCREG_MEPC, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_MCAUSE, + {"mcause", MISCREG_MCAUSE, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_MTVAL, + {"mtval", MISCREG_MTVAL, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_MIP, + {"mip", MISCREG_IP, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_PMPCFG0, + {"pmpcfg0", MISCREG_PMPCFG0, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + // pmpcfg1 rv32 only + {CSR_PMPCFG1, + {"pmpcfg1", MISCREG_PMPCFG1, rvTypeFlags(RV32), isaExtsFlags()}}, + {CSR_PMPCFG2, + {"pmpcfg2", MISCREG_PMPCFG2, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + // pmpcfg3 rv32 only + {CSR_PMPCFG3, + {"pmpcfg3", MISCREG_PMPCFG3, rvTypeFlags(RV32), isaExtsFlags()}}, + {CSR_PMPADDR00, + {"pmpaddr0", MISCREG_PMPADDR00, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_PMPADDR01, + {"pmpaddr1", MISCREG_PMPADDR01, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_PMPADDR02, + {"pmpaddr2", MISCREG_PMPADDR02, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_PMPADDR03, + {"pmpaddr3", MISCREG_PMPADDR03, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_PMPADDR04, + {"pmpaddr4", MISCREG_PMPADDR04, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_PMPADDR05, + {"pmpaddr5", MISCREG_PMPADDR05, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_PMPADDR06, + {"pmpaddr6", MISCREG_PMPADDR06, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_PMPADDR07, + {"pmpaddr7", MISCREG_PMPADDR07, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_PMPADDR08, + {"pmpaddr8", MISCREG_PMPADDR08, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_PMPADDR09, + {"pmpaddr9", MISCREG_PMPADDR09, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_PMPADDR10, + {"pmpaddr10", MISCREG_PMPADDR10, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_PMPADDR11, + {"pmpaddr11", MISCREG_PMPADDR11, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_PMPADDR12, + {"pmpaddr12", MISCREG_PMPADDR12, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_PMPADDR13, + {"pmpaddr13", MISCREG_PMPADDR13, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_PMPADDR14, + {"pmpaddr14", MISCREG_PMPADDR14, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_PMPADDR15, + {"pmpaddr15", MISCREG_PMPADDR15, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MCYCLE, + {"mcycle", MISCREG_CYCLE, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_MINSTRET, + {"minstret", MISCREG_INSTRET, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER03, + {"mhpmcounter03", MISCREG_HPMCOUNTER03, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER04, + {"mhpmcounter04", MISCREG_HPMCOUNTER04, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER05, + {"mhpmcounter05", MISCREG_HPMCOUNTER05, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER06, + {"mhpmcounter06", MISCREG_HPMCOUNTER06, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER07, + {"mhpmcounter07", MISCREG_HPMCOUNTER07, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER08, + {"mhpmcounter08", MISCREG_HPMCOUNTER08, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER09, + {"mhpmcounter09", MISCREG_HPMCOUNTER09, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER10, + {"mhpmcounter10", MISCREG_HPMCOUNTER10, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER11, + {"mhpmcounter11", MISCREG_HPMCOUNTER11, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER12, + {"mhpmcounter12", MISCREG_HPMCOUNTER12, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER13, + {"mhpmcounter13", MISCREG_HPMCOUNTER13, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER14, + {"mhpmcounter14", MISCREG_HPMCOUNTER14, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER15, + {"mhpmcounter15", MISCREG_HPMCOUNTER15, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER16, + {"mhpmcounter16", MISCREG_HPMCOUNTER16, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER17, + {"mhpmcounter17", MISCREG_HPMCOUNTER17, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER18, + {"mhpmcounter18", MISCREG_HPMCOUNTER18, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER19, + {"mhpmcounter19", MISCREG_HPMCOUNTER19, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER20, + {"mhpmcounter20", MISCREG_HPMCOUNTER20, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER21, + {"mhpmcounter21", MISCREG_HPMCOUNTER21, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER22, + {"mhpmcounter22", MISCREG_HPMCOUNTER22, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER23, + {"mhpmcounter23", MISCREG_HPMCOUNTER23, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER24, + {"mhpmcounter24", MISCREG_HPMCOUNTER24, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER25, + {"mhpmcounter25", MISCREG_HPMCOUNTER25, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER26, + {"mhpmcounter26", MISCREG_HPMCOUNTER26, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER27, + {"mhpmcounter27", MISCREG_HPMCOUNTER27, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER28, + {"mhpmcounter28", MISCREG_HPMCOUNTER28, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER29, + {"mhpmcounter29", MISCREG_HPMCOUNTER29, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER30, + {"mhpmcounter30", MISCREG_HPMCOUNTER30, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER31, + {"mhpmcounter31", MISCREG_HPMCOUNTER31, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, - {CSR_MCYCLEH, {"mcycleh", MISCREG_CYCLEH, rvTypeFlags(RV32)}}, - {CSR_MINSTRETH, {"minstreth", MISCREG_INSTRETH, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER03H, {"mhpmcounter03h", MISCREG_HPMCOUNTER03H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER04H, {"mhpmcounter04h", MISCREG_HPMCOUNTER04H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER05H, {"mhpmcounter05h", MISCREG_HPMCOUNTER05H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER06H, {"mhpmcounter06h", MISCREG_HPMCOUNTER06H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER07H, {"mhpmcounter07h", MISCREG_HPMCOUNTER07H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER08H, {"mhpmcounter08h", MISCREG_HPMCOUNTER08H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER09H, {"mhpmcounter09h", MISCREG_HPMCOUNTER09H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER10H, {"mhpmcounter10h", MISCREG_HPMCOUNTER10H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER11H, {"mhpmcounter11h", MISCREG_HPMCOUNTER11H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER12H, {"mhpmcounter12h", MISCREG_HPMCOUNTER12H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER13H, {"mhpmcounter13h", MISCREG_HPMCOUNTER13H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER14H, {"mhpmcounter14h", MISCREG_HPMCOUNTER14H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER15H, {"mhpmcounter15h", MISCREG_HPMCOUNTER15H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER16H, {"mhpmcounter16h", MISCREG_HPMCOUNTER16H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER17H, {"mhpmcounter17h", MISCREG_HPMCOUNTER17H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER18H, {"mhpmcounter18h", MISCREG_HPMCOUNTER18H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER19H, {"mhpmcounter19h", MISCREG_HPMCOUNTER19H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER20H, {"mhpmcounter20h", MISCREG_HPMCOUNTER20H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER21H, {"mhpmcounter21h", MISCREG_HPMCOUNTER21H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER22H, {"mhpmcounter22h", MISCREG_HPMCOUNTER22H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER23H, {"mhpmcounter23h", MISCREG_HPMCOUNTER23H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER24H, {"mhpmcounter24h", MISCREG_HPMCOUNTER24H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER25H, {"mhpmcounter25h", MISCREG_HPMCOUNTER25H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER26H, {"mhpmcounter26h", MISCREG_HPMCOUNTER26H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER27H, {"mhpmcounter27h", MISCREG_HPMCOUNTER27H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER28H, {"mhpmcounter28h", MISCREG_HPMCOUNTER28H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER29H, {"mhpmcounter29h", MISCREG_HPMCOUNTER29H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER30H, {"mhpmcounter30h", MISCREG_HPMCOUNTER30H, rvTypeFlags(RV32)}}, - {CSR_MHPMCOUNTER31H, {"mhpmcounter31h", MISCREG_HPMCOUNTER31H, rvTypeFlags(RV32)}}, + {CSR_MCYCLEH, + {"mcycleh", MISCREG_CYCLEH, rvTypeFlags(RV32), isaExtsFlags()}}, + {CSR_MINSTRETH, + {"minstreth", MISCREG_INSTRETH, rvTypeFlags(RV32), isaExtsFlags()}}, + {CSR_MHPMCOUNTER03H, + {"mhpmcounter03h", MISCREG_HPMCOUNTER03H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER04H, + {"mhpmcounter04h", MISCREG_HPMCOUNTER04H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER05H, + {"mhpmcounter05h", MISCREG_HPMCOUNTER05H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER06H, + {"mhpmcounter06h", MISCREG_HPMCOUNTER06H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER07H, + {"mhpmcounter07h", MISCREG_HPMCOUNTER07H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER08H, + {"mhpmcounter08h", MISCREG_HPMCOUNTER08H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER09H, + {"mhpmcounter09h", MISCREG_HPMCOUNTER09H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER10H, + {"mhpmcounter10h", MISCREG_HPMCOUNTER10H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER11H, + {"mhpmcounter11h", MISCREG_HPMCOUNTER11H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER12H, + {"mhpmcounter12h", MISCREG_HPMCOUNTER12H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER13H, + {"mhpmcounter13h", MISCREG_HPMCOUNTER13H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER14H, + {"mhpmcounter14h", MISCREG_HPMCOUNTER14H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER15H, + {"mhpmcounter15h", MISCREG_HPMCOUNTER15H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER16H, + {"mhpmcounter16h", MISCREG_HPMCOUNTER16H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER17H, + {"mhpmcounter17h", MISCREG_HPMCOUNTER17H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER18H, + {"mhpmcounter18h", MISCREG_HPMCOUNTER18H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER19H, + {"mhpmcounter19h", MISCREG_HPMCOUNTER19H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER20H, + {"mhpmcounter20h", MISCREG_HPMCOUNTER20H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER21H, + {"mhpmcounter21h", MISCREG_HPMCOUNTER21H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER22H, + {"mhpmcounter22h", MISCREG_HPMCOUNTER22H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER23H, + {"mhpmcounter23h", MISCREG_HPMCOUNTER23H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER24H, + {"mhpmcounter24h", MISCREG_HPMCOUNTER24H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER25H, + {"mhpmcounter25h", MISCREG_HPMCOUNTER25H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER26H, + {"mhpmcounter26h", MISCREG_HPMCOUNTER26H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER27H, + {"mhpmcounter27h", MISCREG_HPMCOUNTER27H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER28H, + {"mhpmcounter28h", MISCREG_HPMCOUNTER28H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER29H, + {"mhpmcounter29h", MISCREG_HPMCOUNTER29H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER30H, + {"mhpmcounter30h", MISCREG_HPMCOUNTER30H, rvTypeFlags(RV32), + isaExtsFlags()}}, + {CSR_MHPMCOUNTER31H, + {"mhpmcounter31h", MISCREG_HPMCOUNTER31H, rvTypeFlags(RV32), + isaExtsFlags()}}, - {CSR_MHPMEVENT03, {"mhpmevent03", MISCREG_HPMEVENT03, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT04, {"mhpmevent04", MISCREG_HPMEVENT04, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT05, {"mhpmevent05", MISCREG_HPMEVENT05, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT06, {"mhpmevent06", MISCREG_HPMEVENT06, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT07, {"mhpmevent07", MISCREG_HPMEVENT07, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT08, {"mhpmevent08", MISCREG_HPMEVENT08, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT09, {"mhpmevent09", MISCREG_HPMEVENT09, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT10, {"mhpmevent10", MISCREG_HPMEVENT10, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT11, {"mhpmevent11", MISCREG_HPMEVENT11, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT12, {"mhpmevent12", MISCREG_HPMEVENT12, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT13, {"mhpmevent13", MISCREG_HPMEVENT13, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT14, {"mhpmevent14", MISCREG_HPMEVENT14, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT15, {"mhpmevent15", MISCREG_HPMEVENT15, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT16, {"mhpmevent16", MISCREG_HPMEVENT16, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT17, {"mhpmevent17", MISCREG_HPMEVENT17, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT18, {"mhpmevent18", MISCREG_HPMEVENT18, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT19, {"mhpmevent19", MISCREG_HPMEVENT19, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT20, {"mhpmevent20", MISCREG_HPMEVENT20, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT21, {"mhpmevent21", MISCREG_HPMEVENT21, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT22, {"mhpmevent22", MISCREG_HPMEVENT22, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT23, {"mhpmevent23", MISCREG_HPMEVENT23, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT24, {"mhpmevent24", MISCREG_HPMEVENT24, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT25, {"mhpmevent25", MISCREG_HPMEVENT25, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT26, {"mhpmevent26", MISCREG_HPMEVENT26, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT27, {"mhpmevent27", MISCREG_HPMEVENT27, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT28, {"mhpmevent28", MISCREG_HPMEVENT28, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT29, {"mhpmevent29", MISCREG_HPMEVENT29, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT30, {"mhpmevent30", MISCREG_HPMEVENT30, rvTypeFlags(RV64, RV32)}}, - {CSR_MHPMEVENT31, {"mhpmevent31", MISCREG_HPMEVENT31, rvTypeFlags(RV64, RV32)}}, + {CSR_MHPMEVENT03, + {"mhpmevent03", MISCREG_HPMEVENT03, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT04, + {"mhpmevent04", MISCREG_HPMEVENT04, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT05, + {"mhpmevent05", MISCREG_HPMEVENT05, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT06, + {"mhpmevent06", MISCREG_HPMEVENT06, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT07, + {"mhpmevent07", MISCREG_HPMEVENT07, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT08, + {"mhpmevent08", MISCREG_HPMEVENT08, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT09, + {"mhpmevent09", MISCREG_HPMEVENT09, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT10, + {"mhpmevent10", MISCREG_HPMEVENT10, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT11, + {"mhpmevent11", MISCREG_HPMEVENT11, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT12, + {"mhpmevent12", MISCREG_HPMEVENT12, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT13, + {"mhpmevent13", MISCREG_HPMEVENT13, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT14, + {"mhpmevent14", MISCREG_HPMEVENT14, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT15, + {"mhpmevent15", MISCREG_HPMEVENT15, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT16, + {"mhpmevent16", MISCREG_HPMEVENT16, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT17, + {"mhpmevent17", MISCREG_HPMEVENT17, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT18, + {"mhpmevent18", MISCREG_HPMEVENT18, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT19, + {"mhpmevent19", MISCREG_HPMEVENT19, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT20, + {"mhpmevent20", MISCREG_HPMEVENT20, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT21, + {"mhpmevent21", MISCREG_HPMEVENT21, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT22, + {"mhpmevent22", MISCREG_HPMEVENT22, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT23, + {"mhpmevent23", MISCREG_HPMEVENT23, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT24, + {"mhpmevent24", MISCREG_HPMEVENT24, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT25, + {"mhpmevent25", MISCREG_HPMEVENT25, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT26, + {"mhpmevent26", MISCREG_HPMEVENT26, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT27, + {"mhpmevent27", MISCREG_HPMEVENT27, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT28, + {"mhpmevent28", MISCREG_HPMEVENT28, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT29, + {"mhpmevent29", MISCREG_HPMEVENT29, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT30, + {"mhpmevent30", MISCREG_HPMEVENT30, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + {CSR_MHPMEVENT31, + {"mhpmevent31", MISCREG_HPMEVENT31, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, - {CSR_TSELECT, {"tselect", MISCREG_TSELECT, rvTypeFlags(RV64, RV32)}}, - {CSR_TDATA1, {"tdata1", MISCREG_TDATA1, rvTypeFlags(RV64, RV32)}}, - {CSR_TDATA2, {"tdata2", MISCREG_TDATA2, rvTypeFlags(RV64, RV32)}}, - {CSR_TDATA3, {"tdata3", MISCREG_TDATA3, rvTypeFlags(RV64, RV32)}}, - {CSR_DCSR, {"dcsr", MISCREG_DCSR, rvTypeFlags(RV64, RV32)}}, - {CSR_DPC, {"dpc", MISCREG_DPC, rvTypeFlags(RV64, RV32)}}, - {CSR_DSCRATCH, {"dscratch", MISCREG_DSCRATCH, rvTypeFlags(RV64, RV32)}} + {CSR_TSELECT, + {"tselect", MISCREG_TSELECT, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_TDATA1, + {"tdata1", MISCREG_TDATA1, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_TDATA2, + {"tdata2", MISCREG_TDATA2, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_TDATA3, + {"tdata3", MISCREG_TDATA3, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_DCSR, + {"dcsr", MISCREG_DCSR, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_DPC, + {"dpc", MISCREG_DPC, rvTypeFlags(RV64, RV32), isaExtsFlags()}}, + {CSR_DSCRATCH, + {"dscratch", MISCREG_DSCRATCH, rvTypeFlags(RV64, RV32), + isaExtsFlags()}}, + + {CSR_VSTART, + {"vstart", MISCREG_VSTART, rvTypeFlags(RV64, RV32), + isaExtsFlags('v')}}, + {CSR_VXSAT, + {"vxsat", MISCREG_VXSAT, rvTypeFlags(RV64, RV32), isaExtsFlags('v')}}, + {CSR_VXRM, + {"vxrm", MISCREG_VXRM, rvTypeFlags(RV64, RV32), isaExtsFlags('v')}}, + {CSR_VCSR, + {"vcsr", MISCREG_VCSR, rvTypeFlags(RV64, RV32), isaExtsFlags('v')}}, + {CSR_VL, + {"vl", MISCREG_VL, rvTypeFlags(RV64, RV32), isaExtsFlags('v')}}, + {CSR_VTYPE, + {"vtype", MISCREG_VTYPE, rvTypeFlags(RV64, RV32), isaExtsFlags('v')}}, + {CSR_VLENB, + {"VLENB", MISCREG_VLENB, rvTypeFlags(RV64, RV32), isaExtsFlags('v')}} }; /** @@ -816,6 +1259,7 @@ const off_t SBE_OFFSET[enums::Num_RiscvType] = { const off_t SXL_OFFSET = 34; const off_t UXL_OFFSET = 32; const off_t FS_OFFSET = 13; +const off_t VS_OFFSET = 9; const off_t FRM_OFFSET = 5; const RegVal ISA_MXL_MASKS[enums::Num_RiscvType] = { @@ -853,7 +1297,7 @@ const RegVal STATUS_MPRV_MASK = 1ULL << 17; const RegVal STATUS_XS_MASK = 3ULL << 15; const RegVal STATUS_FS_MASK = 3ULL << FS_OFFSET; const RegVal STATUS_MPP_MASK = 3ULL << 11; -const RegVal STATUS_VS_MASK = 3ULL << 9; +const RegVal STATUS_VS_MASK = 3ULL << VS_OFFSET; const RegVal STATUS_SPP_MASK = 1ULL << 8; const RegVal STATUS_MPIE_MASK = 1ULL << 7; const RegVal STATUS_SPIE_MASK = 1ULL << 5; @@ -861,41 +1305,137 @@ const RegVal STATUS_UPIE_MASK = 1ULL << 4; const RegVal STATUS_MIE_MASK = 1ULL << 3; const RegVal STATUS_SIE_MASK = 1ULL << 1; const RegVal STATUS_UIE_MASK = 1ULL << 0; -const RegVal MSTATUS_MASKS[enums::Num_RiscvType] = { - [RV32] = STATUS_SD_MASKS[RV32] | STATUS_TSR_MASK | STATUS_TW_MASK | - STATUS_TVM_MASK | STATUS_MXR_MASK | STATUS_SUM_MASK | - STATUS_MPRV_MASK | STATUS_XS_MASK | STATUS_FS_MASK | - STATUS_VS_MASK | STATUS_MPP_MASK | STATUS_SPP_MASK | - STATUS_MPIE_MASK | STATUS_SPIE_MASK | STATUS_UPIE_MASK | - STATUS_MIE_MASK | STATUS_SIE_MASK | STATUS_UIE_MASK, - [RV64] = STATUS_SD_MASKS[RV64] | STATUS_MBE_MASK[RV64] | - STATUS_SBE_MASK[RV64] | STATUS_SXL_MASK | STATUS_UXL_MASK | - STATUS_TSR_MASK | STATUS_TW_MASK | STATUS_TVM_MASK | - STATUS_MXR_MASK | STATUS_SUM_MASK | STATUS_MPRV_MASK | - STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK| - STATUS_MPP_MASK | STATUS_SPP_MASK | STATUS_MPIE_MASK | - STATUS_SPIE_MASK | STATUS_UPIE_MASK | STATUS_MIE_MASK | - STATUS_SIE_MASK | STATUS_UIE_MASK, +const RegVal +MSTATUS_MASKS[enums::Num_RiscvType][enums::Num_PrivilegeModeSet] = { + [RV32] = { + [enums::M] = STATUS_SD_MASKS[RV32] | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_MPIE_MASK | STATUS_MIE_MASK, + [enums::MU] = STATUS_SD_MASKS[RV32] | STATUS_TW_MASK | + STATUS_MPRV_MASK | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_MPP_MASK | STATUS_MPIE_MASK | STATUS_MIE_MASK, + [enums::MNU] = STATUS_SD_MASKS[RV32] | STATUS_TW_MASK | + STATUS_MPRV_MASK | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_MPP_MASK | + STATUS_MPIE_MASK | STATUS_UPIE_MASK | + STATUS_MIE_MASK | STATUS_UIE_MASK, + [enums::MSU] = STATUS_SD_MASKS[RV32] | STATUS_TSR_MASK | + STATUS_TW_MASK | STATUS_TVM_MASK | STATUS_MXR_MASK | + STATUS_SUM_MASK | STATUS_MPRV_MASK | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_MPP_MASK | STATUS_SPP_MASK | + STATUS_MPIE_MASK | STATUS_SPIE_MASK | + STATUS_MIE_MASK | STATUS_SIE_MASK, + [enums::MNSU] = STATUS_SD_MASKS[RV32] | STATUS_TSR_MASK | + STATUS_TW_MASK | STATUS_TVM_MASK | STATUS_MXR_MASK | + STATUS_SUM_MASK | STATUS_MPRV_MASK | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_MPP_MASK | STATUS_SPP_MASK | + STATUS_MPIE_MASK | STATUS_SPIE_MASK | + STATUS_UPIE_MASK | STATUS_MIE_MASK | STATUS_SIE_MASK | + STATUS_UIE_MASK, + }, + [RV64] = { + [enums::M] = STATUS_SD_MASKS[RV64] | STATUS_MBE_MASK[RV64] | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_MPIE_MASK | STATUS_MIE_MASK, + [enums::MU] = STATUS_SD_MASKS[RV64] | STATUS_MBE_MASK[RV64] | + STATUS_UXL_MASK | STATUS_TW_MASK | STATUS_MPRV_MASK | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_MPP_MASK | STATUS_MPIE_MASK | STATUS_MIE_MASK, + [enums::MNU] = STATUS_SD_MASKS[RV64] | STATUS_MBE_MASK[RV64] | + STATUS_UXL_MASK | STATUS_TW_MASK | STATUS_MPRV_MASK | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_MPP_MASK | + STATUS_MPIE_MASK | STATUS_UPIE_MASK | + STATUS_MIE_MASK | STATUS_UIE_MASK, + [enums::MSU] = STATUS_SD_MASKS[RV64] | + STATUS_MBE_MASK[RV64] | STATUS_SBE_MASK[RV64] | + STATUS_SXL_MASK | STATUS_UXL_MASK | + STATUS_TSR_MASK | STATUS_TW_MASK | STATUS_TVM_MASK | + STATUS_MXR_MASK | STATUS_SUM_MASK | STATUS_MPRV_MASK | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_MPP_MASK | STATUS_SPP_MASK | + STATUS_MPIE_MASK | STATUS_SPIE_MASK | + STATUS_MIE_MASK | STATUS_SIE_MASK, + [enums::MNSU] = STATUS_SD_MASKS[RV64] | + STATUS_MBE_MASK[RV64] | STATUS_SBE_MASK[RV64] | + STATUS_SXL_MASK | STATUS_UXL_MASK | + STATUS_TSR_MASK | STATUS_TW_MASK | STATUS_TVM_MASK | + STATUS_MXR_MASK | STATUS_SUM_MASK | STATUS_MPRV_MASK | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_MPP_MASK | STATUS_SPP_MASK | + STATUS_MPIE_MASK | STATUS_SPIE_MASK | STATUS_UPIE_MASK | + STATUS_MIE_MASK | STATUS_SIE_MASK | STATUS_UIE_MASK, + }, }; // rv32 only -const RegVal MSTATUSH_MASKS = STATUS_MBE_MASK[RV32] | STATUS_SBE_MASK[RV32]; -const RegVal SSTATUS_MASKS[enums::Num_RiscvType] = { - [RV32] = STATUS_SD_MASKS[RV32] | STATUS_MXR_MASK | STATUS_SUM_MASK | - STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | - STATUS_SPP_MASK | STATUS_SPIE_MASK | STATUS_UPIE_MASK | - STATUS_SIE_MASK | STATUS_UIE_MASK, - [RV64] = STATUS_SD_MASKS[RV64] | STATUS_UXL_MASK | STATUS_MXR_MASK | - STATUS_SUM_MASK | STATUS_XS_MASK | STATUS_FS_MASK | - STATUS_VS_MASK | STATUS_SPP_MASK | STATUS_SPIE_MASK | - STATUS_UPIE_MASK | STATUS_SIE_MASK | STATUS_UIE_MASK, +const RegVal MSTATUSH_MASKS[enums::Num_PrivilegeModeSet] = { + [enums::M] = STATUS_MBE_MASK[RV32], + [enums::MU] = STATUS_MBE_MASK[RV32], + [enums::MNU] = STATUS_MBE_MASK[RV32], + [enums::MSU] = STATUS_MBE_MASK[RV32] | STATUS_SBE_MASK[RV32], + [enums::MNSU] = STATUS_MBE_MASK[RV32] | STATUS_SBE_MASK[RV32], }; -const RegVal USTATUS_MASKS[enums::Num_RiscvType] = { - [RV32] = STATUS_SD_MASKS[RV32] | STATUS_MXR_MASK | STATUS_SUM_MASK | - STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | - STATUS_UPIE_MASK | STATUS_UIE_MASK, - [RV64] = STATUS_SD_MASKS[RV64] | STATUS_MXR_MASK | STATUS_SUM_MASK | - STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | - STATUS_UPIE_MASK | STATUS_UIE_MASK, +const RegVal +SSTATUS_MASKS[enums::Num_RiscvType][enums::Num_PrivilegeModeSet] = { + [RV32] = { + [enums::M] = 0ULL, + [enums::MU] = 0ULL, + [enums::MNU] = 0ULL, + [enums::MSU] = STATUS_SD_MASKS[RV32] | STATUS_MXR_MASK | + STATUS_SUM_MASK | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_SPP_MASK | STATUS_SPIE_MASK | STATUS_SIE_MASK, + [enums::MNSU] = STATUS_SD_MASKS[RV32] | STATUS_MXR_MASK | + STATUS_SUM_MASK | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_SPP_MASK | STATUS_SPIE_MASK | STATUS_UPIE_MASK | + STATUS_SIE_MASK | STATUS_UIE_MASK, + }, + [RV64] = { + [enums::M] = 0ULL, + [enums::MU] = 0ULL, + [enums::MNU] = 0ULL, + [enums::MSU] = STATUS_SD_MASKS[RV64] | STATUS_UXL_MASK | + STATUS_MXR_MASK | STATUS_SUM_MASK | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_SPP_MASK | STATUS_SPIE_MASK | STATUS_SIE_MASK, + [enums::MNSU] = STATUS_SD_MASKS[RV64] | STATUS_UXL_MASK | + STATUS_MXR_MASK | STATUS_SUM_MASK | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_SPP_MASK | STATUS_SPIE_MASK | + STATUS_UPIE_MASK | STATUS_SIE_MASK | STATUS_UIE_MASK, + }, +}; +const RegVal +USTATUS_MASKS[enums::Num_RiscvType][enums::Num_PrivilegeModeSet] = { + [RV32] = { + [enums::M] = 0ULL, + [enums::MU] = 0ULL, + [enums::MNU] = STATUS_SD_MASKS[RV32] | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_UPIE_MASK | STATUS_UIE_MASK, + [enums::MSU] = 0ULL, + [enums::MNSU] = STATUS_SD_MASKS[RV32] | STATUS_MXR_MASK | + STATUS_SUM_MASK | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_UPIE_MASK | STATUS_UIE_MASK, + }, + [RV64] = { + [enums::M] = 0ULL, + [enums::MU] = 0ULL, + [enums::MNU] = STATUS_SD_MASKS[RV64] | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_UPIE_MASK | STATUS_UIE_MASK, + [enums::MSU] = 0ULL, + [enums::MNSU] = STATUS_SD_MASKS[RV64] | STATUS_MXR_MASK | + STATUS_SUM_MASK | + STATUS_XS_MASK | STATUS_FS_MASK | STATUS_VS_MASK | + STATUS_UPIE_MASK | STATUS_UIE_MASK, + }, }; const RegVal MEI_MASK = 1ULL << 11; @@ -907,13 +1447,33 @@ const RegVal UTI_MASK = 1ULL << 4; const RegVal MSI_MASK = 1ULL << 3; const RegVal SSI_MASK = 1ULL << 1; const RegVal USI_MASK = 1ULL << 0; -const RegVal MI_MASK = MEI_MASK | SEI_MASK | UEI_MASK | - MTI_MASK | STI_MASK | UTI_MASK | - MSI_MASK | SSI_MASK | USI_MASK; -const RegVal SI_MASK = SEI_MASK | UEI_MASK | - STI_MASK | UTI_MASK | - SSI_MASK | USI_MASK; -const RegVal UI_MASK = UEI_MASK | UTI_MASK | USI_MASK; +const RegVal MI_MASK[enums::Num_PrivilegeModeSet] = { + [enums::M] = MEI_MASK| MTI_MASK | MSI_MASK, + [enums::MU] = MEI_MASK| MTI_MASK | MSI_MASK, + [enums::MNU] = MEI_MASK | UEI_MASK | MTI_MASK | UTI_MASK | + MSI_MASK | USI_MASK, + [enums::MSU] = MEI_MASK | SEI_MASK | MTI_MASK | STI_MASK | + MSI_MASK | SSI_MASK, + [enums::MNSU] = MEI_MASK | SEI_MASK | UEI_MASK | + MTI_MASK | STI_MASK | UTI_MASK | + MSI_MASK | SSI_MASK | USI_MASK, +}; +const RegVal SI_MASK[enums::Num_PrivilegeModeSet] = { + [enums::M] = 0ULL, + [enums::MU] = 0ULL, + [enums::MNU] = UEI_MASK | UTI_MASK | USI_MASK, + [enums::MSU] = SEI_MASK | STI_MASK | SSI_MASK, + [enums::MNSU] = SEI_MASK | UEI_MASK | + STI_MASK | UTI_MASK | + SSI_MASK | USI_MASK, +}; +const RegVal UI_MASK[enums::Num_PrivilegeModeSet] = { + [enums::M] = 0ULL, + [enums::MU] = 0ULL, + [enums::MNU] = UEI_MASK | UTI_MASK | USI_MASK, + [enums::MSU] = 0ULL, + [enums::MNSU] = UEI_MASK | UTI_MASK | USI_MASK, +}; const RegVal FFLAGS_MASK = (1 << FRM_OFFSET) - 1; const RegVal FRM_MASK = 0x7; @@ -922,34 +1482,167 @@ const RegVal CAUSE_INTERRUPT_MASKS[enums::Num_RiscvType] = { [RV64] = (1ULL << 63), }; -const std::unordered_map CSRMasks[enums::Num_RiscvType] = { - [RV32] = {{CSR_USTATUS, USTATUS_MASKS[RV32]}, - {CSR_UIE, UI_MASK}, - {CSR_UIP, UI_MASK}, - {CSR_FFLAGS, FFLAGS_MASK}, - {CSR_FRM, FRM_MASK}, - {CSR_FCSR, FFLAGS_MASK | (FRM_MASK << FRM_OFFSET)}, - {CSR_SSTATUS, SSTATUS_MASKS[RV32]}, - {CSR_SIE, SI_MASK}, - {CSR_SIP, SI_MASK}, - {CSR_MSTATUS, MSTATUS_MASKS[RV32]}, - {CSR_MISA, MISA_MASKS[RV32]}, - {CSR_MIE, MI_MASK}, - {CSR_MSTATUSH, MSTATUSH_MASKS}, - {CSR_MIP, MI_MASK}}, - [RV64] = {{CSR_USTATUS, USTATUS_MASKS[RV64]}, - {CSR_UIE, UI_MASK}, - {CSR_UIP, UI_MASK}, - {CSR_FFLAGS, FFLAGS_MASK}, - {CSR_FRM, FRM_MASK}, - {CSR_FCSR, FFLAGS_MASK | (FRM_MASK << FRM_OFFSET)}, - {CSR_SSTATUS, SSTATUS_MASKS[RV64]}, - {CSR_SIE, SI_MASK}, - {CSR_SIP, SI_MASK}, - {CSR_MSTATUS, MSTATUS_MASKS[RV64]}, - {CSR_MISA, MISA_MASKS[RV64]}, - {CSR_MIE, MI_MASK}, - {CSR_MIP, MI_MASK}}, +const std::unordered_map +CSRMasks[enums::Num_RiscvType][enums::Num_PrivilegeModeSet] = { + [RV32] = { + [enums::M] = { + {CSR_USTATUS, USTATUS_MASKS[RV32][enums::M]}, + {CSR_UIE, UI_MASK[enums::M]}, + {CSR_UIP, UI_MASK[enums::M]}, + {CSR_FFLAGS, FFLAGS_MASK}, + {CSR_FRM, FRM_MASK}, + {CSR_FCSR, FFLAGS_MASK | (FRM_MASK << FRM_OFFSET)}, + {CSR_SSTATUS, SSTATUS_MASKS[RV32][enums::M]}, + {CSR_SIE, SI_MASK[enums::M]}, + {CSR_SIP, SI_MASK[enums::M]}, + {CSR_MSTATUS, MSTATUS_MASKS[RV32][enums::M]}, + {CSR_MISA, MISA_MASKS[RV32]}, + {CSR_MIE, MI_MASK[enums::M]}, + {CSR_MSTATUSH, MSTATUSH_MASKS[enums::M]}, + {CSR_MIP, MI_MASK[enums::M]}, + }, + [enums::MU] = { + {CSR_USTATUS, USTATUS_MASKS[RV32][enums::MU]}, + {CSR_UIE, UI_MASK[enums::MU]}, + {CSR_UIP, UI_MASK[enums::MU]}, + {CSR_FFLAGS, FFLAGS_MASK}, + {CSR_FRM, FRM_MASK}, + {CSR_FCSR, FFLAGS_MASK | (FRM_MASK << FRM_OFFSET)}, + {CSR_SSTATUS, SSTATUS_MASKS[RV32][enums::MU]}, + {CSR_SIE, SI_MASK[enums::MU]}, + {CSR_SIP, SI_MASK[enums::MU]}, + {CSR_MSTATUS, MSTATUS_MASKS[RV32][enums::MU]}, + {CSR_MISA, MISA_MASKS[RV32]}, + {CSR_MIE, MI_MASK[enums::MU]}, + {CSR_MSTATUSH, MSTATUSH_MASKS[enums::MU]}, + {CSR_MIP, MI_MASK[enums::MU]}, + }, + [enums::MNU] = { + {CSR_USTATUS, USTATUS_MASKS[RV32][enums::MNU]}, + {CSR_UIE, UI_MASK[enums::MNU]}, + {CSR_UIP, UI_MASK[enums::MNU]}, + {CSR_FFLAGS, FFLAGS_MASK}, + {CSR_FRM, FRM_MASK}, + {CSR_FCSR, FFLAGS_MASK | (FRM_MASK << FRM_OFFSET)}, + {CSR_SSTATUS, SSTATUS_MASKS[RV32][enums::MNU]}, + {CSR_SIE, SI_MASK[enums::MNU]}, + {CSR_SIP, SI_MASK[enums::MNU]}, + {CSR_MSTATUS, MSTATUS_MASKS[RV32][enums::MNU]}, + {CSR_MISA, MISA_MASKS[RV32]}, + {CSR_MIE, MI_MASK[enums::MNU]}, + {CSR_MSTATUSH, MSTATUSH_MASKS[enums::MNU]}, + {CSR_MIP, MI_MASK[enums::MNU]}, + }, + [enums::MSU] = { + {CSR_USTATUS, USTATUS_MASKS[RV32][enums::MSU]}, + {CSR_UIE, UI_MASK[enums::MSU]}, + {CSR_UIP, UI_MASK[enums::MSU]}, + {CSR_FFLAGS, FFLAGS_MASK}, + {CSR_FRM, FRM_MASK}, + {CSR_FCSR, FFLAGS_MASK | (FRM_MASK << FRM_OFFSET)}, + {CSR_SSTATUS, SSTATUS_MASKS[RV32][enums::MSU]}, + {CSR_SIE, SI_MASK[enums::MSU]}, + {CSR_SIP, SI_MASK[enums::MSU]}, + {CSR_MSTATUS, MSTATUS_MASKS[RV32][enums::MSU]}, + {CSR_MISA, MISA_MASKS[RV32]}, + {CSR_MIE, MI_MASK[enums::MSU]}, + {CSR_MSTATUSH, MSTATUSH_MASKS[enums::MSU]}, + {CSR_MIP, MI_MASK[enums::MSU]}, + }, + [enums::MNSU] = { + {CSR_USTATUS, USTATUS_MASKS[RV32][enums::MNSU]}, + {CSR_UIE, UI_MASK[enums::MNSU]}, + {CSR_UIP, UI_MASK[enums::MNSU]}, + {CSR_FFLAGS, FFLAGS_MASK}, + {CSR_FRM, FRM_MASK}, + {CSR_FCSR, FFLAGS_MASK | (FRM_MASK << FRM_OFFSET)}, + {CSR_SSTATUS, SSTATUS_MASKS[RV32][enums::MNSU]}, + {CSR_SIE, SI_MASK[enums::MNSU]}, + {CSR_SIP, SI_MASK[enums::MNSU]}, + {CSR_MSTATUS, MSTATUS_MASKS[RV32][enums::MNSU]}, + {CSR_MISA, MISA_MASKS[RV32]}, + {CSR_MIE, MI_MASK[enums::MNSU]}, + {CSR_MSTATUSH, MSTATUSH_MASKS[enums::MNSU]}, + {CSR_MIP, MI_MASK[enums::MNSU]}, + }, + }, + [RV64] = { + [enums::M] = { + {CSR_USTATUS, USTATUS_MASKS[RV64][enums::M]}, + {CSR_UIE, UI_MASK[enums::M]}, + {CSR_UIP, UI_MASK[enums::M]}, + {CSR_FFLAGS, FFLAGS_MASK}, + {CSR_FRM, FRM_MASK}, + {CSR_FCSR, FFLAGS_MASK | (FRM_MASK << FRM_OFFSET)}, + {CSR_SSTATUS, SSTATUS_MASKS[RV64][enums::M]}, + {CSR_SIE, SI_MASK[enums::M]}, + {CSR_SIP, SI_MASK[enums::M]}, + {CSR_MSTATUS, MSTATUS_MASKS[RV64][enums::M]}, + {CSR_MISA, MISA_MASKS[RV64]}, + {CSR_MIE, MI_MASK[enums::M]}, + {CSR_MIP, MI_MASK[enums::M]}, + }, + [enums::MU] = { + {CSR_USTATUS, USTATUS_MASKS[RV64][enums::MU]}, + {CSR_UIE, UI_MASK[enums::MU]}, + {CSR_UIP, UI_MASK[enums::MU]}, + {CSR_FFLAGS, FFLAGS_MASK}, + {CSR_FRM, FRM_MASK}, + {CSR_FCSR, FFLAGS_MASK | (FRM_MASK << FRM_OFFSET)}, + {CSR_SSTATUS, SSTATUS_MASKS[RV64][enums::MU]}, + {CSR_SIE, SI_MASK[enums::MU]}, + {CSR_SIP, SI_MASK[enums::MU]}, + {CSR_MSTATUS, MSTATUS_MASKS[RV64][enums::MU]}, + {CSR_MISA, MISA_MASKS[RV64]}, + {CSR_MIE, MI_MASK[enums::MU]}, + {CSR_MIP, MI_MASK[enums::MU]}, + }, + [enums::MNU] = { + {CSR_USTATUS, USTATUS_MASKS[RV64][enums::MNU]}, + {CSR_UIE, UI_MASK[enums::MNU]}, + {CSR_UIP, UI_MASK[enums::MNU]}, + {CSR_FFLAGS, FFLAGS_MASK}, + {CSR_FRM, FRM_MASK}, + {CSR_FCSR, FFLAGS_MASK | (FRM_MASK << FRM_OFFSET)}, + {CSR_SSTATUS, SSTATUS_MASKS[RV64][enums::MNU]}, + {CSR_SIE, SI_MASK[enums::MNU]}, + {CSR_SIP, SI_MASK[enums::MNU]}, + {CSR_MSTATUS, MSTATUS_MASKS[RV64][enums::MNU]}, + {CSR_MISA, MISA_MASKS[RV64]}, + {CSR_MIE, MI_MASK[enums::MNU]}, + {CSR_MIP, MI_MASK[enums::MNU]}, + }, + [enums::MSU] = { + {CSR_USTATUS, USTATUS_MASKS[RV64][enums::MSU]}, + {CSR_UIE, UI_MASK[enums::MSU]}, + {CSR_UIP, UI_MASK[enums::MSU]}, + {CSR_FFLAGS, FFLAGS_MASK}, + {CSR_FRM, FRM_MASK}, + {CSR_FCSR, FFLAGS_MASK | (FRM_MASK << FRM_OFFSET)}, + {CSR_SSTATUS, SSTATUS_MASKS[RV64][enums::MSU]}, + {CSR_SIE, SI_MASK[enums::MSU]}, + {CSR_SIP, SI_MASK[enums::MSU]}, + {CSR_MSTATUS, MSTATUS_MASKS[RV64][enums::MSU]}, + {CSR_MISA, MISA_MASKS[RV64]}, + {CSR_MIE, MI_MASK[enums::MSU]}, + {CSR_MIP, MI_MASK[enums::MSU]}, + }, + [enums::MNSU] = { + {CSR_USTATUS, USTATUS_MASKS[RV64][enums::MNSU]}, + {CSR_UIE, UI_MASK[enums::MNSU]}, + {CSR_UIP, UI_MASK[enums::MNSU]}, + {CSR_FFLAGS, FFLAGS_MASK}, + {CSR_FRM, FRM_MASK}, + {CSR_FCSR, FFLAGS_MASK | (FRM_MASK << FRM_OFFSET)}, + {CSR_SSTATUS, SSTATUS_MASKS[RV64][enums::MNSU]}, + {CSR_SIE, SI_MASK[enums::MNSU]}, + {CSR_SIP, SI_MASK[enums::MNSU]}, + {CSR_MSTATUS, MSTATUS_MASKS[RV64][enums::MNSU]}, + {CSR_MISA, MISA_MASKS[RV64]}, + {CSR_MIE, MI_MASK[enums::MNSU]}, + {CSR_MIP, MI_MASK[enums::MNSU]}, + }, + }, }; } // namespace RiscvISA diff --git a/src/arch/riscv/regs/vector.hh b/src/arch/riscv/regs/vector.hh new file mode 100644 index 0000000000..60c840395f --- /dev/null +++ b/src/arch/riscv/regs/vector.hh @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022 PLCT Lab + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef __ARCH_RISCV_REGS_VECTOR_HH__ +#define __ARCH_RISCV_REGS_VECTOR_HH__ + +#include +#include +#include + +#include "arch/generic/vec_pred_reg.hh" +#include "arch/generic/vec_reg.hh" +#include "arch/riscv/types.hh" +#include "base/bitunion.hh" +#include "cpu/reg_class.hh" +#include "debug/VecRegs.hh" + +namespace gem5 +{ + +namespace RiscvISA +{ + +using VecRegContainer = gem5::VecRegContainer; +using vreg_t = VecRegContainer; + + +const int NumVecStandardRegs = 32; +const int NumVecInternalRegs = 8; // Used by vector uop +const int NumVecRegs = NumVecStandardRegs + NumVecInternalRegs; + +const std::vector VecRegNames = { + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", + "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", + "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", + "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31", + "vtmp0", "vtmp1", "vtmp2", "vtmp3", "vtmp4", "vtmp5", "vtmp6", "vtmp7" +}; + +// vector index +const int VecMemInternalReg0 = NumVecStandardRegs; + +static inline TypedRegClassOps vecRegClassOps; + +inline constexpr RegClass vecRegClass = + RegClass(VecRegClass, VecRegClassName, NumVecRegs, debug::VecRegs). + ops(vecRegClassOps). + regType(); + +BitUnion64(VTYPE) + Bitfield<63> vill; + Bitfield<7, 0> vtype8; + Bitfield<7> vma; + Bitfield<6> vta; + Bitfield<5, 3> vsew; + Bitfield<2, 0> vlmul; +EndBitUnion(VTYPE) + +} // namespace RiscvISA +} // namespace gem5 + +#endif // __ARCH_RISCV_REGS_VECTOR_HH__ diff --git a/src/arch/riscv/remote_gdb.cc b/src/arch/riscv/remote_gdb.cc index 48ce1d5d3b..661fe2e2ca 100644 --- a/src/arch/riscv/remote_gdb.cc +++ b/src/arch/riscv/remote_gdb.cc @@ -167,13 +167,22 @@ getRvType(ThreadContext* tc) return isa->rvType(); } +static PrivilegeModeSet +getPrivilegeModeSet(ThreadContext* tc) +{ + auto isa = dynamic_cast(tc->getIsaPtr()); + panic_if(!isa, "Cannot derive rv_type from non-riscv isa"); + return isa->getPrivilegeModeSet(); +} + template static void setRegNoEffectWithMask( - ThreadContext *context, RiscvType type, CSRIndex idx, xint val) + ThreadContext *context, RiscvType type, PrivilegeModeSet pms, + CSRIndex idx, xint val) { RegVal oldVal, newVal; - RegVal mask = CSRMasks[type].at(idx); + RegVal mask = CSRMasks[type][pms].at(idx); oldVal = context->readMiscRegNoEffect(CSRData.at(idx).physIndex); newVal = (oldVal & ~mask) | (val & mask); context->setMiscRegNoEffect(CSRData.at(idx).physIndex, newVal); @@ -181,10 +190,12 @@ setRegNoEffectWithMask( template static void -setRegWithMask(ThreadContext *context, RiscvType type, CSRIndex idx, xint val) +setRegWithMask( + ThreadContext *context, RiscvType type, PrivilegeModeSet pms, + CSRIndex idx, xint val) { RegVal oldVal, newVal; - RegVal mask = CSRMasks[type].at(idx); + RegVal mask = CSRMasks[type][pms].at(idx); oldVal = context->readMiscReg(CSRData.at(idx).physIndex); newVal = (oldVal & ~mask) | (val & mask); context->setMiscReg(CSRData.at(idx).physIndex, newVal); @@ -207,7 +218,8 @@ RemoteGDB::acc(Addr va, size_t len) PrivilegeMode pmode = mmu->getMemPriv(context(), BaseMMU::Read); SATP satp = context()->readMiscReg(MISCREG_SATP); - if (pmode != PrivilegeMode::PRV_M && + MISA misa = tc->readMiscRegNoEffect(MISCREG_ISA); + if (misa.rvs && pmode != PrivilegeMode::PRV_M && satp.mode != AddrXlateMode::BARE) { Walker *walker = mmu->getDataWalker(); Fault fault = walker->startFunctional( @@ -225,7 +237,8 @@ void RemoteGDB::Riscv32GdbRegCache::getRegs(ThreadContext *context) { DPRINTF(GDBAcc, "getregs in remotegdb, size %lu\n", size()); - auto& RVxCSRMasks = CSRMasks[RV32]; + PrivilegeModeSet pms = getPrivilegeModeSet(context); + auto& RVxCSRMasks = CSRMasks[RV32][pms]; // General registers for (int i = 0; i < int_reg::NumArchRegs; i++) { @@ -339,6 +352,7 @@ void RemoteGDB::Riscv32GdbRegCache::setRegs(ThreadContext *context) const { DPRINTF(GDBAcc, "setregs in remotegdb \n"); + PrivilegeModeSet pms = getPrivilegeModeSet(context); for (int i = 0; i < int_reg::NumArchRegs; i++) context->setReg(intRegClass[i], r.gpr[i]); context->pcState(r.pc); @@ -347,16 +361,16 @@ RemoteGDB::Riscv32GdbRegCache::setRegs(ThreadContext *context) const for (int i = 0; i < float_reg::NumRegs; i++) context->setReg(floatRegClass[i], r.fpu[i]); - setRegNoEffectWithMask(context, RV32, CSR_FFLAGS, r.fflags); - setRegNoEffectWithMask(context, RV32, CSR_FRM, r.frm); - setRegNoEffectWithMask(context, RV32, CSR_FCSR, r.fcsr); + setRegNoEffectWithMask(context, RV32, pms, CSR_FFLAGS, r.fflags); + setRegNoEffectWithMask(context, RV32, pms, CSR_FRM, r.frm); + setRegNoEffectWithMask(context, RV32, pms, CSR_FCSR, r.fcsr); // TODO: implement CSR counter registers for mcycle(h), minstret(h) // U mode CSR - setRegNoEffectWithMask(context, RV32, CSR_USTATUS, r.ustatus); - setRegWithMask(context, RV32, CSR_UIE, r.uie); - setRegWithMask(context, RV32, CSR_UIP, r.uip); + setRegNoEffectWithMask(context, RV32, pms, CSR_USTATUS, r.ustatus); + setRegWithMask(context, RV32, pms, CSR_UIE, r.uie); + setRegWithMask(context, RV32, pms, CSR_UIP, r.uip); context->setMiscRegNoEffect( CSRData.at(CSR_UTVEC).physIndex, r.utvec); context->setMiscRegNoEffect( @@ -369,9 +383,9 @@ RemoteGDB::Riscv32GdbRegCache::setRegs(ThreadContext *context) const CSRData.at(CSR_UTVAL).physIndex, r.utval); // S mode CSR - setRegNoEffectWithMask(context, RV32, CSR_SSTATUS, r.sstatus); - setRegWithMask(context, RV32, CSR_SIE, r.sie); - setRegWithMask(context, RV32, CSR_SIP, r.sip); + setRegNoEffectWithMask(context, RV32, pms, CSR_SSTATUS, r.sstatus); + setRegWithMask(context, RV32, pms, CSR_SIE, r.sie); + setRegWithMask(context, RV32, pms, CSR_SIP, r.sip); context->setMiscRegNoEffect( CSRData.at(CSR_SEDELEG).physIndex, r.sedeleg); context->setMiscRegNoEffect( @@ -392,10 +406,10 @@ RemoteGDB::Riscv32GdbRegCache::setRegs(ThreadContext *context) const CSRData.at(CSR_SATP).physIndex, r.satp); // M mode CSR - setRegNoEffectWithMask(context, RV32, CSR_MSTATUS, r.mstatus); - setRegNoEffectWithMask(context, RV32, CSR_MISA, r.misa); - setRegWithMask(context, RV32, CSR_MIE, r.mie); - setRegWithMask(context, RV32, CSR_MIP, r.mip); + setRegNoEffectWithMask(context, RV32, pms, CSR_MSTATUS, r.mstatus); + setRegNoEffectWithMask(context, RV32, pms, CSR_MISA, r.misa); + setRegWithMask(context, RV32, pms, CSR_MIE, r.mie); + setRegWithMask(context, RV32, pms, CSR_MIP, r.mip); context->setMiscRegNoEffect( CSRData.at(CSR_MEDELEG).physIndex, r.medeleg); context->setMiscRegNoEffect( @@ -420,7 +434,8 @@ void RemoteGDB::Riscv64GdbRegCache::getRegs(ThreadContext *context) { DPRINTF(GDBAcc, "getregs in remotegdb, size %lu\n", size()); - auto& RVxCSRMasks = CSRMasks[RV64]; + PrivilegeModeSet pms = getPrivilegeModeSet(context); + auto& RVxCSRMasks = CSRMasks[RV64][pms]; // General registers for (int i = 0; i < int_reg::NumArchRegs; i++) { @@ -528,6 +543,7 @@ void RemoteGDB::Riscv64GdbRegCache::setRegs(ThreadContext *context) const { DPRINTF(GDBAcc, "setregs in remotegdb \n"); + PrivilegeModeSet pms = getPrivilegeModeSet(context); for (int i = 0; i < int_reg::NumArchRegs; i++) context->setReg(intRegClass[i], r.gpr[i]); context->pcState(r.pc); @@ -536,16 +552,16 @@ RemoteGDB::Riscv64GdbRegCache::setRegs(ThreadContext *context) const for (int i = 0; i < float_reg::NumRegs; i++) context->setReg(floatRegClass[i], r.fpu[i]); - setRegNoEffectWithMask(context, RV64, CSR_FFLAGS, r.fflags); - setRegNoEffectWithMask(context, RV64, CSR_FRM, r.frm); - setRegNoEffectWithMask(context, RV64, CSR_FCSR, r.fcsr); + setRegNoEffectWithMask(context, RV64, pms, CSR_FFLAGS, r.fflags); + setRegNoEffectWithMask(context, RV64, pms, CSR_FRM, r.frm); + setRegNoEffectWithMask(context, RV64, pms, CSR_FCSR, r.fcsr); // TODO: implement CSR counter registers for mcycle, minstret // U mode CSR - setRegNoEffectWithMask(context, RV64, CSR_USTATUS, r.ustatus); - setRegWithMask(context, RV64, CSR_UIE, r.uie); - setRegWithMask(context, RV64, CSR_UIP, r.uip); + setRegNoEffectWithMask(context, RV64, pms, CSR_USTATUS, r.ustatus); + setRegWithMask(context, RV64, pms, CSR_UIE, r.uie); + setRegWithMask(context, RV64, pms, CSR_UIP, r.uip); context->setMiscRegNoEffect( CSRData.at(CSR_UTVEC).physIndex, r.utvec); context->setMiscRegNoEffect( @@ -558,9 +574,10 @@ RemoteGDB::Riscv64GdbRegCache::setRegs(ThreadContext *context) const CSRData.at(CSR_UTVAL).physIndex, r.utval); // S mode CSR - setRegNoEffectWithMask(context, RV64, CSR_SSTATUS, r.sstatus); - setRegWithMask(context, RV64, CSR_SIE, r.sie); - setRegWithMask(context, RV64, CSR_SIP, r.sip); + setRegNoEffectWithMask( + context, RV64, pms, CSR_SSTATUS, r.sstatus); + setRegWithMask(context, RV64, pms, CSR_SIE, r.sie); + setRegWithMask(context, RV64, pms, CSR_SIP, r.sip); context->setMiscRegNoEffect( CSRData.at(CSR_SEDELEG).physIndex, r.sedeleg); context->setMiscRegNoEffect( @@ -581,10 +598,11 @@ RemoteGDB::Riscv64GdbRegCache::setRegs(ThreadContext *context) const CSRData.at(CSR_SATP).physIndex, r.satp); // M mode CSR - setRegNoEffectWithMask(context, RV64, CSR_MSTATUS, r.mstatus); - setRegNoEffectWithMask(context, RV64, CSR_MISA, r.misa); - setRegWithMask(context, RV64, CSR_MIE, r.mie); - setRegWithMask(context, RV64, CSR_MIP, r.mip); + setRegNoEffectWithMask( + context, RV64, pms, CSR_MSTATUS, r.mstatus); + setRegNoEffectWithMask(context, RV64, pms, CSR_MISA, r.misa); + setRegWithMask(context, RV64, pms, CSR_MIE, r.mie); + setRegWithMask(context, RV64, pms, CSR_MIP, r.mip); context->setMiscRegNoEffect( CSRData.at(CSR_MEDELEG).physIndex, r.medeleg); context->setMiscRegNoEffect( diff --git a/src/arch/riscv/tlb.cc b/src/arch/riscv/tlb.cc index ac8c8ae029..679806ec8c 100644 --- a/src/arch/riscv/tlb.cc +++ b/src/arch/riscv/tlb.cc @@ -292,7 +292,7 @@ TLB::doTranslate(const RequestPtr &req, ThreadContext *tc, delayed = true; return fault; } - e = lookup(vaddr, satp.asid, mode, false); + e = lookup(vaddr, satp.asid, mode, true); assert(e != nullptr); } @@ -341,9 +341,12 @@ TLB::translate(const RequestPtr &req, ThreadContext *tc, if (FullSystem) { PrivilegeMode pmode = getMemPriv(tc, mode); + MISA misa = tc->readMiscRegNoEffect(MISCREG_ISA); SATP satp = tc->readMiscReg(MISCREG_SATP); - if (pmode == PrivilegeMode::PRV_M || satp.mode == AddrXlateMode::BARE) + if (!misa.rvs || pmode == PrivilegeMode::PRV_M || + satp.mode == AddrXlateMode::BARE) { req->setFlags(Request::PHYSICAL); + } Fault fault; if (req->getFlags() & Request::PHYSICAL) { @@ -434,8 +437,9 @@ TLB::translateFunctional(const RequestPtr &req, ThreadContext *tc, MMU *mmu = static_cast(tc->getMMUPtr()); PrivilegeMode pmode = mmu->getMemPriv(tc, mode); + MISA misa = tc->readMiscRegNoEffect(MISCREG_ISA); SATP satp = tc->readMiscReg(MISCREG_SATP); - if (pmode != PrivilegeMode::PRV_M && + if (misa.rvs && pmode != PrivilegeMode::PRV_M && satp.mode != AddrXlateMode::BARE) { Walker *walker = mmu->getDataWalker(); unsigned logBytes; diff --git a/src/arch/riscv/types.hh b/src/arch/riscv/types.hh index 1d501dc05f..c7edffc2f7 100644 --- a/src/arch/riscv/types.hh +++ b/src/arch/riscv/types.hh @@ -42,7 +42,6 @@ #ifndef __ARCH_RISCV_TYPES_HH__ #define __ARCH_RISCV_TYPES_HH__ -#include "arch/riscv/pcstate.hh" #include "base/bitunion.hh" namespace gem5 @@ -178,6 +177,10 @@ BitUnion64(ExtMachInst) EndBitUnion(ExtMachInst) +constexpr unsigned MaxVecLenInBits = 65536; +constexpr unsigned MaxVecLenInBytes = MaxVecLenInBits >> 3; + + } // namespace RiscvISA } // namespace gem5 diff --git a/src/arch/riscv/utility.hh b/src/arch/riscv/utility.hh index 5fccc84c79..bac499e523 100644 --- a/src/arch/riscv/utility.hh +++ b/src/arch/riscv/utility.hh @@ -51,6 +51,7 @@ #include "arch/riscv/regs/float.hh" #include "arch/riscv/regs/int.hh" +#include "arch/riscv/regs/vector.hh" #include "base/types.hh" #include "cpu/reg_class.hh" #include "cpu/static_inst.hh" @@ -130,7 +131,14 @@ registerName(RegId reg) return str.str(); } return float_reg::RegNames[reg.index()]; - } else { + } else if (reg.is(VecRegClass)) { + if (reg.index() >= NumVecRegs) { + std::stringstream str; + str << "?? (v" << reg.index() << ')'; + return str.str(); + } + return VecRegNames[reg.index()]; + } else { /* It must be an InvalidRegClass, in RISC-V we should treat it as a * zero register for the disassembler to work correctly. */ @@ -233,6 +241,543 @@ remu(T rs1, T rs2) return (rs2 == 0) ? rs1 : rs1 % rs2; } +// Vector extension functions +inline uint64_t +vtype_SEW(const uint64_t vtype) +{ + return 8 << bits(vtype, 5, 3); +} + +/* +* Encode LMUL to lmul as follows: +* LMUL vlmul lmul +* 1 000 0 +* 2 001 1 +* 4 010 2 +* 8 011 3 +* - 100 - +* 1/8 101 -3 +* 1/4 110 -2 +* 1/2 111 -1 +* +* then, we can calculate VLMAX = vlen >> (vsew + 3 - lmul) +* e.g. vlen = 256 bits, SEW = 16, LMUL = 1/8 +* => VLMAX = vlen >> (1 + 3 - (-3)) +* = 256 >> 7 +* = 2 +* Ref: https://github.com/qemu/qemu/blob/5e9d14f2/target/riscv/cpu.h +*/ +inline uint64_t +vtype_VLMAX(const uint64_t vtype, const uint64_t vlen, + const bool per_reg = false) +{ + int64_t lmul = (int64_t)sext<3>(bits(vtype, 2, 0)); + lmul = per_reg ? std::min(0, lmul) : lmul; + int64_t vsew = bits(vtype, 5, 3); + return vlen >> (vsew + 3 - lmul); +} + +inline int64_t +vtype_vlmul(const uint64_t vtype) +{ + return (int64_t)sext<3>(bits(vtype, 2, 0)); +} + +inline uint64_t +vtype_regs_per_group(const uint64_t vtype) +{ + int64_t lmul = (int64_t)sext<3>(bits(vtype, 2, 0)); + return 1 << std::max(0, lmul); +} + +inline void +vtype_set_vill(uint64_t& vtype) +{ + vtype = (uint64_t)0 ^ (1UL << (sizeof(RegVal) * 8 - 1)); +} + +inline uint64_t +width_EEW(uint64_t width) +{ + switch (width) { + case 0b000: return 8; + case 0b101: return 16; + case 0b110: return 32; + case 0b111: return 64; + default: GEM5_UNREACHABLE; + } +} + +/* + * Spec Section 4.5 + * Ref: + * https://github.com/qemu/qemu/blob/c7d773ae/target/riscv/vector_helper.c +*/ +template +inline int +elem_mask(const T* vs, const int index) +{ + static_assert(std::is_integral_v); + int idx = index / (sizeof(T)*8); + int pos = index % (sizeof(T)*8); + return (vs[idx] >> pos) & 1; +} + +template struct double_width; +template<> struct double_width { using type = uint16_t;}; +template<> struct double_width { using type = uint32_t;}; +template<> struct double_width { using type = uint64_t;}; +template<> struct double_width { using type = int16_t; }; +template<> struct double_width { using type = int32_t; }; +template<> struct double_width { using type = int64_t; }; +template<> struct double_width { using type = float64_t;}; + +template struct double_widthf; +template<> struct double_widthf { using type = float64_t;}; +template<> struct double_widthf { using type = float64_t;}; + +template auto +ftype(IntType a) -> FloatType +{ + if constexpr(std::is_same_v) + return f32(a); + else if constexpr(std::is_same_v) + return f64(a); + GEM5_UNREACHABLE; +} + +// TODO: Consolidate ftype_freg(freg_t a) and ftype(IntType a) into a +// single function +template auto +ftype_freg(freg_t a) -> FloatType +{ + if constexpr(std::is_same_v) + return f32(a); + else if constexpr(std::is_same_v) + return f64(a); + GEM5_UNREACHABLE; +} + +template FloatType +fadd(FloatType a, FloatType b) +{ + if constexpr(std::is_same_v) + return f32_add(a, b); + else if constexpr(std::is_same_v) + return f64_add(a, b); + GEM5_UNREACHABLE; +} + +template FloatType +fsub(FloatType a, FloatType b) +{ + if constexpr(std::is_same_v) + return f32_sub(a, b); + else if constexpr(std::is_same_v) + return f64_sub(a, b); + GEM5_UNREACHABLE; +} + +template FloatType +fmin(FloatType a, FloatType b) +{ + if constexpr(std::is_same_v) + return f32_min(a, b); + else if constexpr(std::is_same_v) + return f64_min(a, b); + GEM5_UNREACHABLE; +} + +template FloatType +fmax(FloatType a, FloatType b) +{ + if constexpr(std::is_same_v) + return f32_max(a, b); + else if constexpr(std::is_same_v) + return f64_max(a, b); + GEM5_UNREACHABLE; +} + +template FloatType +fdiv(FloatType a, FloatType b) +{ + if constexpr(std::is_same_v) + return f32_div(a, b); + else if constexpr(std::is_same_v) + return f64_div(a, b); + GEM5_UNREACHABLE; +} + +template FloatType +fmul(FloatType a, FloatType b) +{ + if constexpr(std::is_same_v) + return f32_mul(a, b); + else if constexpr(std::is_same_v) + return f64_mul(a, b); + GEM5_UNREACHABLE; +} + +template FloatType +fsqrt(FloatType a) +{ + if constexpr(std::is_same_v) + return f32_sqrt(a); + else if constexpr(std::is_same_v) + return f64_sqrt(a); + GEM5_UNREACHABLE; +} + +template FloatType +frsqrte7(FloatType a) +{ + if constexpr(std::is_same_v) + return f32_rsqrte7(a); + else if constexpr(std::is_same_v) + return f64_rsqrte7(a); + GEM5_UNREACHABLE; +} + +template FloatType +frecip7(FloatType a) +{ + if constexpr(std::is_same_v) + return f32_recip7(a); + else if constexpr(std::is_same_v) + return f64_recip7(a); + GEM5_UNREACHABLE; +} + +template FloatType +fclassify(FloatType a) +{ + if constexpr(std::is_same_v) + return f32(f32_classify(a)); + else if constexpr(std::is_same_v) + return f64(f64_classify(a)); + GEM5_UNREACHABLE; +} + +template FloatType +fsgnj(FloatType a, FloatType b, bool n, bool x) +{ + if constexpr(std::is_same_v) + return fsgnj32(a, b, n, x); + else if constexpr(std::is_same_v) + return fsgnj64(a, b, n, x); + GEM5_UNREACHABLE; +} + +template bool +fle(FloatType a, FloatType b) +{ + if constexpr(std::is_same_v) + return f32_le(a, b); + else if constexpr(std::is_same_v) + return f64_le(a, b); + GEM5_UNREACHABLE; +} + +template bool +feq(FloatType a, FloatType b) +{ + if constexpr(std::is_same_v) + return f32_eq(a, b); + else if constexpr(std::is_same_v) + return f64_eq(a, b); + GEM5_UNREACHABLE; +} + +template bool +flt(FloatType a, FloatType b) +{ + if constexpr(std::is_same_v) + return f32_lt(a, b); + else if constexpr(std::is_same_v) + return f64_lt(a, b); + GEM5_UNREACHABLE; +} + +template FloatType +fmadd(FloatType a, FloatType b, FloatType c) +{ + if constexpr(std::is_same_v) + return f32_mulAdd(a, b, c); + else if constexpr(std::is_same_v) + return f64_mulAdd(a, b, c); + GEM5_UNREACHABLE; +} + +template FloatType +fneg(FloatType a) +{ + if constexpr(std::is_same_v) + return f32(a.v ^ uint32_t(mask(31, 31))); + else if constexpr(std::is_same_v) + return f64(a.v ^ mask(63, 63)); + GEM5_UNREACHABLE; +} + +template::type> WFT +fwiden(FT a) +{ + if constexpr(std::is_same_v) + return f32_to_f64(a); + GEM5_UNREACHABLE; +} + +template IntType +f_to_ui(FloatType a, uint_fast8_t mode) +{ + if constexpr(std::is_same_v) + return f32_to_ui32(a, mode, true); + else if constexpr(std::is_same_v) + return f64_to_ui64(a, mode, true); + GEM5_UNREACHABLE; +} + +template< + typename FloatType, + typename IntType = decltype(double_width::type::v) +> IntType +f_to_wui(FloatType a, uint_fast8_t mode) +{ + if constexpr(std::is_same_v) + return f32_to_ui64(a, mode, true); + GEM5_UNREACHABLE; +} + +template< + typename IntType, + typename FloatType = typename double_widthf::type +> IntType +f_to_nui(FloatType a, uint_fast8_t mode) +{ + if constexpr(std::is_same_v) + return f64_to_ui32(a, mode, true); + GEM5_UNREACHABLE; +} + +template IntType +f_to_i(FloatType a, uint_fast8_t mode) +{ + if constexpr(std::is_same_v) + return (uint32_t)f32_to_i32(a, mode, true); + else if constexpr(std::is_same_v) + return (uint64_t)f64_to_i64(a, mode, true); + GEM5_UNREACHABLE; +} + +template< + typename FloatType, + typename IntType = decltype(double_width::type::v) +> IntType +f_to_wi(FloatType a, uint_fast8_t mode) +{ + if constexpr(std::is_same_v) + return (uint64_t)f32_to_i64(a, mode, true); + GEM5_UNREACHABLE; +} + +template< + typename IntType, + typename FloatType = typename double_widthf::type +> IntType +f_to_ni(FloatType a, uint_fast8_t mode) +{ + if constexpr(std::is_same_v) + return (uint32_t)f64_to_i32(a, mode, true); + GEM5_UNREACHABLE; +} + +template +FloatType +ui_to_f(IntType a) +{ + if constexpr(std::is_same_v) + return ui32_to_f32(a); + else if constexpr(std::is_same_v) + return ui64_to_f64(a); + GEM5_UNREACHABLE; +} + +template< + typename IntType, + typename FloatType = typename double_widthf::type +> FloatType +ui_to_wf(IntType a) +{ + if constexpr(std::is_same_v) + return ui32_to_f64(a); + GEM5_UNREACHABLE; +} + +template< + typename FloatType, + typename IntType = decltype(double_width::type::v) +> FloatType +ui_to_nf(IntType a) +{ + if constexpr(std::is_same_v) + return ui64_to_f32(a); + GEM5_UNREACHABLE; +} + +template +FloatType +i_to_f(IntType a) +{ + if constexpr(std::is_same_v) + return i32_to_f32((int32_t)a); + else if constexpr(std::is_same_v) + return i64_to_f64((int64_t)a); + GEM5_UNREACHABLE; +} + +template< + typename IntType, + typename FloatType = typename double_widthf::type +> FloatType +i_to_wf(IntType a) +{ + if constexpr(std::is_same_v) + return i32_to_f64((int32_t)a); + GEM5_UNREACHABLE; +} + +template< + typename FloatType, + typename IntType = std::make_signed_t< + decltype(double_width::type::v) + > +> FloatType +i_to_nf(IntType a) +{ + if constexpr(std::is_same_v) + return i64_to_f32(a); + GEM5_UNREACHABLE; +} + +template< + typename FloatType, + typename FloatWType = typename double_width::type +> FloatWType +f_to_wf(FloatType a) +{ + if constexpr(std::is_same_v) + return f32_to_f64(a); + GEM5_UNREACHABLE; +} + +template< + typename FloatNType, + typename FloatType = typename double_width::type +> FloatNType +f_to_nf(FloatType a) +{ + if constexpr(std::is_same_v) + return f64_to_f32(a); + GEM5_UNREACHABLE; +} + +//ref: https://locklessinc.com/articles/sat_arithmetic/ +template T +sat_add(T x, T y, bool* sat) +{ + using UT = std::make_unsigned_t; + UT ux = x; + UT uy = y; + UT res = ux + uy; + + int sh = sizeof(T) * 8 - 1; + + ux = (ux >> sh) + (((UT)0x1 << sh) - 1); + + if ((T) ((ux ^ uy) | ~(uy ^ res)) >= 0) { + res = ux; + *sat = true; + } + return res; +} + +template T +sat_sub(T x, T y, bool* sat) +{ + using UT = std::make_unsigned_t; + UT ux = x; + UT uy = y; + UT res = ux - uy; + + int sh = sizeof(T) * 8 - 1; + + ux = (ux >> sh) + (((UT)0x1 << sh) - 1); + + if ((T) ((ux ^ uy) & (ux ^ res)) < 0) { + res = ux; + *sat = true; + } + return res; +} + +template T +sat_addu(T x, T y, bool* sat) +{ + T res = x + y; + + bool t = res < x; + if (false == *sat){ + *sat = t; + } + res |= -(res < x); + + return res; +} + +template T +sat_subu(T x, T y, bool* sat) +{ + T res = x - y; + + bool t = !(res <= x); + if (false == *sat){ + *sat = t; + } + + res &= -(res <= x); + + return res; +} + +/** + * Ref: + * https://github.com/riscv-software-src/riscv-isa-sim + */ +template T +int_rounding(T result, uint8_t xrm, unsigned gb) { + const uint64_t lsb = 1UL << gb; + const uint64_t lsb_half = lsb >> 1; + switch (xrm) { + case 0 /* RNU */: + result += lsb_half; + break; + case 1 /* RNE */: + if ((result & lsb_half) && + ((result & (lsb_half - 1)) || (result & lsb))) + result += lsb; + break; + case 2 /* RDN */: + break; + case 3 /* ROD */: + if (result & (lsb - 1)) + result |= lsb; + break; + default: + panic("Invalid xrm value %d", (int)xrm); + } + + return result; +} + } // namespace RiscvISA } // namespace gem5 diff --git a/src/arch/sparc/Kconfig b/src/arch/sparc/Kconfig new file mode 100644 index 0000000000..ec2aef7326 --- /dev/null +++ b/src/arch/sparc/Kconfig @@ -0,0 +1,27 @@ +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +config USE_SPARC_ISA + bool "SPARC ISA support" diff --git a/src/arch/sparc/SConscript b/src/arch/sparc/SConscript index a721c4ae56..9291c8a741 100644 --- a/src/arch/sparc/SConscript +++ b/src/arch/sparc/SConscript @@ -28,7 +28,7 @@ Import('*') -if env['USE_SPARC_ISA']: +if env['CONF']['USE_SPARC_ISA']: env.TagImplies('sparc isa', 'gem5 lib') Source('asi.cc', tags='sparc isa') diff --git a/src/arch/sparc/SConsopts b/src/arch/sparc/SConsopts deleted file mode 100644 index 917485af9c..0000000000 --- a/src/arch/sparc/SConsopts +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2021 Google, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Import('*') -sticky_vars.Add(BoolVariable('USE_SPARC_ISA', 'Enable SPARC ISA support', - False)) diff --git a/src/arch/sparc/SparcCPU.py b/src/arch/sparc/SparcCPU.py index 44d9ceed08..fdc17729e2 100644 --- a/src/arch/sparc/SparcCPU.py +++ b/src/arch/sparc/SparcCPU.py @@ -24,14 +24,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.objects.BaseAtomicSimpleCPU import BaseAtomicSimpleCPU -from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU -from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU -from m5.objects.BaseO3CPU import BaseO3CPU from m5.objects.BaseMinorCPU import BaseMinorCPU +from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU +from m5.objects.BaseO3CPU import BaseO3CPU +from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU from m5.objects.SparcDecoder import SparcDecoder -from m5.objects.SparcMMU import SparcMMU from m5.objects.SparcInterrupts import SparcInterrupts from m5.objects.SparcISA import SparcISA +from m5.objects.SparcMMU import SparcMMU class SparcCPU: diff --git a/src/arch/sparc/SparcFsWorkload.py b/src/arch/sparc/SparcFsWorkload.py index ba70dcfa59..89b17a82f9 100644 --- a/src/arch/sparc/SparcFsWorkload.py +++ b/src/arch/sparc/SparcFsWorkload.py @@ -24,9 +24,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * - from m5.objects.Workload import Workload +from m5.params import * class SparcFsWorkload(Workload): diff --git a/src/arch/sparc/SparcMMU.py b/src/arch/sparc/SparcMMU.py index 1202594fc6..a00be60685 100644 --- a/src/arch/sparc/SparcMMU.py +++ b/src/arch/sparc/SparcMMU.py @@ -35,10 +35,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * from m5.objects.BaseMMU import BaseMMU from m5.objects.SparcTLB import SparcTLB +from m5.params import * +from m5.SimObject import SimObject class SparcMMU(BaseMMU): diff --git a/src/arch/sparc/SparcNativeTrace.py b/src/arch/sparc/SparcNativeTrace.py index 0a93126f2c..9b215e19c6 100644 --- a/src/arch/sparc/SparcNativeTrace.py +++ b/src/arch/sparc/SparcNativeTrace.py @@ -24,10 +24,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * - from m5.objects.CPUTracers import NativeTrace +from m5.params import * +from m5.SimObject import SimObject class SparcNativeTrace(NativeTrace): diff --git a/src/arch/sparc/SparcSeWorkload.py b/src/arch/sparc/SparcSeWorkload.py index 3dbd341801..060bf21005 100644 --- a/src/arch/sparc/SparcSeWorkload.py +++ b/src/arch/sparc/SparcSeWorkload.py @@ -23,9 +23,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * - from m5.objects.Workload import SEWorkload +from m5.params import * class SparcSEWorkload(SEWorkload): diff --git a/src/arch/sparc/SparcTLB.py b/src/arch/sparc/SparcTLB.py index e834d62000..6b43d69cfc 100644 --- a/src/arch/sparc/SparcTLB.py +++ b/src/arch/sparc/SparcTLB.py @@ -24,10 +24,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * - from m5.objects.BaseTLB import BaseTLB +from m5.params import * +from m5.SimObject import SimObject class SparcTLB(BaseTLB): diff --git a/src/arch/sparc/insts/branch.cc b/src/arch/sparc/insts/branch.cc index 84861cc2c3..7109f9a95d 100644 --- a/src/arch/sparc/insts/branch.cc +++ b/src/arch/sparc/insts/branch.cc @@ -88,9 +88,9 @@ BranchDisp::generateDisassembly( loader::SymbolTable::const_iterator it; if (symtab && (it = symtab->findNearest(target)) != symtab->end()) { - ccprintf(response, " <%s", it->name); - if (it->address != target) - ccprintf(response, "+%d>", target - it->address); + ccprintf(response, " <%s", it->name()); + if (it->address() != target) + ccprintf(response, "+%d>", target - it->address()); else ccprintf(response, ">"); } diff --git a/src/arch/x86/Kconfig b/src/arch/x86/Kconfig new file mode 100644 index 0000000000..ba10fad55b --- /dev/null +++ b/src/arch/x86/Kconfig @@ -0,0 +1,27 @@ +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +config USE_X86_ISA + bool "X86 ISA support" diff --git a/src/arch/x86/SConscript b/src/arch/x86/SConscript index 97c34f51c3..cbf5356761 100644 --- a/src/arch/x86/SConscript +++ b/src/arch/x86/SConscript @@ -40,7 +40,7 @@ Import('*') -if env['USE_X86_ISA']: +if env['CONF']['USE_X86_ISA']: env.TagImplies('x86 isa', 'gem5 lib') Source('cpuid.cc', tags='x86 isa') diff --git a/src/arch/x86/X86CPU.py b/src/arch/x86/X86CPU.py index bd39f6d0f5..ac3398bb6d 100644 --- a/src/arch/x86/X86CPU.py +++ b/src/arch/x86/X86CPU.py @@ -23,19 +23,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.proxy import Self - from m5.objects.BaseAtomicSimpleCPU import BaseAtomicSimpleCPU -from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU -from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU -from m5.objects.BaseO3CPU import BaseO3CPU from m5.objects.BaseMinorCPU import BaseMinorCPU +from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU +from m5.objects.BaseO3CPU import BaseO3CPU +from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU from m5.objects.FuncUnit import * from m5.objects.FUPool import * from m5.objects.X86Decoder import X86Decoder -from m5.objects.X86MMU import X86MMU -from m5.objects.X86LocalApic import X86LocalApic from m5.objects.X86ISA import X86ISA +from m5.objects.X86LocalApic import X86LocalApic +from m5.objects.X86MMU import X86MMU +from m5.proxy import Self class X86CPU: diff --git a/src/arch/x86/X86FsWorkload.py b/src/arch/x86/X86FsWorkload.py index 294241b51c..6cff4211ae 100644 --- a/src/arch/x86/X86FsWorkload.py +++ b/src/arch/x86/X86FsWorkload.py @@ -33,13 +33,21 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * - -from m5.objects.E820 import X86E820Table, X86E820Entry -from m5.objects.SMBios import X86SMBiosSMBiosTable -from m5.objects.IntelMP import X86IntelMPFloatingPointer, X86IntelMPConfigTable from m5.objects.ACPI import X86ACPIRSDP -from m5.objects.Workload import KernelWorkload, Workload +from m5.objects.E820 import ( + X86E820Entry, + X86E820Table, +) +from m5.objects.IntelMP import ( + X86IntelMPConfigTable, + X86IntelMPFloatingPointer, +) +from m5.objects.SMBios import X86SMBiosSMBiosTable +from m5.objects.Workload import ( + KernelWorkload, + Workload, +) +from m5.params import * class X86BareMetalWorkload(Workload): @@ -65,6 +73,7 @@ class X86FsWorkload(KernelWorkload): acpi_description_table_pointer = Param.X86ACPIRSDP( X86ACPIRSDP(), "ACPI root description pointer structure" ) + enable_osxsave = Param.Bool(False, "Enable OSXSAVE in CR4 register") class X86FsLinux(X86FsWorkload): diff --git a/src/arch/x86/X86ISA.py b/src/arch/x86/X86ISA.py index bb72c415e9..aa48d1aa6e 100644 --- a/src/arch/x86/X86ISA.py +++ b/src/arch/x86/X86ISA.py @@ -54,3 +54,73 @@ class X86ISA(BaseISA): vendor_string = Param.String( "HygonGenuine", "Vendor string for CPUID instruction" ) + name_string = Param.String( + "Fake gem5 x86_64 CPU", "Processor name for CPUID instruction" + ) + + # For the functions that return numerical values we use a vector of ints. + # The order of the values is: EAX, EBX, EDX, ECX. + # + # If the CPU function can take an index, the index value is used as an + # offset into the vector and four numerical values are added for each + # possible index value. For example, if the function accepts 3 index + # values, there are 12 total ints in the vector param. In addition, the + # last values for functions which take an index must be all zeros. All + # zeros indicates to the KVM cpu / OS that there are no more index values + # to iterate over. + # + # A good resource for these values can be found here: + # https://sandpile.org/x86/cpuid.htm + # 0000_0001h + FamilyModelStepping = VectorParam.UInt32( + [0x00020F51, 0x00000805, 0xEFDBFBFF, 0x00000209], + "type/family/model/stepping and feature flags", + ) + # 0000_0004h + CacheParams = VectorParam.UInt32( + [0x00000000, 0x00000000, 0x00000000, 0x00000000], + "cache configuration descriptors", + ) + # 0000_0007h + ExtendedFeatures = VectorParam.UInt32( + [0x00000000, 0x01800000, 0x00000000, 0x00000000], "feature flags" + ) + # 0000_000Dh - This uses ECX index, so the last entry must be all zeros + ExtendedState = VectorParam.UInt32( + [ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + ], + "extended state enumeration", + ) + # 8000_0001h + FamilyModelSteppingBrandFeatures = VectorParam.UInt32( + [0x00020F51, 0x00000405, 0xEBD3FBFF, 0x00020001], + "family/model/stepping and features flags", + ) + # 8000_0005h + L1CacheAndTLB = VectorParam.UInt32( + [0xFF08FF08, 0xFF20FF20, 0x40020140, 0x40020140], + "L1 cache and L1 TLB configuration descriptors", + ) + # 8000_0006h + L2L3CacheAndL2TLB = VectorParam.UInt32( + [0x00000000, 0x42004200, 0x00000000, 0x04008140], + "L2/L3 cache and L2 TLB configuration descriptors", + ) + # 8000_0007h + APMInfo = VectorParam.UInt32( + [0x80000018, 0x68747541, 0x69746E65, 0x444D4163], + "processor feedback capabilities", + ) + # 8000_0008h + LongModeAddressSize = VectorParam.UInt32( + [0x00003030, 0x00000000, 0x00000000, 0x00000000], + "miscellaneous information", + ) diff --git a/src/arch/x86/X86LocalApic.py b/src/arch/x86/X86LocalApic.py index d7defce7e5..8b7d9a831d 100644 --- a/src/arch/x86/X86LocalApic.py +++ b/src/arch/x86/X86LocalApic.py @@ -37,12 +37,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.defines import buildEnv -from m5.params import * -from m5.proxy import * - from m5.objects.BaseInterrupts import BaseInterrupts from m5.objects.ClockDomain import DerivedClockDomain from m5.objects.IntPin import IntSinkPin +from m5.params import * +from m5.proxy import * class X86LocalApic(BaseInterrupts): diff --git a/src/arch/x86/X86NativeTrace.py b/src/arch/x86/X86NativeTrace.py index d0b94ecc40..773071674d 100644 --- a/src/arch/x86/X86NativeTrace.py +++ b/src/arch/x86/X86NativeTrace.py @@ -24,10 +24,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * - from m5.objects.CPUTracers import NativeTrace +from m5.params import * +from m5.SimObject import SimObject class X86NativeTrace(NativeTrace): diff --git a/src/arch/x86/X86SeWorkload.py b/src/arch/x86/X86SeWorkload.py index 6674bdb9a5..e8e75a3ec3 100644 --- a/src/arch/x86/X86SeWorkload.py +++ b/src/arch/x86/X86SeWorkload.py @@ -23,9 +23,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * - from m5.objects.Workload import SEWorkload +from m5.params import * class X86EmuLinux(SEWorkload): diff --git a/src/arch/x86/X86TLB.py b/src/arch/x86/X86TLB.py index 8532ddf8c6..8c3c65cce3 100644 --- a/src/arch/x86/X86TLB.py +++ b/src/arch/x86/X86TLB.py @@ -33,11 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * - from m5.objects.BaseTLB import BaseTLB from m5.objects.ClockedObject import ClockedObject +from m5.params import * +from m5.proxy import * class X86PagetableWalker(ClockedObject): diff --git a/src/arch/x86/bios/ACPI.py b/src/arch/x86/bios/ACPI.py index fbbeda015c..90d0ce094c 100644 --- a/src/arch/x86/bios/ACPI.py +++ b/src/arch/x86/bios/ACPI.py @@ -36,6 +36,7 @@ from m5.params import * from m5.SimObject import SimObject + # ACPI description table header. Subclasses contain and handle the actual # contents as appropriate for that type of table. class X86ACPISysDescTable(SimObject): diff --git a/src/arch/x86/cpuid.cc b/src/arch/x86/cpuid.cc index ac4709ce0e..2ce9ec9289 100644 --- a/src/arch/x86/cpuid.cc +++ b/src/arch/x86/cpuid.cc @@ -31,162 +31,135 @@ #include "arch/x86/isa.hh" #include "base/bitfield.hh" #include "cpu/thread_context.hh" +#include "debug/X86.hh" namespace gem5 { -namespace X86ISA { - enum StandardCpuidFunction - { - VendorAndLargestStdFunc, - FamilyModelStepping, - CacheAndTLB, - SerialNumber, - CacheParams, - MonitorMwait, - ThermalPowerMgmt, - ExtendedFeatures, - NumStandardCpuidFuncs - }; +namespace X86ISA +{ - enum ExtendedCpuidFunctions - { - VendorAndLargestExtFunc, - FamilyModelSteppingBrandFeatures, - NameString1, - NameString2, - NameString3, - L1CacheAndTLB, - L2L3CacheAndL2TLB, - APMInfo, - LongModeAddressSize, +X86CPUID::X86CPUID(const std::string& vendor, const std::string& name) + : vendorString(vendor), nameString(name) +{ + fatal_if(vendorString.size() != 12, + "CPUID vendor string must be 12 characters\n"); +} - /* - * The following are defined by the spec but not yet implemented - */ -/* // Function 9 is reserved - SVMInfo = 10, - // Functions 11-24 are reserved - TLB1GBPageInfo = 25, - PerformanceInfo,*/ +void +X86CPUID::addStandardFunc(uint32_t func, std::vector values) +{ + capabilities[func] = values; +} - NumExtendedCpuidFuncs - }; +void +X86CPUID::addExtendedFunc(uint32_t func, std::vector values) +{ + // Extended functions begin with 8000_0000h, but the enum is based from + // zero, so we need to add that to the function value. + capabilities[func | 0x80000000] = values; +} - static const int nameStringSize = 48; - static const char nameString[nameStringSize] = "Fake M5 x86_64 CPU"; +bool +X86CPUID::doCpuid(ThreadContext * tc, uint32_t function, uint32_t index, + CpuidResult &result) +{ + constexpr uint32_t ext = 0x80000000; - uint64_t - stringToRegister(const char *str) - { - uint64_t reg = 0; - for (int pos = 3; pos >=0; pos--) { - reg <<= 8; - reg |= str[pos]; - } - return reg; - } + DPRINTF(X86, "Calling CPUID function %x with index %d\n", function, index); - bool - doCpuid(ThreadContext * tc, uint32_t function, - uint32_t index, CpuidResult &result) - { - uint16_t family = bits(function, 31, 16); - uint16_t funcNum = bits(function, 15, 0); - if (family == 0x8000) { - // The extended functions - switch (funcNum) { - case VendorAndLargestExtFunc: - { - ISA *isa = dynamic_cast(tc->getIsaPtr()); - auto vendor_string = isa->getVendorString(); - result = CpuidResult( - 0x80000000 + NumExtendedCpuidFuncs - 1, - stringToRegister(vendor_string.c_str()), - stringToRegister(vendor_string.c_str() + 4), - stringToRegister(vendor_string.c_str() + 8)); - } - break; - case FamilyModelSteppingBrandFeatures: - result = CpuidResult(0x00020f51, 0x00000405, - 0xebd3fbff, 0x00020001); - break; - case NameString1: - case NameString2: - case NameString3: - { - // Zero fill anything beyond the end of the string. This - // should go away once the string is a vetted parameter. - char cleanName[nameStringSize]; - memset(cleanName, '\0', nameStringSize); - strncpy(cleanName, nameString, nameStringSize); + // Handle the string-related CPUID functions specially + if (function == VendorAndLargestStdFunc) { + result = CpuidResult(NumStandardCpuidFuncs - 1, + stringToRegister(vendorString.c_str()), + stringToRegister(vendorString.c_str() + 4), + stringToRegister(vendorString.c_str() + 8)); - int offset = (funcNum - NameString1) * 16; - assert(nameStringSize >= offset + 16); - result = CpuidResult( - stringToRegister(cleanName + offset + 0), - stringToRegister(cleanName + offset + 4), - stringToRegister(cleanName + offset + 12), - stringToRegister(cleanName + offset + 8)); - } - break; - case L1CacheAndTLB: - result = CpuidResult(0xff08ff08, 0xff20ff20, - 0x40020140, 0x40020140); - break; - case L2L3CacheAndL2TLB: - result = CpuidResult(0x00000000, 0x42004200, - 0x00000000, 0x04008140); - break; - case APMInfo: - result = CpuidResult(0x80000018, 0x68747541, - 0x69746e65, 0x444d4163); - break; - case LongModeAddressSize: - result = CpuidResult(0x00003030, 0x00000000, - 0x00000000, 0x00000000); - break; -/* case SVMInfo: - case TLB1GBPageInfo: - case PerformanceInfo:*/ - default: - warn("x86 cpuid family 0x8000: unimplemented function %u", - funcNum); - return false; - } - } else if (family == 0x0000) { - // The standard functions - switch (funcNum) { - case VendorAndLargestStdFunc: - { - ISA *isa = dynamic_cast(tc->getIsaPtr()); - auto vendor_string = isa->getVendorString(); - result = CpuidResult( - NumStandardCpuidFuncs - 1, - stringToRegister(vendor_string.c_str()), - stringToRegister(vendor_string.c_str() + 4), - stringToRegister(vendor_string.c_str() + 8)); - } - break; - case FamilyModelStepping: - result = CpuidResult(0x00020f51, 0x00000805, - 0xefdbfbff, 0x00000209); - break; - case ExtendedFeatures: - result = CpuidResult(0x00000000, 0x01800000, - 0x00000000, 0x00000000); - break; - default: - warn("x86 cpuid family 0x0000: unimplemented function %u", - funcNum); - return false; - } - } else { - warn("x86 cpuid: unknown family %#x", family); - return false; - } + return true; + } else if (function == (ext | VendorAndLargestExtFunc)) { + result = CpuidResult(0x80000000 + NumExtendedCpuidFuncs - 1, + stringToRegister(vendorString.c_str()), + stringToRegister(vendorString.c_str() + 4), + stringToRegister(vendorString.c_str() + 8)); + + return true; + } else if ((function == (ext | NameString1)) || + (function == (ext | NameString2)) || + (function == (ext | NameString3))) { + // Zero fill anything beyond the end of the string. This + // should go away once the string is a vetted parameter. + char cleanName[nameStringSize]; + memset(cleanName, '\0', nameStringSize); + strncpy(cleanName, nameString.c_str(), nameStringSize-1); + + int funcNum = bits(function, 15, 0); + int offset = (funcNum - NameString1) * 16; + assert(nameStringSize >= offset + 16); + result = CpuidResult( + stringToRegister(cleanName + offset + 0), + stringToRegister(cleanName + offset + 4), + stringToRegister(cleanName + offset + 12), + stringToRegister(cleanName + offset + 8)); return true; } + + // Ignore anything not in the map of supported CPUID functions. + // This is checked after the string-related functions as those are not + // in the capabilities map. + if (!capabilities.count(function)) { + return false; + } + + int cap_offset = 0; + + // Ignore index values for functions that do not take index values. + if (hasSignificantIndex(function)) { + cap_offset = index * 4; + } + + // Ensure we have the offset and 4 dwords after it. + assert(capabilities[function].size() >= (cap_offset + 4)); + + auto &cap_vec = capabilities[function]; + result = CpuidResult(cap_vec[cap_offset + 0], cap_vec[cap_offset + 1], + cap_vec[cap_offset + 2], cap_vec[cap_offset + 3]); + DPRINTF(X86, "CPUID function %x returning (%x, %x, %x, %x)\n", + function, result.rax, result.rbx, result.rdx, result.rcx); + + return true; +} + +uint64_t +X86CPUID::stringToRegister(const char *str) +{ + uint64_t reg = 0; + for (int pos = 3; pos >=0; pos--) { + reg <<= 8; + reg |= str[pos]; + } + return reg; +} + +// Return true if the CPUID function takes ECX index as an input AND +// those multiple index values are supported in gem5. +bool +X86CPUID::hasSignificantIndex(uint32_t function) +{ + uint16_t family = bits(function, 31, 16); + uint16_t funcNum = bits(function, 15, 0); + + if (family == 0x0000) { + switch (funcNum) { + case ExtendedState: + return true; + default: + return false; + } + } + + return false; +} + } // namespace X86ISA } // namespace gem5 diff --git a/src/arch/x86/cpuid.hh b/src/arch/x86/cpuid.hh index 5c1a8ccb16..1c932980d2 100644 --- a/src/arch/x86/cpuid.hh +++ b/src/arch/x86/cpuid.hh @@ -29,7 +29,10 @@ #ifndef __ARCH_X86_CPUID_HH__ #define __ARCH_X86_CPUID_HH__ +#include + #include "base/types.hh" +#include "params/X86ISA.hh" namespace gem5 { @@ -38,28 +41,74 @@ class ThreadContext; namespace X86ISA { - struct CpuidResult - { - uint64_t rax; - uint64_t rbx; - uint64_t rcx; - uint64_t rdx; - // These are not in alphebetical order on purpose. The order reflects - // how the CPUID orders the registers when it returns results. - CpuidResult(uint64_t _rax, uint64_t _rbx, - uint64_t _rdx, uint64_t _rcx) : - rax(_rax), rbx(_rbx), rcx(_rcx), rdx(_rdx) - {} +enum StandardCpuidFunction +{ + VendorAndLargestStdFunc, + FamilyModelStepping, + CacheAndTLB, + SerialNumber, + CacheParams, + MonitorMwait, + ThermalPowerMgmt, + ExtendedFeatures, + ExtendedState = 0xD, + NumStandardCpuidFuncs +}; - CpuidResult() - {} - }; +enum ExtendedCpuidFunctions +{ + VendorAndLargestExtFunc, + FamilyModelSteppingBrandFeatures, + NameString1, + NameString2, + NameString3, + L1CacheAndTLB, + L2L3CacheAndL2TLB, + APMInfo, + LongModeAddressSize, + NumExtendedCpuidFuncs +}; - uint64_t stringToRegister(const char *str); +constexpr int nameStringSize = 48; + +struct CpuidResult +{ + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + + // These are not in alphebetical order on purpose. The order reflects + // how the CPUID orders the registers when it returns results. + CpuidResult(uint64_t _rax, uint64_t _rbx, + uint64_t _rdx, uint64_t _rcx) : + rax(_rax), rbx(_rbx), rcx(_rcx), rdx(_rdx) + {} + + CpuidResult() + {} +}; + +class X86CPUID +{ + public: + X86CPUID(const std::string& vendor, const std::string& name); + + void addStandardFunc(uint32_t func, std::vector values); + void addExtendedFunc(uint32_t func, std::vector values); bool doCpuid(ThreadContext * tc, uint32_t function, - uint32_t index, CpuidResult &result); + uint32_t index, CpuidResult &result); + bool hasSignificantIndex(uint32_t function); + + private: + const std::string vendorString; + const std::string nameString; + std::unordered_map> capabilities; + + uint64_t stringToRegister(const char *str); +}; } // namespace X86ISA } // namespace gem5 diff --git a/src/arch/x86/decoder.cc b/src/arch/x86/decoder.cc index ef87ff37c4..af2456d6ab 100644 --- a/src/arch/x86/decoder.cc +++ b/src/arch/x86/decoder.cc @@ -687,6 +687,8 @@ Decoder::decode(ExtMachInst mach_inst, Addr addr) (*instMap)[mach_inst] = si; } + si->size(basePC + offset - origPC); + DPRINTF(Decode, "Decode: Decoded %s instruction: %#x\n", si->getName(), mach_inst); return si; @@ -732,8 +734,7 @@ Decoder::decode(PCStateBase &next_pc) start = 0; } - si = decode(emi, origPC); - return si; + return decode(emi, origPC); } StaticInstPtr diff --git a/src/arch/x86/fs_workload.cc b/src/arch/x86/fs_workload.cc index 1a412380a6..88d7deed68 100644 --- a/src/arch/x86/fs_workload.cc +++ b/src/arch/x86/fs_workload.cc @@ -58,7 +58,8 @@ FsWorkload::FsWorkload(const Params &p) : KernelWorkload(p), smbiosTable(p.smbios_table), mpFloatingPointer(p.intel_mp_pointer), mpConfigTable(p.intel_mp_table), - rsdp(p.acpi_description_table_pointer) + rsdp(p.acpi_description_table_pointer), + enable_osxsave(p.enable_osxsave) {} void @@ -295,6 +296,7 @@ FsWorkload::initState() CR4 cr4 = tc->readMiscRegNoEffect(misc_reg::Cr4); // Turn on pae. cr4.pae = 1; + cr4.osxsave = enable_osxsave; tc->setMiscReg(misc_reg::Cr4, cr4); // Point to the page tables. diff --git a/src/arch/x86/fs_workload.hh b/src/arch/x86/fs_workload.hh index 9d14f91bb5..81db414fb2 100644 --- a/src/arch/x86/fs_workload.hh +++ b/src/arch/x86/fs_workload.hh @@ -106,6 +106,9 @@ class FsWorkload : public KernelWorkload Addr &fpSize, Addr &tableSize, Addr table=0); void writeOutACPITables(Addr begin, Addr &size); + + private: + bool enable_osxsave; }; } // namespace X86ISA diff --git a/src/arch/x86/insts/macroop.hh b/src/arch/x86/insts/macroop.hh index 36718f77fd..071037b173 100644 --- a/src/arch/x86/insts/macroop.hh +++ b/src/arch/x86/insts/macroop.hh @@ -103,6 +103,14 @@ class MacroopBase : public X86StaticInst { return env; } + + void size(size_t newSize) override + { + for (int i = 0; i < numMicroops; i++) { + microops[i]->size(newSize); + } + _size = newSize; + } }; } // namespace X86ISA diff --git a/src/arch/x86/isa.cc b/src/arch/x86/isa.cc index 31efae3a43..7d401a6c59 100644 --- a/src/arch/x86/isa.cc +++ b/src/arch/x86/isa.cc @@ -151,10 +151,20 @@ RegClass matRegClass(MatRegClass, MatRegClassName, 1, debug::MatRegs); } // anonymous namespace -ISA::ISA(const X86ISAParams &p) : BaseISA(p), vendorString(p.vendor_string) +ISA::ISA(const X86ISAParams &p) + : BaseISA(p), cpuid(new X86CPUID(p.vendor_string, p.name_string)) { - fatal_if(vendorString.size() != 12, - "CPUID vendor string must be 12 characters\n"); + cpuid->addStandardFunc(FamilyModelStepping, p.FamilyModelStepping); + cpuid->addStandardFunc(CacheParams, p.CacheParams); + cpuid->addStandardFunc(ExtendedFeatures, p.ExtendedFeatures); + cpuid->addStandardFunc(ExtendedState, p.ExtendedState); + + cpuid->addExtendedFunc(FamilyModelSteppingBrandFeatures, + p.FamilyModelSteppingBrandFeatures); + cpuid->addExtendedFunc(L1CacheAndTLB, p.L1CacheAndTLB); + cpuid->addExtendedFunc(L2L3CacheAndL2TLB, p.L2L3CacheAndL2TLB); + cpuid->addExtendedFunc(APMInfo, p.APMInfo); + cpuid->addExtendedFunc(LongModeAddressSize, p.LongModeAddressSize); _regClasses.push_back(&flatIntRegClass); _regClasses.push_back(&flatFloatRegClass); @@ -252,7 +262,7 @@ ISA::setMiscRegNoEffect(RegIndex idx, RegVal val) reg_width = 3; break; case misc_reg::Ftw: - reg_width = 8; + reg_width = 16; break; case misc_reg::Fsw: case misc_reg::Fcw: diff --git a/src/arch/x86/isa.hh b/src/arch/x86/isa.hh index f7ae210f96..9c6dcf0921 100644 --- a/src/arch/x86/isa.hh +++ b/src/arch/x86/isa.hh @@ -33,6 +33,7 @@ #include #include "arch/generic/isa.hh" +#include "arch/x86/cpuid.hh" #include "arch/x86/pcstate.hh" #include "arch/x86/regs/ccr.hh" #include "arch/x86/regs/float.hh" @@ -93,6 +94,8 @@ class ISA : public BaseISA void setThreadContext(ThreadContext *_tc) override; std::string getVendorString() const; + + std::unique_ptr cpuid; }; } // namespace X86ISA diff --git a/src/arch/x86/isa/decoder/one_byte_opcodes.isa b/src/arch/x86/isa/decoder/one_byte_opcodes.isa index ef596dfd71..13b05e62ff 100644 --- a/src/arch/x86/isa/decoder/one_byte_opcodes.isa +++ b/src/arch/x86/isa/decoder/one_byte_opcodes.isa @@ -287,25 +287,31 @@ 0x1: MOV(Ev,Gv); 0x2: MOV(Gb,Eb); 0x3: MOV(Gv,Ev); - 0x4: decode MODRM_REG { - 0x0, 0x1, 0x2, - 0x3, 0x4, 0x5: MOV(Ev,Sv); + 0x4: decode REX_R { + 0x0: decode MODRM_REG { + 0x0, 0x1, 0x2, + 0x3, 0x4, 0x5: MOV(Ev,Sv); + } + default: UD2(); } 0x5: LEA(Gv,M); - 0x6: decode MODE_SUBMODE { - 0x3, 0x4: decode MODRM_REG { - // Moving to the CS selector (0x1) is illegal, and 0x6 and - // 0x7 are reserved. - 0x1, 0x6, 0x7: UD2(); - default: MOV_REAL(Sv,Ev); - } - default: decode MODRM_REG { - // Moving to the CS selector (0x1) is illegal, and 0x6 and - // 0x7 are reserved. - 0x1, 0x6, 0x7: UD2(); - 0x2: MOVSS(Sv,Ev); - default: MOV(Sv,Ev); + 0x6: decode REX_R { + 0x0: decode MODE_SUBMODE { + 0x3, 0x4: decode MODRM_REG { + // Moving to the CS selector (0x1) is illegal, and 0x6 and + // 0x7 are reserved. + 0x1, 0x6, 0x7: UD2(); + default: MOV_REAL(Sv,Ev); + } + default: decode MODRM_REG { + // Moving to the CS selector (0x1) is illegal, and 0x6 and + // 0x7 are reserved. + 0x1, 0x6, 0x7: UD2(); + 0x2: MOVSS(Sv,Ev); + default: MOV(Sv,Ev); + } } + default: UD2(); } //0x7: group10_Ev(); 0x7: decode MODRM_REG { diff --git a/src/arch/x86/isa/decoder/two_byte_opcodes.isa b/src/arch/x86/isa/decoder/two_byte_opcodes.isa index 38937cb3e2..a32d41a169 100644 --- a/src/arch/x86/isa/decoder/two_byte_opcodes.isa +++ b/src/arch/x86/isa/decoder/two_byte_opcodes.isa @@ -287,24 +287,27 @@ 0x6: HINT_NOP(); 0x7: HINT_NOP(); } - 0x04: decode LEGACY_DECODEVAL { - // no prefix - 0x0: decode OPCODE_OP_BOTTOM3 { - 0x0: Cpl0CondInst::MOV( - {{misc_reg::isValid(misc_reg::cr(MODRM_REG))}},Rd,Cd); - 0x1: Cpl0CondInst::MOV({{MODRM_REG < 8}},Rd,Dd); - 0x2: Cpl0CondInst::MOV( - {{misc_reg::isValid(misc_reg::cr(MODRM_REG))}},Cd,Rd); - 0x3: Cpl0CondInst::MOV({{MODRM_REG < 8}},Dd,Rd); + 0x04: decode REX_R { + 0x0: decode LEGACY_DECODEVAL { + // no prefix + 0x0: decode OPCODE_OP_BOTTOM3 { + 0x0: Cpl0CondInst::MOV( + {{misc_reg::isValid(misc_reg::cr(MODRM_REG))}},Rd,Cd); + 0x1: Cpl0CondInst::MOV({{MODRM_REG < 8}},Rd,Dd); + 0x2: Cpl0CondInst::MOV( + {{misc_reg::isValid(misc_reg::cr(MODRM_REG))}},Cd,Rd); + 0x3: Cpl0CondInst::MOV({{MODRM_REG < 8}},Dd,Rd); + default: UD2(); + } + // operand size (0x66) + 0x1: decode OPCODE_OP_BOTTOM3 { + 0x0: Cpl0CondInst::MOV( + {{misc_reg::isValid(misc_reg::cr(MODRM_REG))}},Rd,Cd); + 0x2: Cpl0CondInst::MOV( + {{misc_reg::isValid(misc_reg::cr(MODRM_REG))}},Cd,Rd); + } default: UD2(); } - // operand size (0x66) - 0x1: decode OPCODE_OP_BOTTOM3 { - 0x0: Cpl0CondInst::MOV( - {{misc_reg::isValid(misc_reg::cr(MODRM_REG))}},Rd,Cd); - 0x2: Cpl0CondInst::MOV( - {{misc_reg::isValid(misc_reg::cr(MODRM_REG))}},Cd,Rd); - } default: UD2(); } 0x05: decode LEGACY_DECODEVAL { @@ -690,8 +693,9 @@ } 0x2: CPUIDInst::CPUID({{ CpuidResult result; - bool success = doCpuid(xc->tcBase(), bits(Rax, 31, 0), - bits(Rcx, 31, 0), result); + ISA *isa = dynamic_cast(xc->tcBase()->getIsaPtr()); + bool success = isa->cpuid->doCpuid(xc->tcBase(), + bits(Rax, 31, 0), bits(Rcx, 31, 0), result); if (success) { Rax = result.rax; Rbx = result.rbx; diff --git a/src/arch/x86/isa/includes.isa b/src/arch/x86/isa/includes.isa index 6fc5f448a0..9445f2032b 100644 --- a/src/arch/x86/isa/includes.isa +++ b/src/arch/x86/isa/includes.isa @@ -63,6 +63,7 @@ output header {{ #include "arch/x86/insts/microregop.hh" #include "arch/x86/insts/microspecop.hh" #include "arch/x86/insts/static_inst.hh" +#include "arch/x86/isa.hh" #include "arch/x86/regs/ccr.hh" #include "arch/x86/regs/int.hh" #include "arch/x86/regs/misc.hh" diff --git a/src/arch/x86/isa/microops/fpop.isa b/src/arch/x86/isa/microops/fpop.isa index 5365c587ec..b0b925f679 100644 --- a/src/arch/x86/isa/microops/fpop.isa +++ b/src/arch/x86/isa/microops/fpop.isa @@ -430,4 +430,6 @@ let {{ class Pop87(Fp0Op): code = '' op_class = 'IntAluOp' + def __init__(self): + super().__init__(spm=1) }}; diff --git a/src/arch/x86/isa/microops/mediaop.isa b/src/arch/x86/isa/microops/mediaop.isa index 599b5faef5..0b1d1fe0eb 100644 --- a/src/arch/x86/isa/microops/mediaop.isa +++ b/src/arch/x86/isa/microops/mediaop.isa @@ -393,7 +393,7 @@ let {{ // Handle saturation. if (signBit) { - if (overflow != mask(destBits - srcBits + 1)) { + if (overflow != mask(srcBits - destBits + 1)) { if (signedOp()) picked = (1ULL << (destBits - 1)); else @@ -421,7 +421,7 @@ let {{ // Handle saturation. if (signBit) { - if (overflow != mask(destBits - srcBits + 1)) { + if (overflow != mask(srcBits - destBits + 1)) { if (signedOp()) picked = (1ULL << (destBits - 1)); else diff --git a/src/arch/x86/isa/specialize.isa b/src/arch/x86/isa/specialize.isa index a86d5126b6..236465dc1b 100644 --- a/src/arch/x86/isa/specialize.isa +++ b/src/arch/x86/isa/specialize.isa @@ -240,7 +240,7 @@ let {{ regFormat = \ "printReg(out, intRegClass[%s], regSize);\n" regSuffix = "_R" - env.addToDisassembly(regFormat % ModRMRegIndex) + env.addToDisassembly(regFormat % ModRMRMIndex) return doSplitDecode("MODRM_MOD", {"3" : (specializeInst, Name + regSuffix, copy.copy(opTypes), regEnv)}, @@ -268,7 +268,7 @@ let {{ regFormat = \ "printReg(out, intRegClass[%s], regSize);\n" Name += "_R" - env.addToDisassembly(regFormat % ModRMRegIndex) + env.addToDisassembly(regFormat % ModRMRMIndex) elif opType.tag in ("X", "Y"): # This type of memory addressing is for string instructions. # They'll use the right index and segment internally. diff --git a/src/arch/x86/kvm/X86KvmCPU.py b/src/arch/x86/kvm/X86KvmCPU.py index df32fe8b40..6f50ba2511 100644 --- a/src/arch/x86/kvm/X86KvmCPU.py +++ b/src/arch/x86/kvm/X86KvmCPU.py @@ -24,12 +24,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.SimObject import * - from m5.objects.BaseKvmCPU import BaseKvmCPU from m5.objects.X86CPU import X86CPU from m5.objects.X86MMU import X86MMU +from m5.params import * +from m5.SimObject import * class X86KvmCPU(BaseKvmCPU, X86CPU): diff --git a/src/arch/x86/kvm/x86_cpu.cc b/src/arch/x86/kvm/x86_cpu.cc index 7faa9159ab..da6e1bb9e1 100644 --- a/src/arch/x86/kvm/x86_cpu.cc +++ b/src/arch/x86/kvm/x86_cpu.cc @@ -37,10 +37,12 @@ #include "arch/x86/cpuid.hh" #include "arch/x86/faults.hh" #include "arch/x86/interrupts.hh" +#include "arch/x86/isa.hh" #include "arch/x86/regs/float.hh" #include "arch/x86/regs/int.hh" #include "arch/x86/regs/msr.hh" #include "arch/x86/utility.hh" +#include "base/bitunion.hh" #include "base/compiler.hh" #include "cpu/kvm/base.hh" #include "debug/Drain.hh" @@ -73,6 +75,13 @@ using namespace X86ISA; // data) is used to indicate that a segment has been accessed. #define SEG_TYPE_BIT_ACCESSED 1 +// Some linux distro s(e.g., RHEL7) define the KVM macros using "BIT" but do +// not include where BIT is defined, so define it here in that case. +#ifndef BIT +#define BIT(nr) (1UL << (nr)) +#endif + + struct GEM5_PACKED FXSave { uint16_t fcw; @@ -109,6 +118,32 @@ struct GEM5_PACKED FXSave static_assert(sizeof(FXSave) == 512, "Unexpected size of FXSave"); +BitUnion64(XStateBV) + Bitfield<0> fpu; + Bitfield<1> sse; + Bitfield<2> avx; + Bitfield<4, 3> mpx; + Bitfield<7, 5> avx512; + Bitfield<8> pt; + Bitfield<9> pkru; + Bitfield<10> pasid; + Bitfield<12, 11> cet; + Bitfield<13> hdc; + Bitfield<14> uintr; + Bitfield<15> lbr; + Bitfield<16> hwp; + Bitfield<18, 17> amx; + Bitfield<63, 19> reserved; +EndBitUnion(XStateBV) + +struct XSaveHeader +{ + XStateBV xstate_bv; + uint64_t reserved[7]; +}; + +static_assert(sizeof(XSaveHeader) == 64, "Unexpected size of XSaveHeader"); + #define FOREACH_IREG() \ do { \ APPLY_IREG(rax, int_reg::Rax); \ @@ -904,6 +939,19 @@ X86KvmCPU::updateKvmStateFPUXSave() updateKvmStateFPUCommon(tc, xsave); + /** + * The xsave header (Vol. 1, Section 13.4.2 of the Intel Software + * Development Manual) directly follows the legacy xsave region + * (i.e., the FPU/SSE state). The first 8 bytes of the xsave header + * hold a state-component bitmap called xstate_bv. We need to set + * the state component bits corresponding to the FPU and SSE + * states. + */ + XSaveHeader& xsave_hdr = + * (XSaveHeader *) ((char *) &kxsave + sizeof(FXSave)); + xsave_hdr.xstate_bv.fpu = 1; + xsave_hdr.xstate_bv.sse = 1; + if (tc->readMiscRegNoEffect(misc_reg::Fiseg)) warn_once("misc_reg::Fiseg is non-zero.\n"); @@ -1419,12 +1467,12 @@ X86KvmCPU::ioctlRun() static struct kvm_cpuid_entry2 makeKvmCpuid(uint32_t function, uint32_t index, - CpuidResult &result) + CpuidResult &result, uint32_t flags = 0) { struct kvm_cpuid_entry2 e; e.function = function; e.index = index; - e.flags = 0; + e.flags = flags; e.eax = (uint32_t)result.rax; e.ebx = (uint32_t)result.rbx; e.ecx = (uint32_t)result.rcx; @@ -1437,33 +1485,76 @@ void X86KvmCPU::updateCPUID() { Kvm::CPUIDVector m5_supported; - - /* TODO: We currently don't support any of the functions that - * iterate through data structures in the CPU using an index. It's - * currently not a problem since M5 doesn't expose any of them at - * the moment. - */ + X86ISA::ISA *isa = dynamic_cast(tc->getIsaPtr()); /* Basic features */ CpuidResult func0; - X86ISA::doCpuid(tc, 0x0, 0, func0); + isa->cpuid->doCpuid(tc, 0x0, 0, func0); for (uint32_t function = 0; function <= func0.rax; ++function) { CpuidResult cpuid; uint32_t idx(0); - X86ISA::doCpuid(tc, function, idx, cpuid); - m5_supported.push_back(makeKvmCpuid(function, idx, cpuid)); + if (!isa->cpuid->hasSignificantIndex(function)) { + isa->cpuid->doCpuid(tc, function, idx, cpuid); + m5_supported.push_back(makeKvmCpuid(function, idx, cpuid)); + } else { + while (true) { + [[maybe_unused]] bool rv = isa->cpuid->doCpuid( + tc, function, idx, cpuid); + assert(rv); + + if (idx && + !cpuid.rax && !cpuid.rbx && !cpuid.rdx && !cpuid.rcx) { + break; + } + + /* + * For functions in family 0, this flag tells Linux to compare + * the index as well as the function number rather than only + * the function number. Important: Do NOT set this flag if the + * function does not take an index. Doing so will break SMP. + */ + uint32_t flag = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + m5_supported.push_back( + makeKvmCpuid(function, idx, cpuid, flag)); + idx++; + } + } } /* Extended features */ CpuidResult efunc0; - X86ISA::doCpuid(tc, 0x80000000, 0, efunc0); + isa->cpuid->doCpuid(tc, 0x80000000, 0, efunc0); for (uint32_t function = 0x80000000; function <= efunc0.rax; ++function) { CpuidResult cpuid; uint32_t idx(0); - X86ISA::doCpuid(tc, function, idx, cpuid); - m5_supported.push_back(makeKvmCpuid(function, idx, cpuid)); + if (!isa->cpuid->hasSignificantIndex(function)) { + isa->cpuid->doCpuid(tc, function, idx, cpuid); + m5_supported.push_back(makeKvmCpuid(function, idx, cpuid)); + } else { + while (true) { + [[maybe_unused]] bool rv = isa->cpuid->doCpuid( + tc, function, idx, cpuid); + assert(rv); + + if (idx && + !cpuid.rax && !cpuid.rbx && !cpuid.rdx && !cpuid.rcx) { + break; + } + + /* + * For functions in family 0, this flag tells Linux to compare + * the index as well as the function number rather than only + * the function number. Important: Do NOT set this flag if the + * function does not take an index. Doing so will break SMP. + */ + uint32_t flag = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + m5_supported.push_back( + makeKvmCpuid(function, idx, cpuid, flag)); + idx++; + } + } } setCPUID(m5_supported); diff --git a/src/arch/x86/pcstate.hh b/src/arch/x86/pcstate.hh index a0ed6ffe9f..95984d7a96 100644 --- a/src/arch/x86/pcstate.hh +++ b/src/arch/x86/pcstate.hh @@ -66,7 +66,7 @@ class PCState : public GenericISA::UPCState<8> } void - set(Addr val) + set(Addr val) override { Base::set(val); _size = 0; diff --git a/src/arch/x86/process.cc b/src/arch/x86/process.cc index a195fdf888..10833783fd 100644 --- a/src/arch/x86/process.cc +++ b/src/arch/x86/process.cc @@ -397,6 +397,7 @@ X86_64Process::initState() tc->setMiscReg(misc_reg::Cr8, cr8); tc->setMiscReg(misc_reg::Mxcsr, 0x1f80); + tc->setMiscReg(misc_reg::Ftw, 0xffff); tc->setMiscReg(misc_reg::ApicBase, 0xfee00900); @@ -482,13 +483,35 @@ X86_64Process::initState() physProxy.writeBlob(idtPhysAddr + 0xE0, &PFGate, sizeof(PFGate)); /* System call handler */ + // First, we write to the MMIO m5ops range (0xffffc90000007000) + // to trap out of the VM back into gem5 to emulate the system + // call. Upon re-entering the VM, we need to flush the TLB in + // case the system call modified existing page mappings (e.g., + // munmap, mremap, brk). To do this, we can simply read/write + // cr3; however, doing so requires saving the value to an + // intermediate GPR (%rax, in this case). We save/restore the + // value of %rax in the scratch region syscallDataBuf. + const Addr syscallDataBuf = syscallCodeVirtAddr + 0x100; uint8_t syscallBlob[] = { // mov %rax, (0xffffc90000007000) 0x48, 0xa3, 0x00, 0x70, 0x00, 0x00, 0x00, 0xc9, 0xff, 0xff, + // mov %rax, (syscallDataBuf) + 0x48, 0xa3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + // mov %cr3, %rax + 0x0f, 0x20, 0xd8, + // mov %rax, %cr3 + 0x0f, 0x22, 0xd8, + // mov (syscallDataBuf), %rax + 0x48, 0xa1, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, // sysret 0x48, 0x0f, 0x07 }; + assert(syscallDataBuf >= syscallCodePhysAddr + sizeof syscallBlob); + std::memcpy(&syscallBlob[12], &syscallDataBuf, sizeof syscallDataBuf); + std::memcpy(&syscallBlob[28], &syscallDataBuf, sizeof syscallDataBuf); physProxy.writeBlob(syscallCodePhysAddr, syscallBlob, sizeof(syscallBlob)); @@ -593,6 +616,7 @@ X86_64Process::initState() tc->setMiscReg(misc_reg::Cr0, cr0); tc->setMiscReg(misc_reg::Mxcsr, 0x1f80); + tc->setMiscReg(misc_reg::Ftw, 0xffff); // Setting CR3 to the process pid so that concatinated // page addr with lower 12 bits of CR3 can be used in SE @@ -727,6 +751,7 @@ I386Process::initState() tc->setMiscReg(misc_reg::Cr0, cr0); tc->setMiscReg(misc_reg::Mxcsr, 0x1f80); + tc->setMiscReg(misc_reg::Ftw, 0xffff); } } @@ -994,7 +1019,8 @@ X86Process::argsInit(int pageSize, initVirtMem->write(auxv_array_end, zero); auxv_array_end += sizeof(zero); - initVirtMem->writeString(aux_data_base, platform.c_str()); + initVirtMem->writeString(aux_data_base + numRandomBytes, + platform.c_str()); copyStringArray(envp, envp_array_base, env_data_base, ByteOrder::little, *initVirtMem); diff --git a/src/arch/x86/tlb.cc b/src/arch/x86/tlb.cc index 5ccd3e832d..c7d144bd8a 100644 --- a/src/arch/x86/tlb.cc +++ b/src/arch/x86/tlb.cc @@ -509,6 +509,9 @@ TLB::translateAtomic(const RequestPtr &req, ThreadContext *tc, BaseMMU::Mode mode) { bool delayedResponse; + // CLFLUSHOPT/WB/FLUSH should be treated as read for protection checks + if (req->isCacheClean()) + mode = BaseMMU::Read; return TLB::translate(req, tc, NULL, mode, delayedResponse, false); } @@ -516,6 +519,9 @@ Fault TLB::translateFunctional(const RequestPtr &req, ThreadContext *tc, BaseMMU::Mode mode) { + // CLFLUSHOPT/WB/FLUSH should be treated as read for protection checks + if (req->isCacheClean()) + mode = BaseMMU::Read; unsigned logBytes; const Addr vaddr = req->getVaddr(); Addr addr = vaddr; @@ -553,6 +559,9 @@ TLB::translateTiming(const RequestPtr &req, ThreadContext *tc, { bool delayedResponse; assert(translation); + // CLFLUSHOPT/WB/FLUSH should be treated as read for protection checks + if (req->isCacheClean()) + mode = BaseMMU::Read; Fault fault = TLB::translate(req, tc, translation, mode, delayedResponse, true); if (!delayedResponse) diff --git a/src/base/Graphics.py b/src/base/Graphics.py index b0bec3b137..824a3c4a9d 100644 --- a/src/base/Graphics.py +++ b/src/base/Graphics.py @@ -33,8 +33,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * +from m5.SimObject import SimObject + # Image Formats: # Auto option will let gem5 to choose the image format it prefers. diff --git a/src/base/Kconfig b/src/base/Kconfig new file mode 100644 index 0000000000..19d58c865a --- /dev/null +++ b/src/base/Kconfig @@ -0,0 +1,45 @@ +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +config HAVE_FENV + def_bool $(HAVE_FENV) + +config HAVE_PNG + def_bool $(HAVE_PNG) + +config HAVE_VALGRIND + def_bool $(HAVE_VALGRIND) + +config HAVE_DEPRECATED_NAMESPACE + def_bool $(HAVE_DEPRECATED_NAMESPACE) + +config HAVE_POSIX_CLOCK + def_bool $(HAVE_POSIX_CLOCK) + +config USE_POSIX_CLOCK + depends on HAVE_POSIX_CLOCK + bool "Use POSIX clocks" + +rsource "stats/Kconfig" diff --git a/src/base/SConsopts b/src/base/SConsopts index 8e0661203f..6c438768b2 100644 --- a/src/base/SConsopts +++ b/src/base/SConsopts @@ -69,16 +69,15 @@ werror_env.Append(CCFLAGS=['-Werror']) with gem5_scons.Configure(werror_env) as conf: # Store result in the main environment - main['CONF']['HAVE_DEPRECATED_NAMESPACE'] = conf.TryCompile(''' - int main() {return 0;} - namespace [[gnu::deprecated("Test namespace deprecation")]] - test_deprecated_namespace {} - ''', '.cc') + main['CONF']['HAVE_DEPRECATED_NAMESPACE'] = bool( + conf.TryCompile(''' + int main() {return 0;} + namespace [[gnu::deprecated("Test namespace deprecation")]] + test_deprecated_namespace {} + ''', '.cc') + ) if not main['CONF']['HAVE_DEPRECATED_NAMESPACE']: warning("Deprecated namespaces are not supported by this compiler.\n" "Please make sure to check the mailing list for deprecation " "announcements.") - -sticky_vars.Add(BoolVariable('USE_POSIX_CLOCK', 'Use POSIX Clocks', - '${CONF["HAVE_POSIX_CLOCK"]}')) diff --git a/src/base/amo.test.cc b/src/base/amo.test.cc index 10e5540da4..e511117eea 100644 --- a/src/base/amo.test.cc +++ b/src/base/amo.test.cc @@ -64,9 +64,10 @@ TEST(AmoTest, AtomicOpMin) std::string test_string_smaller = "apple"; std::string test_string_bigger = "cat"; - TypedAtomicOpFunctor *amo_op_int = new AtomicOpMin(10); - TypedAtomicOpFunctor *amo_op_string = - new AtomicOpMin("base"); + std::unique_ptr> amo_op_int = + std::make_unique>(10); + std::unique_ptr> amo_op_string = + std::make_unique>("base"); amo_op_int->execute(&test_int_smaller); amo_op_int->execute(&test_int_bigger); amo_op_string->execute(&test_string_smaller); @@ -85,9 +86,10 @@ TEST(AmoTest, AtomicOpMax) std::string test_string_smaller = "apple"; std::string test_string_bigger = "cat"; - TypedAtomicOpFunctor *amo_op_int = new AtomicOpMax(10); - TypedAtomicOpFunctor *amo_op_string = - new AtomicOpMax("base"); + std::unique_ptr> amo_op_int = + std::make_unique>(10); + std::unique_ptr> amo_op_string = + std::make_unique>("base"); amo_op_int->execute(&test_int_smaller); amo_op_int->execute(&test_int_bigger); amo_op_string->execute(&test_string_smaller); @@ -104,8 +106,10 @@ TEST(AmoTest, AtomicOpDec) int test_int = 10; char test_char = 'c'; - TypedAtomicOpFunctor *amo_op_int = new AtomicOpDec(); - TypedAtomicOpFunctor *amo_op_char = new AtomicOpDec(); + std::unique_ptr> amo_op_int = + std::make_unique>(); + std::unique_ptr> amo_op_char = + std::make_unique>(); amo_op_int->execute(&test_int); amo_op_char->execute(&test_char); @@ -118,8 +122,10 @@ TEST(AmoTest, AtomicOpInc) int test_int = 10; char test_char = 'c'; - TypedAtomicOpFunctor *amo_op_int = new AtomicOpInc(); - TypedAtomicOpFunctor *amo_op_char = new AtomicOpInc(); + std::unique_ptr> amo_op_int = + std::make_unique>(); + std::unique_ptr> amo_op_char = + std::make_unique>(); amo_op_int->execute(&test_int); amo_op_char->execute(&test_char); @@ -132,8 +138,10 @@ TEST(AmoTest, AtomicOpSub) int test_int = 10; char test_char = 'c'; - TypedAtomicOpFunctor *amo_op_int = new AtomicOpSub(2); - TypedAtomicOpFunctor *amo_op_char = new AtomicOpSub('a'); + std::unique_ptr> amo_op_int = + std::make_unique>(2); + std::unique_ptr> amo_op_char = + std::make_unique>('a'); amo_op_int->execute(&test_int); amo_op_char->execute(&test_char); @@ -146,8 +154,10 @@ TEST(AmoTest, AtomicOpAdd) int test_int = 10; char test_char = 'c'; - TypedAtomicOpFunctor *amo_op_int = new AtomicOpAdd(2); - TypedAtomicOpFunctor *amo_op_char = new AtomicOpAdd(2); + std::unique_ptr> amo_op_int = + std::make_unique>(2); + std::unique_ptr> amo_op_char = + std::make_unique>(2); amo_op_int->execute(&test_int); amo_op_char->execute(&test_char); @@ -160,8 +170,10 @@ TEST(AmoTest, AtomicOpExch) int test_int = 10; char test_char = 'c'; - TypedAtomicOpFunctor *amo_op_int = new AtomicOpExch(2); - TypedAtomicOpFunctor *amo_op_char = new AtomicOpExch('a'); + std::unique_ptr> amo_op_int = + std::make_unique>(2); + std::unique_ptr> amo_op_char = + std::make_unique>('a'); amo_op_int->execute(&test_int); amo_op_char->execute(&test_char); @@ -174,8 +186,10 @@ TEST(AmoTest, AtomicOpXor) int test_int = 10; char test_char = 'c'; - TypedAtomicOpFunctor *amo_op_int = new AtomicOpXor(2); - TypedAtomicOpFunctor *amo_op_char = new AtomicOpXor('a'); + std::unique_ptr> amo_op_int = + std::make_unique>(2); + std::unique_ptr> amo_op_char = + std::make_unique>('a'); amo_op_int->execute(&test_int); amo_op_char->execute(&test_char); @@ -188,8 +202,10 @@ TEST(AmoTest, AtomicOpOr) int test_int = 8; bool test_bool = true; - TypedAtomicOpFunctor *amo_op_int = new AtomicOpOr(2); - TypedAtomicOpFunctor *amo_op_bool = new AtomicOpOr(false); + std::unique_ptr> amo_op_int = + std::make_unique>(2); + std::unique_ptr> amo_op_bool = + std::make_unique>(false); amo_op_int->execute(&test_int); amo_op_bool->execute(&test_bool); @@ -202,8 +218,10 @@ TEST(AmoTest, AtomicOpAnd) int test_int = 10; char test_char = 'c'; - TypedAtomicOpFunctor *amo_op_int = new AtomicOpAnd(6); - TypedAtomicOpFunctor *amo_op_char = new AtomicOpAnd('a'); + std::unique_ptr> amo_op_int = + std::make_unique>(6); + std::unique_ptr> amo_op_char = + std::make_unique>('a'); amo_op_int->execute(&test_int); amo_op_char->execute(&test_char); @@ -215,8 +233,8 @@ TEST(AmoTest, AtomicGeneric2Op) { int test_int = 9; - TypedAtomicOpFunctor *amo_op_int = - new AtomicGeneric2Op(9, multiply2Op); + std::unique_ptr> amo_op_int = + std::make_unique>(9, multiply2Op); amo_op_int->execute(&test_int); EXPECT_EQ(test_int, 81); @@ -226,8 +244,8 @@ TEST(AmoTest, AtomicGeneric3Op) { int test_int = 2; - TypedAtomicOpFunctor *amo_op_int = - new AtomicGeneric3Op(4, 3, multiply3Op); + std::unique_ptr> amo_op_int = + std::make_unique>(4, 3, multiply3Op); amo_op_int->execute(&test_int); EXPECT_EQ(test_int, 24); @@ -239,8 +257,8 @@ TEST(AmoTest, AtomicGenericPair3Op) std::array a = {6, 3}; std::array c = {10, 8}; - TypedAtomicOpFunctor *amo_op_int = - new AtomicGenericPair3Op(a, c, addSubColumns); + std::unique_ptr> amo_op_int = + std::make_unique>(a, c, addSubColumns); amo_op_int->execute(&test_int); EXPECT_EQ(test_int, 10); diff --git a/src/base/bitfield.hh b/src/base/bitfield.hh index eecea02981..bceed60f4e 100644 --- a/src/base/bitfield.hh +++ b/src/base/bitfield.hh @@ -41,9 +41,12 @@ #ifndef __BASE_BITFIELD_HH__ #define __BASE_BITFIELD_HH__ +#include #include +#include #include #include +#include #include namespace gem5 @@ -303,17 +306,31 @@ findMsbSet(uint64_t val) return msb; } -/** - * Returns the bit position of the LSB that is set in the input - * - * @ingroup api_bitfield - */ +namespace { +template +constexpr bool +hasBuiltinCtz() { +// Since the defined(__has_builtin) in the subsequent #if statement +// won't short-circuit the macro expansion of +// __has_builtin(__builtin_ctz), we must explicitly define it as zero +// if it's undefined to avoid a preprocessor error. +#ifndef __has_builtin +# define __has_builtin(foo) 0 +#endif +#if defined(__has_builtin) && __has_builtin(__builtin_ctz) + return sizeof(unsigned long long) >= sizeof(T); +#else + return false; +#endif +} + +[[maybe_unused]] constexpr int -findLsbSet(uint64_t val) -{ +findLsbSetFallback(uint64_t val) { int lsb = 0; - if (!val) + if (!val) { return sizeof(val) * 8; + } if (!bits(val, 31, 0)) { lsb += 32; val >>= 32; @@ -334,10 +351,58 @@ findLsbSet(uint64_t val) lsb += 2; val >>= 2; } - if (!bits(val, 0, 0)) + if (!bits(val, 0, 0)) { lsb += 1; + } return lsb; } +} // anonymous namespace + +/** + * Returns the bit position of the LSB that is set in the input + * That function will either use a builtin that exploit a "count trailing + * zeros" instruction or use fall back method, `findLsbSetFallback`. + * + * @ingroup api_bitfield + */ +constexpr int +findLsbSet(uint64_t val) { + if (val == 0) return 64; + + if constexpr (hasBuiltinCtz()) { + return __builtin_ctzll(val); + } else { + return findLsbSetFallback(val); + } +} + + +template +constexpr int +findLsbSet(std::bitset bs) +{ + if constexpr (N <= 64) { + return findLsbSet(bs.to_ullong()); + } else { + if (bs.none()) return N; + // Mask of ones + constexpr std::bitset mask(std::numeric_limits::max()); + // Is the lsb set in the rightmost 64 bits ? + auto nextQword{bs & mask}; + int i{0}; + while (nextQword.none()) { + // If no, shift by 64 bits and repeat + i += 64; + bs >>= 64; + nextQword = bs & mask; + } + // If yes, account for the bumber of 64-bit shifts and add the + // remaining using the uint64_t implementation. Store in intermediate + // variable to ensure valid conversion from ullong to uint64_t. + uint64_t remaining{nextQword.to_ullong()}; + return i + findLsbSet(remaining); + } +} /** * Returns the number of set ones in the provided value. diff --git a/src/base/bitfield.test.cc b/src/base/bitfield.test.cc index 1711ea68bf..94cff8e155 100644 --- a/src/base/bitfield.test.cc +++ b/src/base/bitfield.test.cc @@ -316,6 +316,7 @@ TEST(BitfieldTest, FindLsb) { uint64_t val = (1ULL << 63) + (1 << 1); EXPECT_EQ(1, findLsbSet(val)); + EXPECT_EQ(1, findLsbSetFallback(val)); } TEST(BitfieldTest, FindLsbZero) @@ -323,6 +324,23 @@ TEST(BitfieldTest, FindLsbZero) EXPECT_EQ(64, findLsbSet(0)); } +TEST(BitfieldTest, FindLsbGeneralized) +{ + static constexpr size_t N{1000}; + std::bitset bs{0}; + EXPECT_EQ(findLsbSet(bs), N); + for (size_t i{0}; i < N ; ++i) { + bs = std::bitset{1} << i; + ASSERT_EQ(findLsbSet(bs), i); + } + + const auto leadingOne = std::bitset{1} << (N-1); + for (size_t i{0}; i < N ; ++i) { + bs = leadingOne | (std::bitset{1} << i); + ASSERT_EQ(findLsbSet(bs), i); + } +} + /* * The following tests "popCount(X)". popCount counts the number of bits set to * one. diff --git a/src/base/cprintf_formats.hh b/src/base/cprintf_formats.hh index 4a64780c4a..e4c1048e01 100644 --- a/src/base/cprintf_formats.hh +++ b/src/base/cprintf_formats.hh @@ -34,6 +34,8 @@ #include #include +#include "base/stl_helpers.hh" + namespace gem5 { @@ -221,6 +223,7 @@ template static inline void _formatString(std::ostream &out, const T &data, Format &fmt) { + using stl_helpers::operator<<; if (fmt.width > 0) { std::stringstream foo; foo << data; diff --git a/src/base/inifile.cc b/src/base/inifile.cc index 8c0662d0e3..23253e9df5 100644 --- a/src/base/inifile.cc +++ b/src/base/inifile.cc @@ -42,17 +42,6 @@ namespace gem5 IniFile::IniFile() {} -IniFile::~IniFile() -{ - SectionTable::iterator i = table.begin(); - SectionTable::iterator end = table.end(); - - while (i != end) { - delete (*i).second; - ++i; - } -} - bool IniFile::load(const std::string &file) { @@ -82,15 +71,15 @@ IniFile::Section::addEntry(const std::string &entryName, if (ei == table.end()) { // new entry - table[entryName] = new Entry(value); + table.emplace(entryName, value); } else if (append) { // append new reult to old entry - ei->second->appendValue(value); + ei->second.appendValue(value); } else { // override old entry - ei->second->setValue(value); + ei->second.setValue(value); } } @@ -120,39 +109,42 @@ IniFile::Section::add(const std::string &assignment) IniFile::Entry * +IniFile::Section::findEntry(const std::string &entryName) +{ + return const_cast( + std::as_const(*this).findEntry(entryName)); +} + +const IniFile::Entry * IniFile::Section::findEntry(const std::string &entryName) const { referenced = true; - EntryTable::const_iterator ei = table.find(entryName); + auto ei = table.find(entryName); - return (ei == table.end()) ? NULL : ei->second; + return (ei == table.end()) ? nullptr : &ei->second; } IniFile::Section * IniFile::addSection(const std::string §ionName) { - SectionTable::iterator i = table.find(sectionName); - - if (i != table.end()) { - return i->second; - } - else { - // new entry - Section *sec = new Section(); - table[sectionName] = sec; - return sec; - } + return &table[sectionName]; } - IniFile::Section * +IniFile::findSection(const std::string §ionName) +{ + return const_cast( + std::as_const(*this).findSection(sectionName)); +} + +const IniFile::Section * IniFile::findSection(const std::string §ionName) const { - SectionTable::const_iterator i = table.find(sectionName); + auto i = table.find(sectionName); - return (i == table.end()) ? NULL : i->second; + return (i == table.end()) ? nullptr : &i->second; } @@ -215,11 +207,11 @@ bool IniFile::find(const std::string §ionName, const std::string &entryName, std::string &value) const { - Section *section = findSection(sectionName); + auto* section = findSection(sectionName); if (section == NULL) return false; - Entry *entry = section->findEntry(entryName); + auto* entry = section->findEntry(entryName); if (entry == NULL) return false; @@ -232,7 +224,7 @@ bool IniFile::entryExists(const std::string §ionName, const std::string &entryName) const { - Section *section = findSection(sectionName); + auto* section = findSection(sectionName); if (!section) return false; @@ -248,13 +240,13 @@ IniFile::sectionExists(const std::string §ionName) const bool -IniFile::Section::printUnreferenced(const std::string §ionName) +IniFile::Section::printUnreferenced(const std::string §ionName) const { bool unref = false; bool search_unref_entries = false; std::vector unref_ok_entries; - Entry *entry = findEntry("unref_entries_ok"); + auto* entry = findEntry("unref_entries_ok"); if (entry != NULL) { tokenize(unref_ok_entries, entry->getValue(), ' '); if (unref_ok_entries.size()) { @@ -262,10 +254,9 @@ IniFile::Section::printUnreferenced(const std::string §ionName) } } - for (EntryTable::iterator ei = table.begin(); - ei != table.end(); ++ei) { - const std::string &entryName = ei->first; - entry = ei->second; + for (auto& ei: table) { + const std::string &entryName = ei.first; + entry = &ei.second; if (entryName == "unref_section_ok" || entryName == "unref_entries_ok") @@ -294,32 +285,29 @@ IniFile::Section::printUnreferenced(const std::string §ionName) void IniFile::getSectionNames(std::vector &list) const { - for (SectionTable::const_iterator i = table.begin(); - i != table.end(); ++i) - { - list.push_back((*i).first); + for (auto& entry: table) { + auto& sectionName = entry.first; + list.push_back(sectionName); } } bool -IniFile::printUnreferenced() +IniFile::printUnreferenced() const { bool unref = false; - for (SectionTable::iterator i = table.begin(); - i != table.end(); ++i) { - const std::string §ionName = i->first; - Section *section = i->second; + for (auto& entry: table) { + auto& [sectionName, section] = entry; - if (!section->isReferenced()) { - if (section->findEntry("unref_section_ok") == NULL) { + if (!section.isReferenced()) { + if (section.findEntry("unref_section_ok") == NULL) { std::cerr << "Section " << sectionName << " not referenced." << std::endl; unref = true; } } else { - if (section->printUnreferenced(sectionName)) { + if (section.printUnreferenced(sectionName)) { unref = true; } } @@ -330,12 +318,11 @@ IniFile::printUnreferenced() void -IniFile::Section::dump(const std::string §ionName) +IniFile::Section::dump(const std::string §ionName) const { - for (EntryTable::iterator ei = table.begin(); - ei != table.end(); ++ei) { - std::cout << sectionName << ": " << (*ei).first << " => " - << (*ei).second->getValue() << "\n"; + for (auto& ei: table) { + std::cout << sectionName << ": " << ei.first << " => " + << ei.second.getValue() << "\n"; } } @@ -344,7 +331,7 @@ IniFile::dump() { for (SectionTable::iterator i = table.begin(); i != table.end(); ++i) { - i->second->dump(i->first); + i->second.dump(i->first); } } @@ -364,9 +351,9 @@ void IniFile::visitSection(const std::string §ionName, IniFile::VisitSectionCallback cb) { - const auto& section = *table.at(sectionName); + const auto& section = table.at(sectionName); for (const auto& pair : section) { - cb(pair.first, pair.second->getValue()); + cb(pair.first, pair.second.getValue()); } } diff --git a/src/base/inifile.hh b/src/base/inifile.hh index 72f1df7b05..d17193d5bf 100644 --- a/src/base/inifile.hh +++ b/src/base/inifile.hh @@ -72,7 +72,7 @@ class IniFile } /// Has this entry been used? - bool isReferenced() { return referenced; } + bool isReferenced() const { return referenced; } /// Fetch the value. const std::string &getValue() const; @@ -94,7 +94,7 @@ class IniFile class Section { /// EntryTable type. Map of strings to Entry object pointers. - typedef std::unordered_map EntryTable; + typedef std::unordered_map EntryTable; EntryTable table; ///< Table of entries. mutable bool referenced; ///< Has this section been used? @@ -107,7 +107,7 @@ class IniFile } /// Has this section been used? - bool isReferenced() { return referenced; } + bool isReferenced() const { return referenced; } /// Add an entry to the table. If an entry with the same name /// already exists, the 'append' parameter is checked If true, @@ -125,24 +125,25 @@ class IniFile /// Find the entry with the given name. /// @retval Pointer to the entry object, or NULL if none. - Entry *findEntry(const std::string &entryName) const; + Entry *findEntry(const std::string &entryName); + const Entry *findEntry(const std::string &entryName) const; /// Print the unreferenced entries in this section to cerr. /// Messages can be suppressed using "unref_section_ok" and /// "unref_entries_ok". /// @param sectionName Name of this section, for use in output message. /// @retval True if any entries were printed. - bool printUnreferenced(const std::string §ionName); + bool printUnreferenced(const std::string §ionName) const; /// Print the contents of this section to cout (for debugging). - void dump(const std::string §ionName); + void dump(const std::string §ionName) const; EntryTable::const_iterator begin() const; EntryTable::const_iterator end() const; }; /// SectionTable type. Map of strings to Section object pointers. - typedef std::unordered_map SectionTable; + typedef std::unordered_map SectionTable; protected: /// Hash of section names to Section object pointers. @@ -155,15 +156,13 @@ class IniFile /// Look up section with the given name. /// @retval Pointer to section object, or NULL if not found. - Section *findSection(const std::string §ionName) const; + Section *findSection(const std::string §ionName); + const Section *findSection(const std::string §ionName) const; public: /// Constructor. IniFile(); - /// Destructor. - ~IniFile(); - /// Load parameter settings from given istream. This is a helper /// function for load(string) and loadCPP(), which open a file /// and then pass it here. @@ -206,7 +205,7 @@ class IniFile /// Print unreferenced entries in object. Iteratively calls /// printUnreferend() on all the constituent sections. - bool printUnreferenced(); + bool printUnreferenced() const; /// Dump contents to cout. For debugging. void dump(); diff --git a/src/base/loader/elf_object.cc b/src/base/loader/elf_object.cc index 4b1467acf0..26fb443c79 100644 --- a/src/base/loader/elf_object.cc +++ b/src/base/loader/elf_object.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2013, 2019 ARM Limited + * Copyright (c) 2011-2013, 2019, 2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -178,27 +178,52 @@ ElfObject::ElfObject(ImageFileDataPtr ifd) : ObjectFile(ifd) if (!sym_name || sym_name[0] == '$') continue; - loader::Symbol symbol; - symbol.address = sym.st_value; - symbol.name = sym_name; - + loader::Symbol::Binding binding = + loader::Symbol::Binding::Global; switch (GELF_ST_BIND(sym.st_info)) { case STB_GLOBAL: - symbol.binding = loader::Symbol::Binding::Global; + binding = loader::Symbol::Binding::Global; break; case STB_LOCAL: - symbol.binding = loader::Symbol::Binding::Local; + binding = loader::Symbol::Binding::Local; break; case STB_WEAK: - symbol.binding = loader::Symbol::Binding::Weak; + binding = loader::Symbol::Binding::Weak; break; default: continue; } + loader::Symbol::SymbolType symbol_type = + loader::Symbol::SymbolType::NoType; + switch (GELF_ST_TYPE(sym.st_info)) { + case STT_NOTYPE: + symbol_type = loader::Symbol::SymbolType::NoType; + break; + case STT_OBJECT: + symbol_type = loader::Symbol::SymbolType::Object; + break; + case STT_FUNC: + symbol_type = loader::Symbol::SymbolType::Function; + break; + case STT_SECTION: + symbol_type = loader::Symbol::SymbolType::Section; + break; + case STT_FILE: + symbol_type = loader::Symbol::SymbolType::File; + break; + default: + symbol_type = loader::Symbol::SymbolType::Other; + break; + } + + loader::Symbol symbol( + binding, symbol_type, sym_name, sym.st_value, + sym.st_size); + if (_symtab.insert(symbol)) { DPRINTF(Loader, "Symbol: %-40s value %#x.\n", - symbol.name, symbol.address); + symbol.name(), symbol.address()); } } } diff --git a/src/base/loader/symtab.cc b/src/base/loader/symtab.cc index 941ea101c9..cea79c4cae 100644 --- a/src/base/loader/symtab.cc +++ b/src/base/loader/symtab.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2023 Arm Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2002-2005 The Regents of The University of Michigan * All rights reserved. * @@ -53,17 +65,17 @@ SymbolTable::clear() bool SymbolTable::insert(const Symbol &symbol) { - if (symbol.name.empty()) + if (symbol.name().empty()) return false; int idx = symbols.size(); - if (!nameMap.insert({ symbol.name, idx }).second) + if (!nameMap.insert({ symbol.name(), idx }).second) return false; // There can be multiple symbols for the same address, so always // update the addrTable multimap when we see a new symbol name. - addrMap.insert({ symbol.address, idx }); + addrMap.insert({ symbol.address(), idx }); symbols.emplace_back(symbol); @@ -79,8 +91,11 @@ SymbolTable::insert(const SymbolTable &other) nameMap.begin(), nameMap.end(), std::inserter(intersection, intersection.begin()), nameMap.value_comp()); - if (!intersection.empty()) + if (!intersection.empty()) { + warn("Cannot insert a new symbol table due to name collisions. " + "Adding prefix to each symbol's name can resolve this issue."); return false; + } for (const Symbol &symbol: other) insert(symbol); @@ -95,9 +110,15 @@ SymbolTable::serialize(const std::string &base, CheckpointOut &cp) const int i = 0; for (auto &symbol: symbols) { - paramOut(cp, csprintf("%s.addr_%d", base, i), symbol.address); - paramOut(cp, csprintf("%s.symbol_%d", base, i), symbol.name); - paramOut(cp, csprintf("%s.binding_%d", base, i), (int)symbol.binding); + paramOut(cp, csprintf("%s.addr_%d", base, i), symbol.address()); + if (symbol.sizeIsValid()) { + paramOut(cp, csprintf("%s.size_%d", base, i), + symbol.sizeOrDefault(0x0)); + } + paramOut(cp, csprintf("%s.symbol_%d", base, i), symbol.name()); + paramOut(cp, csprintf("%s.binding_%d", base, i), + (int)symbol.binding()); + paramOut(cp, csprintf("%s.type_%d", base, i), (int)symbol.type()); i++; } } @@ -111,14 +132,26 @@ SymbolTable::unserialize(const std::string &base, CheckpointIn &cp, paramIn(cp, base + ".size", size); for (int i = 0; i < size; ++i) { Addr address; + size_t size; std::string name; Symbol::Binding binding = default_binding; + Symbol::SymbolType type = Symbol::SymbolType::Other; paramIn(cp, csprintf("%s.addr_%d", base, i), address); + bool size_present = optParamIn( + cp, csprintf("%s.size_%d", base, i), size, false); paramIn(cp, csprintf("%s.symbol_%d", base, i), name); if (!optParamIn(cp, csprintf("%s.binding_%d", base, i), binding)) binding = default_binding; - insert({binding, name, address}); + if (!optParamIn(cp, csprintf("%s.type_%d", base, i), type)) + type = Symbol::SymbolType::Other; + if (size_present) { + insert(Symbol(binding, type, name, address, size)); + } else { + warn_once( + "warning: one or more Symbols does not have a valid size."); + insert(Symbol(binding, type, name, address)); + } } } diff --git a/src/base/loader/symtab.hh b/src/base/loader/symtab.hh index 2e50523c32..d33ed85f63 100644 --- a/src/base/loader/symtab.hh +++ b/src/base/loader/symtab.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2023 Arm Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2021 Daniel R. Carvalho * Copyright (c) 2002-2005 The Regents of The University of Michigan * All rights reserved. @@ -47,8 +59,9 @@ namespace gem5 namespace loader { -struct Symbol +class Symbol { + public: enum class Binding { Global, @@ -56,11 +69,85 @@ struct Symbol Weak }; - Binding binding; - std::string name; - Addr address; + // The ELF64_ST_TYPE field of gelf's st_info + enum class SymbolType + { + NoType, + Object, + Function, + Section, + File, + Other + }; + + Symbol(const Binding binding, const SymbolType type, + const std::string & name, const Addr addr, const size_t size) + : _binding(binding), _type(type), _name(name), _address(addr), + _size(size), _sizeIsValid(true) + {} + + Symbol(const Binding binding, const SymbolType type, + const std::string & name, const Addr addr) + : _binding(binding), _type(type), _name(name), _address(addr), + _size(0x0), _sizeIsValid(false) + {} + + Symbol(const Symbol & other) = default; + Symbol & operator=(const Symbol & other) = default; + + Binding binding() const { + return _binding; + } + + SymbolType type() const { + return _type; + } + + std::string name() const { + return _name; + } + + void rename(const std::string & new_name) { + _name = new_name; + } + + Addr address() const { + return _address; + } + + void relocate(const Addr new_addr) { + _address = new_addr; + } + + /** + * Return the Symbol size if it is valid, otherwise return the + * default value supplied. + * + * This method forces the client code to consider the possibility + * that the `SymbolTable` may contain `Symbol`s that do not have + * valid sizes. + */ + size_t sizeOrDefault(const size_t default_size) const { + return _sizeIsValid ? _size : default_size; + } + + /** + * Return whether the Symbol size is valid or not. + */ + bool sizeIsValid() const { + return _sizeIsValid; + } + + private: + Binding _binding; + SymbolType _type; + std::string _name; + Addr _address; + size_t _size; + bool _sizeIsValid; }; + class SymbolTable { public: @@ -159,7 +246,23 @@ class SymbolTable filterByBinding(Symbol::Binding binding) const { auto filt = [binding](const Symbol &symbol) { - return symbol.binding == binding; + return symbol.binding() == binding; + }; + return filter(filt); + } + + /** + * Generate a new table by applying a filter that only accepts the symbols + * whose type matches the given symbol type. + * + * @param The type that must be matched. + * @return A new table, filtered by type. + */ + SymbolTablePtr + filterBySymbolType(const Symbol::SymbolType& symbol_type) const + { + auto filt = [symbol_type](const Symbol &symbol) { + return symbol.type() == symbol_type; }; return filter(filt); } @@ -214,9 +317,9 @@ class SymbolTable { SymTabOp op = [addr_offset](SymbolTable &symtab, const Symbol &symbol) { - Symbol sym = symbol; - sym.address += addr_offset; - symtab.insert(sym); + symtab.insert( + Symbol(symbol.binding(), symbol.type(), symbol.name(), + symbol.address() + addr_offset)); }; return operate(op); } @@ -232,9 +335,9 @@ class SymbolTable mask(Addr m) const { SymTabOp op = [m](SymbolTable &symtab, const Symbol &symbol) { - Symbol sym = symbol; - sym.address &= m; - symtab.insert(sym); + symtab.insert( + Symbol(symbol.binding(), symbol.type(), symbol.name(), + symbol.address() & m)); }; return operate(op); } @@ -247,11 +350,11 @@ class SymbolTable * @retval SymbolTablePtr A pointer to the modified SymbolTable copy. */ SymbolTablePtr - rename(std::function func) const + rename(std::function func) const { SymTabOp op = [func](SymbolTable &symtab, const Symbol &symbol) { Symbol sym = symbol; - func(sym.name); + sym.rename(func(sym.name())); symtab.insert(sym); }; return operate(op); @@ -290,6 +393,17 @@ class SymbolTable return filterByBinding(Symbol::Binding::Weak); } + /** + * Generates a new symbol table containing only function symbols. + * + * @return The new table. + */ + SymbolTablePtr + functionSymbols() const + { + return filterBySymbolType(Symbol::SymbolType::Function); + } + /** * Serialize the table's contents. * diff --git a/src/base/loader/symtab.test.cc b/src/base/loader/symtab.test.cc index 313055392a..9a7b8c86b9 100644 --- a/src/base/loader/symtab.test.cc +++ b/src/base/loader/symtab.test.cc @@ -52,20 +52,26 @@ getSymbolError(const loader::Symbol& symbol, const loader::Symbol& expected) { std::stringstream ss; - if (symbol.binding != expected.binding) { + if (symbol.binding() != expected.binding()) { ss << " symbols' bindings do not match: seen `" << - (int)symbol.binding << "`, expected `" << - (int)expected.binding << "`.\n"; + (int)symbol.binding() << "`, expected `" << + (int)expected.binding() << "`.\n"; } - if (symbol.name != expected.name) { - ss << " symbols' names do not match: seen `" << symbol.name << - "`, expected `" << expected.name << "`.\n"; + if (symbol.type() != expected.type()) { + ss << " symbols' types do not match: seen `" << + (int)symbol.type() << "`, expected `" << + (int)expected.type() << "`.\n"; } - if (symbol.address != expected.address) { + if (symbol.name() != expected.name()) { + ss << " symbols' names do not match: seen `" << symbol.name() << + "`, expected `" << expected.name() << "`.\n"; + } + + if (symbol.address() != expected.address()) { ss << " symbols' addresses do not match: seen `" << - symbol.address << "`, expected `" << expected.address << "`.\n"; + symbol.address() << "`, expected `" << expected.address() << "`.\n"; } // No error, symbols match @@ -136,7 +142,9 @@ TEST(LoaderSymtabTest, InsertSymbolNoName) { loader::SymbolTable symtab; - loader::Symbol symbol = {loader::Symbol::Binding::Local, "", 0x10}; + loader::Symbol symbol = \ + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "", 0x10}; ASSERT_FALSE(symtab.insert(symbol)); ASSERT_TRUE(checkTable(symtab, {})); } @@ -146,7 +154,9 @@ TEST(LoaderSymtabTest, InsertOneSymbol) { loader::SymbolTable symtab; - loader::Symbol symbol = {loader::Symbol::Binding::Local, "symbol", 0x10}; + loader::Symbol symbol = \ + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}; ASSERT_TRUE(symtab.insert(symbol)); ASSERT_FALSE(symtab.empty()); @@ -160,8 +170,10 @@ TEST(LoaderSymtabTest, InsertSymbolExistingName) const std::string name = "symbol"; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, name, 0x10}, - {loader::Symbol::Binding::Local, name, 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + name, 0x10}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + name, 0x20}, }; ASSERT_TRUE(symtab.insert(symbols[0])); ASSERT_FALSE(symtab.insert(symbols[1])); @@ -177,8 +189,10 @@ TEST(LoaderSymtabTest, InsertSymbolExistingAddress) const Addr addr = 0x10; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", addr}, - {loader::Symbol::Binding::Local, "symbol2", addr}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", addr}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", addr}, }; ASSERT_TRUE(symtab.insert(symbols[0])); ASSERT_TRUE(symtab.insert(symbols[1])); @@ -193,9 +207,12 @@ TEST(LoaderSymtabTest, InsertMultipleSymbols) loader::SymbolTable symtab; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Local, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, }; EXPECT_TRUE(symtab.insert(symbols[0])); EXPECT_TRUE(symtab.insert(symbols[1])); @@ -212,9 +229,12 @@ TEST(LoaderSymtabTest, ClearMultiple) loader::SymbolTable symtab; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Local, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, }; EXPECT_TRUE(symtab.insert(symbols[0])); EXPECT_TRUE(symtab.insert(symbols[1])); @@ -234,9 +254,12 @@ TEST(LoaderSymtabTest, Offset) loader::SymbolTable symtab; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Local, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, }; EXPECT_TRUE(symtab.insert(symbols[0])); EXPECT_TRUE(symtab.insert(symbols[1])); @@ -250,9 +273,12 @@ TEST(LoaderSymtabTest, Offset) // Check that the new table is offset loader::Symbol expected_symbols[] = { - {symbols[0].binding, symbols[0].name, symbols[0].address + offset}, - {symbols[1].binding, symbols[1].name, symbols[1].address + offset}, - {symbols[2].binding, symbols[2].name, symbols[2].address + offset}, + {symbols[0].binding(), symbols[0].type(), symbols[0].name(), + symbols[0].address() + offset}, + {symbols[1].binding(), symbols[1].type(), symbols[1].name(), + symbols[1].address() + offset}, + {symbols[2].binding(), symbols[2].type(), symbols[2].name(), + symbols[2].address() + offset}, }; ASSERT_TRUE(checkTable(*symtab_new, {expected_symbols[0], expected_symbols[1], expected_symbols[2]})); @@ -267,10 +293,14 @@ TEST(LoaderSymtabTest, Mask) loader::SymbolTable symtab; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x1310}, - {loader::Symbol::Binding::Local, "symbol2", 0x2810}, - {loader::Symbol::Binding::Local, "symbol3", 0x2920}, - {loader::Symbol::Binding::Local, "symbol4", 0x3C20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x1310}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", 0x2810}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x2920}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol4", 0x3C20}, }; EXPECT_TRUE(symtab.insert(symbols[0])); EXPECT_TRUE(symtab.insert(symbols[1])); @@ -286,10 +316,14 @@ TEST(LoaderSymtabTest, Mask) // Check that the new table is masked loader::Symbol expected_symbols[] = { - {symbols[0].binding, symbols[0].name, symbols[0].address & mask}, - {symbols[1].binding, symbols[1].name, symbols[1].address & mask}, - {symbols[2].binding, symbols[2].name, symbols[2].address & mask}, - {symbols[3].binding, symbols[3].name, symbols[3].address & mask}, + {symbols[0].binding(), symbols[0].type(), symbols[0].name(), + symbols[0].address() & mask}, + {symbols[1].binding(), symbols[1].type(), symbols[1].name(), + symbols[1].address() & mask}, + {symbols[2].binding(), symbols[2].type(), symbols[2].name(), + symbols[2].address() & mask}, + {symbols[3].binding(), symbols[3].type(), symbols[3].name(), + symbols[3].address() & mask}, }; ASSERT_TRUE(checkTable(*symtab_new, {expected_symbols[0], expected_symbols[1], expected_symbols[2], expected_symbols[3]})); @@ -304,10 +338,14 @@ TEST(LoaderSymtabTest, Rename) loader::SymbolTable symtab; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Local, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, - {loader::Symbol::Binding::Local, "symbol4", 0x40}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol4", 0x40}, }; EXPECT_TRUE(symtab.insert(symbols[0])); EXPECT_TRUE(symtab.insert(symbols[1])); @@ -315,7 +353,7 @@ TEST(LoaderSymtabTest, Rename) EXPECT_TRUE(symtab.insert(symbols[3])); const auto symtab_new = - symtab.rename([](std::string &name) { name = name + "_suffix"; }); + symtab.rename([](const std::string &name) { return name + "_suffix"; }); // Check that the original table is not modified ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2], @@ -323,10 +361,14 @@ TEST(LoaderSymtabTest, Rename) // Check that the new table's symbols have been renamed loader::Symbol expected_symbols[] = { - {symbols[0].binding, symbols[0].name + "_suffix", symbols[0].address}, - {symbols[1].binding, symbols[1].name + "_suffix", symbols[1].address}, - {symbols[2].binding, symbols[2].name + "_suffix", symbols[2].address}, - {symbols[3].binding, symbols[3].name + "_suffix", symbols[3].address}, + {symbols[0].binding(), symbols[0].type(), symbols[0].name() + "_suffix", + symbols[0].address()}, + {symbols[1].binding(), symbols[1].type(), symbols[1].name() + "_suffix", + symbols[1].address()}, + {symbols[2].binding(), symbols[2].type(), symbols[2].name() + "_suffix", + symbols[2].address()}, + {symbols[3].binding(), symbols[3].type(), symbols[3].name() + "_suffix", + symbols[3].address()}, }; ASSERT_TRUE(checkTable(*symtab_new, {expected_symbols[0], expected_symbols[1], expected_symbols[2], expected_symbols[3]})); @@ -341,10 +383,14 @@ TEST(LoaderSymtabTest, RenameNonUnique) loader::SymbolTable symtab; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Local, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, - {loader::Symbol::Binding::Local, "symbol4", 0x40}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol4", 0x40}, }; EXPECT_TRUE(symtab.insert(symbols[0])); EXPECT_TRUE(symtab.insert(symbols[1])); @@ -352,10 +398,12 @@ TEST(LoaderSymtabTest, RenameNonUnique) EXPECT_TRUE(symtab.insert(symbols[3])); int i = 0; - const auto symtab_new = symtab.rename([&i](std::string &name) + const auto symtab_new = symtab.rename([&i](const std::string &name) { if ((i++ % 2) == 0) { - name = "NonUniqueName"; + return std::string("NonUniqueName"); + } else { + return name; } }); @@ -366,9 +414,12 @@ TEST(LoaderSymtabTest, RenameNonUnique) // Check that the new table's symbols have been renamed, yet it does not // contain the symbols with duplicated names loader::Symbol expected_symbols[] = { - {symbols[0].binding, "NonUniqueName", symbols[0].address}, - {symbols[1].binding, symbols[1].name, symbols[1].address}, - {symbols[3].binding, symbols[3].name, symbols[3].address}, + {symbols[0].binding(), symbols[0].type(), "NonUniqueName", + symbols[0].address()}, + {symbols[1].binding(), symbols[1].type(), symbols[1].name(), + symbols[1].address()}, + {symbols[3].binding(), symbols[3].type(), symbols[3].name(), + symbols[3].address()}, }; ASSERT_TRUE(checkTable(*symtab_new, {expected_symbols[0], expected_symbols[1], expected_symbols[2]})); @@ -383,11 +434,16 @@ TEST(LoaderSymtabTest, Globals) loader::SymbolTable symtab; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Global, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, - {loader::Symbol::Binding::Weak, "symbol4", 0x40}, - {loader::Symbol::Binding::Weak, "symbol5", 0x50} + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, + {loader::Symbol::Binding::Weak, loader::Symbol::SymbolType::Other, + "symbol4", 0x40}, + {loader::Symbol::Binding::Weak, loader::Symbol::SymbolType::Other, + "symbol5", 0x50} }; EXPECT_TRUE(symtab.insert(symbols[0])); EXPECT_TRUE(symtab.insert(symbols[1])); @@ -414,11 +470,16 @@ TEST(LoaderSymtabTest, Locals) loader::SymbolTable symtab; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Global, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, - {loader::Symbol::Binding::Weak, "symbol4", 0x40}, - {loader::Symbol::Binding::Weak, "symbol5", 0x50} + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, + {loader::Symbol::Binding::Weak, loader::Symbol::SymbolType::Other, + "symbol4", 0x40}, + {loader::Symbol::Binding::Weak, loader::Symbol::SymbolType::Other, + "symbol5", 0x50} }; EXPECT_TRUE(symtab.insert(symbols[0])); EXPECT_TRUE(symtab.insert(symbols[1])); @@ -445,11 +506,16 @@ TEST(LoaderSymtabTest, Weaks) loader::SymbolTable symtab; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Global, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, - {loader::Symbol::Binding::Weak, "symbol4", 0x40}, - {loader::Symbol::Binding::Weak, "symbol5", 0x50} + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, + {loader::Symbol::Binding::Weak, loader::Symbol::SymbolType::Other, + "symbol4", 0x40}, + {loader::Symbol::Binding::Weak, loader::Symbol::SymbolType::Other, + "symbol5", 0x50} }; EXPECT_TRUE(symtab.insert(symbols[0])); EXPECT_TRUE(symtab.insert(symbols[1])); @@ -467,12 +533,50 @@ TEST(LoaderSymtabTest, Weaks) ASSERT_TRUE(checkTable(*symtab_new, {symbols[3], symbols[4]})); } +/** + * Test the creation of a new filtered table containing only function symbols + * of the original table. Also verifies if the original table is kept the same. + */ +TEST(LoaderSymtabTest, FunctionSymbols) +{ + loader::SymbolTable symtab; + + loader::Symbol symbols[] = { + {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::NoType, + "symbol", 0x10}, + {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::File, + "symbol2", 0x20}, + {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::Function, + "symbol3", 0x30}, + {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::Object, + "symbol4", 0x40}, + {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::Function, + "symbol5", 0x50} + }; + EXPECT_TRUE(symtab.insert(symbols[0])); + EXPECT_TRUE(symtab.insert(symbols[1])); + EXPECT_TRUE(symtab.insert(symbols[2])); + EXPECT_TRUE(symtab.insert(symbols[3])); + EXPECT_TRUE(symtab.insert(symbols[4])); + + const auto symtab_new = symtab.functionSymbols(); + + // Check that the original table is not modified + ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2], + symbols[3], symbols[4]})); + + // Check that the new table only contains function symbols + ASSERT_TRUE(checkTable(*symtab_new, {symbols[2], symbols[4]})); +} + /** Test searching for a non-existent address. */ TEST(LoaderSymtabTest, FindNonExistentAddress) { loader::SymbolTable symtab; - loader::Symbol symbol = {loader::Symbol::Binding::Local, "symbol", 0x10}; + loader::Symbol symbol = \ + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}; EXPECT_TRUE(symtab.insert(symbol)); ASSERT_EQ(symtab.find(0x0), symtab.end()); @@ -484,15 +588,18 @@ TEST(LoaderSymtabTest, FindUniqueAddress) loader::SymbolTable symtab; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Local, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, }; EXPECT_TRUE(symtab.insert(symbols[0])); EXPECT_TRUE(symtab.insert(symbols[1])); EXPECT_TRUE(symtab.insert(symbols[2])); - const auto it = symtab.find(symbols[2].address); + const auto it = symtab.find(symbols[2].address()); ASSERT_NE(it, symtab.end()); ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[2]); } @@ -506,15 +613,18 @@ TEST(LoaderSymtabTest, FindNonUniqueAddress) const Addr addr = 0x20; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Local, "symbol2", addr}, - {loader::Symbol::Binding::Local, "symbol3", addr}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", addr}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", addr}, }; EXPECT_TRUE(symtab.insert(symbols[0])); EXPECT_TRUE(symtab.insert(symbols[1])); EXPECT_TRUE(symtab.insert(symbols[2])); - const auto it = symtab.find(symbols[1].address); + const auto it = symtab.find(symbols[1].address()); ASSERT_NE(it, symtab.end()); ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[1]); } @@ -524,7 +634,9 @@ TEST(LoaderSymtabTest, FindNonExistentName) { loader::SymbolTable symtab; - loader::Symbol symbol = {loader::Symbol::Binding::Local, "symbol", 0x10}; + loader::Symbol symbol = \ + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}; EXPECT_TRUE(symtab.insert(symbol)); const auto it = symtab.find("symbol2"); @@ -537,15 +649,18 @@ TEST(LoaderSymtabTest, FindExistingName) loader::SymbolTable symtab; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Local, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, }; EXPECT_TRUE(symtab.insert(symbols[0])); EXPECT_TRUE(symtab.insert(symbols[1])); EXPECT_TRUE(symtab.insert(symbols[2])); - const auto it = symtab.find(symbols[1].name); + const auto it = symtab.find(symbols[1].name()); ASSERT_NE(it, symtab.end()); ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[1]); } @@ -556,13 +671,15 @@ TEST(LoaderSymtabTest, FindNearestExact) loader::SymbolTable symtab; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Local, "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, }; EXPECT_TRUE(symtab.insert(symbols[0])); EXPECT_TRUE(symtab.insert(symbols[1])); - const auto it = symtab.findNearest(symbols[1].address); + const auto it = symtab.findNearest(symbols[1].address()); ASSERT_NE(it, symtab.end()); ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[1]); } @@ -575,10 +692,12 @@ TEST(LoaderSymtabTest, FindNearestRound) { loader::SymbolTable symtab; - loader::Symbol symbol = {loader::Symbol::Binding::Local, "symbol", 0x10}; + loader::Symbol symbol = \ + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}; EXPECT_TRUE(symtab.insert(symbol)); - const auto it = symtab.findNearest(symbol.address + 0x1); + const auto it = symtab.findNearest(symbol.address() + 0x1); ASSERT_NE(it, symtab.end()); ASSERT_PRED_FORMAT2(checkSymbol, *it, symbol); } @@ -593,17 +712,19 @@ TEST(LoaderSymtabTest, FindNearestRoundWithNext) loader::SymbolTable symtab; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Local, "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, }; EXPECT_TRUE(symtab.insert(symbols[0])); EXPECT_TRUE(symtab.insert(symbols[1])); Addr next_addr; - const auto it = symtab.findNearest(symbols[0].address + 0x1, next_addr); + const auto it = symtab.findNearest(symbols[0].address() + 0x1, next_addr); ASSERT_NE(it, symtab.end()); ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[0]); - ASSERT_EQ(next_addr, symbols[1].address); + ASSERT_EQ(next_addr, symbols[1].address()); } /** @@ -615,11 +736,13 @@ TEST(LoaderSymtabTest, FindNearestRoundWithNextNonExistent) { loader::SymbolTable symtab; - loader::Symbol symbol = {loader::Symbol::Binding::Local, "symbol", 0x10}; + loader::Symbol symbol = \ + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}; EXPECT_TRUE(symtab.insert(symbol)); Addr next_addr; - const auto it = symtab.findNearest(symbol.address + 0x1, next_addr); + const auto it = symtab.findNearest(symbol.address() + 0x1, next_addr); ASSERT_NE(it, symtab.end()); ASSERT_PRED_FORMAT2(checkSymbol, *it, symbol); ASSERT_EQ(next_addr, 0); @@ -633,10 +756,12 @@ TEST(LoaderSymtabTest, FindNearestNonExistent) { loader::SymbolTable symtab; - loader::Symbol symbol = {loader::Symbol::Binding::Local, "symbol", 0x10}; + loader::Symbol symbol = \ + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}; EXPECT_TRUE(symtab.insert(symbol)); - const auto it = symtab.findNearest(symbol.address - 0x1); + const auto it = symtab.findNearest(symbol.address() - 0x1); ASSERT_EQ(it, symtab.end()); } @@ -648,12 +773,17 @@ TEST(LoaderSymtabTest, InsertTableConflicting) { const std::string name = "symbol"; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, name, 0x10}, - {loader::Symbol::Binding::Local, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, - {loader::Symbol::Binding::Local, "symbol4", 0x40}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + name, 0x10}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol4", 0x40}, // Introduce name conflict - {loader::Symbol::Binding::Local, name, 0x50}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + name, 0x50}, }; // Populate table 1 @@ -682,11 +812,16 @@ TEST(LoaderSymtabTest, InsertTableConflicting) TEST(LoaderSymtabTest, InsertTable) { loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Local, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, - {loader::Symbol::Binding::Local, "symbol4", 0x40}, - {loader::Symbol::Binding::Local, "symbol5", 0x50}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol4", 0x40}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol5", 0x50}, }; // Populate table 1 @@ -719,9 +854,12 @@ TEST_F(LoaderSymtabSerializationFixture, Serialization) // Populate the table loader::SymbolTable symtab; loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Local, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, }; EXPECT_TRUE(symtab.insert(symbols[0])); EXPECT_TRUE(symtab.insert(symbols[1])); @@ -735,22 +873,31 @@ TEST_F(LoaderSymtabSerializationFixture, Serialization) // Verify the output ASSERT_THAT(cp.str(), ::testing::StrEq("\n[Section1]\ntest.size=3\n" "test.addr_0=16\ntest.symbol_0=symbol\ntest.binding_0=1\n" + "test.type_0=5\n" "test.addr_1=32\ntest.symbol_1=symbol2\ntest.binding_1=1\n" - "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n")); + "test.type_1=5\n" + "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n" + "test.type_2=5\n")); } /** Test unserialization. */ TEST_F(LoaderSymtabSerializationFixture, Unserialization) { loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Local, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, }; simulateSerialization("\n[Section1]\ntest.size=3\n" "test.addr_0=16\ntest.symbol_0=symbol\ntest.binding_0=1\n" + "test.type_0=5\n" "test.addr_1=32\ntest.symbol_1=symbol2\ntest.binding_1=1\n" - "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n"); + "test.type_1=5\n" + "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n" + "test.type_2=5\n"); loader::SymbolTable unserialized_symtab; CheckpointIn cp(getDirName()); @@ -771,14 +918,19 @@ TEST_F(LoaderSymtabSerializationFixture, Unserialization) TEST_F(LoaderSymtabSerializationFixture, UnserializationMissingBinding) { loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Global, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, }; simulateSerialization("\n[Section1]\ntest.size=3\n" "test.addr_0=16\ntest.symbol_0=symbol\ntest.binding_0=1\n" - "test.addr_1=32\ntest.symbol_1=symbol2\n" - "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n"); + "test.type_0=5\n" + "test.addr_1=32\ntest.symbol_1=symbol2\ntest.type_1=5\n" + "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n" + "test.type_2=5\n"); loader::SymbolTable unserialized_symtab; CheckpointIn cp(getDirName()); @@ -801,14 +953,20 @@ TEST_F(LoaderSymtabSerializationFixture, UnserializationMissingBindingChangeDefault) { loader::Symbol symbols[] = { - {loader::Symbol::Binding::Local, "symbol", 0x10}, - {loader::Symbol::Binding::Weak, "symbol2", 0x20}, - {loader::Symbol::Binding::Local, "symbol3", 0x30}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol", 0x10}, + {loader::Symbol::Binding::Weak, loader::Symbol::SymbolType::Other, + "symbol2", 0x20}, + {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, + "symbol3", 0x30}, }; simulateSerialization("\n[Section1]\ntest.size=3\n" "test.addr_0=16\ntest.symbol_0=symbol\ntest.binding_0=1\n" + "test.type_0=5\n" "test.addr_1=32\ntest.symbol_1=symbol2\n" - "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n"); + "test.type_1=5\n" + "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n" + "test.type_2=5\n"); loader::SymbolTable unserialized_symtab; CheckpointIn cp(getDirName()); diff --git a/src/base/memoizer.hh b/src/base/memoizer.hh index c3390887b7..4d3816e9ea 100644 --- a/src/base/memoizer.hh +++ b/src/base/memoizer.hh @@ -85,7 +85,7 @@ class Memoizer using ret_type = Ret; using args_type = std::tuple; - constexpr Memoizer(Ret _func(Args...)) + constexpr Memoizer(Ret (*_func)(Args...)) : func(_func) { validateMemoizer(); diff --git a/src/base/stats/Kconfig b/src/base/stats/Kconfig new file mode 100644 index 0000000000..2b7bb9610d --- /dev/null +++ b/src/base/stats/Kconfig @@ -0,0 +1,27 @@ +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +config HAVE_HDF5 + def_bool $(HAVE_HDF5) diff --git a/src/base/stl_helpers.hh b/src/base/stl_helpers.hh index d12f266350..b70eea9992 100644 --- a/src/base/stl_helpers.hh +++ b/src/base/stl_helpers.hh @@ -26,57 +26,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BASE_STL_HELPERS_HH__ -#define __BASE_STL_HELPERS_HH__ +#ifndef BASE_STL_HELPERS_HH +#define BASE_STL_HELPERS_HH -#include -#include -#include -#include +#include "base/stl_helpers/hash_helpers.hh" +#include "base/stl_helpers/ostream_helpers.hh" -#include "base/compiler.hh" - -namespace gem5 -{ - -namespace stl_helpers -{ - -template -struct IsHelpedContainer : public std::false_type {}; - -template -struct IsHelpedContainer> : public std::true_type {}; - -template -constexpr bool IsHelpedContainerV = IsHelpedContainer::value; - -/** - * Write out all elements in an stl container as a space separated - * list enclosed in square brackets - * - * @ingroup api_base_utils - */ - -template -std::enable_if_t, std::ostream &> -operator<<(std::ostream& out, const T &t) -{ - out << "[ "; - bool first = true; - auto printer = [&first, &out](const auto &elem) { - if (first) - out << elem; - else - out << " " << elem; - }; - std::for_each(t.begin(), t.end(), printer); - out << " ]"; - out << std::flush; - return out; -} - -} // namespace stl_helpers -} // namespace gem5 - -#endif // __BASE_STL_HELPERS_HH__ +#endif // BASE_STL_HELPERS_HH diff --git a/src/base/stl_helpers/SConscript b/src/base/stl_helpers/SConscript new file mode 100644 index 0000000000..7328cf066a --- /dev/null +++ b/src/base/stl_helpers/SConscript @@ -0,0 +1,29 @@ +# Copyright (c) 2023 Arteris, Inc. and its applicable licensors and affiliates. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer; redistributions in binary +# form must reproduce the above copyright notice, this list of conditions and +# the following disclaimer in the documentation and/or other materials provided +# with the distribution; neither the name of the copyright holders nor the +# names of its contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +Import('*') + +GTest('hash_helpers.test', 'hash_helpers.test.cc') +GTest('ostream_helpers.test', 'ostream_helpers.test.cc') diff --git a/src/base/stl_helpers/hash_helpers.hh b/src/base/stl_helpers/hash_helpers.hh new file mode 100644 index 0000000000..1432d522bd --- /dev/null +++ b/src/base/stl_helpers/hash_helpers.hh @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2023 Arteris, Inc. and its applicable licensors and + * affiliates. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_STL_HELPERS_HASH_HELPERS_HH +#define BASE_STL_HELPERS_HASH_HELPERS_HH + +#include +#include +#include +#include +#include +#include +#include + +#include "base/type_traits.hh" + +#include +#include +#include +#include + +namespace gem5::stl_helpers +{ + +namespace hash_impl +{ +// The math in hash_combine and hash_refine functions are inspired from Jon +// Maiga's work hosted at https://github.com/jonmaiga/mx3 under the CC0 +// license. It makes use of two components: a stream mixer for combination and +// a scalar mixer for refinement. +// The stream mixer is a lighter weight function with lower entropy used to +// combine hash values while the scalar mixer is a high entropy function that +// increases the overall hashing quality. +// The tradeoff of not using hash_refine has not been thoroughtly tested and is +// only done based on Maiga's return on exprerience. +static constexpr uint64_t C = 0xbea225f9eb34556d; +template +constexpr size_t hash_combine(T... hashes) { + // gcc reports unused variable if T is the empty pack + [[maybe_unused]] auto combine = [](uint64_t a, uint64_t b) { + b *= C; + b ^= b >> 39; + a += b * C; + a *= C; + return a; + }; + // The following couple of expressions is equivalent to a hypothetical + // functional "acc = hashes.fold_left(0, combine)". The comma operator + // effectively repeats the expression in the second level parenthesis for + // each argument in the parameter pack hashes, in order. Thus, final value + // of acc is the recursive combination of all hashes. + uint64_t acc{0}; + ((acc = combine(acc, static_cast(hashes))), ...); + return static_cast(acc); +} + +constexpr size_t hash_refine(size_t x) { + x ^= x >> 32; + x *= C; + x ^= x >> 29; + x *= C; + x ^= x >> 32; + x *= C; + x ^= x >> 29; + return static_cast(x); +} + +// SFINAE-enabled hash functor +template +struct hash; + +// Reuse std::hash whenever possible +template +struct hash>>: std::hash +{}; + +// Enable type deduction for hash object construction +template +constexpr auto make_hash_for(const T&) { + return hash(); +} + +// Compute a hash without the hassle of constructing a hash functor +template +constexpr auto hash_value(const T& v) { + return make_hash_for(v)(v); +} + +// Hash for tuple +template +struct hash> +{ + constexpr size_t operator()(const std::tuple& t) const { + if constexpr (sizeof...(T) == 0) { + return 0; + } else { + return std::apply([](const auto&... e){ + return hash_refine(hash_combine(hash_value(e)...)); + }, t); + } + } +}; + +// Hash for pairs (based on hash for 2-uple) +template +struct hash> +{ + constexpr size_t operator()(const std::pair& p) const { + return hash_value(std::tie(p.first, p.second)); + } +}; + +// Hash for any iterable of stl_helpers::hash-enabled types. +template +struct hash && is_iterable_v>> +{ + constexpr size_t operator()(const T& t) const { + auto b = begin(t); + auto e = end(t); + if (b == e) return 0; + // Equivalent to hypothetical functional style + // return t.map(hash_value).reduce(hash_combine) + auto h = std::accumulate(next(b), e, hash_value(*b), + [](const auto& acc, const auto& val) { + return hash_combine(acc, hash_value(val)); + }); + return hash_refine(h); + } +}; + +template +constexpr bool is_hash_enabled = false; + +template +constexpr bool is_hash_enabled()(std::declval()))>> = true; + +} // namespace hash_impl + +// Export useful hash_impl functions +using hash_impl::hash; +using hash_impl::make_hash_for; +using hash_impl::hash_value; +using hash_impl::is_hash_enabled; + +/* + * Provide unordered_map and unordered_set with stl_helpers::hash functions. + * These aliases enable clean use of stl_helpers::hash as default Hash template + * parameter. The reason for not using an alias is that template type aliases + * with default template arguments do not behave well with template parameter + * deductions in certain situations. One must remember that std::unordered_X + * is not a polymorphic type and as such, gem5::stl_helpers::unordered_X shall + * never be owned as a std::unordered_X. + */ +template< + typename Key, + typename T, + typename Hash = hash, + typename KeyEqual = std::equal_to, + typename Allocator = std::allocator< std::pair >> +struct unordered_map: std::unordered_map +{}; + +template< + typename Key, + typename Hash = hash, + typename KeyEqual = std::equal_to, + typename Allocator = std::allocator> +struct unordered_set: std::unordered_set +{}; + +} // namespace gem5::stl_helpers + +#endif // BASE_STL_HELPERS_HASH_HELPERS_HH diff --git a/src/base/stl_helpers/hash_helpers.test.cc b/src/base/stl_helpers/hash_helpers.test.cc new file mode 100644 index 0000000000..f6f07c5808 --- /dev/null +++ b/src/base/stl_helpers/hash_helpers.test.cc @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023 Arteris, Inc. and its applicable licensors and + * affiliates. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include + +#include "base/stl_helpers/hash_helpers.hh" + +using namespace gem5; + +TEST(HashHelpers, isHashEnabled) +{ + EXPECT_TRUE(stl_helpers::is_hash_enabled); + EXPECT_TRUE(stl_helpers::is_hash_enabled); + EXPECT_TRUE(stl_helpers::is_hash_enabled); + EXPECT_TRUE(stl_helpers::is_hash_enabled); + EXPECT_TRUE(stl_helpers::is_hash_enabled); + using vector_t = std::vector; + EXPECT_TRUE(stl_helpers::is_hash_enabled); + using tuple_t = std::tuple; + EXPECT_TRUE(stl_helpers::is_hash_enabled); + EXPECT_TRUE((stl_helpers::is_hash_enabled>)); + EXPECT_TRUE((stl_helpers::is_hash_enabled< + std::unordered_map>)); +} + +// The following tests do not test the hash value as it is considered an +// implementation detail and there is no contract on the way that value is +// computed. Testing for hash quality is extremelly computationnaly intensive +// and is not suitable for unit tests. Consider these tests to be more of a +// "does it compile?" check as well as a small set of examples for the user. +TEST(HashHelpers, hashPair) +{ + auto p = std::make_pair(1, std::string("hello")); + auto hashVal = stl_helpers::hash_value(p); + auto hashFunc = stl_helpers::hash>{}; + EXPECT_EQ(hashVal, hashFunc(p)); +} + +TEST(HashHelpers, hashTuple) +{ + auto t = std::make_tuple(1, "hello", 4.2, std::make_pair(true, 0.f)); + auto hashVal = stl_helpers::hash_value(t); + auto hashFunc = stl_helpers::hash{}; + EXPECT_EQ(hashVal, hashFunc(t)); +} + +TEST(HashHelpers, hashVector) +{ + auto v = std::vector{1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto hashVal = stl_helpers::hash_value(v); + auto hashFunc = stl_helpers::hash{}; + EXPECT_EQ(hashVal, hashFunc(v)); +} diff --git a/src/base/stl_helpers/ostream_helpers.hh b/src/base/stl_helpers/ostream_helpers.hh new file mode 100644 index 0000000000..680d55f23c --- /dev/null +++ b/src/base/stl_helpers/ostream_helpers.hh @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2023 Arteris, Inc. and its applicable licensors and + * affiliates. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_STL_HELPERS_OSTREAM_HELPERS_HH +#define BASE_STL_HELPERS_OSTREAM_HELPERS_HH + +#include +#include +#include +#include + +#include "base/type_traits.hh" +#include "magic_enum/magic_enum.hh" + +namespace gem5::stl_helpers +{ + +/* + * Wrap any object in a Printer object to force using a opExtract_impl printing + * function. This is not required for types that do not already enable + * operator<< in another namespace. However, to enable the special printing + * function for, e.g., raw pointers, those must be wrapped in a Printer. + */ +template +struct Printer +{ + Printer(const T& value): value{value} {} + const T& value; +}; + +namespace opExtract_impl +{ + +/* + * In order to provide a specialization for operator<< with stl_helpers-enabled + * types + * without loosing the hability to use it with other types, a dual-dispatch + * mechanism is used. The only entry point in the system is through a primary + * dispatch function that won't resolve for non-helped types. Then, recursive + * calls go through the secondary dispatch interface that sort between helped + * and non-helped types. Helped types will enter the system back through the + * primary dispatch interface while other types will look for operator<< + * through regular lookup, especially ADL. + */ + +template +std::ostream& +opExtractSecDisp(std::ostream& os, const T& v); + +template +std::enable_if_t, +std::ostream&> +opExtractPrimDisp(std::ostream& os, const E& e) +{ + return os << magic_enum::enum_name(e); +} + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, const std::tuple& p) +{ + std::apply([&](auto&&... e) { + std::size_t n{0}; + os << '('; + ((opExtractSecDisp(os, e) << (++n != sizeof...(T) ? ", " : "")), ...); + os << ')'; + }, p); + return os; +} + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, const std::pair& p) +{ + return opExtractPrimDisp(os, std::tie(p.first, p.second)); +} + +template +std::enable_if_t, std::ostream&> +opExtractPrimDisp(std::ostream& os, const T& v) +{ + os << "[ "; + for (auto& e: v) { + opExtractSecDisp(os, e) << ", "; + } + return os << ']'; +} + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, const std::optional& o) +{ + if (o) { + return opExtractSecDisp(os, *o); + } else { + return os << "(-)"; + } +} + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, T* p); + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, const std::shared_ptr& p) +{ + return opExtractPrimDisp(os, p.get()); +} + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, const std::unique_ptr& p) +{ + return opExtractPrimDisp(os, p.get()); +} + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, const Printer& p); + +template +constexpr bool isOpExtractNativelySupported = false; + +template +constexpr bool isOpExtractNativelySupported() << std::declval())>> = true; + +template +constexpr bool isOpExtractHelped = false; + +template +constexpr bool isOpExtractHelped(), + std::declval()))>> + = true; + +template +constexpr bool needsDispatch = + isOpExtractHelped && !isOpExtractNativelySupported; + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, T* p) +{ + if (!p) { + return os << "nullptr"; + } + if constexpr (isOpExtractHelped || isOpExtractNativelySupported) { + os << '(' << p << ": "; + opExtractSecDisp(os, *p); + return os << ')'; + } else { + return os << p; + } +} + +template +std::ostream& +opExtractPrimDisp(std::ostream& os, const Printer& p) +{ + if constexpr (isOpExtractHelped) { + return opExtractPrimDisp(os, p.value); + } else { + return os << p.value; + } +} + + +template +std::ostream& +opExtractSecDisp(std::ostream& os, const T& v) +{ + if constexpr (needsDispatch) { + return opExtractPrimDisp(os, v); + } else { + return os << v; + } +} + +} // namespace opExtract_impl + +// use the Printer wrapper or add "using stl_helpers::operator<<" in the scope +// where you want to use that operator<<. +template +std::enable_if_t, std::ostream&> +operator<<(std::ostream& os, const T& v) +{ + return opExtract_impl::opExtractPrimDisp(os, v); +} + +} // namespace gem5::stl_helpers + +#endif // BASE_STL_HELPERS_OSTREAM_HELPERS_HH diff --git a/src/base/stl_helpers/ostream_helpers.test.cc b/src/base/stl_helpers/ostream_helpers.test.cc new file mode 100644 index 0000000000..84c936f4a8 --- /dev/null +++ b/src/base/stl_helpers/ostream_helpers.test.cc @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2023 Arteris, Inc. and its applicable licensors and + * affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer; redistributions in binary + * form must reproduce the above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or other materials + * provided with the distribution; neither the name of the copyright holders + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +#include "base/stl_helpers/ostream_helpers.hh" + + +TEST(OstreamHelpers, pair) { + using gem5::stl_helpers::operator<<; + auto p = std::make_pair(1, 2); + std::ostringstream os; + os << p; + EXPECT_EQ(os.str(), "(1, 2)"); +} + +TEST(OstreamHelpers, tuple) { + using gem5::stl_helpers::operator<<; + auto t = std::make_tuple(true, + std::make_pair("Hello", std::string_view("World")), '!'); + std::ostringstream os; + os << t; + EXPECT_EQ(os.str(), "(1, (Hello, World), !)"); +} + +TEST(OstreamHelpers, vector) { + using gem5::stl_helpers::operator<<; + auto v = std::vector{"abc", "defg", "hijklm", "\n"}; + std::ostringstream os; + os << v; + EXPECT_EQ(os.str(), "[ abc, defg, hijklm, \n, ]"); +} + +TEST(OstreamHelpers, map) { + using gem5::stl_helpers::operator<<; + auto m = std::map{{'a', 0}, {'b', 1}, {'c', 2}, {'d', 3}}; + std::ostringstream os; + os << m; + EXPECT_EQ(os.str(), "[ (a, 0), (b, 1), (c, 2), (d, 3), ]"); +} + +TEST(OstreamHelpers, optional) { + using gem5::stl_helpers::operator<<; + auto m = std::make_optional(42); + std::ostringstream os; + os << m; + EXPECT_EQ(os.str(), "42"); + os.str(""); + m.reset(); + os << m; + EXPECT_EQ(os.str(), "(-)"); +} + +TEST(OstreamHelpers, printer) { + std::string hello = "Hello"; + std::ostringstream os; + os << hello; + EXPECT_EQ(os.str(), hello); + + std::ostringstream os2; + os2 << gem5::stl_helpers::Printer(hello); + EXPECT_EQ(os2.str(), "[ H, e, l, l, o, ]"); +} + + +TEST(OstreamHelpers, pointers) { + auto helped_representation = [](const auto& val) { + std::ostringstream os; + os << gem5::stl_helpers::Printer(val); + return os.str(); + }; + auto expected_representation = [&](const auto& ptr) { + using gem5::stl_helpers::operator<<; + std::ostringstream os; + auto* raw_ptr = &*ptr; + os << '(' << raw_ptr << ": " << *ptr << ')'; + return os.str(); + }; + + int x = 42; + auto* ptr = &x; + EXPECT_EQ(helped_representation(ptr), expected_representation(ptr)); + + auto uptr = std::make_unique("Hello, World!"); + EXPECT_EQ(helped_representation(uptr), expected_representation(uptr)); + + auto sptr = std::make_shared>(); + EXPECT_EQ(helped_representation(sptr), expected_representation(sptr)); +} diff --git a/src/base/trie.hh b/src/base/trie.hh index 477bfbde14..47d60b7632 100644 --- a/src/base/trie.hh +++ b/src/base/trie.hh @@ -70,7 +70,7 @@ class Trie Value *value; Node *parent; - Node *kids[2]; + std::unique_ptr kids[2]; Node(Key _key, Key _mask, Value *_val) : key(_key & _mask), mask(_mask), value(_val), @@ -83,16 +83,8 @@ class Trie void clear() { - if (kids[1]) { - kids[1]->clear(); - delete kids[1]; - kids[1] = NULL; - } - if (kids[0]) { - kids[0]->clear(); - delete kids[0]; - kids[0] = NULL; - } + kids[1].reset(); + kids[0].reset(); } void @@ -188,9 +180,9 @@ class Trie return node; if (node->kids[0] && node->kids[0]->matches(key)) - node = node->kids[0]; + node = node->kids[0].get(); else if (node->kids[1] && node->kids[1]->matches(key)) - node = node->kids[1]; + node = node->kids[1].get(); else node = NULL; } @@ -225,8 +217,8 @@ class Trie // Walk past all the nodes this new node will be inserted after. They // can be ignored for the purposes of this function. Node *node = &head; - while (goesAfter(&node, node->kids[0], key, new_mask) || - goesAfter(&node, node->kids[1], key, new_mask)) + while (goesAfter(&node, node->kids[0].get(), key, new_mask) || + goesAfter(&node, node->kids[1].get(), key, new_mask)) {} assert(node); @@ -239,14 +231,13 @@ class Trie } for (unsigned int i = 0; i < 2; i++) { - Node *&kid = node->kids[i]; - Node *new_node; + auto& kid = node->kids[i]; if (!kid) { // No kid. Add a new one. - new_node = new Node(key, new_mask, val); + auto new_node = std::make_unique(key, new_mask, val); new_node->parent = node; - kid = new_node; - return new_node; + kid = std::move(new_node); + return kid.get(); } // Walk down the leg until something doesn't match or we run out @@ -266,23 +257,23 @@ class Trie continue; // At the point we walked to above, add a new node. - new_node = new Node(key, cur_mask, NULL); + auto new_node = std::make_unique(key, cur_mask, nullptr); new_node->parent = node; - kid->parent = new_node; - new_node->kids[0] = kid; - kid = new_node; + kid->parent = new_node.get(); + new_node->kids[0] = std::move(kid); + kid = std::move(new_node); // If we ran out of bits, the value goes right here. if (cur_mask == new_mask) { - new_node->value = val; - return new_node; + kid->value = val; + return kid.get(); } // Still more bits to deal with, so add a new node for that path. - new_node = new Node(key, new_mask, val); - new_node->parent = kid; - kid->kids[1] = new_node; - return new_node; + new_node = std::make_unique(key, new_mask, val); + new_node->parent = kid.get(); + kid->kids[1] = std::move(new_node); + return kid->kids[1].get(); } panic("Reached the end of the Trie insert function!\n"); @@ -332,23 +323,22 @@ class Trie if (node->kids[0]) node->kids[0]->parent = parent; // Figure out which kid we are, and update our parent's pointers. - if (parent->kids[0] == node) - parent->kids[0] = node->kids[0]; - else if (parent->kids[1] == node) - parent->kids[1] = node->kids[0]; + if (parent->kids[0].get() == node) + parent->kids[0] = std::move(node->kids[0]); + else if (parent->kids[1].get() == node) + parent->kids[1] = std::move(node->kids[0]); else panic("Trie: Inconsistent parent/kid relationship.\n"); // Make sure if the parent only has one kid, it's kid[0]. if (parent->kids[1] && !parent->kids[0]) { - parent->kids[0] = parent->kids[1]; - parent->kids[1] = NULL; + parent->kids[0] = std::move(parent->kids[1]); + parent->kids[1] = nullptr; } // If the parent has less than two kids and no cargo and isn't the // root, delete it too. if (!parent->kids[1] && !parent->value && parent->parent) remove(parent); - delete node; return val; } diff --git a/src/base/type_traits.hh b/src/base/type_traits.hh index 1fec93d9d1..fd13044765 100644 --- a/src/base/type_traits.hh +++ b/src/base/type_traits.hh @@ -27,8 +27,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BASE_TYPETRAITS_HH__ -#define __BASE_TYPETRAITS_HH__ +#ifndef BASE_TYPETRAITS_HH +#define BASE_TYPETRAITS_HH #include #include @@ -92,6 +92,30 @@ template using MemberFunctionArgsTuple_t = typename MemberFunctionSignature::argsTuple_t; + +// iterable type trait +template +struct is_iterable: std::false_type {}; + +template +struct is_iterable())), + decltype(end(std::declval()))>>: std::true_type {}; + +template +constexpr bool is_iterable_v = is_iterable::value; + +// std::hash-enabled type trait +template +struct is_std_hash_enabled: std::false_type {}; + +template +struct is_std_hash_enabled())>>: std::true_type {}; + +template +constexpr bool is_std_hash_enabled_v = is_std_hash_enabled::value; + } // namespace gem5 -#endif // __BASE_TYPETRAITS_HH__ +#endif // BASE_TYPETRAITS_HH diff --git a/src/base/version.cc b/src/base/version.cc index bfff67f5b6..30b2bddb73 100644 --- a/src/base/version.cc +++ b/src/base/version.cc @@ -32,6 +32,6 @@ namespace gem5 /** * @ingroup api_base_utils */ -const char *gem5Version = "23.0.1.0"; +const char *gem5Version = "23.1.0.0"; } // namespace gem5 diff --git a/src/base/vnc/Vnc.py b/src/base/vnc/Vnc.py index e7012ecb06..5ee03c3f8b 100644 --- a/src/base/vnc/Vnc.py +++ b/src/base/vnc/Vnc.py @@ -33,9 +33,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * from m5.objects.Graphics import * +from m5.params import * +from m5.SimObject import SimObject class VncInput(SimObject): diff --git a/src/cpu/BaseCPU.py b/src/cpu/BaseCPU.py index 556af52612..9ba60ef1b8 100644 --- a/src/cpu/BaseCPU.py +++ b/src/cpu/BaseCPU.py @@ -40,20 +40,19 @@ import sys -from m5.SimObject import * from m5.defines import buildEnv -from m5.params import * -from m5.proxy import * -from m5.util.fdthelper import * - -from m5.objects.ClockedObject import ClockedObject -from m5.objects.XBar import L2XBar -from m5.objects.InstTracer import InstTracer -from m5.objects.CPUTracers import ExeTracer -from m5.objects.SubSystem import SubSystem from m5.objects.ClockDomain import * +from m5.objects.ClockedObject import ClockedObject +from m5.objects.CPUTracers import ExeTracer +from m5.objects.InstTracer import InstTracer from m5.objects.Platform import Platform from m5.objects.ResetPort import ResetResponsePort +from m5.objects.SubSystem import SubSystem +from m5.objects.XBar import L2XBar +from m5.params import * +from m5.proxy import * +from m5.SimObject import * +from m5.util.fdthelper import * default_tracer = ExeTracer() @@ -293,8 +292,7 @@ class BaseCPU(ClockedObject): # Generate nodes from the BaseCPU children (hence under the root node, # and don't add them as subnode). Please note: this is mainly needed # for the ISA class, to generate the PMU entry in the DTB. - for child_node in self.recurseDeviceTree(state): - yield child_node + yield from self.recurseDeviceTree(state) def __init__(self, **kwargs): super().__init__(**kwargs) diff --git a/src/cpu/CPUTracers.py b/src/cpu/CPUTracers.py index f491a0ef43..ce6c3b4e33 100644 --- a/src/cpu/CPUTracers.py +++ b/src/cpu/CPUTracers.py @@ -24,9 +24,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * from m5.objects.InstTracer import InstTracer +from m5.params import * +from m5.SimObject import SimObject class ExeTracer(InstTracer): diff --git a/tests/configs/minor-timing.py b/src/cpu/Capstone.py similarity index 86% rename from tests/configs/minor-timing.py rename to src/cpu/Capstone.py index e6680d7702..ac6e0b7e95 100644 --- a/tests/configs/minor-timing.py +++ b/src/cpu/Capstone.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013 ARM Limited +# Copyright (c) 2023 Arm Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -10,9 +10,6 @@ # unmodified and in its entirety in all distributions of the software, # modified or unmodified, in source code or in binary form. # -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# All rights reserved. -# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: redistributions of source code must retain the above copyright @@ -36,9 +33,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * -from base_config import * +from m5.objects.InstTracer import InstDisassembler +from m5.params import * +from m5.SimObject import SimObject -root = BaseSESystemUniprocessor( - mem_mode="timing", mem_class=DDR3_1600_8x8, cpu_class=MinorCPU -).create_root() + +class CapstoneDisassembler(InstDisassembler): + type = "CapstoneDisassembler" + cxx_class = "gem5::trace::CapstoneDisassembler" + cxx_header = "cpu/capstone.hh" + abstract = True diff --git a/src/cpu/CheckerCPU.py b/src/cpu/CheckerCPU.py index beb04b79e1..9cd0ce4c33 100644 --- a/src/cpu/CheckerCPU.py +++ b/src/cpu/CheckerCPU.py @@ -24,9 +24,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * - from m5.objects.BaseCPU import BaseCPU +from m5.params import * from m5.SimObject import SimObject diff --git a/src/cpu/CpuCluster.py b/src/cpu/CpuCluster.py index 42a71122a3..c26bc653af 100644 --- a/src/cpu/CpuCluster.py +++ b/src/cpu/CpuCluster.py @@ -33,8 +33,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.SubSystem import SubSystem +from m5.params import * class CpuCluster(SubSystem): diff --git a/src/cpu/DummyChecker.py b/src/cpu/DummyChecker.py index 916d25a92a..7176498e6c 100644 --- a/src/cpu/DummyChecker.py +++ b/src/cpu/DummyChecker.py @@ -33,8 +33,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.CheckerCPU import CheckerCPU +from m5.params import * class DummyChecker(CheckerCPU): diff --git a/src/cpu/FuncUnit.py b/src/cpu/FuncUnit.py index 012dfd0ee4..cba3eda878 100644 --- a/src/cpu/FuncUnit.py +++ b/src/cpu/FuncUnit.py @@ -36,8 +36,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * +from m5.SimObject import SimObject class OpClass(Enum): diff --git a/src/cpu/InstPBTrace.py b/src/cpu/InstPBTrace.py index 167443d3cd..ca50ed4644 100644 --- a/src/cpu/InstPBTrace.py +++ b/src/cpu/InstPBTrace.py @@ -24,10 +24,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * - from m5.objects.InstTracer import InstTracer +from m5.params import * +from m5.SimObject import SimObject class InstPBTrace(InstTracer): diff --git a/src/cpu/Kconfig b/src/cpu/Kconfig new file mode 100644 index 0000000000..e1f96678c8 --- /dev/null +++ b/src/cpu/Kconfig @@ -0,0 +1,35 @@ +# Copyright 2023 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +config HAVE_CAPSTONE + def_bool $(HAVE_CAPSTONE) + +rsource "kvm/Kconfig" + +config USE_CAPSTONE + depends on HAVE_CAPSTONE + depends on USE_ARM_ISA + bool "Use CapstoneDisassembler" + default y diff --git a/src/cpu/SConscript b/src/cpu/SConscript index d6dcd2f6ea..64c64903bd 100644 --- a/src/cpu/SConscript +++ b/src/cpu/SConscript @@ -1,6 +1,6 @@ # -*- mode:python -*- -# Copyright (c) 2020 ARM Limited +# Copyright (c) 2020, 2023 Arm Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -116,6 +116,11 @@ Source('thread_context.cc') Source('thread_state.cc') Source('timing_expr.cc') +if env['CONF']['USE_CAPSTONE']: + SourceLib('capstone') + Source('capstone.cc') + SimObject('Capstone.py', sim_objects=['CapstoneDisassembler']) + SimObject('DummyChecker.py', sim_objects=['DummyChecker']) Source('checker/cpu.cc') DebugFlag('Checker') diff --git a/tests/configs/pc-switcheroo-full.py b/src/cpu/SConsopts similarity index 80% rename from tests/configs/pc-switcheroo-full.py rename to src/cpu/SConsopts index a69f80c341..94e55ece32 100644 --- a/tests/configs/pc-switcheroo-full.py +++ b/src/cpu/SConsopts @@ -1,5 +1,4 @@ -# Copyright (c) 2012 ARM Limited -# Copyright (c) 2013 Mark D. Hill and David A. Wood +# Copyright (c) 2023 Arm Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -34,15 +33,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * -from x86_generic import * -import switcheroo +Import('*') -root = LinuxX86FSSwitcheroo( - mem_class=DDR3_1600_8x8, - cpu_classes=(AtomicSimpleCPU, TimingSimpleCPU, DerivO3CPU), -).create_root() +from gem5_scons import warning -# Setup a custom test method that uses the switcheroo tester that -# switches between CPU models. -run_test = switcheroo.run_test +import gem5_scons + +with gem5_scons.Configure(main) as conf: + # Check for + conf.env['CONF']['HAVE_CAPSTONE'] = conf.CheckHeader('capstone/capstone.h', '<>') + + if conf.env['CONF']['HAVE_CAPSTONE']: + conf.env.TagImplies('capstone', 'gem5 lib') + else: + warning("Header file not found.\n" + "This host has no capstone library installed.") diff --git a/src/cpu/StaticInstFlags.py b/src/cpu/StaticInstFlags.py index d562dd5645..4ab6cc499c 100644 --- a/src/cpu/StaticInstFlags.py +++ b/src/cpu/StaticInstFlags.py @@ -1,4 +1,14 @@ -# Copyright (c) 2020 ARM Limited +# Copyright (c) 2020, 2023 Arm Limited +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# # Copyright (c) 2003-2005 The Regents of The University of Michigan # Copyright (c) 2013 Advanced Micro Devices, Inc. # All rights reserved. @@ -75,6 +85,7 @@ class StaticInstFlags(Enum): "IsNonSpeculative", # Should not be executed speculatively "IsQuiesce", # Is a quiesce instruction "IsUnverifiable", # Can't be verified by a checker + "IsPseudo", # Is a gem5 pseudo-op "IsSyscall", # Causes a system call to be emulated in syscall # emulation mode. # Flags for microcode diff --git a/src/cpu/base.cc b/src/cpu/base.cc index a61c99796c..9e015f8c16 100644 --- a/src/cpu/base.cc +++ b/src/cpu/base.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2012,2016-2017, 2019-2020 ARM Limited + * Copyright (c) 2011-2012,2016-2017, 2019-2020 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -257,8 +257,8 @@ BaseCPU::mwait(ThreadID tid, PacketPtr pkt) AddressMonitor &monitor = addressMonitor[tid]; if (!monitor.gotWakeup) { - int block_size = cacheLineSize(); - uint64_t mask = ~((uint64_t)(block_size - 1)); + Addr block_size = cacheLineSize(); + Addr mask = ~(block_size - 1); assert(pkt->req->hasPaddr()); monitor.pAddr = pkt->getAddr() & mask; @@ -282,8 +282,8 @@ BaseCPU::mwaitAtomic(ThreadID tid, ThreadContext *tc, BaseMMU *mmu) RequestPtr req = std::make_shared(); Addr addr = monitor.vAddr; - int block_size = cacheLineSize(); - uint64_t mask = ~((uint64_t)(block_size - 1)); + Addr block_size = cacheLineSize(); + Addr mask = ~(block_size - 1); int size = block_size; //The address of the next line if it crosses a cache line boundary. @@ -795,8 +795,8 @@ BaseCPU::traceFunctionsInternal(Addr pc) currentFunctionStart = pc; currentFunctionEnd = pc + 1; } else { - sym_str = it->name; - currentFunctionStart = it->address; + sym_str = it->name(); + currentFunctionStart = it->address(); } ccprintf(*functionTraceStream, " (%d)\n%d: %s", diff --git a/src/cpu/base.hh b/src/cpu/base.hh index 3976b66fe4..a6c80dadbe 100644 --- a/src/cpu/base.hh +++ b/src/cpu/base.hh @@ -143,7 +143,7 @@ class BaseCPU : public ClockedObject bool _switchedOut; /** Cache the cache line size that we get from the system */ - const unsigned int _cacheLineSize; + const Addr _cacheLineSize; /** Global CPU statistics that are merged into the Root object. */ struct GlobalStats : public statistics::Group @@ -394,7 +394,7 @@ class BaseCPU : public ClockedObject /** * Get the cache line size of the system. */ - inline unsigned int cacheLineSize() const { return _cacheLineSize; } + inline Addr cacheLineSize() const { return _cacheLineSize; } /** * Serialize this object to the given output stream. diff --git a/src/cpu/capstone.cc b/src/cpu/capstone.cc new file mode 100644 index 0000000000..4c2896312d --- /dev/null +++ b/src/cpu/capstone.cc @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 Arm Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cpu/capstone.hh" + +#include "base/output.hh" + +namespace gem5 +{ + +namespace trace +{ + +std::string +CapstoneDisassembler::disassemble(StaticInstPtr inst, + const PCStateBase &pc, + const loader::SymbolTable *symtab) const +{ + std::string inst_dist; + if (inst->isPseudo() || inst->isMicroop()) { + // Capstone doesn't have any visibility over microops nor over + // gem5 pseudo ops. Use native disassembler instead + inst_dist = InstDisassembler::disassemble(inst, pc, symtab); + } else { + // Stripping the extended fields from the ExtMachInst + auto mach_inst = inst->getEMI() & mask(inst->size() * 8); + + cs_insn *insn; + // capstone disassembler + if (const csh *curr_handle = currHandle(pc); curr_handle != nullptr) { + size_t count = cs_disasm(*curr_handle, (uint8_t*)&mach_inst, + inst->size(), 0, 0, &insn); + + // As we are passing only one instruction, we are expecting one instruction only + // being disassembled + assert(count <= 1); + + for (int idx = 0; idx < count; idx++) { + inst_dist += csprintf(" %s %s", insn[idx].mnemonic, insn[idx].op_str); + } + } else { + // No valid handle; return an invalid string + inst_dist += " capstone failure"; + } + } + + return inst_dist; +} + +CapstoneDisassembler::CapstoneDisassembler(const Params &p) + : InstDisassembler(p) +{ +} + +} // namespace trace +} // namespace gem5 diff --git a/src/cpu/capstone.hh b/src/cpu/capstone.hh new file mode 100644 index 0000000000..1a197e5086 --- /dev/null +++ b/src/cpu/capstone.hh @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2023 Arm Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __CPU_CAPSTONE_HH__ +#define __CPU_CAPSTONE_HH__ + +#include + +#include "params/CapstoneDisassembler.hh" +#include "sim/insttracer.hh" + +namespace gem5 +{ + +class ThreadContext; + +namespace trace { + +/** + * Capstone Disassembler: + * The disassembler relies on the capstone library to convert + * the StaticInst encoding into the disassembled string. + * + * One thing to keep in mind is that the disassembled + * instruction might not coincide with the instruction being + * decoded + executed in gem5. This could be the case if + * there was a bug in either gem5 or in capstone itself. + * This scenatio is not possible with the native gem5 disassembler + * as the instruction mnemonic is tightly coupled with the + * decoded(=generated) instruction (you print what you decode) + * + * The Capstone dispatches to the native disassembler in + * two cases: + * + * a) m5 pseudo ops + * b) micro-ops + */ +class CapstoneDisassembler : public InstDisassembler +{ + public: + PARAMS(CapstoneDisassembler); + CapstoneDisassembler(const Params &p); + + std::string + disassemble(StaticInstPtr inst, + const PCStateBase &pc, + const loader::SymbolTable *symtab) const override; + + protected: + + /** + * Return a pointer to the current capstone handle (csh). + * + * Any ISA extension of the Capstone disassembler should + * initialize (with cs_open) one or more capstone handles + * at construcion time. + * (You might need more than one handle in case the ISA + * has more than one mode of operation, e.g. arm and arm64) + * The current handle in use should be returned every time + * the currHandle is called. + */ + virtual const csh* currHandle(const PCStateBase &pc) const = 0; +}; + +} // namespace trace +} // namespace gem5 + +#endif // __CPU_CAPSTONE_HH__ diff --git a/src/cpu/exetrace.cc b/src/cpu/exetrace.cc index 22d0d4be69..2be3557d4b 100644 --- a/src/cpu/exetrace.cc +++ b/src/cpu/exetrace.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019 ARM Limited + * Copyright (c) 2017, 2019, 2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -81,11 +81,11 @@ ExeTracerRecord::traceInst(const StaticInstPtr &inst, bool ran) if (debug::ExecSymbol && (!FullSystem || !in_user_mode) && (it = loader::debugSymbolTable.findNearest(cur_pc)) != loader::debugSymbolTable.end()) { - Addr delta = cur_pc - it->address; + Addr delta = cur_pc - it->address(); if (delta) - ccprintf(outs, " @%s+%d", it->name, delta); + ccprintf(outs, " @%s+%d", it->name(), delta); else - ccprintf(outs, " @%s", it->name); + ccprintf(outs, " @%s", it->name()); } if (inst->isMicroop()) { @@ -101,7 +101,7 @@ ExeTracerRecord::traceInst(const StaticInstPtr &inst, bool ran) // outs << std::setw(26) << std::left; - outs << inst->disassemble(cur_pc, &loader::debugSymbolTable); + outs << tracer.disassemble(inst, *pc, &loader::debugSymbolTable); if (ran) { outs << " : "; diff --git a/src/cpu/exetrace.hh b/src/cpu/exetrace.hh index 143cfa0eb3..3fbeb98bc3 100644 --- a/src/cpu/exetrace.hh +++ b/src/cpu/exetrace.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2023 Arm Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2001-2005 The Regents of The University of Michigan * All rights reserved. * @@ -49,14 +61,19 @@ class ExeTracerRecord : public InstRecord public: ExeTracerRecord(Tick _when, ThreadContext *_thread, const StaticInstPtr _staticInst, const PCStateBase &_pc, + const ExeTracer &_tracer, const StaticInstPtr _macroStaticInst = NULL) - : InstRecord(_when, _thread, _staticInst, _pc, _macroStaticInst) + : InstRecord(_when, _thread, _staticInst, _pc, _macroStaticInst), + tracer(_tracer) { } void traceInst(const StaticInstPtr &inst, bool ran); void dump(); + + protected: + const ExeTracer &tracer; }; class ExeTracer : public InstTracer @@ -75,7 +92,7 @@ class ExeTracer : public InstTracer return NULL; return new ExeTracerRecord(when, tc, - staticInst, pc, macroStaticInst); + staticInst, pc, *this, macroStaticInst); } }; diff --git a/src/cpu/kvm/BaseKvmCPU.py b/src/cpu/kvm/BaseKvmCPU.py index 610663fa41..2c90acf3e8 100644 --- a/src/cpu/kvm/BaseKvmCPU.py +++ b/src/cpu/kvm/BaseKvmCPU.py @@ -33,12 +33,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import * -from m5.params import * -from m5.proxy import * - from m5.objects.BaseCPU import BaseCPU from m5.objects.KvmVM import KvmVM +from m5.params import * +from m5.proxy import * +from m5.SimObject import * class BaseKvmCPU(BaseCPU): diff --git a/src/cpu/kvm/Kconfig b/src/cpu/kvm/Kconfig new file mode 100644 index 0000000000..2b76e94360 --- /dev/null +++ b/src/cpu/kvm/Kconfig @@ -0,0 +1,33 @@ +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +config KVM_ISA + string + default "$(KVM_ISA)" + +config USE_KVM + depends on KVM_ISA != "" + bool "Enable hardware virtualized (KVM) CPU models" + default y diff --git a/src/cpu/kvm/KvmVM.py b/src/cpu/kvm/KvmVM.py index cdb826cf6c..5198de10f2 100644 --- a/src/cpu/kvm/KvmVM.py +++ b/src/cpu/kvm/KvmVM.py @@ -35,7 +35,6 @@ from m5.params import * from m5.proxy import * - from m5.SimObject import SimObject diff --git a/src/cpu/kvm/SConsopts b/src/cpu/kvm/SConsopts index 275eedaa54..1363d60951 100644 --- a/src/cpu/kvm/SConsopts +++ b/src/cpu/kvm/SConsopts @@ -61,13 +61,5 @@ with gem5_scons.Configure(main) as conf: warning("perf_event headers lack support for the exclude_host " "attribute. KVM instruction counts will be inaccurate.") - -def create_use_kvm_var(): - if main['CONF']['HAVE_KVM'] and main['CONF']['KVM_ISA']: - sticky_vars.Add(BoolVariable('USE_KVM', - 'Enable hardware virtualized (KVM) CPU models', True)) - else: - main['CONF']['USE_KVM'] = False - warning("Cannot enable KVM, host seems to lack KVM support") - -AfterSConsopts(create_use_kvm_var) +if not main['CONF']['KVM_ISA']: + warning("Can not enable KVM, host seems to lack KVM support") diff --git a/src/cpu/minor/BaseMinorCPU.py b/src/cpu/minor/BaseMinorCPU.py index c20a310447..7110caac2c 100644 --- a/src/cpu/minor/BaseMinorCPU.py +++ b/src/cpu/minor/BaseMinorCPU.py @@ -37,15 +37,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.defines import buildEnv +from m5.objects.BaseCPU import BaseCPU +from m5.objects.BranchPredictor import * +from m5.objects.DummyChecker import DummyChecker +from m5.objects.FuncUnit import OpClass +from m5.objects.TimingExpr import TimingExpr from m5.params import * from m5.proxy import * from m5.SimObject import SimObject -from m5.objects.BaseCPU import BaseCPU -from m5.objects.DummyChecker import DummyChecker -from m5.objects.BranchPredictor import * -from m5.objects.TimingExpr import TimingExpr - -from m5.objects.FuncUnit import OpClass class MinorOpClass(SimObject): diff --git a/src/cpu/minor/SConscript b/src/cpu/minor/SConscript index 9603b4114c..0b98037d6d 100644 --- a/src/cpu/minor/SConscript +++ b/src/cpu/minor/SConscript @@ -40,7 +40,7 @@ Import('*') -if not env['CONF']['USE_NULL_ISA']: +if env['CONF']['BUILD_ISA']: SimObject('BaseMinorCPU.py', sim_objects=[ 'MinorOpClass', 'MinorOpClassSet', 'MinorFUTiming', 'MinorFU', 'MinorFUPool', 'BaseMinorCPU'], diff --git a/src/cpu/minor/fetch1.hh b/src/cpu/minor/fetch1.hh index f6a796ce82..b65bb70d7b 100644 --- a/src/cpu/minor/fetch1.hh +++ b/src/cpu/minor/fetch1.hh @@ -213,13 +213,13 @@ class Fetch1 : public Named /** Line snap size in bytes. All fetches clip to make their ends not * extend beyond this limit. Setting this to the machine L1 cache line * length will result in fetches never crossing line boundaries. */ - unsigned int lineSnap; + Addr lineSnap; /** Maximum fetch width in bytes. Setting this (and lineSnap) to the * machine L1 cache line length will result in fetches of whole cache * lines. Setting this to sizeof(MachInst) will result it fetches of * single instructions (except near the end of lineSnap lines) */ - unsigned int maxLineWidth; + Addr maxLineWidth; /** Maximum number of fetches allowed in flight (in queues or memory) */ unsigned int fetchLimit; diff --git a/src/cpu/minor/lsq.hh b/src/cpu/minor/lsq.hh index 4d7c351e7a..e30a615803 100644 --- a/src/cpu/minor/lsq.hh +++ b/src/cpu/minor/lsq.hh @@ -548,7 +548,7 @@ class LSQ : public Named const unsigned int inMemorySystemLimit; /** Memory system access width (and snap) in bytes */ - const unsigned int lineWidth; + const Addr lineWidth; public: /** The LSQ consists of three queues: requests, transfers and the diff --git a/src/cpu/minor/scoreboard.cc b/src/cpu/minor/scoreboard.cc index 475d650d3a..25228c1ef5 100644 --- a/src/cpu/minor/scoreboard.cc +++ b/src/cpu/minor/scoreboard.cc @@ -62,10 +62,13 @@ Scoreboard::findIndex(const RegId& reg, Index &scoreboard_index) ret = true; break; case VecRegClass: - case VecElemClass: scoreboard_index = vecRegOffset + reg.index(); ret = true; break; + case VecElemClass: + scoreboard_index = vecRegElemOffset + reg.index(); + ret = true; + break; case VecPredRegClass: scoreboard_index = vecPredRegOffset + reg.index(); ret = true; diff --git a/src/cpu/minor/scoreboard.hh b/src/cpu/minor/scoreboard.hh index d3df324b99..dd42bc6f67 100644 --- a/src/cpu/minor/scoreboard.hh +++ b/src/cpu/minor/scoreboard.hh @@ -71,6 +71,7 @@ class Scoreboard : public Named const unsigned floatRegOffset; const unsigned ccRegOffset; const unsigned vecRegOffset; + const unsigned vecRegElemOffset; const unsigned vecPredRegOffset; const unsigned matRegOffset; @@ -115,7 +116,8 @@ class Scoreboard : public Named floatRegOffset(intRegOffset + reg_classes.at(IntRegClass)->numRegs()), ccRegOffset(floatRegOffset + reg_classes.at(FloatRegClass)->numRegs()), vecRegOffset(ccRegOffset + reg_classes.at(CCRegClass)->numRegs()), - vecPredRegOffset(vecRegOffset + + vecRegElemOffset(vecRegOffset + reg_classes.at(VecRegClass)->numRegs()), + vecPredRegOffset(vecRegElemOffset + reg_classes.at(VecElemClass)->numRegs()), matRegOffset(vecPredRegOffset + reg_classes.at(VecPredRegClass)->numRegs()), diff --git a/src/cpu/nativetrace.cc b/src/cpu/nativetrace.cc index 3070205b9f..60efc791de 100644 --- a/src/cpu/nativetrace.cc +++ b/src/cpu/nativetrace.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2023 Arm Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2006-2009 The Regents of The University of Michigan * All rights reserved. * @@ -49,6 +61,17 @@ NativeTrace::NativeTrace(const Params &p) fd = native_listener->accept(); } +NativeTraceRecord::NativeTraceRecord( + NativeTrace *_parent, + Tick _when, ThreadContext *_thread, + const StaticInstPtr _staticInst, const PCStateBase &_pc, + const StaticInstPtr _macroStaticInst) + : ExeTracerRecord(_when, _thread, _staticInst, _pc, + *_parent, _macroStaticInst), + parent(_parent) +{ +} + void NativeTraceRecord::dump() { diff --git a/src/cpu/nativetrace.hh b/src/cpu/nativetrace.hh index a19acaca3f..a0866e130f 100644 --- a/src/cpu/nativetrace.hh +++ b/src/cpu/nativetrace.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2023 Arm Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2006-2009 The Regents of The University of Michigan * All rights reserved. * @@ -50,20 +62,16 @@ class NativeTrace; class NativeTraceRecord : public ExeTracerRecord { - protected: - NativeTrace * parent; - public: - NativeTraceRecord(NativeTrace * _parent, + NativeTraceRecord(NativeTrace *_parent, Tick _when, ThreadContext *_thread, const StaticInstPtr _staticInst, const PCStateBase &_pc, - const StaticInstPtr _macroStaticInst=nullptr) - : ExeTracerRecord(_when, _thread, _staticInst, _pc, _macroStaticInst), - parent(_parent) - { - } + const StaticInstPtr _macroStaticInst=nullptr); void dump(); + + private: + NativeTrace *parent; }; class NativeTrace : public ExeTracer diff --git a/src/cpu/o3/BaseO3CPU.py b/src/cpu/o3/BaseO3CPU.py index 2e1a602e4c..dfb1068c7c 100644 --- a/src/cpu/o3/BaseO3CPU.py +++ b/src/cpu/o3/BaseO3CPU.py @@ -37,14 +37,13 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.defines import buildEnv -from m5.params import * -from m5.proxy import * - from m5.objects.BaseCPU import BaseCPU -from m5.objects.FUPool import * # from m5.objects.O3Checker import O3Checker from m5.objects.BranchPredictor import * +from m5.objects.FUPool import * +from m5.params import * +from m5.proxy import * class SMTFetchPolicy(ScopedEnum): diff --git a/src/cpu/o3/BaseO3Checker.py b/src/cpu/o3/BaseO3Checker.py index 7b480f8057..cda6dad57e 100644 --- a/src/cpu/o3/BaseO3Checker.py +++ b/src/cpu/o3/BaseO3Checker.py @@ -24,8 +24,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.CheckerCPU import CheckerCPU +from m5.params import * class BaseO3Checker(CheckerCPU): diff --git a/src/cpu/o3/FUPool.py b/src/cpu/o3/FUPool.py index 4e18094ef4..67f523787b 100644 --- a/src/cpu/o3/FUPool.py +++ b/src/cpu/o3/FUPool.py @@ -36,10 +36,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * from m5.objects.FuncUnit import * from m5.objects.FuncUnitConfig import * +from m5.params import * +from m5.SimObject import SimObject class FUPool(SimObject): diff --git a/src/cpu/o3/FuncUnitConfig.py b/src/cpu/o3/FuncUnitConfig.py index 7ba49c93bf..617cef9749 100644 --- a/src/cpu/o3/FuncUnitConfig.py +++ b/src/cpu/o3/FuncUnitConfig.py @@ -36,11 +36,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.defines import buildEnv -from m5.params import * - from m5.objects.FuncUnit import * +from m5.params import * +from m5.SimObject import SimObject class IntALU(FUDesc): diff --git a/src/cpu/o3/SConscript b/src/cpu/o3/SConscript index 2ac703b041..c49db9373d 100755 --- a/src/cpu/o3/SConscript +++ b/src/cpu/o3/SConscript @@ -30,7 +30,7 @@ import sys Import('*') -if not env['CONF']['USE_NULL_ISA']: +if env['CONF']['BUILD_ISA']: SimObject('FUPool.py', sim_objects=['FUPool']) SimObject('FuncUnitConfig.py', sim_objects=[]) SimObject('BaseO3CPU.py', sim_objects=['BaseO3CPU'], enums=[ diff --git a/src/cpu/o3/commit.cc b/src/cpu/o3/commit.cc index 538505bcf9..f5e601f679 100644 --- a/src/cpu/o3/commit.cc +++ b/src/cpu/o3/commit.cc @@ -254,7 +254,7 @@ Commit::setActiveThreads(std::list *at_ptr) } void -Commit::setRenameMap(UnifiedRenameMap rm_ptr[]) +Commit::setRenameMap(UnifiedRenameMap rm_ptr[MaxThreads]) { for (ThreadID tid = 0; tid < numThreads; tid++) renameMap[tid] = &rm_ptr[tid]; diff --git a/src/cpu/o3/cpu.cc b/src/cpu/o3/cpu.cc index 85cc3dbf71..a93d5bc74d 100644 --- a/src/cpu/o3/cpu.cc +++ b/src/cpu/o3/cpu.cc @@ -989,10 +989,10 @@ CPU::getWritableReg(PhysRegIdPtr phys_reg, ThreadID tid) { switch (phys_reg->classValue()) { case VecRegClass: - executeStats[tid]->numVecRegReads++; + executeStats[tid]->numVecRegWrites++; break; case VecPredRegClass: - executeStats[tid]->numVecPredRegReads++; + executeStats[tid]->numVecPredRegWrites++; break; default: break; diff --git a/src/cpu/o3/fetch.hh b/src/cpu/o3/fetch.hh index 6add31444d..2c6da6708a 100644 --- a/src/cpu/o3/fetch.hh +++ b/src/cpu/o3/fetch.hh @@ -470,7 +470,7 @@ class Fetch ThreadID retryTid; /** Cache block size. */ - unsigned int cacheBlkSize; + Addr cacheBlkSize; /** The size of the fetch buffer in bytes. The fetch buffer * itself may be smaller than a cache line. diff --git a/src/cpu/o3/lsq.hh b/src/cpu/o3/lsq.hh index 130c3479fa..2ed1342eb0 100644 --- a/src/cpu/o3/lsq.hh +++ b/src/cpu/o3/lsq.hh @@ -541,7 +541,7 @@ class LSQ { flags.set(Flag::WritebackDone); /* If the lsq resources are already free */ - if (isReleased()) { + if (_numOutstandingPackets == 0 && isReleased()) { delete this; } } diff --git a/src/cpu/o3/lsq_unit.cc b/src/cpu/o3/lsq_unit.cc index 139e0de337..68fd464627 100644 --- a/src/cpu/o3/lsq_unit.cc +++ b/src/cpu/o3/lsq_unit.cc @@ -451,7 +451,7 @@ LSQUnit::checkSnoop(PacketPtr pkt) LSQRequest *request = iter->request(); // Check that this snoop didn't just invalidate our lock flag - if (ld_inst->effAddrValid() && + if (ld_inst->effAddrValid() && request && request->isCacheBlockHit(invalidate_addr, cacheBlockMask) && ld_inst->memReqFlags & Request::LLSC) { ld_inst->tcBase()->getIsaPtr()->handleLockedSnoopHit(ld_inst.get()); @@ -463,7 +463,7 @@ LSQUnit::checkSnoop(PacketPtr pkt) ld_inst = iter->instruction(); assert(ld_inst); request = iter->request(); - if (!ld_inst->effAddrValid() || ld_inst->strictlyOrdered()) + if (!ld_inst->effAddrValid() || ld_inst->strictlyOrdered() || !request) continue; DPRINTF(LSQUnit, "-- inst [sn:%lli] to pktAddr:%#x\n", @@ -1510,6 +1510,8 @@ LSQUnit::read(LSQRequest *request, ssize_t load_idx) // first time this load got executed. Signal the senderSate // that response packets should be discarded. request->discard(); + // Avoid checking snoops on this discarded request. + load_entry.setRequest(nullptr); } WritebackEvent *wb = new WritebackEvent(load_inst, data_pkt, diff --git a/src/cpu/o3/probe/SConscript b/src/cpu/o3/probe/SConscript index 6039ef2eb9..3065e50a8c 100644 --- a/src/cpu/o3/probe/SConscript +++ b/src/cpu/o3/probe/SConscript @@ -37,7 +37,7 @@ Import('*') -if not env['CONF']['USE_NULL_ISA']: +if env['CONF']['BUILD_ISA']: SimObject('SimpleTrace.py', sim_objects=['SimpleTrace']) Source('simple_trace.cc') DebugFlag('SimpleTrace') diff --git a/src/cpu/o3/rename.cc b/src/cpu/o3/rename.cc index f8c305eb1c..c20edc2e46 100644 --- a/src/cpu/o3/rename.cc +++ b/src/cpu/o3/rename.cc @@ -286,7 +286,7 @@ Rename::setActiveThreads(std::list *at_ptr) void -Rename::setRenameMap(UnifiedRenameMap rm_ptr[]) +Rename::setRenameMap(UnifiedRenameMap rm_ptr[MaxThreads]) { for (ThreadID tid = 0; tid < numThreads; tid++) renameMap[tid] = &rm_ptr[tid]; @@ -940,8 +940,11 @@ Rename::doSquash(const InstSeqNum &squashed_seq_num, ThreadID tid) // previous physical register that it was renamed to. renameMap[tid]->setEntry(hb_it->archReg, hb_it->prevPhysReg); - // Put the renamed physical register back on the free list. - freeList->addReg(hb_it->newPhysReg); + // The phys regs can still be owned by squashing but + // executing instructions in IEW at this moment. To avoid + // ownership hazard in SMT CPU, we delay the freelist update + // until they are indeed squashed in the commit stage. + freeingInProgress[tid].push_back(hb_it->newPhysReg); } // Notify potential listeners that the register mapping needs to be @@ -1296,6 +1299,18 @@ Rename::checkSignalsAndUpdate(ThreadID tid) squash(fromCommit->commitInfo[tid].doneSeqNum, tid); return true; + } else if (!fromCommit->commitInfo[tid].robSquashing && + !freeingInProgress[tid].empty()) { + DPRINTF(Rename, "[tid:%i] Freeing phys regs of misspeculated " + "instructions.\n", tid); + + auto reg_it = freeingInProgress[tid].cbegin(); + while ( reg_it != freeingInProgress[tid].cend()){ + // Put the renamed physical register back on the free list. + freeList->addReg(*reg_it); + ++reg_it; + } + freeingInProgress[tid].clear(); } if (checkStall(tid)) { diff --git a/src/cpu/o3/rename.hh b/src/cpu/o3/rename.hh index 61ef476501..81e63e5019 100644 --- a/src/cpu/o3/rename.hh +++ b/src/cpu/o3/rename.hh @@ -359,6 +359,9 @@ class Rename /** Free list interface. */ UnifiedFreeList *freeList; + /** Hold phys regs to be released after squash finish */ + std::vector freeingInProgress[MaxThreads]; + /** Pointer to the list of active threads. */ std::list *activeThreads; diff --git a/src/cpu/pred/2bit_local.cc b/src/cpu/pred/2bit_local.cc index c9aa714ed1..00baf92882 100644 --- a/src/cpu/pred/2bit_local.cc +++ b/src/cpu/pred/2bit_local.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2004-2006 The Regents of The University of Michigan * All rights reserved. * @@ -67,10 +79,10 @@ LocalBP::LocalBP(const LocalBPParams ¶ms) } void -LocalBP::btbUpdate(ThreadID tid, Addr branch_addr, void * &bp_history) +LocalBP::updateHistories(ThreadID tid, Addr pc, bool uncond, + bool taken, Addr target, void * &bp_history) { -// Place holder for a function that is called to update predictor history when -// a BTB entry is invalid or not found. +// Place holder for a function that is called to update predictor history } @@ -94,8 +106,8 @@ LocalBP::lookup(ThreadID tid, Addr branch_addr, void * &bp_history) } void -LocalBP::update(ThreadID tid, Addr branch_addr, bool taken, void *bp_history, - bool squashed, const StaticInstPtr & inst, Addr corrTarget) +LocalBP::update(ThreadID tid, Addr branch_addr, bool taken, void *&bp_history, + bool squashed, const StaticInstPtr & inst, Addr target) { assert(bp_history == NULL); unsigned local_predictor_idx; @@ -135,10 +147,6 @@ LocalBP::getLocalIndex(Addr &branch_addr) return (branch_addr >> instShiftAmt) & indexMask; } -void -LocalBP::uncondBranch(ThreadID tid, Addr pc, void *&bp_history) -{ -} } // namespace branch_prediction } // namespace gem5 diff --git a/src/cpu/pred/2bit_local.hh b/src/cpu/pred/2bit_local.hh index 55f45ca55c..9bdb1131fd 100644 --- a/src/cpu/pred/2bit_local.hh +++ b/src/cpu/pred/2bit_local.hh @@ -1,5 +1,6 @@ /* * Copyright (c) 2011, 2014 ARM Limited + * Copyright (c) 2022-2023 The University of Edinburgh * All rights reserved * * The license below extends only to copyright in the software and shall @@ -69,35 +70,17 @@ class LocalBP : public BPredUnit */ LocalBP(const LocalBPParams ¶ms); - virtual void uncondBranch(ThreadID tid, Addr pc, void * &bp_history); + // Overriding interface functions + bool lookup(ThreadID tid, Addr pc, void * &bp_history) override; - /** - * Looks up the given address in the branch predictor and returns - * a true/false value as to whether it is taken. - * @param branch_addr The address of the branch to look up. - * @param bp_history Pointer to any bp history state. - * @return Whether or not the branch is taken. - */ - bool lookup(ThreadID tid, Addr branch_addr, void * &bp_history); + void updateHistories(ThreadID tid, Addr pc, bool uncond, bool taken, + Addr target, void * &bp_history) override; - /** - * Updates the branch predictor to Not Taken if a BTB entry is - * invalid or not found. - * @param branch_addr The address of the branch to look up. - * @param bp_history Pointer to any bp history state. - * @return Whether or not the branch is taken. - */ - void btbUpdate(ThreadID tid, Addr branch_addr, void * &bp_history); + void update(ThreadID tid, Addr pc, bool taken, + void * &bp_history, bool squashed, + const StaticInstPtr & inst, Addr target) override; - /** - * Updates the branch predictor with the actual result of a branch. - * @param branch_addr The address of the branch to update. - * @param taken Whether or not the branch was taken. - */ - void update(ThreadID tid, Addr branch_addr, bool taken, void *bp_history, - bool squashed, const StaticInstPtr & inst, Addr corrTarget); - - void squash(ThreadID tid, void *bp_history) + void squash(ThreadID tid, void * &bp_history) override { assert(bp_history == NULL); } private: diff --git a/src/cpu/pred/BranchPredictor.py b/src/cpu/pred/BranchPredictor.py index d18ca3f821..a10b2c2cef 100644 --- a/src/cpu/pred/BranchPredictor.py +++ b/src/cpu/pred/BranchPredictor.py @@ -1,3 +1,15 @@ +# Copyright (c) 2022-2023 The University of Edinburgh +# All rights reserved. +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# # Copyright (c) 2012 Mark D. Hill and David A. Wood # Copyright (c) 2015 The University of Wisconsin # All rights reserved. @@ -25,9 +37,62 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * +from m5.SimObject import * + + +class BranchType(Enum): + vals = [ + "NoBranch", + "Return", + "CallDirect", + "CallIndirect", # 'Call', + "DirectCond", + "DirectUncond", # 'Direct', + "IndirectCond", + "IndirectUncond", #'Indirect', + ] + + +class TargetProvider(Enum): + vals = [ + "NoTarget", + "BTB", + "RAS", + "Indirect", + ] + + +class ReturnAddrStack(SimObject): + type = "ReturnAddrStack" + cxx_class = "gem5::branch_prediction::ReturnAddrStack" + cxx_header = "cpu/pred/ras.hh" + + numThreads = Param.Unsigned(Parent.numThreads, "Number of threads") + numEntries = Param.Unsigned(16, "Number of RAS entries") + + +class BranchTargetBuffer(ClockedObject): + type = "BranchTargetBuffer" + cxx_class = "gem5::branch_prediction::BranchTargetBuffer" + cxx_header = "cpu/pred/btb.hh" + abstract = True + + numThreads = Param.Unsigned(Parent.numThreads, "Number of threads") + + +class SimpleBTB(BranchTargetBuffer): + type = "SimpleBTB" + cxx_class = "gem5::branch_prediction::SimpleBTB" + cxx_header = "cpu/pred/simple_btb.hh" + + numEntries = Param.Unsigned(4096, "Number of BTB entries") + tagBits = Param.Unsigned(16, "Size of the BTB tags, in bits") + instShiftAmt = Param.Unsigned( + Parent.instShiftAmt, "Number of bits to shift instructions by" + ) class IndirectPredictor(SimObject): @@ -52,6 +117,13 @@ class SimpleIndirectPredictor(IndirectPredictor): indirectPathLength = Param.Unsigned( 3, "Previous indirect targets to use for path history" ) + speculativePathLength = Param.Unsigned( + 256, + "Additional buffer space to store speculative path history. " + "If there are more speculative branches in flight the history cannot " + "be recovered. Set this to an appropriate value respective the CPU" + "pipeline depth or a high value e.g. 256 to make it 'unlimited'.", + ) indirectGHRBits = Param.Unsigned(13, "Indirect GHR number of bits") instShiftAmt = Param.Unsigned(2, "Number of bits to shift instructions by") @@ -63,14 +135,25 @@ class BranchPredictor(SimObject): abstract = True numThreads = Param.Unsigned(Parent.numThreads, "Number of threads") - BTBEntries = Param.Unsigned(4096, "Number of BTB entries") - BTBTagSize = Param.Unsigned(16, "Size of the BTB tags, in bits") - RASSize = Param.Unsigned(16, "RAS size") instShiftAmt = Param.Unsigned(2, "Number of bits to shift instructions by") + requiresBTBHit = Param.Bool( + False, + "Requires the BTB to hit for returns and indirect branches. For an" + "advanced front-end there is no other way than a BTB hit to know " + "that the branch exists in the first place. Furthermore, the BPU " + "needs to know the branch type to make the correct RAS operations. " + "This info is only available from the BTB. " + "Low-end CPUs predecoding might be used to identify branches. ", + ) + btb = Param.BranchTargetBuffer(SimpleBTB(), "Branch target buffer (BTB)") + ras = Param.ReturnAddrStack( + ReturnAddrStack(), "Return address stack, set to NULL to disable RAS." + ) indirectBranchPred = Param.IndirectPredictor( SimpleIndirectPredictor(), - "Indirect branch predictor, set to NULL to disable indirect predictions", + "Indirect branch predictor, set to NULL to disable " + "indirect predictions", ) diff --git a/src/cpu/pred/SConscript b/src/cpu/pred/SConscript index f4b6870ec5..ec3102cada 100644 --- a/src/cpu/pred/SConscript +++ b/src/cpu/pred/SConscript @@ -1,5 +1,17 @@ # -*- mode:python -*- +# Copyright (c) 2022-2023 The University of Edinburgh +# All rights reserved. +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# # Copyright (c) 2006 The Regents of The University of Michigan # All rights reserved. # @@ -28,8 +40,13 @@ Import('*') -SimObject('BranchPredictor.py', sim_objects=[ - 'IndirectPredictor', 'SimpleIndirectPredictor', 'BranchPredictor', + +SimObject('BranchPredictor.py', + sim_objects=[ + 'BranchPredictor', + 'IndirectPredictor', 'SimpleIndirectPredictor', + 'BranchTargetBuffer', 'SimpleBTB', + 'ReturnAddrStack', 'LocalBP', 'TournamentBP', 'BiModeBP', 'TAGEBase', 'TAGE', 'LoopPredictor', 'TAGE_SC_L_TAGE', 'TAGE_SC_L_TAGE_64KB', 'TAGE_SC_L_TAGE_8KB', 'LTAGE', 'TAGE_SC_L_LoopPredictor', 'StatisticalCorrector', 'TAGE_SC_L', @@ -41,17 +58,16 @@ SimObject('BranchPredictor.py', sim_objects=[ 'MultiperspectivePerceptronTAGE', 'MPP_StatisticalCorrector_64KB', 'MultiperspectivePerceptronTAGE64KB', 'MPP_TAGE_8KB', 'MPP_LoopPredictor_8KB', 'MPP_StatisticalCorrector_8KB', - 'MultiperspectivePerceptronTAGE8KB']) + 'MultiperspectivePerceptronTAGE8KB'], + enums=['BranchType', 'TargetProvider']) -DebugFlag('Indirect') Source('bpred_unit.cc') Source('2bit_local.cc') -Source('btb.cc') Source('simple_indirect.cc') Source('indirect.cc') Source('ras.cc') Source('tournament.cc') -Source ('bi_mode.cc') +Source('bi_mode.cc') Source('tage_base.cc') Source('tage.cc') Source('loop_predictor.cc') @@ -66,6 +82,11 @@ Source('statistical_corrector.cc') Source('tage_sc_l.cc') Source('tage_sc_l_8KB.cc') Source('tage_sc_l_64KB.cc') +Source('btb.cc') +Source('simple_btb.cc') +DebugFlag('Indirect') +DebugFlag('BTB') +DebugFlag('RAS') DebugFlag('FreeList') DebugFlag('Branch') DebugFlag('Tage') diff --git a/src/cpu/pred/bi_mode.cc b/src/cpu/pred/bi_mode.cc index 40dcbad7db..e55237f6d8 100644 --- a/src/cpu/pred/bi_mode.cc +++ b/src/cpu/pred/bi_mode.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2014 The Regents of The University of Michigan * All rights reserved. * @@ -73,7 +85,7 @@ BiModeBP::BiModeBP(const BiModeBPParams ¶ms) * chooses the taken array and the taken array predicts taken. */ void -BiModeBP::uncondBranch(ThreadID tid, Addr pc, void * &bpHistory) +BiModeBP::uncondBranch(ThreadID tid, Addr pc, void * &bp_history) { BPHistory *history = new BPHistory; history->globalHistoryReg = globalHistoryReg[tid]; @@ -81,17 +93,29 @@ BiModeBP::uncondBranch(ThreadID tid, Addr pc, void * &bpHistory) history->takenPred = true; history->notTakenPred = true; history->finalPred = true; - bpHistory = static_cast(history); - updateGlobalHistReg(tid, true); + bp_history = static_cast(history); } void -BiModeBP::squash(ThreadID tid, void *bpHistory) +BiModeBP::updateHistories(ThreadID tid, Addr pc, bool uncond, + bool taken, Addr target, void * &bp_history) { - BPHistory *history = static_cast(bpHistory); + assert(uncond || bp_history); + if (uncond) { + uncondBranch(tid, pc, bp_history); + } + updateGlobalHistReg(tid, taken); +} + + +void +BiModeBP::squash(ThreadID tid, void * &bp_history) +{ + BPHistory *history = static_cast(bp_history); globalHistoryReg[tid] = history->globalHistoryReg; delete history; + bp_history = nullptr; } /* @@ -104,7 +128,7 @@ BiModeBP::squash(ThreadID tid, void *bpHistory) * direction predictors for the final branch prediction. */ bool -BiModeBP::lookup(ThreadID tid, Addr branchAddr, void * &bpHistory) +BiModeBP::lookup(ThreadID tid, Addr branchAddr, void * &bp_history) { unsigned choiceHistoryIdx = ((branchAddr >> instShiftAmt) & choiceHistoryMask); @@ -136,17 +160,11 @@ BiModeBP::lookup(ThreadID tid, Addr branchAddr, void * &bpHistory) } history->finalPred = finalPrediction; - bpHistory = static_cast(history); - updateGlobalHistReg(tid, finalPrediction); + bp_history = static_cast(history); return finalPrediction; } -void -BiModeBP::btbUpdate(ThreadID tid, Addr branchAddr, void * &bpHistory) -{ - globalHistoryReg[tid] &= (historyRegisterMask & ~1ULL); -} /* Only the selected direction predictor will be updated with the final * outcome; the status of the unselected one will not be altered. The choice @@ -155,12 +173,12 @@ BiModeBP::btbUpdate(ThreadID tid, Addr branchAddr, void * &bpHistory) * the direction predictors makes a correct final prediction. */ void -BiModeBP::update(ThreadID tid, Addr branchAddr, bool taken, void *bpHistory, - bool squashed, const StaticInstPtr & inst, Addr corrTarget) +BiModeBP::update(ThreadID tid, Addr branchAddr, bool taken,void * &bp_history, + bool squashed, const StaticInstPtr & inst, Addr target) { - assert(bpHistory); + assert(bp_history); - BPHistory *history = static_cast(bpHistory); + BPHistory *history = static_cast(bp_history); // We do not update the counters speculatively on a squash. // We just restore the global history register. @@ -222,6 +240,7 @@ BiModeBP::update(ThreadID tid, Addr branchAddr, bool taken, void *bpHistory, } delete history; + bp_history = nullptr; } void diff --git a/src/cpu/pred/bi_mode.hh b/src/cpu/pred/bi_mode.hh index 721d21b79a..46c3cc2b69 100644 --- a/src/cpu/pred/bi_mode.hh +++ b/src/cpu/pred/bi_mode.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2014 The Regents of The University of Michigan * All rights reserved. * @@ -61,15 +73,17 @@ class BiModeBP : public BPredUnit { public: BiModeBP(const BiModeBPParams ¶ms); - void uncondBranch(ThreadID tid, Addr pc, void * &bp_history); - void squash(ThreadID tid, void *bp_history); - bool lookup(ThreadID tid, Addr branch_addr, void * &bp_history); - void btbUpdate(ThreadID tid, Addr branch_addr, void * &bp_history); - void update(ThreadID tid, Addr branch_addr, bool taken, void *bp_history, - bool squashed, const StaticInstPtr & inst, Addr corrTarget); + bool lookup(ThreadID tid, Addr pc, void * &bp_history) override; + void updateHistories(ThreadID tid, Addr pc, bool uncond, bool taken, + Addr target, void * &bp_history) override; + void squash(ThreadID tid, void * &bp_history) override; + void update(ThreadID tid, Addr pc, bool taken, + void * &bp_history, bool squashed, + const StaticInstPtr & inst, Addr target) override; private: void updateGlobalHistReg(ThreadID tid, bool taken); + void uncondBranch(ThreadID tid, Addr pc, void * &bp_history); struct BPHistory { diff --git a/src/cpu/pred/bpred_unit.cc b/src/cpu/pred/bpred_unit.cc index ec751f7dc6..c59a592980 100644 --- a/src/cpu/pred/bpred_unit.cc +++ b/src/cpu/pred/bpred_unit.cc @@ -1,6 +1,6 @@ /* * Copyright (c) 2011-2012, 2014 ARM Limited - * Copyright (c) 2010 The University of Edinburgh + * Copyright (c) 2010,2022-2023 The University of Edinburgh * Copyright (c) 2012 Mark D. Hill and David A. Wood * All rights reserved * @@ -58,50 +58,16 @@ namespace branch_prediction BPredUnit::BPredUnit(const Params ¶ms) : SimObject(params), numThreads(params.numThreads), + requiresBTBHit(params.requiresBTBHit), + instShiftAmt(params.instShiftAmt), predHist(numThreads), - BTB(params.BTBEntries, - params.BTBTagSize, - params.instShiftAmt, - params.numThreads), - RAS(numThreads), + btb(params.btb), + ras(params.ras), iPred(params.indirectBranchPred), - stats(this), - instShiftAmt(params.instShiftAmt) + stats(this) { - for (auto& r : RAS) - r.init(params.RASSize); } -BPredUnit::BPredUnitStats::BPredUnitStats(statistics::Group *parent) - : statistics::Group(parent), - ADD_STAT(lookups, statistics::units::Count::get(), - "Number of BP lookups"), - ADD_STAT(condPredicted, statistics::units::Count::get(), - "Number of conditional branches predicted"), - ADD_STAT(condIncorrect, statistics::units::Count::get(), - "Number of conditional branches incorrect"), - ADD_STAT(BTBLookups, statistics::units::Count::get(), - "Number of BTB lookups"), - ADD_STAT(BTBUpdates, statistics::units::Count::get(), - "Number of BTB updates"), - ADD_STAT(BTBHits, statistics::units::Count::get(), "Number of BTB hits"), - ADD_STAT(BTBHitRatio, statistics::units::Ratio::get(), "BTB Hit Ratio", - BTBHits / BTBLookups), - ADD_STAT(RASUsed, statistics::units::Count::get(), - "Number of times the RAS was used to get a target."), - ADD_STAT(RASIncorrect, statistics::units::Count::get(), - "Number of incorrect RAS predictions."), - ADD_STAT(indirectLookups, statistics::units::Count::get(), - "Number of indirect predictor lookups."), - ADD_STAT(indirectHits, statistics::units::Count::get(), - "Number of indirect target hits."), - ADD_STAT(indirectMisses, statistics::units::Count::get(), - "Number of indirect misses."), - ADD_STAT(indirectMispredicted, statistics::units::Count::get(), - "Number of mispredicted indirect branches.") -{ - BTBHitRatio.precision(6); -} probing::PMUUPtr BPredUnit::pmuProbePoint(const char *name) @@ -128,266 +94,361 @@ BPredUnit::drainSanityCheck() const assert(ph.empty()); } + bool BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum, PCStateBase &pc, ThreadID tid) { + /** Perform the prediction. */ + PredictorHistory* bpu_history = nullptr; + bool taken = predict(inst, seqNum, pc, tid, bpu_history); + + assert(bpu_history!=nullptr); + + /** Push the record into the history buffer */ + predHist[tid].push_front(bpu_history); + + DPRINTF(Branch, "[tid:%i] [sn:%llu] History entry added. " + "predHist.size(): %i\n", tid, seqNum, predHist[tid].size()); + + return taken; +} + + + + +bool +BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum, + PCStateBase &pc, ThreadID tid, PredictorHistory* &hist) +{ + assert(hist == nullptr); + + // See if branch predictor predicts taken. // If so, get its target addr either from the BTB or the RAS. - // Save off record of branch stuff so the RAS can be fixed - // up once it's done. + // Save off branch stuff into `hist` so we can correct the predictor + // if prediction was wrong. - bool pred_taken = false; - std::unique_ptr target(pc.clone()); + BranchType brType = getBranchType(inst); + hist = new PredictorHistory(tid, seqNum, pc.instAddr(), inst); - ++stats.lookups; + stats.lookups[tid][brType]++; ppBranches->notify(1); - void *bp_history = NULL; - void *indirect_history = NULL; + + /* ----------------------------------------------- + * Get branch direction + * ----------------------------------------------- + * Lookup the direction predictor for every + * conditional branch. For unconditional branches + * the direction is always taken + */ if (inst->isUncondCtrl()) { - DPRINTF(Branch, "[tid:%i] [sn:%llu] Unconditional control\n", - tid,seqNum); - pred_taken = true; - // Tell the BP there was an unconditional branch. - uncondBranch(tid, pc.instAddr(), bp_history); + // Unconditional branches ----- + hist->condPred = true; } else { + // Conditional branches ------- ++stats.condPredicted; - pred_taken = lookup(tid, pc.instAddr(), bp_history); + hist->condPred = lookup(tid, pc.instAddr(), hist->bpHistory); - DPRINTF(Branch, "[tid:%i] [sn:%llu] " - "Branch predictor predicted %i for PC %s\n", - tid, seqNum, pred_taken, pc); - } - - const bool orig_pred_taken = pred_taken; - if (iPred) { - iPred->genIndirectInfo(tid, indirect_history); + if (hist->condPred) { + ++stats.condPredictedTaken; + } } + hist->predTaken = hist->condPred; DPRINTF(Branch, - "[tid:%i] [sn:%llu] Creating prediction history for PC %s\n", - tid, seqNum, pc); + "[tid:%i, sn:%llu] Branch predictor predicted %i for PC:%#x %s\n", + tid, seqNum, hist->condPred, hist->pc, toString(brType)); - PredictorHistory predict_record(seqNum, pc.instAddr(), pred_taken, - bp_history, indirect_history, tid, inst); - // Now lookup in the BTB or RAS. - if (pred_taken) { - // Note: The RAS may be both popped and pushed to - // support coroutines. - if (inst->isReturn()) { - ++stats.RASUsed; - predict_record.wasReturn = true; - // If it's a function return call, then look up the address - // in the RAS. - const PCStateBase *ras_top = RAS[tid].top(); - if (ras_top) - set(target, inst->buildRetPC(pc, *ras_top)); + // The direction is done now get the target address + // from BTB, RAS or indirect predictor. + hist->targetProvider = TargetProvider::NoTarget; - // Record the top entry of the RAS, and its index. - predict_record.usedRAS = true; - predict_record.RASIndex = RAS[tid].topIdx(); - set(predict_record.RASTarget, ras_top); + /* ----------------------------------------------- + * Branch Target Buffer (BTB) + * ----------------------------------------------- + * First check for a BTB hit. This will be done + * regardless of whether the RAS or the indirect + * predictor provide the final target. That is + * necessary as modern front-end does not have a + * chance to detect a branch without a BTB hit. + */ + stats.BTBLookups++; + const PCStateBase * btb_target = btb->lookup(tid, pc.instAddr(), brType); + if (btb_target) { + stats.BTBHits++; + hist->btbHit = true; - RAS[tid].pop(); - - DPRINTF(Branch, "[tid:%i] [sn:%llu] Instruction %s is a return, " - "RAS predicted target: %s, RAS index: %i\n", - tid, seqNum, pc, *target, predict_record.RASIndex); + if (hist->predTaken) { + hist->targetProvider = TargetProvider::BTB; + set(hist->target, btb_target); } + } + DPRINTF(Branch, "[tid:%i, sn:%llu] PC:%#x BTB:%s\n", + tid, seqNum, hist->pc, (hist->btbHit) ? "hit" : "miss"); + + + // In a high performance CPU there is no other way than a BTB hit + // to know about a branch instruction. In that case consolidate + // indirect and RAS predictor only if there was a BTB it. + // For low end CPUs predecoding might be used to identify branches. + const bool branch_detected = (hist->btbHit || !requiresBTBHit); + + + /* ----------------------------------------------- + * Return Address Stack (RAS) + * ----------------------------------------------- + * Perform RAS operations for calls and returns. + * Calls: push their RETURN address onto + * the RAS. + * Return: pop the the return address from the + * top of the RAS. + */ + if (ras && branch_detected) { if (inst->isCall()) { - RAS[tid].push(pc); - predict_record.pushedRAS = true; + // In case of a call build the return address and + // push it to the RAS. + auto return_addr = inst->buildRetPC(pc, pc); + ras->push(tid, *return_addr, hist->rasHistory); - // Record that it was a call so that the top RAS entry can - // be popped off if the speculation is incorrect. - predict_record.wasCall = true; + DPRINTF(Branch, "[tid:%i] [sn:%llu] Instr. %s was " + "a call, push return address %s onto the RAS\n", + tid, seqNum, pc, *return_addr); - DPRINTF(Branch, - "[tid:%i] [sn:%llu] Instruction %s was a call, adding " - "%s to the RAS index: %i\n", - tid, seqNum, pc, pc, RAS[tid].topIdx()); } + else if (inst->isReturn()) { - // The target address is not predicted by RAS. - // Thus, BTB/IndirectBranch Predictor is employed. - if (!inst->isReturn()) { - if (inst->isDirectCtrl() || !iPred) { - ++stats.BTBLookups; - // Check BTB on direct branches - if (BTB.valid(pc.instAddr(), tid)) { - ++stats.BTBHits; - // If it's not a return, use the BTB to get target addr. - set(target, BTB.lookup(pc.instAddr(), tid)); - DPRINTF(Branch, - "[tid:%i] [sn:%llu] Instruction %s predicted " - "target is %s\n", - tid, seqNum, pc, *target); - } else { - DPRINTF(Branch, "[tid:%i] [sn:%llu] BTB doesn't have a " - "valid entry\n", tid, seqNum); - pred_taken = false; - predict_record.predTaken = pred_taken; - // The Direction of the branch predictor is altered - // because the BTB did not have an entry - // The predictor needs to be updated accordingly - if (!inst->isCall() && !inst->isReturn()) { - btbUpdate(tid, pc.instAddr(), bp_history); - DPRINTF(Branch, - "[tid:%i] [sn:%llu] btbUpdate " - "called for %s\n", - tid, seqNum, pc); - } else if (inst->isCall() && !inst->isUncondCtrl()) { - RAS[tid].pop(); - predict_record.pushedRAS = false; - } - inst->advancePC(*target); - } - } else { - predict_record.wasIndirect = true; - ++stats.indirectLookups; - //Consult indirect predictor on indirect control - if (iPred->lookup(pc.instAddr(), *target, tid)) { - // Indirect predictor hit - ++stats.indirectHits; - DPRINTF(Branch, - "[tid:%i] [sn:%llu] Instruction %s predicted " - "indirect target is %s\n", - tid, seqNum, pc, *target); - } else { - ++stats.indirectMisses; - pred_taken = false; - predict_record.predTaken = pred_taken; - DPRINTF(Branch, - "[tid:%i] [sn:%llu] Instruction %s no indirect " - "target\n", - tid, seqNum, pc); - if (!inst->isCall() && !inst->isReturn()) { + // If it's a return from a function call, then look up the + // RETURN address in the RAS. + const PCStateBase *return_addr = ras->pop(tid, hist->rasHistory); + if (return_addr) { - } else if (inst->isCall() && !inst->isUncondCtrl()) { - RAS[tid].pop(); - predict_record.pushedRAS = false; - } - inst->advancePC(*target); - } - iPred->recordIndirect(pc.instAddr(), target->instAddr(), - seqNum, tid); + // Set the target to the return address + set(hist->target, *return_addr); + hist->targetProvider = TargetProvider::RAS; + + DPRINTF(Branch, "[tid:%i] [sn:%llu] Instr. %s is a " + "return, RAS poped return addr: %s\n", + tid, seqNum, pc, *hist->target); } } - } else { - if (inst->isReturn()) { - predict_record.wasReturn = true; - } - inst->advancePC(*target); } - predict_record.target = target->instAddr(); - set(pc, *target); + + /* ----------------------------------------------- + * Indirect Predictor + * ----------------------------------------------- + * For indirect branches/calls check the indirect + * predictor if one is available. Not for returns. + * Note that depending on the implementation a + * indirect predictor might only return a target + * for an indirect branch with a changing target. + * As most indirect branches have a static target + * using the target from the BTB is the optimal + * to save space in the indirect predictor itself. + */ + if (iPred && hist->predTaken && branch_detected && + inst->isIndirectCtrl() && !inst->isReturn()) { + + ++stats.indirectLookups; + + const PCStateBase *itarget = iPred->lookup(tid, seqNum, + pc.instAddr(), + hist->indirectHistory); + + if (itarget) { + // Indirect predictor hit + ++stats.indirectHits; + hist->targetProvider = TargetProvider::Indirect; + set(hist->target, *itarget); + + DPRINTF(Branch, + "[tid:%i, sn:%llu] Instruction %s predicted " + "indirect target is %s\n", + tid, seqNum, pc, *hist->target); + } else { + ++stats.indirectMisses; + DPRINTF(Branch, + "[tid:%i, sn:%llu] PC:%#x no indirect target\n", + tid, seqNum, pc.instAddr()); + } + } + + + /** ---------------------------------------------- + * Fallthrough + * ----------------------------------------------- + * All the target predictors did their job. + * If there is no target its either not taken or + * a BTB miss. In that case we just fallthrough. + * */ + if (hist->targetProvider == TargetProvider::NoTarget) { + set(hist->target, pc); + inst->advancePC(*hist->target); + hist->predTaken = false; + } + stats.targetProvider[tid][hist->targetProvider]++; + + // The actual prediction is done. + // For now the BPU assume its correct. The update + // functions will correct the branch if needed. + // If prediction and actual direction are the same + // at commit the prediction was correct. + hist->actuallyTaken = hist->predTaken; + set(pc, *hist->target); + + DPRINTF(Branch, "%s(tid:%i, sn:%i, PC:%#x, %s) -> taken:%i, target:%s " + "provider:%s\n", __func__, tid, seqNum, hist->pc, + toString(brType), hist->predTaken, *hist->target, + enums::TargetProviderStrings[hist->targetProvider]); + + + /** ---------------------------------------------- + * Speculative history update + * ----------------------------------------------- + * Now that the prediction is done the predictor + * may update its histories speculative. (local + * and global path). A later squash will revert + * the history update if needed. + * The actual prediction tables will updated once + * we know the correct direction. + **/ + updateHistories(tid, hist->pc, hist->uncond, hist->predTaken, + hist->target->instAddr(), hist->bpHistory); + if (iPred) { // Update the indirect predictor with the direction prediction - // Note that this happens after indirect lookup, so it does not use - // the new information - // Note also that we use orig_pred_taken instead of pred_taken in - // as this is the actual outcome of the direction prediction - iPred->updateDirectionInfo(tid, orig_pred_taken); + iPred->update(tid, seqNum, hist->pc, false, hist->predTaken, + *hist->target, brType, hist->indirectHistory); } - predHist[tid].push_front(predict_record); - - DPRINTF(Branch, - "[tid:%i] [sn:%llu] History entry added. " - "predHist.size(): %i\n", - tid, seqNum, predHist[tid].size()); - - return pred_taken; + return hist->predTaken; } + void BPredUnit::update(const InstSeqNum &done_sn, ThreadID tid) { DPRINTF(Branch, "[tid:%i] Committing branches until " - "sn:%llu]\n", tid, done_sn); + "[sn:%llu]\n", tid, done_sn); while (!predHist[tid].empty() && - predHist[tid].back().seqNum <= done_sn) { - // Update the branch predictor with the correct results. - update(tid, predHist[tid].back().pc, - predHist[tid].back().predTaken, - predHist[tid].back().bpHistory, false, - predHist[tid].back().inst, - predHist[tid].back().target); + predHist[tid].back()->seqNum <= done_sn) { - if (iPred) { - iPred->commit(done_sn, tid, predHist[tid].back().indirectHistory); - } + // Iterate from the back to front. Least recent + // sequence number until the most recent done number + commitBranch(tid, *predHist[tid].rbegin()); + delete predHist[tid].back(); predHist[tid].pop_back(); + DPRINTF(Branch, "[tid:%i] [commit sn:%llu] pred_hist.size(): %i\n", + tid, done_sn, predHist[tid].size()); } } +void +BPredUnit::commitBranch(ThreadID tid, PredictorHistory* &hist) +{ + + stats.committed[tid][hist->type]++; + if (hist->mispredict) { + stats.mispredicted[tid][hist->type]++; + } + + + DPRINTF(Branch, "Commit branch: sn:%llu, PC:%#x %s, " + "pred:%i, taken:%i, target:%#x\n", + hist->seqNum, hist->pc, toString(hist->type), + hist->predTaken, hist->actuallyTaken, + hist->target->instAddr()); + + // Update the branch predictor with the correct results. + update(tid, hist->pc, + hist->actuallyTaken, + hist->bpHistory, false, + hist->inst, + hist->target->instAddr()); + + // Commit also Indirect predictor and RAS + if (iPred) { + iPred->commit(tid, hist->seqNum, + hist->indirectHistory); + } + + if (ras) { + ras->commit(tid, hist->mispredict, + hist->type, + hist->rasHistory); + } +} + + + void BPredUnit::squash(const InstSeqNum &squashed_sn, ThreadID tid) { - History &pred_hist = predHist[tid]; - if (iPred) { - iPred->squash(squashed_sn, tid); - } + while (!predHist[tid].empty() && + predHist[tid].front()->seqNum > squashed_sn) { - while (!pred_hist.empty() && - pred_hist.front().seqNum > squashed_sn) { - if (pred_hist.front().wasCall && pred_hist.front().pushedRAS) { - // Was a call but predicated false. Pop RAS here - DPRINTF(Branch, "[tid:%i] [squash sn:%llu] Squashing" - " Call [sn:%llu] PC: %s Popping RAS\n", tid, squashed_sn, - pred_hist.front().seqNum, pred_hist.front().pc); - RAS[tid].pop(); - } - if (pred_hist.front().usedRAS) { - if (pred_hist.front().RASTarget != nullptr) { - DPRINTF(Branch, "[tid:%i] [squash sn:%llu]" - " Restoring top of RAS to: %i," - " target: %s\n", tid, squashed_sn, - pred_hist.front().RASIndex, - *pred_hist.front().RASTarget); - } - else { - DPRINTF(Branch, "[tid:%i] [squash sn:%llu]" - " Restoring top of RAS to: %i," - " target: INVALID_TARGET\n", tid, squashed_sn, - pred_hist.front().RASIndex); - } + auto hist = predHist[tid].front(); - RAS[tid].restore(pred_hist.front().RASIndex, - pred_hist.front().RASTarget.get()); - } + squashHistory(tid, hist); - // This call should delete the bpHistory. - squash(tid, pred_hist.front().bpHistory); - if (iPred) { - iPred->deleteIndirectInfo(tid, pred_hist.front().indirectHistory); - } + DPRINTF(Branch, "[tid:%i, squash sn:%llu] Removing history for " + "sn:%llu, PC:%#x\n", tid, squashed_sn, hist->seqNum, + hist->pc); - DPRINTF(Branch, "[tid:%i] [squash sn:%llu] " - "Removing history for [sn:%llu] " - "PC %#x\n", tid, squashed_sn, pred_hist.front().seqNum, - pred_hist.front().pc); - pred_hist.pop_front(); + delete predHist[tid].front(); + predHist[tid].pop_front(); - DPRINTF(Branch, "[tid:%i] [squash sn:%llu] predHist.size(): %i\n", + DPRINTF(Branch, "[tid:%i] [squash sn:%llu] pred_hist.size(): %i\n", tid, squashed_sn, predHist[tid].size()); } } + + +void +BPredUnit::squashHistory(ThreadID tid, PredictorHistory* &history) +{ + + stats.squashes[tid][history->type]++; + DPRINTF(Branch, "[tid:%i] [squash sn:%llu] Incorrect: %s\n", + tid, history->seqNum, + toString(history->type)); + + + if (history->rasHistory) { + assert(ras); + + DPRINTF(Branch, "[tid:%i] [squash sn:%llu] Incorrect call/return " + "PC %#x. Fix RAS.\n", tid, history->seqNum, + history->pc); + + ras->squash(tid, history->rasHistory); + } + + if (iPred) { + iPred->squash(tid, history->seqNum, + history->indirectHistory); + } + + // This call should delete the bpHistory. + squash(tid, history->bpHistory); +} + + void BPredUnit::squash(const InstSeqNum &squashed_sn, const PCStateBase &corr_target, - bool actually_taken, ThreadID tid) + bool actually_taken, ThreadID tid, bool from_commit) { // Now that we know that a branch was mispredicted, we need to undo // all the branches that have been seen up until this branch and @@ -405,10 +466,15 @@ BPredUnit::squash(const InstSeqNum &squashed_sn, ++stats.condIncorrect; ppMisses->notify(1); - DPRINTF(Branch, "[tid:%i] Squashing from sequence number %i, " - "setting target to %s\n", tid, squashed_sn, corr_target); + + DPRINTF(Branch, "[tid:%i] Squash from %s start from sequence number %i, " + "setting target to %s\n", tid, from_commit ? "commit" : "decode", + squashed_sn, corr_target); + + // dump(); // Squash All Branches AFTER this mispredicted branch + // First the Prefetch history then the main history. squash(squashed_sn, tid); // If there's a squash due to a syscall, there may not be an entry @@ -416,26 +482,32 @@ BPredUnit::squash(const InstSeqNum &squashed_sn, // fix up the entry. if (!pred_hist.empty()) { - auto hist_it = pred_hist.begin(); - //HistoryIt hist_it = find(pred_hist.begin(), pred_hist.end(), - // squashed_sn); + PredictorHistory* const hist = pred_hist.front(); - //assert(hist_it != pred_hist.end()); - if (pred_hist.front().seqNum != squashed_sn) { - DPRINTF(Branch, "Front sn %i != Squash sn %i\n", - pred_hist.front().seqNum, squashed_sn); + DPRINTF(Branch, "[tid:%i] [squash sn:%llu] Mispredicted: %s, PC:%#x\n", + tid, squashed_sn, toString(hist->type), hist->pc); - assert(pred_hist.front().seqNum == squashed_sn); + // Update stats + stats.corrected[tid][hist->type]++; + if (hist->target && + (hist->target->instAddr() != corr_target.instAddr())) { + stats.targetWrong[tid][hist->targetProvider]++; } - - if ((*hist_it).usedRAS) { - ++stats.RASIncorrect; - DPRINTF(Branch, - "[tid:%i] [squash sn:%llu] Incorrect RAS [sn:%llu]\n", - tid, squashed_sn, hist_it->seqNum); + // If the squash is comming from decode it can be + // redirected earlier. Note that this branch might never get + // committed as a preceeding branch was mispredicted + if (!from_commit) { + stats.earlyResteers[tid][hist->type]++; } + if (actually_taken) { + ++stats.NotTakenMispredicted; + } else { + ++stats.TakenMispredicted; + } + + // There are separate functions for in-order and out-of-order // branch prediction, but not for update. Therefore, this // call should take into account that the mispredicted branch may @@ -445,95 +517,111 @@ BPredUnit::squash(const InstSeqNum &squashed_sn, // local/global histories. The counter tables will be updated when // the branch actually commits. - // Remember the correct direction for the update at commit. - pred_hist.front().predTaken = actually_taken; - pred_hist.front().target = corr_target.instAddr(); + // Remember the correct direction and target for the update at commit. + hist->mispredict = true; + hist->actuallyTaken = actually_taken; + set(hist->target, corr_target); - update(tid, (*hist_it).pc, actually_taken, - pred_hist.front().bpHistory, true, pred_hist.front().inst, - corr_target.instAddr()); + // Correct Direction predictor ------------------ + update(tid, hist->pc, actually_taken, hist->bpHistory, + true, hist->inst, corr_target.instAddr()); + + // Correct Indirect predictor ------------------- if (iPred) { - iPred->changeDirectionPrediction(tid, - pred_hist.front().indirectHistory, actually_taken); + iPred->update(tid, squashed_sn, hist->pc, + true, actually_taken, corr_target, + hist->type, hist->indirectHistory); } - if (actually_taken) { - if (hist_it->wasReturn && !hist_it->usedRAS) { - DPRINTF(Branch, "[tid:%i] [squash sn:%llu] " - "Incorrectly predicted " - "return [sn:%llu] PC: %#x\n", tid, squashed_sn, - hist_it->seqNum, - hist_it->pc); - RAS[tid].pop(); - hist_it->usedRAS = true; - } - if (hist_it->wasIndirect) { - ++stats.indirectMispredicted; - if (iPred) { - iPred->recordTarget( - hist_it->seqNum, pred_hist.front().indirectHistory, - corr_target, tid); + // Correct RAS --------------------------------- + if (ras) { + // The branch was taken and the RAS was not updated. + // In case of call or return that needs to be fixed. + if (actually_taken && (hist->rasHistory == nullptr)) { + + // A return has not poped the RAS. + if (hist->type == BranchType::Return) { + DPRINTF(Branch, "[tid:%i] [squash sn:%llu] " + "Incorrectly predicted return [sn:%llu] PC: %#x\n", + tid, squashed_sn, hist->seqNum, hist->pc); + + ras->pop(tid, hist->rasHistory); } - } else { - DPRINTF(Branch,"[tid:%i] [squash sn:%llu] " - "BTB Update called for [sn:%llu] " - "PC %#x\n", tid, squashed_sn, - hist_it->seqNum, hist_it->pc); - ++stats.BTBUpdates; - BTB.update(hist_it->pc, corr_target, tid); + // A call has not pushed a return address to the RAS. + if (hist->call) { + // In case of a call build the return address and + // push it to the RAS. + auto return_addr = hist->inst->buildRetPC( + corr_target, corr_target); + + DPRINTF(Branch, "[tid:%i] [squash sn:%llu] " + "Incorrectly predicted call: [sn:%llu,PC:%#x] " + " Push return address %s onto RAS\n", tid, + squashed_sn, hist->seqNum, hist->pc, + *return_addr); + ras->push(tid, *return_addr, hist->rasHistory); + } + + // The branch was not taken but the RAS modified. + } else if (!actually_taken && (hist->rasHistory != nullptr)) { + // The branch was not taken but the RAS was modified. + // Needs to be fixed. + ras->squash(tid, hist->rasHistory); } - } else { - //Actually not Taken - if (hist_it->wasCall && hist_it->pushedRAS) { - //Was a Call but predicated false. Pop RAS here - DPRINTF(Branch, - "[tid:%i] [squash sn:%llu] " - "Incorrectly predicted " - "Call [sn:%llu] PC: %s Popping RAS\n", - tid, squashed_sn, - hist_it->seqNum, hist_it->pc); - RAS[tid].pop(); - hist_it->pushedRAS = false; - } - if (hist_it->usedRAS) { - DPRINTF(Branch, - "[tid:%i] [squash sn:%llu] Incorrectly predicted " - "return [sn:%llu] PC: %#x Restoring RAS\n", tid, - squashed_sn, - hist_it->seqNum, hist_it->pc); - DPRINTF(Branch, - "[tid:%i] [squash sn:%llu] Restoring top of RAS " - "to: %i, target: %s\n", tid, squashed_sn, - hist_it->RASIndex, *hist_it->RASTarget); - RAS[tid].restore(hist_it->RASIndex, hist_it->RASTarget.get()); - hist_it->usedRAS = false; - } } + + // Correct BTB --------------------------------------------------- + // Update the BTB for all mispredicted taken branches. + // Always if `requiresBTBHit` is true otherwise only if the + // branch was direct or no indirect predictor is available. + if (actually_taken && + (requiresBTBHit || hist->inst->isDirectCtrl() || + (!iPred && !hist->inst->isReturn()))) { + + if (!hist->btbHit) { + ++stats.BTBMispredicted; + if (hist->condPred) + ++stats.predTakenBTBMiss; + } + + DPRINTF(Branch,"[tid:%i] BTB Update called for [sn:%llu] " + "PC %#x -> T: %#x\n", tid, + hist->seqNum, hist->pc, hist->target->instAddr()); + + stats.BTBUpdates++; + btb->update(tid, hist->pc, + *hist->target, + hist->type, + hist->inst); + btb->incorrectTarget(hist->pc, hist->type); + } + } else { DPRINTF(Branch, "[tid:%i] [sn:%llu] pred_hist empty, can't " "update\n", tid, squashed_sn); } } + void BPredUnit::dump() { int i = 0; for (const auto& ph : predHist) { if (!ph.empty()) { - auto pred_hist_it = ph.begin(); + auto hist = ph.begin(); cprintf("predHist[%i].size(): %i\n", i++, ph.size()); - while (pred_hist_it != ph.end()) { + while (hist != ph.end()) { cprintf("sn:%llu], PC:%#x, tid:%i, predTaken:%i, " - "bpHistory:%#x\n", - pred_hist_it->seqNum, pred_hist_it->pc, - pred_hist_it->tid, pred_hist_it->predTaken, - pred_hist_it->bpHistory); - pred_hist_it++; + "bpHistory:%#x, rasHistory:%#x\n", + (*hist)->seqNum, (*hist)->pc, + (*hist)->tid, (*hist)->predTaken, + (*hist)->bpHistory, (*hist)->rasHistory); + hist++; } cprintf("\n"); @@ -541,5 +629,108 @@ BPredUnit::dump() } } + +BPredUnit::BPredUnitStats::BPredUnitStats(BPredUnit *bp) + : statistics::Group(bp), + ADD_STAT(lookups, statistics::units::Count::get(), + "Number of BP lookups"), + ADD_STAT(squashes, statistics::units::Count::get(), + "Number of branches that got squashed (completely removed) as " + "an earlier branch was mispredicted."), + ADD_STAT(corrected, statistics::units::Count::get(), + "Number of branches that got corrected but not yet commited. " + "Branches get corrected by decode or after execute. Also a " + "branch misprediction can be detected out-of-order. Therefore, " + "a corrected branch might not end up beeing committed in case " + "an even earlier branch was mispredicted"), + ADD_STAT(earlyResteers, statistics::units::Count::get(), + "Number of branches that got redirected after decode."), + ADD_STAT(committed, statistics::units::Count::get(), + "Number of branches finally committed "), + ADD_STAT(mispredicted, statistics::units::Count::get(), + "Number of committed branches that were mispredicted."), + ADD_STAT(targetProvider, statistics::units::Count::get(), + "The component providing the target for taken branches"), + ADD_STAT(targetWrong, statistics::units::Count::get(), + "Number of branches where the target was incorrect or not " + "available at prediction time."), + ADD_STAT(condPredicted, statistics::units::Count::get(), + "Number of conditional branches predicted"), + ADD_STAT(condPredictedTaken, statistics::units::Count::get(), + "Number of conditional branches predicted as taken"), + ADD_STAT(condIncorrect, statistics::units::Count::get(), + "Number of conditional branches incorrect"), + ADD_STAT(predTakenBTBMiss, statistics::units::Count::get(), + "Number of branches predicted taken but missed in BTB"), + ADD_STAT(NotTakenMispredicted, statistics::units::Count::get(), + "Number branches predicted 'not taken' but turned out " + "to be taken"), + ADD_STAT(TakenMispredicted, statistics::units::Count::get(), + "Number branches predicted taken but are actually not taken"), + ADD_STAT(BTBLookups, statistics::units::Count::get(), + "Number of BTB lookups"), + ADD_STAT(BTBUpdates, statistics::units::Count::get(), + "Number of BTB updates"), + ADD_STAT(BTBHits, statistics::units::Count::get(), + "Number of BTB hits"), + ADD_STAT(BTBHitRatio, statistics::units::Ratio::get(), "BTB Hit Ratio", + BTBHits / BTBLookups), + ADD_STAT(BTBMispredicted, statistics::units::Count::get(), + "Number BTB mispredictions. No target found or target wrong"), + ADD_STAT(indirectLookups, statistics::units::Count::get(), + "Number of indirect predictor lookups."), + ADD_STAT(indirectHits, statistics::units::Count::get(), + "Number of indirect target hits."), + ADD_STAT(indirectMisses, statistics::units::Count::get(), + "Number of indirect misses."), + ADD_STAT(indirectMispredicted, statistics::units::Count::get(), + "Number of mispredicted indirect branches.") + +{ + using namespace statistics; + BTBHitRatio.precision(6); + + lookups + .init(bp->numThreads, enums::Num_BranchType) + .flags(total | pdf); + lookups.ysubnames(enums::BranchTypeStrings); + + squashes + .init(bp->numThreads, enums::Num_BranchType) + .flags(total | pdf); + squashes.ysubnames(enums::BranchTypeStrings); + + corrected + .init(bp->numThreads, enums::Num_BranchType) + .flags(total | pdf); + corrected.ysubnames(enums::BranchTypeStrings); + + earlyResteers + .init(bp->numThreads, enums::Num_BranchType) + .flags(total | pdf); + earlyResteers.ysubnames(enums::BranchTypeStrings); + + committed + .init(bp->numThreads, enums::Num_BranchType) + .flags(total | pdf); + committed.ysubnames(enums::BranchTypeStrings); + + mispredicted + .init(bp->numThreads, enums::Num_BranchType) + .flags(total | pdf); + mispredicted.ysubnames(enums::BranchTypeStrings); + + targetProvider + .init(bp->numThreads, enums::Num_TargetProvider) + .flags(total | pdf); + targetProvider.ysubnames(enums::TargetProviderStrings); + + targetWrong + .init(bp->numThreads, enums::Num_BranchType) + .flags(total | pdf); + targetWrong.ysubnames(enums::BranchTypeStrings); + +} + } // namespace branch_prediction } // namespace gem5 diff --git a/src/cpu/pred/bpred_unit.hh b/src/cpu/pred/bpred_unit.hh index 4af1d876a8..5a4faf1a1e 100644 --- a/src/cpu/pred/bpred_unit.hh +++ b/src/cpu/pred/bpred_unit.hh @@ -1,6 +1,6 @@ /* * Copyright (c) 2011-2012, 2014 ARM Limited - * Copyright (c) 2010 The University of Edinburgh + * Copyright (c) 2010,2022-2023 The University of Edinburgh * All rights reserved * * The license below extends only to copyright in the software and shall @@ -46,11 +46,13 @@ #include "base/statistics.hh" #include "base/types.hh" +#include "cpu/inst_seq.hh" +#include "cpu/pred/branch_type.hh" #include "cpu/pred/btb.hh" #include "cpu/pred/indirect.hh" #include "cpu/pred/ras.hh" -#include "cpu/inst_seq.hh" #include "cpu/static_inst.hh" +#include "enums/TargetProvider.hh" #include "params/BranchPredictor.hh" #include "sim/probe/pmu.hh" #include "sim/sim_object.hh" @@ -67,8 +69,14 @@ namespace branch_prediction */ class BPredUnit : public SimObject { + typedef BranchPredictorParams Params; + typedef enums::TargetProvider TargetProvider; + + /** Branch Predictor Unit (BPU) interface functions */ public: - typedef BranchPredictorParams Params; + + + /** * @param params The params object, that has the size of the BP and BTB. */ @@ -90,9 +98,6 @@ class BPredUnit : public SimObject bool predict(const StaticInstPtr &inst, const InstSeqNum &seqNum, PCStateBase &pc, ThreadID tid); - // @todo: Rename this function. - virtual void uncondBranch(ThreadID tid, Addr pc, void * &bp_history) = 0; - /** * Tells the branch predictor to commit any updates until the given * sequence number. @@ -117,82 +122,123 @@ class BPredUnit : public SimObject * @param corr_target The correct branch target. * @param actually_taken The correct branch direction. * @param tid The thread id. + * @param from_commit Indicate whether the squash is comming from commit + * or from decode. Its optional and used for statistics. */ - void squash(const InstSeqNum &squashed_sn, - const PCStateBase &corr_target, - bool actually_taken, ThreadID tid); + void squash(const InstSeqNum &squashed_sn, const PCStateBase &corr_target, + bool actually_taken, ThreadID tid, bool from_commit=true); + + protected: + + /** ******************************************************* + * Interface functions to the conditional branch predictor + * + */ /** - * @param bp_history Pointer to the history object. The predictor - * will need to update any state and delete the object. - */ - virtual void squash(ThreadID tid, void *bp_history) = 0; - - /** - * Looks up a given PC in the BP to see if it is taken or not taken. - * @param inst_PC The PC to look up. + * Looks up a given conditional branch PC of in the BP to see if it + * is taken or not taken. + * @param pc The PC to look up. * @param bp_history Pointer that will be set to an object that * has the branch predictor state associated with the lookup. * @return Whether the branch is taken or not taken. */ - virtual bool lookup(ThreadID tid, Addr instPC, void * &bp_history) = 0; + virtual bool lookup(ThreadID tid, Addr pc, void * &bp_history) = 0; - /** - * If a branch is not taken, because the BTB address is invalid or missing, - * this function sets the appropriate counter in the global and local - * predictors to not taken. - * @param inst_PC The PC to look up the local predictor. + /** + * Ones done with the prediction this function updates the + * path and global history. All branches call this function + * including unconditional once. + * @param tid The thread id. + * @param PC The branch's PC that will be updated. + * @param uncond Wheather or not this branch is an unconditional branch. + * @param taken Whether or not the branch was taken + * @param target The final target of branch. Some modern + * predictors use the target in their history. * @param bp_history Pointer that will be set to an object that * has the branch predictor state associated with the lookup. */ - virtual void btbUpdate(ThreadID tid, Addr instPC, void * &bp_history) = 0; + virtual void updateHistories(ThreadID tid, Addr pc, bool uncond, + bool taken, Addr target, void * &bp_history) = 0; /** - * Looks up a given PC in the BTB to see if a matching entry exists. - * @param inst_PC The PC to look up. - * @return Whether the BTB contains the given PC. + * @param tid The thread id. + * @param bp_history Pointer to the history object. The predictor + * will need to update any state and delete the object. */ - bool BTBValid(Addr instPC) { return BTB.valid(instPC, 0); } + virtual void squash(ThreadID tid, void * &bp_history) = 0; - /** - * Looks up a given PC in the BTB to get the predicted target. The PC may - * be changed or deleted in the future, so it needs to be used immediately, - * and/or copied for use later. - * @param inst_PC The PC to look up. - * @return The address of the target of the branch. - */ - const PCStateBase * - BTBLookup(Addr inst_pc) - { - return BTB.lookup(inst_pc, 0); - } /** * Updates the BP with taken/not taken information. - * @param inst_PC The branch's PC that will be updated. + * @param tid The thread id. + * @param PC The branch's PC that will be updated. * @param taken Whether the branch was taken or not taken. * @param bp_history Pointer to the branch predictor state that is * associated with the branch lookup that is being updated. * @param squashed Set to true when this function is called during a * squash operation. * @param inst Static instruction information - * @param corrTarget The resolved target of the branch (only needed + * @param target The resolved target of the branch (only needed * for squashed branches) * @todo Make this update flexible enough to handle a global predictor. */ - virtual void update(ThreadID tid, Addr instPC, bool taken, - void *bp_history, bool squashed, - const StaticInstPtr &inst, Addr corrTarget) = 0; + virtual void update(ThreadID tid, Addr pc, bool taken, + void * &bp_history, bool squashed, + const StaticInstPtr &inst, Addr target) = 0; + + + /** + * Looks up a given PC in the BTB to see if a matching entry exists. + * @param tid The thread id. + * @param inst_PC The PC to look up. + * @return Whether the BTB contains the given PC. + */ + bool BTBValid(ThreadID tid, Addr instPC) + { + return btb->valid(tid, instPC); + } + + /** + * Looks up a given PC in the BTB to get the predicted target. The PC may + * be changed or deleted in the future, so it needs to be used immediately, + * and/or copied for use later. + * @param tid The thread id. + * @param inst_PC The PC to look up. + * @return The address of the target of the branch. + */ + const PCStateBase * + BTBLookup(ThreadID tid, PCStateBase &instPC) + { + return btb->lookup(tid, instPC.instAddr()); + } + + /** + * Looks up a given PC in the BTB to get current static instruction + * information. This is necessary in a decoupled frontend as + * the information does not usually exist at that this point. + * Only for instructions (branches) that hit in the BTB this information + * is available as the BTB stores them together with the target. + * + * @param inst_PC The PC to look up. + * @return The static instruction info of the given PC if existant. + */ + const StaticInstPtr + BTBGetInst(ThreadID tid, Addr instPC) + { + return btb->getInst(tid, instPC); + } + /** * Updates the BTB with the target of a branch. * @param inst_PC The branch's PC that will be updated. * @param target_PC The branch's target that will be added to the BTB. */ void - BTBUpdate(Addr instPC, const PCStateBase &target) + BTBUpdate(ThreadID tid, Addr instPC, const PCStateBase &target) { ++stats.BTBUpdates; - BTB.update(instPC, target, 0); + return btb->update(tid, instPC, target); } @@ -205,26 +251,28 @@ class BPredUnit : public SimObject * Makes a predictor history struct that contains any * information needed to update the predictor, BTB, and RAS. */ - PredictorHistory(const InstSeqNum &seq_num, Addr instPC, - bool pred_taken, void *bp_history, - void *indirect_history, ThreadID _tid, + PredictorHistory(ThreadID _tid, InstSeqNum sn, Addr _pc, const StaticInstPtr & inst) - : seqNum(seq_num), pc(instPC), bpHistory(bp_history), - indirectHistory(indirect_history), tid(_tid), - predTaken(pred_taken), inst(inst) - {} + : seqNum(sn), tid(_tid), pc(_pc), + inst(inst), type(getBranchType(inst)), + call(inst->isCall()), uncond(inst->isUncondCtrl()), + predTaken(false), actuallyTaken(false), condPred(false), + btbHit(false), targetProvider(TargetProvider::NoTarget), + resteered(false), mispredict(false), target(nullptr), + bpHistory(nullptr), + indirectHistory(nullptr), rasHistory(nullptr) + { } - PredictorHistory(const PredictorHistory &other) : - seqNum(other.seqNum), pc(other.pc), bpHistory(other.bpHistory), - indirectHistory(other.indirectHistory), RASIndex(other.RASIndex), - tid(other.tid), predTaken(other.predTaken), usedRAS(other.usedRAS), - pushedRAS(other.pushedRAS), wasCall(other.wasCall), - wasReturn(other.wasReturn), wasIndirect(other.wasIndirect), - target(other.target), inst(other.inst) + ~PredictorHistory() { - set(RASTarget, other.RASTarget); + assert(bpHistory == nullptr); + assert(indirectHistory == nullptr); + assert(rasHistory == nullptr); } + PredictorHistory (const PredictorHistory&) = delete; + PredictorHistory& operator= (const PredictorHistory&) = delete; + bool operator==(const PredictorHistory &entry) const { @@ -232,60 +280,104 @@ class BPredUnit : public SimObject } /** The sequence number for the predictor history entry. */ - InstSeqNum seqNum; + const InstSeqNum seqNum; + + /** The thread id. */ + const ThreadID tid; /** The PC associated with the sequence number. */ - Addr pc; + const Addr pc; - /** Pointer to the history object passed back from the branch - * predictor. It is used to update or restore state of the - * branch predictor. + /** The branch instrction */ + const StaticInstPtr inst; + + /** The type of the branch */ + const BranchType type; + + /** Whether or not the instruction was a call. */ + const bool call; + + /** Was unconditional control */ + const bool uncond; + + /** Whether or not it was predicted taken. */ + bool predTaken; + + /** To record the actual outcome of the branch */ + bool actuallyTaken; + + /** The prediction of the conditional predictor */ + bool condPred; + + /** Was BTB hit at prediction time */ + bool btbHit; + + /** Which component provided the target */ + TargetProvider targetProvider; + + /** Resteered */ + bool resteered; + + /** The branch was corrected hence was mispredicted. */ + bool mispredict; + + /** The predicted target */ + std::unique_ptr target; + + /** + * Pointer to the history objects passed back from the branch + * predictor subcomponents. + * It is used to update or restore state. + * Respectively for conditional, indirect and RAS. */ void *bpHistory = nullptr; void *indirectHistory = nullptr; - /** The RAS target (only valid if a return). */ - std::unique_ptr RASTarget; + void *rasHistory = nullptr; - /** The RAS index of the instruction (only valid if a call). */ - unsigned RASIndex = 0; - - /** The thread id. */ - ThreadID tid; - - /** Whether or not it was predicted taken. */ - bool predTaken; - - /** Whether or not the RAS was used. */ - bool usedRAS = false; - - /* Whether or not the RAS was pushed */ - bool pushedRAS = false; - - /** Whether or not the instruction was a call. */ - bool wasCall = false; - - /** Whether or not the instruction was a return. */ - bool wasReturn = false; - - /** Wether this instruction was an indirect branch */ - bool wasIndirect = false; - - /** Target of the branch. First it is predicted, and fixed later - * if necessary - */ - Addr target = MaxAddr; - - /** The branch instrction */ - const StaticInstPtr inst; }; - typedef std::deque History; + typedef std::deque History; + + /** + * Internal prediction function. + */ + bool predict(const StaticInstPtr &inst, const InstSeqNum &seqNum, + PCStateBase &pc, ThreadID tid, PredictorHistory* &bpu_history); + + /** + * Squashes a particular branch instance + * @param tid The thread id. + * @param bpu_history The history to be squashed. + */ + void squashHistory(ThreadID tid, PredictorHistory* &bpu_history); + + + /** + * Commit a particular branch + * @param tid The thread id. + * @param bpu_history The history of the branch to be commited. + */ + void commitBranch(ThreadID tid, PredictorHistory* &bpu_history); + + + + protected: /** Number of the threads for which the branch history is maintained. */ const unsigned numThreads; + /** Requires the BTB to hit for returns and indirect branches. For an + * advanced front-end there is no other way than a BTB hit to know + * that the branch exists in the first place. Furthermore, the BPU needs + * to know the branch type to make the correct RAS operations. + * This info is only available from the BTB. + * Low-end CPUs predecoding might be used to identify branches. */ + const bool requiresBTBHit; + + /** Number of bits to shift instructions by for predictor addresses. */ + const unsigned instShiftAmt; /** * The per-thread predictor history. This is used to update the predictor @@ -295,50 +387,55 @@ class BPredUnit : public SimObject std::vector predHist; /** The BTB. */ - DefaultBTB BTB; + BranchTargetBuffer * btb; - /** The per-thread return address stack. */ - std::vector RAS; + /** The return address stack. */ + ReturnAddrStack * ras; /** The indirect target predictor. */ IndirectPredictor * iPred; + /** Statistics */ struct BPredUnitStats : public statistics::Group { - BPredUnitStats(statistics::Group *parent); + BPredUnitStats(BPredUnit *bp); - /** Stat for number of BP lookups. */ - statistics::Scalar lookups; - /** Stat for number of conditional branches predicted. */ + /** Stats per branch type */ + statistics::Vector2d lookups; + statistics::Vector2d squashes; + statistics::Vector2d corrected; + statistics::Vector2d earlyResteers; + statistics::Vector2d committed; + statistics::Vector2d mispredicted; + + /** Target prediction per branch type */ + statistics::Vector2d targetProvider; + statistics::Vector2d targetWrong; + + /** Additional scalar stats for conditional branches */ statistics::Scalar condPredicted; - /** Stat for number of conditional branches predicted incorrectly. */ + statistics::Scalar condPredictedTaken; statistics::Scalar condIncorrect; - /** Stat for number of BTB lookups. */ - statistics::Scalar BTBLookups; - /** Stat for number of BTB updates. */ - statistics::Scalar BTBUpdates; - /** Stat for number of BTB hits. */ - statistics::Scalar BTBHits; - /** Stat for the ratio between BTB hits and BTB lookups. */ - statistics::Formula BTBHitRatio; - /** Stat for number of times the RAS is used to get a target. */ - statistics::Scalar RASUsed; - /** Stat for number of times the RAS is incorrect. */ - statistics::Scalar RASIncorrect; + statistics::Scalar predTakenBTBMiss; + statistics::Scalar NotTakenMispredicted; + statistics::Scalar TakenMispredicted; - /** Stat for the number of indirect target lookups.*/ + /** BTB stats. */ + statistics::Scalar BTBLookups; + statistics::Scalar BTBUpdates; + statistics::Scalar BTBHits; + statistics::Formula BTBHitRatio; + statistics::Scalar BTBMispredicted; + + /** Indirect stats */ statistics::Scalar indirectLookups; - /** Stat for the number of indirect target hits.*/ statistics::Scalar indirectHits; - /** Stat for the number of indirect target misses.*/ statistics::Scalar indirectMisses; - /** Stat for the number of indirect target mispredictions.*/ statistics::Scalar indirectMispredicted; + } stats; protected: - /** Number of bits to shift instructions by for predictor addresses. */ - const unsigned instShiftAmt; /** * @{ diff --git a/src/cpu/pred/branch_type.hh b/src/cpu/pred/branch_type.hh new file mode 100644 index 0000000000..dcc6149a9b --- /dev/null +++ b/src/cpu/pred/branch_type.hh @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* @file + * A helper for branch type information + */ + +#ifndef __CPU_PRED_BRANCH_TYPE_HH__ +#define __CPU_PRED_BRANCH_TYPE_HH__ + +#include "cpu/static_inst.hh" +#include "enums/BranchType.hh" + +namespace gem5 +{ + +namespace branch_prediction +{ + +typedef enums::BranchType BranchType; + +inline BranchType getBranchType(StaticInstPtr inst) +{ + if (inst->isReturn()) { + return BranchType::Return; + } + + if (inst->isCall()) { + return inst->isDirectCtrl() + ? BranchType::CallDirect + : BranchType::CallIndirect; + } + + if (inst->isDirectCtrl()) { + return inst->isCondCtrl() + ? BranchType::DirectCond + : BranchType::DirectUncond; + } + + if (inst->isIndirectCtrl()) { + return inst->isCondCtrl() + ? BranchType::IndirectCond + : BranchType::IndirectUncond; + } + return BranchType::NoBranch; +} + +inline std::string toString(BranchType type) +{ + return std::string(enums::BranchTypeStrings[type]); +} + + +} // namespace branch_prediction +} // namespace gem5 + +#endif // __CPU_PRED_BRANCH_TYPE_HH__ diff --git a/src/cpu/pred/btb.cc b/src/cpu/pred/btb.cc index 71afd45b9f..85d3e2c9bb 100644 --- a/src/cpu/pred/btb.cc +++ b/src/cpu/pred/btb.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2004-2005 The Regents of The University of Michigan * All rights reserved. * @@ -28,119 +40,59 @@ #include "cpu/pred/btb.hh" -#include "base/intmath.hh" -#include "base/trace.hh" -#include "debug/Fetch.hh" - namespace gem5 { namespace branch_prediction { -DefaultBTB::DefaultBTB(unsigned _numEntries, - unsigned _tagBits, - unsigned _instShiftAmt, - unsigned _num_threads) - : numEntries(_numEntries), - tagBits(_tagBits), - instShiftAmt(_instShiftAmt), - log2NumThreads(floorLog2(_num_threads)) +BranchTargetBuffer::BranchTargetBuffer(const Params ¶ms) + : ClockedObject(params), + numThreads(params.numThreads), + stats(this) { - DPRINTF(Fetch, "BTB: Creating BTB object.\n"); +} - if (!isPowerOf2(numEntries)) { - fatal("BTB entries is not a power of 2!"); +BranchTargetBuffer::BranchTargetBufferStats::BranchTargetBufferStats( + statistics::Group *parent) + : statistics::Group(parent), + ADD_STAT(lookups, statistics::units::Count::get(), + "Number of BTB lookups"), + ADD_STAT(misses, statistics::units::Count::get(), + "Number of BTB misses"), + ADD_STAT(updates, statistics::units::Count::get(), + "Number of BTB updates"), + ADD_STAT(mispredict, statistics::units::Count::get(), + "Number of BTB mispredictions. " + "No target found or target wrong."), + ADD_STAT(evictions, statistics::units::Count::get(), + "Number of BTB evictions") +{ + using namespace statistics; + lookups + .init(enums::Num_BranchType) + .flags(total | pdf); + + misses + .init(enums::Num_BranchType) + .flags(total | pdf); + + updates + .init(enums::Num_BranchType) + .flags(total | pdf); + + mispredict + .init(enums::Num_BranchType) + .flags(total | pdf); + + evictions.flags(nozero); + + for (int i = 0; i < enums::Num_BranchType; i++) { + lookups.subname(i, enums::BranchTypeStrings[i]); + misses.subname(i, enums::BranchTypeStrings[i]); + updates.subname(i, enums::BranchTypeStrings[i]); + mispredict.subname(i, enums::BranchTypeStrings[i]); } - - btb.resize(numEntries); - - for (unsigned i = 0; i < numEntries; ++i) { - btb[i].valid = false; - } - - idxMask = numEntries - 1; - - tagMask = (1 << tagBits) - 1; - - tagShiftAmt = instShiftAmt + floorLog2(numEntries); -} - -void -DefaultBTB::reset() -{ - for (unsigned i = 0; i < numEntries; ++i) { - btb[i].valid = false; - } -} - -inline -unsigned -DefaultBTB::getIndex(Addr instPC, ThreadID tid) -{ - // Need to shift PC over by the word offset. - return ((instPC >> instShiftAmt) - ^ (tid << (tagShiftAmt - instShiftAmt - log2NumThreads))) - & idxMask; -} - -inline -Addr -DefaultBTB::getTag(Addr instPC) -{ - return (instPC >> tagShiftAmt) & tagMask; -} - -bool -DefaultBTB::valid(Addr instPC, ThreadID tid) -{ - unsigned btb_idx = getIndex(instPC, tid); - - Addr inst_tag = getTag(instPC); - - assert(btb_idx < numEntries); - - if (btb[btb_idx].valid - && inst_tag == btb[btb_idx].tag - && btb[btb_idx].tid == tid) { - return true; - } else { - return false; - } -} - -// @todo Create some sort of return struct that has both whether or not the -// address is valid, and also the address. For now will just use addr = 0 to -// represent invalid entry. -const PCStateBase * -DefaultBTB::lookup(Addr inst_pc, ThreadID tid) -{ - unsigned btb_idx = getIndex(inst_pc, tid); - - Addr inst_tag = getTag(inst_pc); - - assert(btb_idx < numEntries); - - if (btb[btb_idx].valid - && inst_tag == btb[btb_idx].tag - && btb[btb_idx].tid == tid) { - return btb[btb_idx].target.get(); - } else { - return nullptr; - } -} - -void -DefaultBTB::update(Addr inst_pc, const PCStateBase &target, ThreadID tid) -{ - unsigned btb_idx = getIndex(inst_pc, tid); - - assert(btb_idx < numEntries); - - btb[btb_idx].tid = tid; - btb[btb_idx].valid = true; - set(btb[btb_idx].target, target); - btb[btb_idx].tag = getTag(inst_pc); } } // namespace branch_prediction diff --git a/src/cpu/pred/btb.hh b/src/cpu/pred/btb.hh index 9213053d77..dd3e56a20f 100644 --- a/src/cpu/pred/btb.hh +++ b/src/cpu/pred/btb.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2004-2005 The Regents of The University of Michigan * All rights reserved. * @@ -29,9 +41,13 @@ #ifndef __CPU_PRED_BTB_HH__ #define __CPU_PRED_BTB_HH__ + #include "arch/generic/pcstate.hh" -#include "base/logging.hh" -#include "base/types.hh" +#include "base/statistics.hh" +#include "cpu/pred/branch_type.hh" +#include "cpu/static_inst.hh" +#include "params/BranchTargetBuffer.hh" +#include "sim/clocked_object.hh" namespace gem5 { @@ -39,93 +55,73 @@ namespace gem5 namespace branch_prediction { -class DefaultBTB +class BranchTargetBuffer : public ClockedObject { - private: - struct BTBEntry - { - /** The entry's tag. */ - Addr tag = 0; - - /** The entry's target. */ - std::unique_ptr target; - - /** The entry's thread id. */ - ThreadID tid; - - /** Whether or not the entry is valid. */ - bool valid = false; - }; - public: - /** Creates a BTB with the given number of entries, number of bits per - * tag, and instruction offset amount. - * @param numEntries Number of entries for the BTB. - * @param tagBits Number of bits for each tag in the BTB. - * @param instShiftAmt Offset amount for instructions to ignore alignment. - */ - DefaultBTB(unsigned numEntries, unsigned tagBits, - unsigned instShiftAmt, unsigned numThreads); + typedef BranchTargetBufferParams Params; + typedef enums::BranchType BranchType; - void reset(); + BranchTargetBuffer(const Params ¶ms); - /** Looks up an address in the BTB. Must call valid() first on the address. + virtual void memInvalidate() override = 0; + + /** Checks if a branch address is in the BTB. Intended as a quick check + * before calling lookup. Does not update statistics. * @param inst_PC The address of the branch to look up. - * @param tid The thread id. - * @return Returns the target of the branch. - */ - const PCStateBase *lookup(Addr instPC, ThreadID tid); - - /** Checks if a branch is in the BTB. - * @param inst_PC The address of the branch to look up. - * @param tid The thread id. * @return Whether or not the branch exists in the BTB. */ - bool valid(Addr instPC, ThreadID tid); + virtual bool valid(ThreadID tid, Addr instPC) = 0; + + /** Looks up an address in the BTB to get the target of the branch. + * @param inst_PC The address of the branch to look up. + * @param type Optional type of the branch to look up. + * @return The target of the branch or nullptr if the branch is not + * in the BTB. + */ + virtual const PCStateBase *lookup(ThreadID tid, Addr instPC, + BranchType type = BranchType::NoBranch) = 0; + + /** Looks up an address in the BTB and return the instruction + * information if existant. Does not update statistics. + * @param inst_PC The address of the branch to look up. + * @return Returns the target of the branch. + */ + virtual const StaticInstPtr getInst(ThreadID tid, Addr instPC) = 0; + /** Updates the BTB with the target of a branch. * @param inst_pc The address of the branch being updated. * @param target_pc The target address of the branch. - * @param tid The thread id. */ - void update(Addr inst_pc, const PCStateBase &target_pc, ThreadID tid); + virtual void update(ThreadID tid, Addr inst_pc, + const PCStateBase &target_pc, + BranchType type = BranchType::NoBranch, + StaticInstPtr inst = nullptr) = 0; - private: - /** Returns the index into the BTB, based on the branch's PC. - * @param inst_PC The branch to look up. - * @return Returns the index into the BTB. + /** Update BTB statistics */ - inline unsigned getIndex(Addr instPC, ThreadID tid); + virtual void incorrectTarget(Addr inst_pc, + BranchType type = BranchType::NoBranch) + { + stats.mispredict[type]++; + } - /** Returns the tag bits of a given address. - * @param inst_PC The branch's address. - * @return Returns the tag bits. - */ - inline Addr getTag(Addr instPC); + protected: + /** Number of the threads for which the branch history is maintained. */ + const unsigned numThreads; - /** The actual BTB. */ - std::vector btb; + struct BranchTargetBufferStats : public statistics::Group + { + BranchTargetBufferStats(statistics::Group *parent); - /** The number of entries in the BTB. */ - unsigned numEntries; + statistics::Vector lookups; + statistics::Vector misses; + statistics::Vector updates; + statistics::Vector mispredict; + statistics::Scalar evictions; - /** The index mask. */ - unsigned idxMask; + } stats; - /** The number of tag bits per entry. */ - unsigned tagBits; - - /** The tag mask. */ - unsigned tagMask; - - /** Number of bits to shift PC when calculating index. */ - unsigned instShiftAmt; - - /** Number of bits to shift PC when calculating tag. */ - unsigned tagShiftAmt; - - /** Log2 NumThreads used for hashing threadid */ - unsigned log2NumThreads; }; } // namespace branch_prediction diff --git a/src/cpu/pred/indirect.hh b/src/cpu/pred/indirect.hh index 5f855b14fd..54c55d0dcb 100644 --- a/src/cpu/pred/indirect.hh +++ b/src/cpu/pred/indirect.hh @@ -1,6 +1,16 @@ /* * Copyright (c) 2014 ARM Limited - * All rights reserved. + * Copyright (c) 2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -26,11 +36,16 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* @file + * Indirect target predictor interface + */ + #ifndef __CPU_PRED_INDIRECT_BASE_HH__ #define __CPU_PRED_INDIRECT_BASE_HH__ #include "arch/generic/pcstate.hh" #include "cpu/inst_seq.hh" +#include "cpu/pred/branch_type.hh" #include "params/IndirectPredictor.hh" #include "sim/sim_object.hh" @@ -51,21 +66,57 @@ class IndirectPredictor : public SimObject { } - virtual bool lookup(Addr br_addr, PCStateBase& br_target, - ThreadID tid) = 0; - virtual void recordIndirect(Addr br_addr, Addr tgt_addr, - InstSeqNum seq_num, ThreadID tid) = 0; - virtual void commit(InstSeqNum seq_num, ThreadID tid, - void * indirect_history) = 0; - virtual void squash(InstSeqNum seq_num, ThreadID tid) = 0; - virtual void recordTarget(InstSeqNum seq_num, void * indirect_history, - const PCStateBase& target, ThreadID tid) = 0; - virtual void genIndirectInfo(ThreadID tid, void* & indirect_history) = 0; - virtual void updateDirectionInfo(ThreadID tid, bool actually_taken) = 0; - virtual void deleteIndirectInfo(ThreadID tid, void * indirect_history) = 0; - virtual void changeDirectionPrediction(ThreadID tid, - void * indirect_history, - bool actually_taken) = 0; + virtual void reset() {}; + + /** + * Predicts the indirect target of an indirect branch. + * @param tid Thread ID of the branch. + * @param sn The sequence number of the branch. + * @param pc The branch PC address. + * @param i_history The pointer to the history object. + * @return For a hit the predictor returns a pointer to the target PCState + * otherwise a nullptr is returned. + */ + virtual const PCStateBase* lookup(ThreadID tid, InstSeqNum sn, + Addr pc, void * &i_history) = 0; + + /** + * Updates the indirect predictor with history information of a branch. + * Is called right after the prediction which updates the state + * speculatively. In case the branch was mispredicted the function + * is called again with the corrected information. + * The function is called for ALL branches as some predictors incooperate + * all branches in their history. + * @param tid Thread ID + * @param sn The sequence number of the branch. + * @param pc The branch PC address. + * @param squash Whether the update is called at a misprediction + * @param taken Whether a conditional branch was taken + * @param target The target address if this branch. + * @param br_type The branch instruction type. + * @param i_history The pointer to the history object. + */ + virtual void update(ThreadID tid, InstSeqNum sn, Addr pc, bool squash, + bool taken, const PCStateBase& target, + BranchType br_type, void * &i_history) = 0; + + /** + * Squashes a branch. If the branch modified the history + * reverts the modification. + * @param tid Thread ID + * @param sn The sequence number of the branch. + * @param i_history The pointer to the history object. + */ + virtual void squash(ThreadID tid, InstSeqNum sn, void * &i_history) = 0; + + /** + * A branch gets finally commited. Updates the internal state of + * the indirect predictor (counter and target information). + * @param tid Thread ID + * @param sn The sequence number of the branch. + * @param i_history The pointer to the history object. + */ + virtual void commit(ThreadID tid, InstSeqNum sn, void * &i_history) = 0; }; } // namespace branch_prediction diff --git a/src/cpu/pred/loop_predictor.cc b/src/cpu/pred/loop_predictor.cc index 6574d61bb1..9e34e5141a 100644 --- a/src/cpu/pred/loop_predictor.cc +++ b/src/cpu/pred/loop_predictor.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2014 The University of Wisconsin * * Copyright (c) 2006 INRIA (Institut National de Recherche en @@ -320,10 +332,13 @@ LoopPredictor::squashLoop(BranchInfo* bi) void LoopPredictor::updateStats(bool taken, BranchInfo* bi) { - if (taken == bi->loopPred) { - stats.correct++; - } else { - stats.wrong++; + if (bi->loopPredUsed) { + stats.used++; + if (taken == bi->loopPred) { + stats.correct++; + } else { + stats.wrong++; + } } } @@ -354,6 +369,8 @@ LoopPredictor::condBranchUpdate(ThreadID tid, Addr branch_pc, bool taken, LoopPredictor::LoopPredictorStats::LoopPredictorStats( statistics::Group *parent) : statistics::Group(parent), + ADD_STAT(used, statistics::units::Count::get(), + "Number of times the loop predictor is the provider."), ADD_STAT(correct, statistics::units::Count::get(), "Number of times the loop predictor is the provider and the " "prediction is correct"), diff --git a/src/cpu/pred/loop_predictor.hh b/src/cpu/pred/loop_predictor.hh index 44d75aba35..333cb3b34e 100644 --- a/src/cpu/pred/loop_predictor.hh +++ b/src/cpu/pred/loop_predictor.hh @@ -1,5 +1,15 @@ /* - * Copyright (c) 2014 The University of Wisconsin + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. * * Copyright (c) 2006 INRIA (Institut National de Recherche en * Informatique et en Automatique / French National Research Institute @@ -92,6 +102,7 @@ class LoopPredictor : public SimObject struct LoopPredictorStats : public statistics::Group { LoopPredictorStats(statistics::Group *parent); + statistics::Scalar used; statistics::Scalar correct; statistics::Scalar wrong; } stats; diff --git a/src/cpu/pred/ltage.cc b/src/cpu/pred/ltage.cc index 930d6bf44a..3da443d20d 100644 --- a/src/cpu/pred/ltage.cc +++ b/src/cpu/pred/ltage.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2014 The University of Wisconsin * * Copyright (c) 2006 INRIA (Institut National de Recherche en @@ -94,8 +106,8 @@ LTAGE::predict(ThreadID tid, Addr branch_pc, bool cond_branch, void* &b) // PREDICTOR UPDATE void -LTAGE::update(ThreadID tid, Addr branch_pc, bool taken, void* bp_history, - bool squashed, const StaticInstPtr & inst, Addr corrTarget) +LTAGE::update(ThreadID tid, Addr pc, bool taken, void * &bp_history, + bool squashed, const StaticInstPtr & inst, Addr target) { assert(bp_history); @@ -105,7 +117,7 @@ LTAGE::update(ThreadID tid, Addr branch_pc, bool taken, void* bp_history, if (tage->isSpeculativeUpdateEnabled()) { // This restores the global history, then update it // and recomputes the folded histories. - tage->squash(tid, taken, bi->tageBranchInfo, corrTarget); + tage->squash(tid, taken, bi->tageBranchInfo, target); if (bi->tageBranchInfo->condBranch) { loopPredictor->squashLoop(bi->lpBranchInfo); @@ -117,26 +129,27 @@ LTAGE::update(ThreadID tid, Addr branch_pc, bool taken, void* bp_history, int nrand = random_mt.random() & 3; if (bi->tageBranchInfo->condBranch) { DPRINTF(LTage, "Updating tables for branch:%lx; taken?:%d\n", - branch_pc, taken); + pc, taken); tage->updateStats(taken, bi->tageBranchInfo); loopPredictor->updateStats(taken, bi->lpBranchInfo); - loopPredictor->condBranchUpdate(tid, branch_pc, taken, + loopPredictor->condBranchUpdate(tid, pc, taken, bi->tageBranchInfo->tagePred, bi->lpBranchInfo, instShiftAmt); - tage->condBranchUpdate(tid, branch_pc, taken, bi->tageBranchInfo, - nrand, corrTarget, bi->lpBranchInfo->predTaken); + tage->condBranchUpdate(tid, pc, taken, bi->tageBranchInfo, + nrand, target, bi->lpBranchInfo->predTaken); } - tage->updateHistories(tid, branch_pc, taken, bi->tageBranchInfo, false, - inst, corrTarget); + tage->updateHistories(tid, pc, taken, bi->tageBranchInfo, false, + inst, target); delete bi; + bp_history = nullptr; } void -LTAGE::squash(ThreadID tid, void *bp_history) +LTAGE::squash(ThreadID tid, void * &bp_history) { LTageBranchInfo* bi = (LTageBranchInfo*)(bp_history); diff --git a/src/cpu/pred/ltage.hh b/src/cpu/pred/ltage.hh index 7deaa2bc04..92d1fd25d7 100644 --- a/src/cpu/pred/ltage.hh +++ b/src/cpu/pred/ltage.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2014 The University of Wisconsin * * Copyright (c) 2006 INRIA (Institut National de Recherche en @@ -69,10 +81,10 @@ class LTAGE : public TAGE LTAGE(const LTAGEParams ¶ms); // Base class methods. - void squash(ThreadID tid, void *bp_history) override; - void update(ThreadID tid, Addr branch_addr, bool taken, void *bp_history, - bool squashed, const StaticInstPtr & inst, - Addr corrTarget) override; + void squash(ThreadID tid, void * &bp_history) override; + void update(ThreadID tid, Addr pc, bool taken, + void * &bp_history, bool squashed, + const StaticInstPtr & inst, Addr target) override; void init() override; @@ -98,6 +110,7 @@ class LTAGE : public TAGE virtual ~LTageBranchInfo() { delete lpBranchInfo; + lpBranchInfo = nullptr; } }; diff --git a/src/cpu/pred/multiperspective_perceptron.cc b/src/cpu/pred/multiperspective_perceptron.cc index 25b4d7d39a..fd54ec8163 100644 --- a/src/cpu/pred/multiperspective_perceptron.cc +++ b/src/cpu/pred/multiperspective_perceptron.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright 2019 Texas A&M University * * Redistribution and use in source and binary forms, with or without @@ -547,10 +559,19 @@ MultiperspectivePerceptron::train(ThreadID tid, MPPBranchInfo &bi, bool taken) } } + void -MultiperspectivePerceptron::uncondBranch(ThreadID tid, Addr pc, - void * &bp_history) +MultiperspectivePerceptron::updateHistories(ThreadID tid, Addr pc, + bool uncond, bool taken, Addr target, void * &bp_history) { + assert(uncond || bp_history); + + // For perceptron there is no speculative history correction. + // Conditional branches are done. + if (!uncond) + return; + + // For uncondition branches create branch info. MPPBranchInfo *bi = new MPPBranchInfo(pc, pcshift, false); std::vector &ghist_words = threadData[tid]->ghist_words; @@ -613,10 +634,10 @@ MultiperspectivePerceptron::lookup(ThreadID tid, Addr instPC, } void -MultiperspectivePerceptron::update(ThreadID tid, Addr instPC, bool taken, - void *bp_history, bool squashed, +MultiperspectivePerceptron::update(ThreadID tid, Addr pc, bool taken, + void * &bp_history, bool squashed, const StaticInstPtr & inst, - Addr corrTarget) + Addr target) { assert(bp_history); MPPBranchInfo *bi = static_cast(bp_history); @@ -627,6 +648,7 @@ MultiperspectivePerceptron::update(ThreadID tid, Addr instPC, bool taken, if (bi->isUnconditional()) { delete bi; + bp_history = nullptr; return; } @@ -693,7 +715,6 @@ MultiperspectivePerceptron::update(ThreadID tid, Addr instPC, bool taken, // four different styles of IMLI if (!bi->filtered || (record_mask & Imli)) { - unsigned int target = corrTarget; if (target < bi->getPC()) { if (taken) { threadData[tid]->imli_counter[0] += 1; @@ -813,20 +834,16 @@ MultiperspectivePerceptron::update(ThreadID tid, Addr instPC, bool taken, threadData[tid]->last_ghist_bit = taken; delete bi; + bp_history = nullptr; } void -MultiperspectivePerceptron::btbUpdate(ThreadID tid, Addr branch_pc, - void* &bp_history) -{ -} - -void -MultiperspectivePerceptron::squash(ThreadID tid, void *bp_history) +MultiperspectivePerceptron::squash(ThreadID tid, void * &bp_history) { assert(bp_history); MPPBranchInfo *bi = static_cast(bp_history); delete bi; + bp_history = nullptr; } } // namespace branch_prediction diff --git a/src/cpu/pred/multiperspective_perceptron.hh b/src/cpu/pred/multiperspective_perceptron.hh index 68ab5f1a23..f761607a29 100644 --- a/src/cpu/pred/multiperspective_perceptron.hh +++ b/src/cpu/pred/multiperspective_perceptron.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright 2019 Texas A&M University * * Redistribution and use in source and binary forms, with or without @@ -1048,14 +1060,14 @@ class MultiperspectivePerceptron : public BPredUnit void init() override; - void uncondBranch(ThreadID tid, Addr pc, void * &bp_history) override; - void squash(ThreadID tid, void *bp_history) override; - bool lookup(ThreadID tid, Addr instPC, void * &bp_history) override; - void update(ThreadID tid, Addr instPC, bool taken, - void *bp_history, bool squashed, - const StaticInstPtr & inst, - Addr corrTarget) override; - void btbUpdate(ThreadID tid, Addr branch_addr, void* &bp_history) override; + // Base class methods. + bool lookup(ThreadID tid, Addr branch_addr, void* &bp_history) override; + void updateHistories(ThreadID tid, Addr pc, bool uncond, bool taken, + Addr target, void * &bp_history) override; + void update(ThreadID tid, Addr pc, bool taken, + void * &bp_history, bool squashed, + const StaticInstPtr & inst, Addr target) override; + void squash(ThreadID tid, void * &bp_history) override; }; } // namespace branch_prediction diff --git a/src/cpu/pred/multiperspective_perceptron_tage.cc b/src/cpu/pred/multiperspective_perceptron_tage.cc index 6176d9ccb2..1075f9d04f 100644 --- a/src/cpu/pred/multiperspective_perceptron_tage.cc +++ b/src/cpu/pred/multiperspective_perceptron_tage.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright 2019 Texas A&M University * * Redistribution and use in source and binary forms, with or without @@ -548,7 +560,7 @@ MultiperspectivePerceptronTAGE::lookup(ThreadID tid, Addr instPC, void MPP_StatisticalCorrector::condBranchUpdate(ThreadID tid, Addr branch_pc, - bool taken, StatisticalCorrector::BranchInfo *bi, Addr corrTarget, + bool taken, StatisticalCorrector::BranchInfo *bi, Addr target, bool bias_bit, int hitBank, int altBank, int64_t phist) { bool scPred = (bi->lsum >= 0); @@ -588,10 +600,10 @@ MPP_StatisticalCorrector::condBranchUpdate(ThreadID tid, Addr branch_pc, } void -MultiperspectivePerceptronTAGE::update(ThreadID tid, Addr instPC, bool taken, - void *bp_history, bool squashed, +MultiperspectivePerceptronTAGE::update(ThreadID tid, Addr pc, bool taken, + void * &bp_history, bool squashed, const StaticInstPtr & inst, - Addr corrTarget) + Addr target) { assert(bp_history); MPPTAGEBranchInfo *bi = static_cast(bp_history); @@ -600,7 +612,7 @@ MultiperspectivePerceptronTAGE::update(ThreadID tid, Addr instPC, bool taken, if (tage->isSpeculativeUpdateEnabled()) { // This restores the global history, then update it // and recomputes the folded histories. - tage->squash(tid, taken, bi->tageBranchInfo, corrTarget); + tage->squash(tid, taken, bi->tageBranchInfo, target); if (bi->tageBranchInfo->condBranch) { loopPredictor->squashLoop(bi->lpBranchInfo); } @@ -609,16 +621,16 @@ MultiperspectivePerceptronTAGE::update(ThreadID tid, Addr instPC, bool taken, } if (bi->isUnconditional()) { - statisticalCorrector->scHistoryUpdate(instPC, inst, taken, - bi->scBranchInfo, corrTarget); - tage->updateHistories(tid, instPC, taken, bi->tageBranchInfo, false, - inst, corrTarget); + statisticalCorrector->scHistoryUpdate(pc, inst, taken, + bi->scBranchInfo, target); + tage->updateHistories(tid, pc, taken, bi->tageBranchInfo, false, + inst, target); } else { tage->updateStats(taken, bi->tageBranchInfo); loopPredictor->updateStats(taken, bi->lpBranchInfo); statisticalCorrector->updateStats(taken, bi->scBranchInfo); - loopPredictor->condBranchUpdate(tid, instPC, taken, + loopPredictor->condBranchUpdate(tid, pc, taken, bi->tageBranchInfo->tagePred, bi->lpBranchInfo, instShiftAmt); bool scPred = (bi->scBranchInfo->lsum >= 0); @@ -626,13 +638,13 @@ MultiperspectivePerceptronTAGE::update(ThreadID tid, Addr instPC, bool taken, ((abs(bi->scBranchInfo->lsum) < bi->scBranchInfo->thres))) { updatePartial(tid, *bi, taken); } - statisticalCorrector->condBranchUpdate(tid, instPC, taken, - bi->scBranchInfo, corrTarget, false /* bias_bit: unused */, + statisticalCorrector->condBranchUpdate(tid, pc, taken, + bi->scBranchInfo, target, false /* bias_bit: unused */, 0 /* hitBank: unused */, 0 /* altBank: unused*/, tage->getPathHist(tid)); - tage->condBranchUpdate(tid, instPC, taken, bi->tageBranchInfo, - random_mt.random(), corrTarget, + tage->condBranchUpdate(tid, pc, taken, bi->tageBranchInfo, + random_mt.random(), target, bi->predictedTaken, true); updateHistories(tid, *bi, taken); @@ -640,8 +652,8 @@ MultiperspectivePerceptronTAGE::update(ThreadID tid, Addr instPC, bool taken, if (!tage->isSpeculativeUpdateEnabled()) { if (inst->isCondCtrl() && inst->isDirectCtrl() && !inst->isCall() && !inst->isReturn()) { - uint32_t truncated_target = corrTarget; - uint32_t truncated_pc = instPC; + uint32_t truncated_target = target; + uint32_t truncated_pc = pc; if (truncated_target < truncated_pc) { if (!taken) { threadData[tid]->imli_counter[0] = 0; @@ -657,20 +669,28 @@ MultiperspectivePerceptronTAGE::update(ThreadID tid, Addr instPC, bool taken, } } - statisticalCorrector->scHistoryUpdate(instPC, inst, taken, - bi->scBranchInfo, corrTarget); + statisticalCorrector->scHistoryUpdate(pc, inst, taken, + bi->scBranchInfo, target); - tage->updateHistories(tid, instPC, taken, bi->tageBranchInfo, - false, inst, corrTarget); + tage->updateHistories(tid, pc, taken, bi->tageBranchInfo, + false, inst, target); } } delete bi; + bp_history = nullptr; } void -MultiperspectivePerceptronTAGE::uncondBranch(ThreadID tid, Addr pc, - void * &bp_history) +MultiperspectivePerceptronTAGE::updateHistories(ThreadID tid, Addr pc, + bool uncond, bool taken, + Addr target, void * &bp_history) { + assert(uncond || bp_history); + + // For perceptron there is no speculative history correction. + // Conditional branches are done. + if (!uncond) return; + MPPTAGEBranchInfo *bi = new MPPTAGEBranchInfo(pc, pcshift, false, *tage, *loopPredictor, *statisticalCorrector); @@ -678,11 +698,12 @@ MultiperspectivePerceptronTAGE::uncondBranch(ThreadID tid, Addr pc, } void -MultiperspectivePerceptronTAGE::squash(ThreadID tid, void *bp_history) +MultiperspectivePerceptronTAGE::squash(ThreadID tid, void * &bp_history) { assert(bp_history); MPPTAGEBranchInfo *bi = static_cast(bp_history); delete bi; + bp_history = nullptr; } } // namespace branch_prediction diff --git a/src/cpu/pred/multiperspective_perceptron_tage.hh b/src/cpu/pred/multiperspective_perceptron_tage.hh index 3a92e3cf07..9c7ee3556f 100644 --- a/src/cpu/pred/multiperspective_perceptron_tage.hh +++ b/src/cpu/pred/multiperspective_perceptron_tage.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright 2019 Texas A&M University * * Redistribution and use in source and binary forms, with or without @@ -178,7 +190,7 @@ class MPP_StatisticalCorrector : public StatisticalCorrector void condBranchUpdate(ThreadID tid, Addr branch_pc, bool taken, StatisticalCorrector::BranchInfo *bi, - Addr corrTarget, bool b, int hitBank, int altBank, + Addr target, bool b, int hitBank, int altBank, int64_t phist) override; virtual void getBiasLSUM(Addr branch_pc, @@ -236,12 +248,12 @@ class MultiperspectivePerceptronTAGE : public MultiperspectivePerceptron bool lookup(ThreadID tid, Addr instPC, void * &bp_history) override; - void update(ThreadID tid, Addr instPC, bool taken, - void *bp_history, bool squashed, - const StaticInstPtr & inst, - Addr corrTarget) override; - void uncondBranch(ThreadID tid, Addr pc, void * &bp_history) override; - void squash(ThreadID tid, void *bp_history) override; + void update(ThreadID tid, Addr pc, bool taken, + void * &bp_history, bool squashed, + const StaticInstPtr & inst, Addr target) override; + void updateHistories(ThreadID tid, Addr pc, bool uncond, bool taken, + Addr target, void * &bp_history) override; + void squash(ThreadID tid, void * &bp_history) override; }; diff --git a/src/cpu/pred/ras.cc b/src/cpu/pred/ras.cc index 8d415b7fbd..f29b265657 100644 --- a/src/cpu/pred/ras.cc +++ b/src/cpu/pred/ras.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2004-2005 The Regents of The University of Michigan * All rights reserved. * @@ -28,30 +40,46 @@ #include "cpu/pred/ras.hh" +#include + +#include "debug/RAS.hh" + namespace gem5 { namespace branch_prediction { + void -ReturnAddrStack::init(unsigned _numEntries) +ReturnAddrStack::AddrStack::init(unsigned _numEntries) { numEntries = _numEntries; addrStack.resize(numEntries); + for (unsigned i = 0; i < numEntries; ++i) { + addrStack[i] = nullptr; + } reset(); } void -ReturnAddrStack::reset() +ReturnAddrStack::AddrStack::reset() { usedEntries = 0; tos = 0; } -void -ReturnAddrStack::push(const PCStateBase &return_addr) +const PCStateBase * +ReturnAddrStack::AddrStack::top() { + return addrStack[tos].get(); +} + + +void +ReturnAddrStack::AddrStack::push(const PCStateBase &return_addr) +{ + incrTos(); set(addrStack[tos], return_addr); @@ -62,7 +90,7 @@ ReturnAddrStack::push(const PCStateBase &return_addr) } void -ReturnAddrStack::pop() +ReturnAddrStack::AddrStack::pop() { if (usedEntries > 0) { --usedEntries; @@ -72,9 +100,10 @@ ReturnAddrStack::pop() } void -ReturnAddrStack::restore(unsigned top_entry_idx, const PCStateBase *restored) +ReturnAddrStack::AddrStack::restore(unsigned _tos, + const PCStateBase *restored) { - tos = top_entry_idx; + tos = _tos; set(addrStack[tos], restored); @@ -83,5 +112,218 @@ ReturnAddrStack::restore(unsigned top_entry_idx, const PCStateBase *restored) } } +std::string +ReturnAddrStack::AddrStack::toString(int n) +{ + std::stringstream ss; + for (int i = 0; i < n; i++) { + int idx = int(tos)-i; + if (idx < 0 || addrStack[idx] == nullptr) { + break; + } + ss << std::dec << idx << ":0x" << std::setfill('0') << std::setw(16) + << std::hex << addrStack[idx]->instAddr() << ";"; + } + return ss.str(); +} + + +// Return address stack class. +// + +ReturnAddrStack::ReturnAddrStack(const Params &p) + : SimObject(p), + numEntries(p.numEntries), + numThreads(p.numThreads), + stats(this) +{ + DPRINTF(RAS, "Create RAS stacks.\n"); + + for (unsigned i = 0; i < numThreads; ++i) { + addrStacks.emplace_back(*this); + addrStacks[i].init(numEntries); + } +} + +void +ReturnAddrStack::reset() +{ + DPRINTF(RAS, "RAS Reset.\n"); + for (auto& r : addrStacks) + r.reset(); +} + +void +ReturnAddrStack::makeRASHistory(void* &ras_history) +{ + RASHistory* history = new RASHistory; + history->pushed = false; + history->poped = false; + ras_history = static_cast(history); +} + +void +ReturnAddrStack::push(ThreadID tid, const PCStateBase &pc, + void * &ras_history) +{ + // Note: The RAS may be both popped and pushed to + // support coroutines. + if (ras_history == nullptr) { + makeRASHistory(ras_history); + } + RASHistory *history = static_cast(ras_history); + stats.pushes++; + history->pushed = true; + + addrStacks[tid].push(pc); + + DPRINTF(RAS, "%s: RAS[%i] <= %#x. Entries used: %i, tid:%i\n", __func__, + addrStacks[tid].tos, pc.instAddr(), + addrStacks[tid].usedEntries,tid); + // DPRINTF(RAS, "[%s]\n", addrStacks[tid].toString(10)); +} + + +const PCStateBase* +ReturnAddrStack::pop(ThreadID tid, void * &ras_history) +{ + // Note: The RAS may be both popped and pushed to + // support coroutines. + if (ras_history == nullptr) { + makeRASHistory(ras_history); + } + RASHistory *history = static_cast(ras_history); + stats.pops++; + + history->poped = true; + history->tos = addrStacks[tid].tos; + + + set(history->ras_entry, addrStacks[tid].top()); + // Pop the top of stack + addrStacks[tid].pop(); + + DPRINTF(RAS, "%s: RAS[%i] => %#x. Entries used: %i, tid:%i\n", __func__, + addrStacks[tid].tos, (history->ras_entry.get() != nullptr) + ? history->ras_entry->instAddr() : 0, + addrStacks[tid].usedEntries, tid); + // DPRINTF(RAS, "[%s]\n", addrStacks[tid].toString(10)); + + return history->ras_entry.get(); +} + +void +ReturnAddrStack::squash(ThreadID tid, void * &ras_history) +{ + if (ras_history == nullptr) { + // If ras_history is null no stack operation was performed for + // this branch. Nothing to be done. + return; + } + stats.squashes++; + + RASHistory *history = static_cast(ras_history); + + if (history->pushed) { + stats.pops++; + addrStacks[tid].pop(); + + DPRINTF(RAS, "RAS::%s Incorrect push. Pop RAS[%i]. " + "Entries used: %i, tid:%i\n", __func__, + addrStacks[tid].tos, addrStacks[tid].usedEntries, tid); + } + + if (history->poped) { + stats.pushes++; + addrStacks[tid].restore(history->tos, history->ras_entry.get()); + DPRINTF(RAS, "RAS::%s Incorrect pop. Restore to: RAS[%i]:%#x. " + "Entries used: %i, tid:%i\n", __func__, + history->tos, (history->ras_entry.get() != nullptr) + ? history->ras_entry->instAddr() : 0, + addrStacks[tid].usedEntries, tid); + } + // DPRINTF(RAS, "[%s]\n", addrStacks[tid].toString(10)); + delete history; + ras_history = nullptr; +} + +void +ReturnAddrStack::commit(ThreadID tid, bool misp, + const BranchType brType, void * &ras_history) +{ + // Skip branches that are not call or returns + if (!(brType == BranchType::Return || + brType == BranchType::CallDirect || + brType == BranchType::CallIndirect)) { + // If its not a call or return there should be no ras history. + assert(ras_history == nullptr); + return; + } + + DPRINTF(RAS, "RAS::%s Commit Branch inst: %s, tid:%i\n", + __func__, toString(brType),tid); + + + if (ras_history == nullptr) { + /** + * The only case where we could have no history at this point is + * for a conditional call that is not taken. + * + * Conditional calls + * + * Conditional calls have different scenarios: + * 1. the call was predicted as non taken but was actually taken + * 2. the call was predicted taken but was actually not taken. + * 3. the call was taken but the target was incorrect. + * 4. the call was correct. + * + * In case of mispredictions they will be handled during squashing + * of the BPU. It will push and pop the RAS accordingly. + **/ + return; + } + + /* Handle all other commited returns and calls */ + RASHistory *history = static_cast(ras_history); + + if (history->poped) { + stats.used++; + if (misp) { + stats.incorrect++; + } else { + stats.correct++; + } + + DPRINTF(RAS, "RAS::%s Commit Return PC %#x, correct:%i, tid:%i\n", + __func__, !misp, (history->ras_entry.get() != nullptr) + ? history->ras_entry->instAddr() : 0, tid); + } + delete history; + ras_history = nullptr; +} + + + +ReturnAddrStack::ReturnAddrStackStats::ReturnAddrStackStats( + statistics::Group *parent) + : statistics::Group(parent), + ADD_STAT(pushes, statistics::units::Count::get(), + "Number of times a PC was pushed onto the RAS"), + ADD_STAT(pops, statistics::units::Count::get(), + "Number of times a PC was poped from the RAS"), + ADD_STAT(squashes, statistics::units::Count::get(), + "Number of times the stack operation was squashed due to " + "wrong speculation."), + ADD_STAT(used, statistics::units::Count::get(), + "Number of times the RAS is the provider"), + ADD_STAT(correct, statistics::units::Count::get(), + "Number of times the RAS is the provider and the " + "prediction is correct"), + ADD_STAT(incorrect, statistics::units::Count::get(), + "Number of times the RAS is the provider and the " + "prediction is wrong") +{ +} + } // namespace branch_prediction } // namespace gem5 diff --git a/src/cpu/pred/ras.hh b/src/cpu/pred/ras.hh index 0b4b471c03..294055965e 100644 --- a/src/cpu/pred/ras.hh +++ b/src/cpu/pred/ras.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2004-2005 The Regents of The University of Michigan * All rights reserved. * @@ -32,7 +44,12 @@ #include #include "arch/generic/pcstate.hh" +#include "base/statistics.hh" #include "base/types.hh" +#include "cpu/pred/branch_type.hh" +#include "cpu/static_inst.hh" +#include "params/ReturnAddrStack.hh" +#include "sim/sim_object.hh" namespace gem5 { @@ -41,70 +58,164 @@ namespace branch_prediction { /** Return address stack class, implements a simple RAS. */ -class ReturnAddrStack +class ReturnAddrStack : public SimObject { public: - /** Creates a return address stack, but init() must be called prior to - * use. - */ - ReturnAddrStack() {} - /** Initializes RAS with a specified number of entries. - * @param numEntries Number of entries in the RAS. + /** Subclass that implements the actual address stack. ****** */ - void init(unsigned numEntries); + class AddrStack + { + public: + AddrStack(ReturnAddrStack &_parent) + : parent(_parent) + {} + + + /** Initializes RAS with a specified number of entries. + * @param numEntries Number of entries in the RAS. + */ + void init(unsigned numEntries); + + void reset(); + + /** Returns the top address on the RAS. */ + const PCStateBase *top(); + + /** Returns the index of the top of the RAS. */ + unsigned topIdx() { return tos; } + + /** Pushes an address onto the RAS. */ + void push(const PCStateBase &return_addr); + + /** Pops the top address from the RAS. */ + void pop(); + + /** Changes index to the top of the RAS, and replaces the top address + * with a new target. + * @param top_of_stack the index saved at the time of the prediction. + * @param restored The new target address of the new top of the RAS. + */ + void restore(unsigned top_of_stack, const PCStateBase *restored); + + bool empty() { return usedEntries == 0; } + + bool full() { return usedEntries >= numEntries; } + + /** Returns the top n entries of the stack as string. For debugging. */ + std::string toString(int n); + + /** Increments the top of stack index. */ + inline void + incrTos() + { + if (++tos == numEntries) + tos = 0; + } + + /** Decrements the top of stack index. */ + inline void + decrTos() + { + tos = (tos == 0 ? numEntries - 1 : tos - 1); + } + + /** The Stack itself. */ + std::vector> addrStack; + + /** The number of entries in the RAS. */ + unsigned numEntries; + + /** The number of used entries in the RAS. */ + unsigned usedEntries; + + /** The top of stack index. */ + unsigned tos; + + protected: + ReturnAddrStack &parent; + }; + + + + public: + // typedef RASParams Params; + typedef ReturnAddrStackParams Params; + + // ReturnAddrStack(BPredUnit &_parent, const RASParams); + ReturnAddrStack(const Params &p); void reset(); - /** Returns the top address on the RAS. */ - const PCStateBase *top() { return addrStack[tos].get(); } - - /** Returns the index of the top of the RAS. */ - unsigned topIdx() { return tos; } - - /** Pushes an address onto the RAS. */ - void push(const PCStateBase &return_addr); - - /** Pops the top address from the RAS. */ - void pop(); - - /** Changes index to the top of the RAS, and replaces the top address with - * a new target. - * @param top_entry_idx The index of the RAS that will now be the top. - * @param restored The new target address of the new top of the RAS. + /** + * Pushes an address onto the RAS. + * @param PC The current PC (should be a call). + * @param ras_history Pointer that will be set to an object that + * has the return address state associated when the address was pushed. */ - void restore(unsigned top_entry_idx, const PCStateBase *restored); + void push(ThreadID tid, const PCStateBase &pc, void * &ras_history); - bool empty() { return usedEntries == 0; } + /** + * Pops the top address from the RAS. + * @param ras_history Pointer that will be set to an object that + * has the return address state associated when an address was poped. + * @return The address that got poped from the stack. + * */ + const PCStateBase* pop(ThreadID tid, void * &ras_history); + + /** + * The branch (call/return) got squashed. + * Restores the state of the RAS and delete history + * @param res_history The pointer to the history object. + */ + void squash(ThreadID tid, void * &ras_history); + + /** + * A branch got finally got finally commited. + * @param misp Whether the branch was mispredicted. + * @param brType The type of the branch. + * @param ras_history The pointer to the history object. + */ + void commit(ThreadID tid, bool misp, + const BranchType brType, void * &ras_history); - bool full() { return usedEntries == numEntries; } private: - /** Increments the top of stack index. */ - inline void - incrTos() - { - if (++tos == numEntries) - tos = 0; - } - /** Decrements the top of stack index. */ - inline void - decrTos() + class RASHistory { - tos = (tos == 0 ? numEntries - 1 : tos - 1); - } + public: + /* Was the RAS pushed or poped for this branch. */ + bool pushed = false; + bool poped = false; + /* Was it a call */ + bool wasReturn = false; + bool wasCall = false; + /** The entry that poped from the RAS (only valid if a return). */ + std::unique_ptr ras_entry; + /** The RAS index (top of stack pointer) of the instruction */ + unsigned tos = 0; + }; + + void makeRASHistory(void* &ras_history); /** The RAS itself. */ - std::vector> addrStack; + std::vector addrStacks; /** The number of entries in the RAS. */ unsigned numEntries; + /** The number of threads */ + unsigned numThreads; - /** The number of used entries in the RAS. */ - unsigned usedEntries; - - /** The top of stack index. */ - unsigned tos; + struct ReturnAddrStackStats : public statistics::Group + { + ReturnAddrStackStats(statistics::Group *parent); + statistics::Scalar pushes; + statistics::Scalar pops; + statistics::Scalar squashes; + statistics::Scalar used; + statistics::Scalar correct; + statistics::Scalar incorrect; + } stats; }; } // namespace branch_prediction diff --git a/src/cpu/pred/simple_btb.cc b/src/cpu/pred/simple_btb.cc new file mode 100644 index 0000000000..c78caac7a8 --- /dev/null +++ b/src/cpu/pred/simple_btb.cc @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cpu/pred/simple_btb.hh" + +#include "base/intmath.hh" +#include "base/trace.hh" +#include "debug/BTB.hh" + +namespace gem5 +{ + +namespace branch_prediction +{ + +SimpleBTB::SimpleBTB(const SimpleBTBParams &p) + : BranchTargetBuffer(p), + numEntries(p.numEntries), + tagBits(p.tagBits), + instShiftAmt(p.instShiftAmt), + log2NumThreads(floorLog2(p.numThreads)) +{ + DPRINTF(BTB, "BTB: Creating BTB object.\n"); + + if (!isPowerOf2(numEntries)) { + fatal("BTB entries is not a power of 2!"); + } + + btb.resize(numEntries); + + for (unsigned i = 0; i < numEntries; ++i) { + btb[i].valid = false; + } + + idxMask = numEntries - 1; + + tagMask = (1 << tagBits) - 1; + + tagShiftAmt = instShiftAmt + floorLog2(numEntries); +} + +void +SimpleBTB::memInvalidate() +{ + for (unsigned i = 0; i < numEntries; ++i) { + btb[i].valid = false; + } +} + +inline +unsigned +SimpleBTB::getIndex(Addr instPC, ThreadID tid) +{ + // Need to shift PC over by the word offset. + return ((instPC >> instShiftAmt) + ^ (tid << (tagShiftAmt - instShiftAmt - log2NumThreads))) + & idxMask; +} + +inline +Addr +SimpleBTB::getTag(Addr instPC) +{ + return (instPC >> tagShiftAmt) & tagMask; +} + +SimpleBTB::BTBEntry * +SimpleBTB::findEntry(Addr instPC, ThreadID tid) +{ + unsigned btb_idx = getIndex(instPC, tid); + Addr inst_tag = getTag(instPC); + + assert(btb_idx < numEntries); + + if (btb[btb_idx].valid + && inst_tag == btb[btb_idx].tag + && btb[btb_idx].tid == tid) { + return &btb[btb_idx]; + } + + return nullptr; +} + +bool +SimpleBTB::valid(ThreadID tid, Addr instPC) +{ + BTBEntry *entry = findEntry(instPC, tid); + + return entry != nullptr; +} + +// @todo Create some sort of return struct that has both whether or not the +// address is valid, and also the address. For now will just use addr = 0 to +// represent invalid entry. +const PCStateBase * +SimpleBTB::lookup(ThreadID tid, Addr instPC, BranchType type) +{ + stats.lookups[type]++; + + BTBEntry *entry = findEntry(instPC, tid); + + if (entry) { + return entry->target.get(); + } + stats.misses[type]++; + return nullptr; +} + +const StaticInstPtr +SimpleBTB::getInst(ThreadID tid, Addr instPC) +{ + BTBEntry *entry = findEntry(instPC, tid); + + if (entry) { + return entry->inst; + } + return nullptr; +} + +void +SimpleBTB::update(ThreadID tid, Addr instPC, + const PCStateBase &target, + BranchType type, StaticInstPtr inst) +{ + unsigned btb_idx = getIndex(instPC, tid); + + assert(btb_idx < numEntries); + + stats.updates[type]++; + + btb[btb_idx].tid = tid; + btb[btb_idx].valid = true; + set(btb[btb_idx].target, target); + btb[btb_idx].tag = getTag(instPC); + btb[btb_idx].inst = inst; +} + +} // namespace branch_prediction +} // namespace gem5 diff --git a/src/cpu/pred/simple_btb.hh b/src/cpu/pred/simple_btb.hh new file mode 100644 index 0000000000..3c76890348 --- /dev/null +++ b/src/cpu/pred/simple_btb.hh @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __CPU_PRED_SIMPLE_BTB_HH__ +#define __CPU_PRED_SIMPLE_BTB_HH__ + +#include "base/logging.hh" +#include "base/types.hh" +#include "cpu/pred/btb.hh" +#include "params/SimpleBTB.hh" + +namespace gem5 +{ + +namespace branch_prediction +{ + +class SimpleBTB : public BranchTargetBuffer +{ + public: + SimpleBTB(const SimpleBTBParams ¶ms); + + void memInvalidate() override; + bool valid(ThreadID tid, Addr instPC) override; + const PCStateBase *lookup(ThreadID tid, Addr instPC, + BranchType type = BranchType::NoBranch) override; + void update(ThreadID tid, Addr instPC, const PCStateBase &target_pc, + BranchType type = BranchType::NoBranch, + StaticInstPtr inst = nullptr) override; + const StaticInstPtr getInst(ThreadID tid, Addr instPC) override; + + + private: + struct BTBEntry + { + /** The entry's tag. */ + Addr tag = 0; + + /** The entry's target. */ + std::unique_ptr target; + + /** The entry's thread id. */ + ThreadID tid; + + /** Whether or not the entry is valid. */ + bool valid = false; + + /** Pointer to the static branch instruction at this address */ + StaticInstPtr inst = nullptr; + }; + + + /** Returns the index into the BTB, based on the branch's PC. + * @param inst_PC The branch to look up. + * @return Returns the index into the BTB. + */ + inline unsigned getIndex(Addr instPC, ThreadID tid); + + /** Returns the tag bits of a given address. + * @param inst_PC The branch's address. + * @return Returns the tag bits. + */ + inline Addr getTag(Addr instPC); + + /** Internal call to find an address in the BTB + * @param instPC The branch's address. + * @return Returns a pointer to the BTB entry if found, nullptr otherwise. + */ + BTBEntry *findEntry(Addr instPC, ThreadID tid); + + /** The actual BTB. */ + std::vector btb; + + /** The number of entries in the BTB. */ + unsigned numEntries; + + /** The index mask. */ + unsigned idxMask; + + /** The number of tag bits per entry. */ + unsigned tagBits; + + /** The tag mask. */ + unsigned tagMask; + + /** Number of bits to shift PC when calculating index. */ + unsigned instShiftAmt; + + /** Number of bits to shift PC when calculating tag. */ + unsigned tagShiftAmt; + + /** Log2 NumThreads used for hashing threadid */ + unsigned log2NumThreads; +}; + +} // namespace branch_prediction +} // namespace gem5 + +#endif // __CPU_PRED_SIMPLE_BTB_HH__ diff --git a/src/cpu/pred/simple_indirect.cc b/src/cpu/pred/simple_indirect.cc index f09cdeef55..815e8bf31b 100644 --- a/src/cpu/pred/simple_indirect.cc +++ b/src/cpu/pred/simple_indirect.cc @@ -1,6 +1,16 @@ /* * Copyright (c) 2014 ARM Limited - * All rights reserved. + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -46,9 +56,11 @@ SimpleIndirectPredictor::SimpleIndirectPredictor( numWays(params.indirectWays), tagBits(params.indirectTagSize), pathLength(params.indirectPathLength), + speculativePathLength(params.speculativePathLength), instShift(params.instShiftAmt), ghrNumBits(params.indirectGHRBits), - ghrMask((1 << params.indirectGHRBits)-1) + ghrMask((1 << params.indirectGHRBits)-1), + stats(this) { if (!isPowerOf2(numSets)) { panic("Indirect predictor requires power of 2 number of sets"); @@ -64,172 +76,292 @@ SimpleIndirectPredictor::SimpleIndirectPredictor( fatal_if(ghrNumBits > (sizeof(ThreadInfo::ghr)*8), "ghr_size is too big"); } + void -SimpleIndirectPredictor::genIndirectInfo(ThreadID tid, - void* & indirect_history) +SimpleIndirectPredictor::reset() { - // record the GHR as it was before this prediction + DPRINTF(Indirect, "Reset Indirect predictor\n"); + + for (auto& ti : threadInfo) { + ti.ghr = 0; + ti.pathHist.clear(); + } + + for (unsigned i = 0; i < numSets; i++) { + for (unsigned j = 0; j < numWays; j++) { + targetCache[i][j].tag = 0; + } + } +} + + +void +SimpleIndirectPredictor::genIndirectInfo(ThreadID tid, void* &i_history) +{ + // Record the GHR as it was before this prediction // It will be used to recover the history in case this prediction is // wrong or belongs to bad path - indirect_history = new unsigned(threadInfo[tid].ghr); + IndirectHistory* history = new IndirectHistory; + history->ghr = threadInfo[tid].ghr; + i_history = static_cast(history); } void -SimpleIndirectPredictor::updateDirectionInfo( - ThreadID tid, bool actually_taken) +SimpleIndirectPredictor::updateDirectionInfo(ThreadID tid, bool taken, + Addr pc, Addr target) { + // Direction history threadInfo[tid].ghr <<= 1; - threadInfo[tid].ghr |= actually_taken; + threadInfo[tid].ghr |= taken; threadInfo[tid].ghr &= ghrMask; } -void -SimpleIndirectPredictor::changeDirectionPrediction(ThreadID tid, - void * indirect_history, bool actually_taken) + + +// Interface methods ------------------------------ +const PCStateBase * +SimpleIndirectPredictor::lookup(ThreadID tid, InstSeqNum sn, + Addr pc, void * &i_history) { - unsigned * previousGhr = static_cast(indirect_history); - threadInfo[tid].ghr = ((*previousGhr) << 1) + actually_taken; - threadInfo[tid].ghr &= ghrMask; + assert(i_history==nullptr); + + genIndirectInfo(tid, i_history); + IndirectHistory *history = static_cast(i_history); + + history->pcAddr = pc; + history->was_indirect = true; + + /** Do the prediction for indirect branches (no returns) */ + PCStateBase* target = nullptr; + history->hit = lookup(tid, pc, target, history); + return target; } bool -SimpleIndirectPredictor::lookup(Addr br_addr, PCStateBase& target, - ThreadID tid) +SimpleIndirectPredictor::lookup(ThreadID tid, Addr br_addr, + PCStateBase * &target, + IndirectHistory * &history) { - Addr set_index = getSetIndex(br_addr, threadInfo[tid].ghr, tid); - Addr tag = getTag(br_addr); - assert(set_index < numSets); + history->set_index = getSetIndex(br_addr, tid); + history->tag = getTag(br_addr); + assert(history->set_index < numSets); + stats.lookups++; - DPRINTF(Indirect, "Looking up %x (set:%d)\n", br_addr, set_index); - const auto &iset = targetCache[set_index]; + DPRINTF(Indirect, "Looking up PC:%#x, (set:%d, tag:%d), " + "ghr:%#x, pathHist sz:%#x\n", + history->pcAddr, history->set_index, history->tag, + history->ghr, threadInfo[tid].pathHist.size()); + + const auto &iset = targetCache[history->set_index]; for (auto way = iset.begin(); way != iset.end(); ++way) { // tag may be 0 and match the default in way->tag, so we also have to // check that way->target has been initialized. - if (way->tag == tag && way->target) { + if (way->tag == history->tag && way->target) { DPRINTF(Indirect, "Hit %x (target:%s)\n", br_addr, *way->target); set(target, *way->target); - return true; + history->hit = true; + stats.hits++; + return history->hit; } } DPRINTF(Indirect, "Miss %x\n", br_addr); - return false; + history->hit = false; + stats.misses++; + return history->hit; +} + + +void +SimpleIndirectPredictor::commit(ThreadID tid, InstSeqNum sn, void * &i_history) +{ + if (i_history == nullptr) return; + // we do not need to recover the GHR, so delete the information + IndirectHistory *history = static_cast(i_history); + + DPRINTF(Indirect, "Committing seq:%d, PC:%#x, ghr:%#x, pathHist sz:%lu\n", + sn, history->pcAddr, history->ghr, + threadInfo[tid].pathHist.size()); + + delete history; + i_history = nullptr; + + /** Delete histories if the history grows to much */ + while (threadInfo[tid].pathHist.size() + >= (pathLength + speculativePathLength)) { + + threadInfo[tid].pathHist.pop_front(); + } } void -SimpleIndirectPredictor::recordIndirect(Addr br_addr, Addr tgt_addr, - InstSeqNum seq_num, ThreadID tid) +SimpleIndirectPredictor::update(ThreadID tid, InstSeqNum sn, Addr pc, + bool squash, bool taken, const PCStateBase& target, + BranchType br_type, void * &i_history) { - DPRINTF(Indirect, "Recording %x seq:%d\n", br_addr, seq_num); - HistoryEntry entry(br_addr, tgt_addr, seq_num); - threadInfo[tid].pathHist.push_back(entry); + // If there is no history we did not use the indirect predictor yet. + // Create one + if (i_history==nullptr) { + genIndirectInfo(tid, i_history); + } + IndirectHistory *history = static_cast(i_history); + assert(history!=nullptr); + + DPRINTF(Indirect, "Update sn:%i PC:%#x, squash:%i, ghr:%#x,path sz:%i\n", + sn, pc, squash, history->ghr, threadInfo[tid].pathHist.size()); + + /** If update was called during squash we need to fix the indirect + * path history and the global path history. + * We restore the state before this branch incorrectly updated it + * and perform the update afterwards again. + */ + history->was_indirect = isIndirectNoReturn(br_type); + if (squash) { + + /** restore global history */ + threadInfo[tid].ghr = history->ghr; + + /** For indirect branches recalculate index and tag */ + if (history->was_indirect) { + if (!threadInfo[tid].pathHist.empty()) { + threadInfo[tid].pathHist.pop_back(); + } + + history->set_index = getSetIndex(history->pcAddr, tid); + history->tag = getTag(history->pcAddr); + + DPRINTF(Indirect, "Record Target seq:%d, PC:%#x, TGT:%#x, " + "ghr:%#x, (set:%x, tag:%x)\n", + sn, history->pcAddr, target, history->ghr, + history->set_index, history->tag); + } + } + + // Only indirect branches are recorded in the path history + if (history->was_indirect) { + + DPRINTF(Indirect, "Recording %x seq:%d\n", history->pcAddr, sn); + threadInfo[tid].pathHist.emplace_back( + history->pcAddr, target.instAddr(), sn); + + stats.indirectRecords++; + } + + // All branches update the global history + updateDirectionInfo(tid,taken, history->pcAddr, target.instAddr()); + + // Finally if update is called during at squash we know the target + // we predicted was wrong therefore we update the target. + // We only record the target if the branch was indirect and taken + if (squash && history->was_indirect && taken) + recordTarget(tid, sn, target, history); } + + void -SimpleIndirectPredictor::commit(InstSeqNum seq_num, ThreadID tid, - void * indirect_history) +SimpleIndirectPredictor::squash(ThreadID tid, InstSeqNum sn, void * &i_history) { - DPRINTF(Indirect, "Committing seq:%d\n", seq_num); - ThreadInfo &t_info = threadInfo[tid]; + if (i_history == nullptr) return; // we do not need to recover the GHR, so delete the information - unsigned * previousGhr = static_cast(indirect_history); - delete previousGhr; + IndirectHistory *history = static_cast(i_history); - if (t_info.pathHist.empty()) return; + DPRINTF(Indirect, "Squashing seq:%d, PC:%#x, indirect:%i, " + "ghr:%#x, pathHist sz:%#x\n", + sn, history->pcAddr, history->was_indirect, + history->ghr, + threadInfo[tid].pathHist.size()); - if (t_info.headHistEntry < t_info.pathHist.size() && - t_info.pathHist[t_info.headHistEntry].seqNum <= seq_num) { - if (t_info.headHistEntry >= pathLength) { - t_info.pathHist.pop_front(); - } else { - ++t_info.headHistEntry; + + // Revert the global history register. + threadInfo[tid].ghr = history->ghr; + + // If we record this branch as indirect branch + // remove it from the history. + // Restore the old head in the history. + if (history->was_indirect) { + + // Should not be empty + if (threadInfo[tid].pathHist.size() < pathLength) { + stats.speculativeOverflows++; + } + + if (!threadInfo[tid].pathHist.empty()) { + threadInfo[tid].pathHist.pop_back(); } } + + delete history; + i_history = nullptr; } -void -SimpleIndirectPredictor::squash(InstSeqNum seq_num, ThreadID tid) -{ - DPRINTF(Indirect, "Squashing seq:%d\n", seq_num); - ThreadInfo &t_info = threadInfo[tid]; - auto squash_itr = t_info.pathHist.begin(); - while (squash_itr != t_info.pathHist.end()) { - if (squash_itr->seqNum > seq_num) { - break; - } - ++squash_itr; - } - if (squash_itr != t_info.pathHist.end()) { - DPRINTF(Indirect, "Squashing series starting with sn:%d\n", - squash_itr->seqNum); - } - t_info.pathHist.erase(squash_itr, t_info.pathHist.end()); -} + + + +// Internal functions ------------------------------ + + void -SimpleIndirectPredictor::deleteIndirectInfo(ThreadID tid, - void * indirect_history) +SimpleIndirectPredictor::recordTarget(ThreadID tid, InstSeqNum sn, + const PCStateBase& target, IndirectHistory * &history) { - unsigned * previousGhr = static_cast(indirect_history); - threadInfo[tid].ghr = *previousGhr; - - delete previousGhr; -} - -void -SimpleIndirectPredictor::recordTarget( - InstSeqNum seq_num, void * indirect_history, const PCStateBase& target, - ThreadID tid) -{ - ThreadInfo &t_info = threadInfo[tid]; - - unsigned * ghr = static_cast(indirect_history); - // Should have just squashed so this branch should be the oldest - auto hist_entry = *(t_info.pathHist.rbegin()); - // Temporarily pop it off the history so we can calculate the set - t_info.pathHist.pop_back(); - Addr set_index = getSetIndex(hist_entry.pcAddr, *ghr, tid); - Addr tag = getTag(hist_entry.pcAddr); - hist_entry.targetAddr = target.instAddr(); - t_info.pathHist.push_back(hist_entry); + // and it should be predicted as indirect. + assert(!threadInfo[tid].pathHist.empty()); + assert(history->was_indirect); - assert(set_index < numSets); + if (threadInfo[tid].pathHist.rbegin()->pcAddr != history->pcAddr) { + DPRINTF(Indirect, "History seems to be corrupted. %#x != %#x\n", + history->pcAddr, + threadInfo[tid].pathHist.rbegin()->pcAddr); + } - auto &iset = targetCache[set_index]; + DPRINTF(Indirect, "Record Target seq:%d, PC:%#x, TGT:%#x, " + "ghr:%#x, (set:%x, tag:%x)\n", + sn, history->pcAddr, target.instAddr(), history->ghr, + history->set_index, history->tag); + + assert(history->set_index < numSets); + stats.targetRecords++; + + // Update the target cache + auto &iset = targetCache[history->set_index]; for (auto way = iset.begin(); way != iset.end(); ++way) { - if (way->tag == tag) { - DPRINTF(Indirect, "Updating Target (seq: %d br:%x set:%d target:" - "%s)\n", seq_num, hist_entry.pcAddr, set_index, target); + if (way->tag == history->tag) { + DPRINTF(Indirect, + "Updating Target (seq: %d br:%x set:%d target:%s)\n", + sn, history->pcAddr, history->set_index, target); set(way->target, target); return; } } DPRINTF(Indirect, "Allocating Target (seq: %d br:%x set:%d target:%s)\n", - seq_num, hist_entry.pcAddr, set_index, target); + sn, history->pcAddr, history->set_index, target); + // Did not find entry, random replacement auto &way = iset[rand() % numWays]; - way.tag = tag; + way.tag = history->tag; set(way.target, target); } -inline Addr -SimpleIndirectPredictor::getSetIndex(Addr br_addr, unsigned ghr, ThreadID tid) -{ - ThreadInfo &t_info = threadInfo[tid]; + +inline Addr +SimpleIndirectPredictor::getSetIndex(Addr br_addr, ThreadID tid) +{ Addr hash = br_addr >> instShift; if (hashGHR) { - hash ^= ghr; + hash ^= threadInfo[tid].ghr; } if (hashTargets) { unsigned hash_shift = floorLog2(numSets) / pathLength; - for (int i = t_info.pathHist.size()-1, p = 0; + for (int i = threadInfo[tid].pathHist.size()-1, p = 0; i >= 0 && p < pathLength; i--, p++) { - hash ^= (t_info.pathHist[i].targetAddr >> + hash ^= (threadInfo[tid].pathHist[i].targetAddr >> (instShift + p*hash_shift)); } } @@ -242,5 +374,26 @@ SimpleIndirectPredictor::getTag(Addr br_addr) return (br_addr >> instShift) & ((0x1< +#include "base/statistics.hh" #include "cpu/inst_seq.hh" #include "cpu/pred/indirect.hh" #include "params/SimpleIndirectPredictor.hh" @@ -46,19 +57,23 @@ class SimpleIndirectPredictor : public IndirectPredictor public: SimpleIndirectPredictor(const SimpleIndirectPredictorParams ¶ms); - bool lookup(Addr br_addr, PCStateBase& br_target, ThreadID tid); - void recordIndirect(Addr br_addr, Addr tgt_addr, InstSeqNum seq_num, - ThreadID tid); - void commit(InstSeqNum seq_num, ThreadID tid, void * indirect_history); - void squash(InstSeqNum seq_num, ThreadID tid); - void recordTarget(InstSeqNum seq_num, void * indirect_history, - const PCStateBase& target, ThreadID tid); - void genIndirectInfo(ThreadID tid, void* & indirect_history); - void updateDirectionInfo(ThreadID tid, bool actually_taken); - void deleteIndirectInfo(ThreadID tid, void * indirect_history); - void changeDirectionPrediction(ThreadID tid, void * indirect_history, - bool actually_taken); + /** Indirect predictor interface */ + void reset() override; + const PCStateBase * lookup(ThreadID tid, InstSeqNum sn, + Addr pc, void * &iHistory) override; + void update(ThreadID tid, InstSeqNum sn, Addr pc, bool squash, + bool taken, const PCStateBase& target, + BranchType br_type, void * &iHistory) override; + void squash(ThreadID tid, InstSeqNum sn, void * &iHistory) override; + void commit(ThreadID tid, InstSeqNum sn, void * &iHistory) override; + + + + /** ------------------ + * The actual predictor + * ------------------- + * */ private: const bool hashGHR; const bool hashTargets; @@ -66,6 +81,7 @@ class SimpleIndirectPredictor : public IndirectPredictor const unsigned numWays; const unsigned tagBits; const unsigned pathLength; + const unsigned speculativePathLength; const unsigned instShift; const unsigned ghrNumBits; const unsigned ghrMask; @@ -78,27 +94,88 @@ class SimpleIndirectPredictor : public IndirectPredictor std::vector > targetCache; - Addr getSetIndex(Addr br_addr, unsigned ghr, ThreadID tid); - Addr getTag(Addr br_addr); + struct HistoryEntry { HistoryEntry(Addr br_addr, Addr tgt_addr, InstSeqNum seq_num) : pcAddr(br_addr), targetAddr(tgt_addr), seqNum(seq_num) { } + HistoryEntry() : pcAddr(0), targetAddr(0), seqNum(0) { } Addr pcAddr; Addr targetAddr; InstSeqNum seqNum; }; + /** Indirect branch history information + * Used for prediction, update and recovery + */ + struct IndirectHistory + { + /* data */ + Addr pcAddr; + Addr targetAddr; + InstSeqNum seqNum; + Addr set_index; + Addr tag; + bool hit; + unsigned ghr; + uint64_t pathHist; + + bool was_indirect; + + IndirectHistory() + : pcAddr(MaxAddr), + targetAddr(MaxAddr), + was_indirect(false) + {} + }; + + /** Per thread path and global history registers*/ struct ThreadInfo { + // Path history register std::deque pathHist; - unsigned headHistEntry = 0; + // Global direction history register unsigned ghr = 0; }; std::vector threadInfo; + + + // ---- Internal functions ----- // + bool lookup(ThreadID tid, Addr br_addr, + PCStateBase * &target, IndirectHistory * &history); + void recordTarget(ThreadID tid, InstSeqNum sn, + const PCStateBase& target, IndirectHistory * &history); + + // Helper functions to generate and modify the + // direction info + void genIndirectInfo(ThreadID tid, void* &iHistory); + void updateDirectionInfo(ThreadID tid, bool taken, Addr pc, Addr target); + + // Helper to compute set and tag + inline Addr getSetIndex(Addr br_addr, ThreadID tid); + inline Addr getTag(Addr br_addr); + + inline bool isIndirectNoReturn(BranchType type) { + return (type == BranchType::CallIndirect) || + (type == BranchType::IndirectUncond); + } + + protected: + struct IndirectStats : public statistics::Group + { + IndirectStats(statistics::Group *parent); + // STATS + statistics::Scalar lookups; + statistics::Scalar hits; + statistics::Scalar misses; + statistics::Scalar targetRecords; + statistics::Scalar indirectRecords; + statistics::Scalar speculativeOverflows; + + } stats; }; } // namespace branch_prediction diff --git a/src/cpu/pred/tage.cc b/src/cpu/pred/tage.cc index 1ba52e27c7..35c0d75352 100644 --- a/src/cpu/pred/tage.cc +++ b/src/cpu/pred/tage.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2014 The University of Wisconsin * * Copyright (c) 2006 INRIA (Institut National de Recherche en @@ -56,8 +68,8 @@ TAGE::TAGE(const TAGEParams ¶ms) : BPredUnit(params), tage(params.tage) // PREDICTOR UPDATE void -TAGE::update(ThreadID tid, Addr branch_pc, bool taken, void* bp_history, - bool squashed, const StaticInstPtr & inst, Addr corrTarget) +TAGE::update(ThreadID tid, Addr pc, bool taken, void * &bp_history, + bool squashed, const StaticInstPtr & inst, Addr target) { assert(bp_history); @@ -67,69 +79,65 @@ TAGE::update(ThreadID tid, Addr branch_pc, bool taken, void* bp_history, if (squashed) { // This restores the global history, then update it // and recomputes the folded histories. - tage->squash(tid, taken, tage_bi, corrTarget); + tage->squash(tid, taken, tage_bi, target); return; } int nrand = random_mt.random() & 3; if (bi->tageBranchInfo->condBranch) { DPRINTF(Tage, "Updating tables for branch:%lx; taken?:%d\n", - branch_pc, taken); + pc, taken); tage->updateStats(taken, bi->tageBranchInfo); - tage->condBranchUpdate(tid, branch_pc, taken, tage_bi, nrand, - corrTarget, bi->tageBranchInfo->tagePred); + tage->condBranchUpdate(tid, pc, taken, tage_bi, nrand, + target, bi->tageBranchInfo->tagePred); } // optional non speculative update of the histories - tage->updateHistories(tid, branch_pc, taken, tage_bi, false, inst, - corrTarget); + tage->updateHistories(tid, pc, taken, tage_bi, false, inst, target); delete bi; + bp_history = nullptr; } void -TAGE::squash(ThreadID tid, void *bp_history) +TAGE::squash(ThreadID tid, void * &bp_history) { TageBranchInfo *bi = static_cast(bp_history); DPRINTF(Tage, "Deleting branch info: %lx\n", bi->tageBranchInfo->branchPC); delete bi; + bp_history = nullptr; } bool -TAGE::predict(ThreadID tid, Addr branch_pc, bool cond_branch, void* &b) +TAGE::predict(ThreadID tid, Addr pc, bool cond_branch, void* &b) { TageBranchInfo *bi = new TageBranchInfo(*tage);//nHistoryTables+1); b = (void*)(bi); - return tage->tagePredict(tid, branch_pc, cond_branch, bi->tageBranchInfo); + return tage->tagePredict(tid, pc, cond_branch, bi->tageBranchInfo); } bool -TAGE::lookup(ThreadID tid, Addr branch_pc, void* &bp_history) +TAGE::lookup(ThreadID tid, Addr pc, void* &bp_history) { - bool retval = predict(tid, branch_pc, true, bp_history); + bool retval = predict(tid, pc, true, bp_history); - TageBranchInfo *bi = static_cast(bp_history); - - DPRINTF(Tage, "Lookup branch: %lx; predict:%d\n", branch_pc, retval); - - tage->updateHistories(tid, branch_pc, retval, bi->tageBranchInfo, true); + DPRINTF(Tage, "Lookup branch: %lx; predict:%d\n", pc, retval); return retval; } void -TAGE::btbUpdate(ThreadID tid, Addr branch_pc, void* &bp_history) +TAGE::updateHistories(ThreadID tid, Addr pc, bool uncond, + bool taken, Addr target, void * &bp_history) { - TageBranchInfo *bi = static_cast(bp_history); - tage->btbUpdate(tid, branch_pc, bi->tageBranchInfo); -} + assert(uncond || bp_history); + if (uncond) { + DPRINTF(Tage, "UnConditionalBranch: %lx\n", pc); + predict(tid, pc, false, bp_history); + } -void -TAGE::uncondBranch(ThreadID tid, Addr br_pc, void* &bp_history) -{ - DPRINTF(Tage, "UnConditionalBranch: %lx\n", br_pc); - predict(tid, br_pc, false, bp_history); + // Update the global history for all branches TageBranchInfo *bi = static_cast(bp_history); - tage->updateHistories(tid, br_pc, true, bi->tageBranchInfo, true); + tage->updateHistories(tid, pc, taken, bi->tageBranchInfo, true); } } // namespace branch_prediction diff --git a/src/cpu/pred/tage.hh b/src/cpu/pred/tage.hh index 568f07bf9e..6d4151cb11 100644 --- a/src/cpu/pred/tage.hh +++ b/src/cpu/pred/tage.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2014 The University of Wisconsin * * Copyright (c) 2006 INRIA (Institut National de Recherche en @@ -87,13 +99,13 @@ class TAGE: public BPredUnit TAGE(const TAGEParams ¶ms); // Base class methods. - void uncondBranch(ThreadID tid, Addr br_pc, void* &bp_history) override; - bool lookup(ThreadID tid, Addr branch_addr, void* &bp_history) override; - void btbUpdate(ThreadID tid, Addr branch_addr, void* &bp_history) override; - void update(ThreadID tid, Addr branch_addr, bool taken, void *bp_history, - bool squashed, const StaticInstPtr & inst, - Addr corrTarget) override; - virtual void squash(ThreadID tid, void *bp_history) override; + bool lookup(ThreadID tid, Addr pc, void* &bp_history) override; + void updateHistories(ThreadID tid, Addr pc, bool uncond, bool taken, + Addr target, void * &bp_history) override; + void update(ThreadID tid, Addr pc, bool taken, + void * &bp_history, bool squashed, + const StaticInstPtr & inst, Addr target) override; + virtual void squash(ThreadID tid, void * &bp_history) override; }; } // namespace branch_prediction diff --git a/src/cpu/pred/tage_sc_l.cc b/src/cpu/pred/tage_sc_l.cc index 615c6230c8..a178ba6fc6 100644 --- a/src/cpu/pred/tage_sc_l.cc +++ b/src/cpu/pred/tage_sc_l.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2018 Metempsy Technology Consulting * All rights reserved. * @@ -362,16 +374,16 @@ TAGE_SC_L_TAGE::extraAltCalc(TAGEBase::BranchInfo* bi) } bool -TAGE_SC_L::predict(ThreadID tid, Addr branch_pc, bool cond_branch, void* &b) +TAGE_SC_L::predict(ThreadID tid, Addr pc, bool cond_branch, void* &b) { TageSCLBranchInfo *bi = new TageSCLBranchInfo(*tage, *statisticalCorrector, *loopPredictor); b = (void*)(bi); - bool pred_taken = tage->tagePredict(tid, branch_pc, cond_branch, + bool pred_taken = tage->tagePredict(tid, pc, cond_branch, bi->tageBranchInfo); - pred_taken = loopPredictor->loopPredict(tid, branch_pc, cond_branch, + pred_taken = loopPredictor->loopPredict(tid, pc, cond_branch, bi->lpBranchInfo, pred_taken, instShiftAmt); @@ -394,7 +406,7 @@ TAGE_SC_L::predict(ThreadID tid, Addr branch_pc, bool cond_branch, void* &b) bool bias = (bi->tageBranchInfo->longestMatchPred != bi->tageBranchInfo->altTaken); - pred_taken = statisticalCorrector->scPredict(tid, branch_pc, cond_branch, + pred_taken = statisticalCorrector->scPredict(tid, pc, cond_branch, bi->scBranchInfo, pred_taken, bias, use_tage_ctr, tage_ctr, tage->getTageCtrBits(), bi->tageBranchInfo->hitBank, bi->tageBranchInfo->altBank, tage->getPathHist(tid)); @@ -410,8 +422,8 @@ TAGE_SC_L::predict(ThreadID tid, Addr branch_pc, bool cond_branch, void* &b) } void -TAGE_SC_L::update(ThreadID tid, Addr branch_pc, bool taken, void *bp_history, - bool squashed, const StaticInstPtr & inst, Addr corrTarget) +TAGE_SC_L::update(ThreadID tid, Addr pc, bool taken, void *&bp_history, + bool squashed, const StaticInstPtr & inst, Addr target) { assert(bp_history); @@ -423,7 +435,7 @@ TAGE_SC_L::update(ThreadID tid, Addr branch_pc, bool taken, void *bp_history, if (tage->isSpeculativeUpdateEnabled()) { // This restores the global history, then update it // and recomputes the folded histories. - tage->squash(tid, taken, tage_bi, corrTarget); + tage->squash(tid, taken, tage_bi, target); if (bi->tageBranchInfo->condBranch) { loopPredictor->squashLoop(bi->lpBranchInfo); } @@ -434,7 +446,7 @@ TAGE_SC_L::update(ThreadID tid, Addr branch_pc, bool taken, void *bp_history, int nrand = random_mt.random() & 3; if (tage_bi->condBranch) { DPRINTF(TageSCL, "Updating tables for branch:%lx; taken?:%d\n", - branch_pc, taken); + pc, taken); tage->updateStats(taken, bi->tageBranchInfo); loopPredictor->updateStats(taken, bi->lpBranchInfo); @@ -443,26 +455,27 @@ TAGE_SC_L::update(ThreadID tid, Addr branch_pc, bool taken, void *bp_history, bool bias = (bi->tageBranchInfo->longestMatchPred != bi->tageBranchInfo->altTaken); - statisticalCorrector->condBranchUpdate(tid, branch_pc, taken, - bi->scBranchInfo, corrTarget, bias, bi->tageBranchInfo->hitBank, + statisticalCorrector->condBranchUpdate(tid, pc, taken, + bi->scBranchInfo, target, bias, bi->tageBranchInfo->hitBank, bi->tageBranchInfo->altBank, tage->getPathHist(tid)); - loopPredictor->condBranchUpdate(tid, branch_pc, taken, + loopPredictor->condBranchUpdate(tid, pc, taken, bi->tageBranchInfo->tagePred, bi->lpBranchInfo, instShiftAmt); - tage->condBranchUpdate(tid, branch_pc, taken, bi->tageBranchInfo, - nrand, corrTarget, bi->lpBranchInfo->predTaken); + tage->condBranchUpdate(tid, pc, taken, bi->tageBranchInfo, + nrand, target, bi->lpBranchInfo->predTaken); } if (!tage->isSpeculativeUpdateEnabled()) { - statisticalCorrector->scHistoryUpdate(branch_pc, inst, taken, - bi->scBranchInfo, corrTarget); + statisticalCorrector->scHistoryUpdate(pc, inst, taken, + bi->scBranchInfo, target); - tage->updateHistories(tid, branch_pc, taken, bi->tageBranchInfo, false, - inst, corrTarget); + tage->updateHistories(tid, pc, taken, bi->tageBranchInfo, false, + inst, target); } delete bi; + bp_history = nullptr; } } // namespace branch_prediction diff --git a/src/cpu/pred/tage_sc_l.hh b/src/cpu/pred/tage_sc_l.hh index 7dead58363..6c56e69982 100644 --- a/src/cpu/pred/tage_sc_l.hh +++ b/src/cpu/pred/tage_sc_l.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2022-2023 The University of Edinburgh + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2018 Metempsy Technology Consulting * All rights reserved. * @@ -159,12 +171,11 @@ class TAGE_SC_L: public LTAGE public: TAGE_SC_L(const TAGE_SC_LParams ¶ms); - bool predict( - ThreadID tid, Addr branch_pc, bool cond_branch, void* &b) override; + bool predict(ThreadID tid, Addr pc, bool cond_branch, void* &b) override; - void update(ThreadID tid, Addr branch_addr, bool taken, void *bp_history, - bool squashed, const StaticInstPtr & inst, - Addr corrTarget) override; + void update(ThreadID tid, Addr pc, bool taken, + void * &bp_history, bool squashed, + const StaticInstPtr & inst, Addr target) override; protected: diff --git a/src/cpu/pred/tournament.cc b/src/cpu/pred/tournament.cc index b3a55313b7..a480497c0a 100644 --- a/src/cpu/pred/tournament.cc +++ b/src/cpu/pred/tournament.cc @@ -1,5 +1,6 @@ /* * Copyright (c) 2011, 2014 ARM Limited + * Copyright (c) 2022-2023 The University of Edinburgh * All rights reserved * * The license below extends only to copyright in the software and shall @@ -134,50 +135,22 @@ TournamentBP::calcLocHistIdx(Addr &branch_addr) inline void -TournamentBP::updateGlobalHistTaken(ThreadID tid) +TournamentBP::updateGlobalHist(ThreadID tid, bool taken) { - globalHistory[tid] = (globalHistory[tid] << 1) | 1; + globalHistory[tid] = (globalHistory[tid] << 1) | taken; globalHistory[tid] = globalHistory[tid] & historyRegisterMask; } inline void -TournamentBP::updateGlobalHistNotTaken(ThreadID tid) -{ - globalHistory[tid] = (globalHistory[tid] << 1); - globalHistory[tid] = globalHistory[tid] & historyRegisterMask; -} - -inline -void -TournamentBP::updateLocalHistTaken(unsigned local_history_idx) +TournamentBP::updateLocalHist(unsigned local_history_idx, bool taken) { localHistoryTable[local_history_idx] = - (localHistoryTable[local_history_idx] << 1) | 1; -} - -inline -void -TournamentBP::updateLocalHistNotTaken(unsigned local_history_idx) -{ - localHistoryTable[local_history_idx] = - (localHistoryTable[local_history_idx] << 1); -} - - -void -TournamentBP::btbUpdate(ThreadID tid, Addr branch_addr, void * &bp_history) -{ - unsigned local_history_idx = calcLocHistIdx(branch_addr); - //Update Global History to Not Taken (clear LSB) - globalHistory[tid] &= (historyRegisterMask & ~1ULL); - //Update Local History to Not Taken - localHistoryTable[local_history_idx] = - localHistoryTable[local_history_idx] & (localPredictorMask & ~1ULL); + (localHistoryTable[local_history_idx] << 1) | taken; } bool -TournamentBP::lookup(ThreadID tid, Addr branch_addr, void * &bp_history) +TournamentBP::lookup(ThreadID tid, Addr pc, void * &bp_history) { bool local_prediction; unsigned local_history_idx; @@ -187,7 +160,7 @@ TournamentBP::lookup(ThreadID tid, Addr branch_addr, void * &bp_history) bool choice_prediction; //Lookup in the local predictor to get its branch prediction - local_history_idx = calcLocHistIdx(branch_addr); + local_history_idx = calcLocHistIdx(pc); local_predictor_idx = localHistoryTable[local_history_idx] & localPredictorMask; local_prediction = localCtrs[local_predictor_idx] > localThreshold; @@ -212,57 +185,53 @@ TournamentBP::lookup(ThreadID tid, Addr branch_addr, void * &bp_history) assert(local_history_idx < localHistoryTableSize); - // Speculative update of the global history and the - // selected local history. + // Select and return the prediction + // History update will be happen in the next function if (choice_prediction) { - if (global_prediction) { - updateGlobalHistTaken(tid); - updateLocalHistTaken(local_history_idx); - return true; - } else { - updateGlobalHistNotTaken(tid); - updateLocalHistNotTaken(local_history_idx); - return false; - } + return global_prediction; } else { - if (local_prediction) { - updateGlobalHistTaken(tid); - updateLocalHistTaken(local_history_idx); - return true; - } else { - updateGlobalHistNotTaken(tid); - updateLocalHistNotTaken(local_history_idx); - return false; - } + return local_prediction; } } void -TournamentBP::uncondBranch(ThreadID tid, Addr pc, void * &bp_history) +TournamentBP::updateHistories(ThreadID tid, Addr pc, bool uncond, + bool taken, Addr target, void * &bp_history) { - // Create BPHistory and pass it back to be recorded. - BPHistory *history = new BPHistory; - history->globalHistory = globalHistory[tid]; - history->localPredTaken = true; - history->globalPredTaken = true; - history->globalUsed = true; - history->localHistoryIdx = invalidPredictorIndex; - history->localHistory = invalidPredictorIndex; - bp_history = static_cast(history); + assert(uncond || bp_history); + if (uncond) { + // Create BPHistory and pass it back to be recorded. + BPHistory *history = new BPHistory; + history->globalHistory = globalHistory[tid]; + history->localPredTaken = true; + history->globalPredTaken = true; + history->globalUsed = true; + history->localHistoryIdx = invalidPredictorIndex; + history->localHistory = invalidPredictorIndex; + bp_history = static_cast(history); + } - updateGlobalHistTaken(tid); + // Update the global history for all branches + updateGlobalHist(tid, taken); + + // Update the local history only for conditional branches + if (!uncond) { + auto history = static_cast(bp_history); + updateLocalHist(history->localHistoryIdx, taken); + } } + void -TournamentBP::update(ThreadID tid, Addr branch_addr, bool taken, - void *bp_history, bool squashed, - const StaticInstPtr & inst, Addr corrTarget) +TournamentBP::update(ThreadID tid, Addr pc, bool taken, + void * &bp_history, bool squashed, + const StaticInstPtr & inst, Addr target) { assert(bp_history); BPHistory *history = static_cast(bp_history); - unsigned local_history_idx = calcLocHistIdx(branch_addr); + unsigned local_history_idx = calcLocHistIdx(pc); assert(local_history_idx < localHistoryTableSize); @@ -330,10 +299,11 @@ TournamentBP::update(ThreadID tid, Addr branch_addr, bool taken, // We're done with this history, now delete it. delete history; + bp_history = nullptr; } void -TournamentBP::squash(ThreadID tid, void *bp_history) +TournamentBP::squash(ThreadID tid, void * &bp_history) { BPHistory *history = static_cast(bp_history); @@ -347,6 +317,7 @@ TournamentBP::squash(ThreadID tid, void *bp_history) // Delete this BPHistory now that we're done with it. delete history; + bp_history = nullptr; } #ifdef GEM5_DEBUG diff --git a/src/cpu/pred/tournament.hh b/src/cpu/pred/tournament.hh index 018d6756e4..1f404c1cce 100644 --- a/src/cpu/pred/tournament.hh +++ b/src/cpu/pred/tournament.hh @@ -1,5 +1,6 @@ /* * Copyright (c) 2011, 2014 ARM Limited + * Copyright (c) 2022-2023 The University of Edinburgh * All rights reserved * * The license below extends only to copyright in the software and shall @@ -70,52 +71,14 @@ class TournamentBP : public BPredUnit */ TournamentBP(const TournamentBPParams ¶ms); - /** - * Looks up the given address in the branch predictor and returns - * a true/false value as to whether it is taken. Also creates a - * BPHistory object to store any state it will need on squash/update. - * @param branch_addr The address of the branch to look up. - * @param bp_history Pointer that will be set to the BPHistory object. - * @return Whether or not the branch is taken. - */ - bool lookup(ThreadID tid, Addr branch_addr, void * &bp_history); - - /** - * Records that there was an unconditional branch, and modifies - * the bp history to point to an object that has the previous - * global history stored in it. - * @param bp_history Pointer that will be set to the BPHistory object. - */ - void uncondBranch(ThreadID tid, Addr pc, void * &bp_history); - /** - * Updates the branch predictor to Not Taken if a BTB entry is - * invalid or not found. - * @param branch_addr The address of the branch to look up. - * @param bp_history Pointer to any bp history state. - * @return Whether or not the branch is taken. - */ - void btbUpdate(ThreadID tid, Addr branch_addr, void * &bp_history); - /** - * Updates the branch predictor with the actual result of a branch. - * @param branch_addr The address of the branch to update. - * @param taken Whether or not the branch was taken. - * @param bp_history Pointer to the BPHistory object that was created - * when the branch was predicted. - * @param squashed is set when this function is called during a squash - * operation. - * @param inst Static instruction information - * @param corrTarget Resolved target of the branch (only needed if - * squashed) - */ - void update(ThreadID tid, Addr branch_addr, bool taken, void *bp_history, - bool squashed, const StaticInstPtr & inst, Addr corrTarget); - - /** - * Restores the global branch history on a squash. - * @param bp_history Pointer to the BPHistory object that has the - * previous global branch history in it. - */ - void squash(ThreadID tid, void *bp_history); + // Base class methods. + bool lookup(ThreadID tid, Addr pc, void* &bp_history) override; + void updateHistories(ThreadID tid, Addr pc, bool uncond, bool taken, + Addr target, void * &bp_history) override; + void update(ThreadID tid, Addr pc, bool taken, + void * &bp_history, bool squashed, + const StaticInstPtr & inst, Addr target) override; + void squash(ThreadID tid, void * &bp_history) override; private: /** @@ -131,25 +94,18 @@ class TournamentBP : public BPredUnit */ inline unsigned calcLocHistIdx(Addr &branch_addr); - /** Updates global history as taken. */ - inline void updateGlobalHistTaken(ThreadID tid); - - /** Updates global history as not taken. */ - inline void updateGlobalHistNotTaken(ThreadID tid); + /** Updates global history with the given direction + * @param taken Whether or not the branch was taken + */ + inline void updateGlobalHist(ThreadID tid, bool taken); /** - * Updates local histories as taken. + * Updates local histories. * @param local_history_idx The local history table entry that * will be updated. + * @param taken Whether or not the branch was taken. */ - inline void updateLocalHistTaken(unsigned local_history_idx); - - /** - * Updates local histories as not taken. - * @param local_history_idx The local history table entry that - * will be updated. - */ - inline void updateLocalHistNotTaken(unsigned local_history_idx); + inline void updateLocalHist(unsigned local_history_idx, bool taken); /** * The branch history information that is created upon predicting diff --git a/src/cpu/probes/PcCountTracker.py b/src/cpu/probes/PcCountTracker.py index 259ec68f8e..6106970436 100644 --- a/src/cpu/probes/PcCountTracker.py +++ b/src/cpu/probes/PcCountTracker.py @@ -24,10 +24,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects import SimObject +from m5.objects.Probe import ProbeListenerObject from m5.params import * from m5.util.pybind import * -from m5.objects.Probe import ProbeListenerObject -from m5.objects import SimObject class PcCountTrackerManager(SimObject): diff --git a/src/cpu/profile.cc b/src/cpu/profile.cc index 25d739c381..06c8d46bc2 100644 --- a/src/cpu/profile.cc +++ b/src/cpu/profile.cc @@ -63,7 +63,7 @@ BaseStackTrace::tryGetSymbol(std::string &symbol, Addr addr, const auto it = symtab->find(addr); if (it == symtab->end()) return false; - symbol = it->name; + symbol = it->name(); return true; } @@ -151,7 +151,7 @@ FunctionProfile::sample(ProfileNode *node, Addr pc) auto it = symtab.findNearest(pc); if (it != symtab.end()) { - pc_count[it->address]++; + pc_count[it->address()]++; } else { // record PC even if we don't have a symbol to avoid // silently biasing the histogram diff --git a/src/cpu/simple/BaseAtomicSimpleCPU.py b/src/cpu/simple/BaseAtomicSimpleCPU.py index 4ee53aef0f..27367bd0cf 100644 --- a/src/cpu/simple/BaseAtomicSimpleCPU.py +++ b/src/cpu/simple/BaseAtomicSimpleCPU.py @@ -36,9 +36,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.BaseSimpleCPU import BaseSimpleCPU from m5.objects.SimPoint import SimPoint +from m5.params import * class BaseAtomicSimpleCPU(BaseSimpleCPU): diff --git a/src/cpu/simple/BaseNonCachingSimpleCPU.py b/src/cpu/simple/BaseNonCachingSimpleCPU.py index 58a7324068..787b4fccb0 100644 --- a/src/cpu/simple/BaseNonCachingSimpleCPU.py +++ b/src/cpu/simple/BaseNonCachingSimpleCPU.py @@ -33,8 +33,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.BaseAtomicSimpleCPU import BaseAtomicSimpleCPU +from m5.params import * class BaseNonCachingSimpleCPU(BaseAtomicSimpleCPU): diff --git a/src/cpu/simple/BaseSimpleCPU.py b/src/cpu/simple/BaseSimpleCPU.py index fe7ad751a7..d4816700c0 100644 --- a/src/cpu/simple/BaseSimpleCPU.py +++ b/src/cpu/simple/BaseSimpleCPU.py @@ -25,11 +25,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.defines import buildEnv -from m5.params import * - from m5.objects.BaseCPU import BaseCPU -from m5.objects.DummyChecker import DummyChecker from m5.objects.BranchPredictor import * +from m5.objects.DummyChecker import DummyChecker +from m5.params import * class BaseSimpleCPU(BaseCPU): diff --git a/src/cpu/simple/BaseTimingSimpleCPU.py b/src/cpu/simple/BaseTimingSimpleCPU.py index 5761816fa7..6b1a1a7699 100644 --- a/src/cpu/simple/BaseTimingSimpleCPU.py +++ b/src/cpu/simple/BaseTimingSimpleCPU.py @@ -24,9 +24,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * - from m5.objects.BaseSimpleCPU import BaseSimpleCPU +from m5.params import * class BaseTimingSimpleCPU(BaseSimpleCPU): diff --git a/src/cpu/simple/SConscript b/src/cpu/simple/SConscript index ffa6467e9b..f2552012bf 100644 --- a/src/cpu/simple/SConscript +++ b/src/cpu/simple/SConscript @@ -28,7 +28,7 @@ Import('*') -if not env['CONF']['USE_NULL_ISA']: +if env['CONF']['BUILD_ISA']: SimObject('BaseAtomicSimpleCPU.py', sim_objects=['BaseAtomicSimpleCPU']) Source('atomic.cc') diff --git a/src/cpu/simple/probes/SConscript b/src/cpu/simple/probes/SConscript index e9fbbb306c..3af318f76f 100644 --- a/src/cpu/simple/probes/SConscript +++ b/src/cpu/simple/probes/SConscript @@ -28,6 +28,6 @@ Import('*') -if not env['CONF']['USE_NULL_ISA']: +if env['CONF']['BUILD_ISA']: SimObject('SimPoint.py', sim_objects=['SimPoint']) Source('simpoint.cc') diff --git a/src/cpu/simple/probes/SimPoint.py b/src/cpu/simple/probes/SimPoint.py index 6f2f13ba74..73a716760f 100644 --- a/src/cpu/simple/probes/SimPoint.py +++ b/src/cpu/simple/probes/SimPoint.py @@ -33,8 +33,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.Probe import ProbeListenerObject +from m5.params import * class SimPoint(ProbeListenerObject): diff --git a/src/cpu/static_inst.hh b/src/cpu/static_inst.hh index 7ecc57d2f0..12b05f9b0e 100644 --- a/src/cpu/static_inst.hh +++ b/src/cpu/static_inst.hh @@ -1,5 +1,6 @@ /* - * Copyright (c) 2017, 2020 ARM Limited + * Copyright (c) 2017, 2020, 2023 Arm Limited + * Copyright (c) 2022-2023 The University of Edinburgh * All rights reserved * * The license below extends only to copyright in the software and shall @@ -181,6 +182,7 @@ class StaticInst : public RefCounted, public StaticInstFlags bool isNonSpeculative() const { return flags[IsNonSpeculative]; } bool isQuiesce() const { return flags[IsQuiesce]; } bool isUnverifiable() const { return flags[IsUnverifiable]; } + bool isPseudo() const { return flags[IsPseudo]; } bool isSyscall() const { return flags[IsSyscall]; } bool isMacroop() const { return flags[IsMacroop]; } bool isMicroop() const { return flags[IsMicroop]; } @@ -250,6 +252,11 @@ class StaticInst : public RefCounted, public StaticInstFlags _destRegIdxPtr = dest; } + /** + * Instruction size in bytes. Necessary for dynamic instruction sizes + */ + size_t _size = 0; + /** * Base mnemonic (e.g., "add"). Used by generateDisassembly() * methods. Also useful to readily identify instructions from @@ -307,6 +314,17 @@ class StaticInst : public RefCounted, public StaticInstFlags panic("buildRetPC not defined!"); } + size_t size() const + { + if (_size == 0) fatal( + "Instruction size for this instruction not set! It's size is " + "required for the decoupled front-end. Either use the standard " + "front-end or this ISA needs to be extended with the instruction " + "size. Refer to the X86, Arm or RiscV decoders for an example."); + return _size; + } + virtual void size(size_t newSize) { _size = newSize; } + /** * Return the microop that goes with a particular micropc. This should * only be defined/used in macroops which will contain microops diff --git a/src/cpu/testers/directedtest/RubyDirectedTester.py b/src/cpu/testers/directedtest/RubyDirectedTester.py index b9297b058b..d01b0bdee2 100644 --- a/src/cpu/testers/directedtest/RubyDirectedTester.py +++ b/src/cpu/testers/directedtest/RubyDirectedTester.py @@ -24,11 +24,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * - -from m5.objects.ClockedObject import ClockedObject +from m5.SimObject import SimObject class DirectedGenerator(SimObject): diff --git a/src/cpu/testers/directedtest/SConscript b/src/cpu/testers/directedtest/SConscript index 6787648608..e4e9f10c2b 100644 --- a/src/cpu/testers/directedtest/SConscript +++ b/src/cpu/testers/directedtest/SConscript @@ -35,7 +35,7 @@ Import('*') # When this dependency is removed, the ruby tester should be compiled # independently from Ruby # -if env['CONF']['PROTOCOL'] == 'None': +if not env['CONF']['RUBY']: Return() SimObject('RubyDirectedTester.py', sim_objects=[ diff --git a/src/cpu/testers/garnet_synthetic_traffic/SConscript b/src/cpu/testers/garnet_synthetic_traffic/SConscript index 14f4abdc67..e2eb4cffd6 100644 --- a/src/cpu/testers/garnet_synthetic_traffic/SConscript +++ b/src/cpu/testers/garnet_synthetic_traffic/SConscript @@ -28,7 +28,7 @@ Import('*') -if env['CONF']['PROTOCOL'] == 'None': +if not env['CONF']['RUBY']: Return() SimObject('GarnetSyntheticTraffic.py', sim_objects=['GarnetSyntheticTraffic']) diff --git a/src/cpu/testers/gpu_ruby_test/CpuThread.py b/src/cpu/testers/gpu_ruby_test/CpuThread.py index f40df272a4..e74301b677 100644 --- a/src/cpu/testers/gpu_ruby_test/CpuThread.py +++ b/src/cpu/testers/gpu_ruby_test/CpuThread.py @@ -27,11 +27,10 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +from m5.objects.TesterThread import TesterThread from m5.params import * from m5.proxy import * -from m5.objects.TesterThread import TesterThread - class CpuThread(TesterThread): type = "CpuThread" diff --git a/src/cpu/testers/gpu_ruby_test/DmaThread.py b/src/cpu/testers/gpu_ruby_test/DmaThread.py index 0a3dbc7289..c1b9b91a43 100644 --- a/src/cpu/testers/gpu_ruby_test/DmaThread.py +++ b/src/cpu/testers/gpu_ruby_test/DmaThread.py @@ -27,11 +27,10 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +from m5.objects.TesterThread import TesterThread from m5.params import * from m5.proxy import * -from m5.objects.TesterThread import TesterThread - class DmaThread(TesterThread): type = "DmaThread" diff --git a/src/cpu/testers/gpu_ruby_test/GpuWavefront.py b/src/cpu/testers/gpu_ruby_test/GpuWavefront.py index 625af91fa4..2a22f4d7c7 100644 --- a/src/cpu/testers/gpu_ruby_test/GpuWavefront.py +++ b/src/cpu/testers/gpu_ruby_test/GpuWavefront.py @@ -27,11 +27,10 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +from m5.objects.TesterThread import TesterThread from m5.params import * from m5.proxy import * -from m5.objects.TesterThread import TesterThread - class GpuWavefront(TesterThread): type = "GpuWavefront" diff --git a/src/cpu/testers/gpu_ruby_test/ProtocolTester.py b/src/cpu/testers/gpu_ruby_test/ProtocolTester.py index 178376bee9..c314392520 100644 --- a/src/cpu/testers/gpu_ruby_test/ProtocolTester.py +++ b/src/cpu/testers/gpu_ruby_test/ProtocolTester.py @@ -73,7 +73,7 @@ class ProtocolTester(ClockedObject): random_seed = Param.Int( 0, "Random seed number. Default value (0) means \ - using runtime-specific value.", + using base/random.hh without seed.", ) log_file = Param.String("Log file's name") system = Param.System(Parent.any, "System we belong to") diff --git a/src/cpu/testers/gpu_ruby_test/SConscript b/src/cpu/testers/gpu_ruby_test/SConscript index 0231649043..b56be9448f 100644 --- a/src/cpu/testers/gpu_ruby_test/SConscript +++ b/src/cpu/testers/gpu_ruby_test/SConscript @@ -34,7 +34,7 @@ Import('*') if not env['CONF']['BUILD_GPU']: Return() -if env['CONF']['PROTOCOL'] == 'None': +if not env['CONF']['RUBY']: Return() SimObject('ProtocolTester.py', sim_objects=['ProtocolTester']) diff --git a/src/cpu/testers/gpu_ruby_test/address_manager.cc b/src/cpu/testers/gpu_ruby_test/address_manager.cc index 049ba86e51..a0c0670a8f 100644 --- a/src/cpu/testers/gpu_ruby_test/address_manager.cc +++ b/src/cpu/testers/gpu_ruby_test/address_manager.cc @@ -33,7 +33,6 @@ #include #include -#include #include "base/intmath.hh" #include "base/logging.hh" @@ -101,7 +100,8 @@ AddressManager::getAddress(Location loc) AddressManager::Location AddressManager::getAtomicLoc() { - Location ret_atomic_loc = random() % numAtomicLocs; + Location ret_atomic_loc = \ + random_mt.random() % numAtomicLocs; atomicStructs[ret_atomic_loc]->startLocSelection(); return ret_atomic_loc; } @@ -206,7 +206,9 @@ AddressManager::AtomicStruct::getLoadLoc() // we can pick any location btw // locArray [firstMark : arraySize-1] int range_size = arraySize - firstMark; - Location ret_loc = locArray[firstMark + random() % range_size]; + Location ret_loc = locArray[ + firstMark + random_mt.random() % range_size + ]; // update loadStoreMap LdStMap::iterator it = loadStoreMap.find(ret_loc); @@ -238,7 +240,9 @@ AddressManager::AtomicStruct::getStoreLoc() } else { // we can pick any location btw [firstMark : secondMark-1] int range_size = secondMark - firstMark; - Location ret_loc = locArray[firstMark + random() % range_size]; + Location ret_loc = locArray[ + firstMark + random_mt.random() % range_size + ]; // update loadStoreMap LdStMap::iterator it = loadStoreMap.find(ret_loc); diff --git a/src/cpu/testers/gpu_ruby_test/episode.cc b/src/cpu/testers/gpu_ruby_test/episode.cc index 6822049bbd..7e16b0ef07 100644 --- a/src/cpu/testers/gpu_ruby_test/episode.cc +++ b/src/cpu/testers/gpu_ruby_test/episode.cc @@ -34,6 +34,7 @@ #include #include +#include "base/random.hh" #include "cpu/testers/gpu_ruby_test/protocol_tester.hh" #include "cpu/testers/gpu_ruby_test/tester_thread.hh" @@ -100,7 +101,7 @@ Episode::initActions() int num_loads = numLoads; int num_stores = numStores; while ((num_loads + num_stores) > 0) { - switch (random() % 2) { + switch (random_mt.random() % 2) { case 0: // Load if (num_loads > 0) { actions.push_back(new Action(Action::Type::LOAD, diff --git a/src/cpu/testers/gpu_ruby_test/protocol_tester.cc b/src/cpu/testers/gpu_ruby_test/protocol_tester.cc index f2fd7f9600..6b3f9e19f1 100644 --- a/src/cpu/testers/gpu_ruby_test/protocol_tester.cc +++ b/src/cpu/testers/gpu_ruby_test/protocol_tester.cc @@ -34,8 +34,8 @@ #include #include #include -#include +#include "base/random.hh" #include "cpu/testers/gpu_ruby_test/cpu_thread.hh" #include "cpu/testers/gpu_ruby_test/dma_thread.hh" #include "cpu/testers/gpu_ruby_test/gpu_wavefront.hh" @@ -141,11 +141,20 @@ ProtocolTester::ProtocolTester(const Params &p) sentExitSignal = false; - // set random seed number + // set random seed number, if specified. + // Note: random_m5 will use a fixed key if random_seed is not set. + // This ensures a reproducable. if (p.random_seed != 0) { - srand(p.random_seed); + random_mt.init(p.random_seed); } else { - srand(time(NULL)); + warn( + "If `random_seed == 0` (or `random_seed` is unset) " + "ProtocolTester does not seed the RNG. This will NOT result in " + "the RNG generating different results each run. In this case the " + "RNG is seeded by a default value. This differs from behavior in " + "previous versions of gem5. Setting `random_seed` to a non-zero " + "value is strongly recommended." + ); } actionCount = 0; diff --git a/src/cpu/testers/gpu_ruby_test/tester_thread.cc b/src/cpu/testers/gpu_ruby_test/tester_thread.cc index 760f8c2d87..ce3a1bccc6 100644 --- a/src/cpu/testers/gpu_ruby_test/tester_thread.cc +++ b/src/cpu/testers/gpu_ruby_test/tester_thread.cc @@ -33,6 +33,7 @@ #include +#include "base/random.hh" #include "debug/ProtocolTest.hh" namespace gem5 @@ -144,7 +145,8 @@ TesterThread::attachTesterThreadToPorts(ProtocolTester *_tester, void TesterThread::issueNewEpisode() { - int num_reg_loads = random() % tester->getEpisodeLength(); + int num_reg_loads = \ + random_mt.random() % tester->getEpisodeLength(); int num_reg_stores = tester->getEpisodeLength() - num_reg_loads; // create a new episode diff --git a/src/cpu/testers/memtest/MemTest.py b/src/cpu/testers/memtest/MemTest.py index 24bd974804..db22d86e4e 100644 --- a/src/cpu/testers/memtest/MemTest.py +++ b/src/cpu/testers/memtest/MemTest.py @@ -36,11 +36,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * -from m5.objects.ClockedObject import ClockedObject - class MemTest(ClockedObject): type = "MemTest" @@ -63,6 +62,7 @@ class MemTest(ClockedObject): percent_reads = Param.Percent(65, "Percentage reads") percent_functional = Param.Percent(50, "Percentage functional accesses") percent_uncacheable = Param.Percent(10, "Percentage uncacheable") + percent_atomic = Param.Percent(0, "Percentage atomics") # Determine how often to print progress messages and what timeout # to use for checking progress of both requests and responses diff --git a/src/cpu/testers/memtest/memtest.cc b/src/cpu/testers/memtest/memtest.cc index 7c256d8642..e3859ff44c 100644 --- a/src/cpu/testers/memtest/memtest.cc +++ b/src/cpu/testers/memtest/memtest.cc @@ -94,6 +94,7 @@ MemTest::MemTest(const Params &p) percentReads(p.percent_reads), percentFunctional(p.percent_functional), percentUncacheable(p.percent_uncacheable), + percentAtomic(p.percent_atomic), requestorId(p.system->getRequestorId(this)), blockSize(p.system->cacheLineSize()), blockAddrMask(blockSize - 1), @@ -115,6 +116,7 @@ MemTest::MemTest(const Params &p) // set up counters numReads = 0; numWrites = 0; + numAtomics = 0; // kick things into action schedule(tickEvent, curTick()); @@ -142,18 +144,36 @@ MemTest::completeRequest(PacketPtr pkt, bool functional) outstandingAddrs.erase(remove_addr); DPRINTF(MemTest, "Completing %s at address %x (blk %x) %s\n", - pkt->isWrite() ? "write" : "read", + pkt->isWrite() ? pkt->isAtomicOp() ? "atomic" : "write" : "read", req->getPaddr(), blockAlign(req->getPaddr()), pkt->isError() ? "error" : "success"); const uint8_t *pkt_data = pkt->getConstPtr(); if (pkt->isError()) { - if (!functional || !suppressFuncErrors) + if (!functional || !pkt->suppressFuncError() || !suppressFuncErrors) panic( "%s access failed at %#x\n", pkt->isWrite() ? "Write" : "Read", req->getPaddr()); } else { - if (pkt->isRead()) { + if (pkt->isAtomicOp()) { + uint8_t ref_data = referenceData[req->getPaddr()]; + if (pkt_data[0] != ref_data) { + panic("%s: read of %x (blk %x) @ cycle %d " + "returns %x, expected %x\n", name(), + req->getPaddr(), blockAlign(req->getPaddr()), curTick(), + pkt_data[0], ref_data); + } + DPRINTF(MemTest, + "Completing atomic at address %x (blk %x) value %x\n", + req->getPaddr(), blockAlign(req->getPaddr()), + pkt_data[0]); + + referenceData[req->getPaddr()] = + atomicPendingData[req->getPaddr()]; + + numAtomics++; + stats.numAtomics++; + } else if (pkt->isRead()) { uint8_t ref_data = referenceData[req->getPaddr()]; if (pkt_data[0] != ref_data) { panic("%s: read of %x (blk %x) @ cycle %d " @@ -167,9 +187,10 @@ MemTest::completeRequest(PacketPtr pkt, bool functional) if (numReads == (uint64_t)nextProgressMessage) { ccprintf(std::cerr, - "%s: completed %d read, %d write accesses @%d\n", - name(), numReads, numWrites, curTick()); - nextProgressMessage += progressInterval; + "%s: completed %d read, %d write, " + "%d atomic accesses @%d\n", + name(), numReads, numWrites, numAtomics, curTick()); + nextProgressMessage += progressInterval; } if (maxLoads != 0 && numReads >= maxLoads) @@ -205,7 +226,9 @@ MemTest::MemTestStats::MemTestStats(statistics::Group *parent) ADD_STAT(numReads, statistics::units::Count::get(), "number of read accesses completed"), ADD_STAT(numWrites, statistics::units::Count::get(), - "number of write accesses completed") + "number of write accesses completed"), + ADD_STAT(numAtomics, statistics::units::Count::get(), + "number of atomic accesses completed") { } @@ -221,6 +244,8 @@ MemTest::tick() unsigned cmd = random_mt.random(0, 100); uint8_t data = random_mt.random(); bool uncacheable = random_mt.random(0, 100) < percentUncacheable; + bool do_atomic = (random_mt.random(0, 100) < percentAtomic) && + !uncacheable; unsigned base = random_mt.random(0, 1); Request::Flags flags; Addr paddr; @@ -281,13 +306,36 @@ MemTest::tick() pkt = new Packet(req, MemCmd::ReadReq); pkt->dataDynamic(pkt_data); } else { - DPRINTF(MemTest, "Initiating %swrite at addr %x (blk %x) value %x\n", - do_functional ? "functional " : "", req->getPaddr(), - blockAlign(req->getPaddr()), data); + if (do_atomic) { + DPRINTF(MemTest, + "Initiating atomic at addr %x (blk %x) value %x\n", + req->getPaddr(), blockAlign(req->getPaddr()), data); - pkt = new Packet(req, MemCmd::WriteReq); - pkt->dataDynamic(pkt_data); - pkt_data[0] = data; + TypedAtomicOpFunctor *_amo_op = + new AtomicGeneric3Op( + data, data, + [](uint8_t* b, uint8_t a, uint8_t c){ + *b = c; + }); + assert(_amo_op); + AtomicOpFunctorPtr amo_op = AtomicOpFunctorPtr(_amo_op); + req->setAtomicOpFunctor(std::move(amo_op)); + req->setFlags(Request::ATOMIC_RETURN_OP); + + pkt = new Packet(req, MemCmd::WriteReq); + pkt->dataDynamic(pkt_data); + pkt_data[0] = data; + atomicPendingData[req->getPaddr()] = data; + } else { + DPRINTF(MemTest, + "Initiating %swrite at addr %x (blk %x) value %x\n", + do_functional ? "functional " : "", req->getPaddr(), + blockAlign(req->getPaddr()), data); + + pkt = new Packet(req, MemCmd::WriteReq); + pkt->dataDynamic(pkt_data); + pkt_data[0] = data; + } } // there is no point in ticking if we are waiting for a retry diff --git a/src/cpu/testers/memtest/memtest.hh b/src/cpu/testers/memtest/memtest.hh index 3fd1674191..32ffd5cd6e 100644 --- a/src/cpu/testers/memtest/memtest.hh +++ b/src/cpu/testers/memtest/memtest.hh @@ -131,6 +131,7 @@ class MemTest : public ClockedObject const unsigned percentReads; const unsigned percentFunctional; const unsigned percentUncacheable; + const unsigned percentAtomic; /** Request id for all generated traffic */ RequestorID requestorId; @@ -138,11 +139,12 @@ class MemTest : public ClockedObject unsigned int id; std::unordered_set outstandingAddrs; + std::unordered_map atomicPendingData; // store the expected value for the addresses we have touched std::unordered_map referenceData; - const unsigned blockSize; + const Addr blockSize; const Addr blockAddrMask; @@ -169,6 +171,7 @@ class MemTest : public ClockedObject uint64_t numReads; uint64_t numWrites; + uint64_t numAtomics; const uint64_t maxLoads; const bool atomic; @@ -180,6 +183,7 @@ class MemTest : public ClockedObject MemTestStats(statistics::Group *parent); statistics::Scalar numReads; statistics::Scalar numWrites; + statistics::Scalar numAtomics; } stats; /** diff --git a/src/cpu/testers/rubytest/RubyTester.py b/src/cpu/testers/rubytest/RubyTester.py index a90cfe1f82..e613eff79c 100644 --- a/src/cpu/testers/rubytest/RubyTester.py +++ b/src/cpu/testers/rubytest/RubyTester.py @@ -25,11 +25,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * -from m5.objects.ClockedObject import ClockedObject - class RubyTester(ClockedObject): type = "RubyTester" diff --git a/src/cpu/testers/rubytest/SConscript b/src/cpu/testers/rubytest/SConscript index cc76d2bdfd..a4eac87499 100644 --- a/src/cpu/testers/rubytest/SConscript +++ b/src/cpu/testers/rubytest/SConscript @@ -35,7 +35,7 @@ Import('*') # When this dependency is removed, the ruby tester should be compiled # independently from Ruby # -if env['CONF']['PROTOCOL'] == 'None': +if not env['CONF']['RUBY']: Return() SimObject('RubyTester.py', sim_objects=['RubyTester']) diff --git a/src/cpu/testers/traffic_gen/BaseTrafficGen.py b/src/cpu/testers/traffic_gen/BaseTrafficGen.py index b5df83e779..48700e0747 100644 --- a/src/cpu/testers/traffic_gen/BaseTrafficGen.py +++ b/src/cpu/testers/traffic_gen/BaseTrafficGen.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * -from m5.objects.ClockedObject import ClockedObject + # Types of Stream Generators. # Those are orthogonal to the other generators in the TrafficGen diff --git a/src/cpu/testers/traffic_gen/GUPSGen.py b/src/cpu/testers/traffic_gen/GUPSGen.py index 6b8b3f72df..747a8de994 100644 --- a/src/cpu/testers/traffic_gen/GUPSGen.py +++ b/src/cpu/testers/traffic_gen/GUPSGen.py @@ -25,9 +25,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * -from m5.objects.ClockedObject import ClockedObject class GUPSGen(ClockedObject): diff --git a/src/cpu/testers/traffic_gen/PyTrafficGen.py b/src/cpu/testers/traffic_gen/PyTrafficGen.py index c3a660f053..be61ec1e56 100644 --- a/src/cpu/testers/traffic_gen/PyTrafficGen.py +++ b/src/cpu/testers/traffic_gen/PyTrafficGen.py @@ -34,9 +34,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.defines import buildEnv -from m5.SimObject import * - from m5.objects.BaseTrafficGen import * +from m5.SimObject import * class PyTrafficGen(BaseTrafficGen): diff --git a/src/cpu/testers/traffic_gen/TrafficGen.py b/src/cpu/testers/traffic_gen/TrafficGen.py index 6f1aa67bfd..d5242099d1 100644 --- a/src/cpu/testers/traffic_gen/TrafficGen.py +++ b/src/cpu/testers/traffic_gen/TrafficGen.py @@ -33,8 +33,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.BaseTrafficGen import * +from m5.params import * + # The behaviour of this traffic generator is specified in a # configuration file, and this file describes a state transition graph diff --git a/src/cpu/trace/TraceCPU.py b/src/cpu/trace/TraceCPU.py index 1be16518d7..7b053e569f 100644 --- a/src/cpu/trace/TraceCPU.py +++ b/src/cpu/trace/TraceCPU.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013 - 2016 ARM Limited +# Copyright (c) 2013 - 2016, 2023 Arm Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -33,11 +33,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.ClockedObject import ClockedObject from m5.params import * -from m5.objects.BaseCPU import BaseCPU +from m5.proxy import * -class TraceCPU(BaseCPU): +class TraceCPU(ClockedObject): """Trace CPU model which replays traces generated in a prior simulation using DerivO3CPU or its derived classes. It interfaces with L1 caches. """ @@ -54,13 +55,10 @@ class TraceCPU(BaseCPU): def require_caches(cls): return True - def addPMU(self, pmu=None): - pass - - @classmethod - def support_take_over(cls): - return True + system = Param.System(Parent.any, "system object") + icache_port = RequestPort("Instruction Port") + dcache_port = RequestPort("Data Port") instTraceFile = Param.String("", "Instruction trace file") dataTraceFile = Param.String("", "Data dependency trace file") sizeStoreBuffer = Param.Unsigned( diff --git a/src/cpu/trace/trace_cpu.cc b/src/cpu/trace/trace_cpu.cc index 7399cf6199..336a13beda 100644 --- a/src/cpu/trace/trace_cpu.cc +++ b/src/cpu/trace/trace_cpu.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 - 2016 ARM Limited + * Copyright (c) 2013 - 2016, 2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -39,6 +39,7 @@ #include "base/compiler.hh" #include "sim/sim_exit.hh" +#include "sim/system.hh" namespace gem5 { @@ -47,7 +48,8 @@ namespace gem5 int TraceCPU::numTraceCPUs = 0; TraceCPU::TraceCPU(const TraceCPUParams ¶ms) - : BaseCPU(params), + : ClockedObject(params), + cacheLineSize(params.system->cacheLineSize()), icachePort(this), dcachePort(this), instRequestorID(params.system->getRequestorId(this, "inst")), @@ -93,14 +95,6 @@ TraceCPU::updateNumOps(uint64_t rob_num) } } -void -TraceCPU::takeOverFrom(BaseCPU *oldCPU) -{ - // Unbind the ports of the old CPU and bind the ports of the TraceCPU. - getInstPort().takeOverFrom(&oldCPU->getInstPort()); - getDataPort().takeOverFrom(&oldCPU->getDataPort()); -} - void TraceCPU::init() { @@ -109,7 +103,7 @@ TraceCPU::init() DPRINTF(TraceCPUData, "Data memory request trace file is \"%s\".\n", dataTraceFile); - BaseCPU::init(); + ClockedObject::init(); // Get the send tick of the first instruction read request Tick first_icache_tick = icacheGen.init(); @@ -176,7 +170,7 @@ TraceCPU::schedDcacheNext() DPRINTF(TraceCPUData, "DcacheGen event.\n"); // Update stat for numCycles - baseStats.numCycles = clockEdge() / clockPeriod(); + traceStats.numCycles = clockEdge() / clockPeriod(); dcacheGen.execute(); if (dcacheGen.isExecComplete()) { @@ -216,7 +210,7 @@ TraceCPU::checkAndSchedExitEvent() ADD_STAT(cpi, statistics::units::Rate< statistics::units::Cycle, statistics::units::Count>::get(), "Cycles per micro-op used as a proxy for CPI", - trace->baseStats.numCycles / numOps) + trace->traceStats.numCycles / numOps) { cpi.precision(6); } @@ -591,7 +585,7 @@ TraceCPU::ElasticDataGen::executeMemReq(GraphNode* node_ptr) // stat counting this is useful to keep a check on how frequently this // happens. If required the code could be revised to mimick splitting such // a request into two. - unsigned blk_size = owner.cacheLineSize(); + Addr blk_size = owner.cacheLineSize; Addr blk_offset = (node_ptr->physAddr & (Addr)(blk_size - 1)); if (!(blk_offset + node_ptr->size <= blk_size)) { node_ptr->size = blk_size - blk_offset; @@ -1152,6 +1146,20 @@ TraceCPU::schedDcacheNextEvent(Tick when) } +Port & +TraceCPU::getPort(const std::string &if_name, PortID idx) +{ + // Get the right port based on name. This applies to all the + // subclasses of the base CPU and relies on their implementation + // of getDataPort and getInstPort. + if (if_name == "dcache_port") + return getDataPort(); + else if (if_name == "icache_port") + return getInstPort(); + else + return ClockedObject::getPort(if_name, idx); +} + bool TraceCPU::IcachePort::recvTimingResp(PacketPtr pkt) { diff --git a/src/cpu/trace/trace_cpu.hh b/src/cpu/trace/trace_cpu.hh index 87f820fe6d..c7fd804351 100644 --- a/src/cpu/trace/trace_cpu.hh +++ b/src/cpu/trace/trace_cpu.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 - 2016 ARM Limited + * Copyright (c) 2013 - 2016, 2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -45,13 +45,16 @@ #include #include "base/statistics.hh" -#include "cpu/base.hh" #include "debug/TraceCPUData.hh" #include "debug/TraceCPUInst.hh" +#include "mem/packet.hh" +#include "mem/port.hh" +#include "mem/request.hh" #include "params/TraceCPU.hh" #include "proto/inst_dep_record.pb.h" #include "proto/packet.pb.h" #include "proto/protoio.hh" +#include "sim/clocked_object.hh" #include "sim/sim_events.hh" namespace gem5 @@ -66,8 +69,7 @@ namespace gem5 * simulation compared to the detailed cpu model and good correlation when the * same trace is used for playback on different memory sub-systems. * - * The TraceCPU inherits from BaseCPU so some virtual methods need to be - * defined. It has two port subclasses inherited from RequestPort for + * The TraceCPU has two port subclasses inherited from RequestPort for * instruction and data ports. It issues the memory requests deducing the * timing from the trace and without performing real execution of micro-ops. As * soon as the last dependency for an instruction is complete, its @@ -139,22 +141,13 @@ namespace gem5 * exit. */ -class TraceCPU : public BaseCPU +class TraceCPU : public ClockedObject { public: TraceCPU(const TraceCPUParams ¶ms); - void init(); - - /** - * This is a pure virtual function in BaseCPU. As we don't know how many - * insts are in the trace but only know how how many micro-ops are we - * cannot count this stat. - * - * @return 0 - */ - Counter totalInsts() const { return 0; } + void init() override; /** * Return totalOps as the number of committed micro-ops plus the @@ -170,17 +163,6 @@ class TraceCPU : public BaseCPU */ void updateNumOps(uint64_t rob_num); - /* Pure virtual function in BaseCPU. Do nothing. */ - void wakeup(ThreadID tid=0) { return; } - - /* - * When resuming from checkpoint in FS mode, the TraceCPU takes over from - * the old cpu. This function overrides the takeOverFrom() function in the - * BaseCPU. It unbinds the ports of the old CPU and binds the ports of the - * TraceCPU. - */ - void takeOverFrom(BaseCPU *oldCPU); - /** * When instruction cache port receives a retry, schedule event * icacheNextEvent. @@ -303,6 +285,9 @@ class TraceCPU : public BaseCPU TraceCPU* owner; }; + /** Cache the cache line size that we get from the system */ + const Addr cacheLineSize; + /** Port to connect to L1 instruction cache. */ IcachePort icachePort; @@ -1112,6 +1097,8 @@ class TraceCPU : public BaseCPU /** Stat for number of simulated micro-ops. */ statistics::Scalar numOps; + /** Number of CPU cycles simulated */ + statistics::Scalar numCycles; /** Stat for the CPI. This is really cycles per * micro-op and not inst. */ statistics::Formula cpi; @@ -1125,6 +1112,18 @@ class TraceCPU : public BaseCPU /** Used to get a reference to the dcache port. */ Port &getDataPort() { return dcachePort; } + /** + * Get a port on this CPU. All CPUs have a data and + * instruction port, and this method uses getDataPort and + * getInstPort of the subclasses to resolve the two ports. + * + * @param if_name the port name + * @param idx ignored index + * + * @return a reference to the port with the given name + */ + Port &getPort(const std::string &if_name, + PortID idx=InvalidPortID) override; }; } // namespace gem5 diff --git a/src/dev/BadDevice.py b/src/dev/BadDevice.py index 2b630c087e..f31ecc346e 100644 --- a/src/dev/BadDevice.py +++ b/src/dev/BadDevice.py @@ -24,8 +24,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.Device import BasicPioDevice +from m5.params import * class BadDevice(BasicPioDevice): diff --git a/src/dev/Device.py b/src/dev/Device.py index 7f8428e6ff..15a68c3eaa 100644 --- a/src/dev/Device.py +++ b/src/dev/Device.py @@ -36,12 +36,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * from m5.util.fdthelper import * -from m5.objects.ClockedObject import ClockedObject - class PioDevice(ClockedObject): type = "PioDevice" diff --git a/src/dev/IntPin.py b/src/dev/IntPin.py index 9336a89900..d66e172433 100644 --- a/src/dev/IntPin.py +++ b/src/dev/IntPin.py @@ -23,12 +23,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import Port, VectorPort +from m5.params import ( + Port, + VectorPort, +) INT_SOURCE_ROLE = "Int Source Pin" INT_SINK_ROLE = "Int Sink Pin" Port.compat(INT_SOURCE_ROLE, INT_SINK_ROLE) + # A source pin generally represents a single pin which might connect to # multiple sinks. class IntSourcePin(VectorPort): diff --git a/src/dev/Platform.py b/src/dev/Platform.py index 5a18f83010..5680eb6cbf 100644 --- a/src/dev/Platform.py +++ b/src/dev/Platform.py @@ -24,9 +24,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * from m5.proxy import * +from m5.SimObject import SimObject class Platform(SimObject): diff --git a/src/dev/ResetPort.py b/src/dev/ResetPort.py index 15caa476ec..1c952ed380 100644 --- a/src/dev/ResetPort.py +++ b/src/dev/ResetPort.py @@ -23,12 +23,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import Port, VectorPort +from m5.params import ( + Port, + VectorPort, +) RESET_REQUEST_ROLE = "Reset Request" RESET_RESPONSE_ROLE = "Reset Response" Port.compat(RESET_REQUEST_ROLE, RESET_RESPONSE_ROLE) + # ResetRequestPort is an artifact request port for reset purpose. class ResetRequestPort(Port): def __init__(self, desc): diff --git a/src/dev/SConscript b/src/dev/SConscript index a7714a22d7..89e797acc8 100644 --- a/src/dev/SConscript +++ b/src/dev/SConscript @@ -54,4 +54,4 @@ Source('pixelpump.cc') DebugFlag('Intel8254Timer') DebugFlag('MC146818') -GTest('reg_bank.test', 'reg_bank.test.cc') +GTest('reg_bank.test', 'reg_bank.test.cc', with_tag('gem5 trace')) diff --git a/src/dev/amdgpu/AMDGPU.py b/src/dev/amdgpu/AMDGPU.py index 616c501c63..0370f09e01 100644 --- a/src/dev/amdgpu/AMDGPU.py +++ b/src/dev/amdgpu/AMDGPU.py @@ -27,12 +27,20 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +from m5.objects.ClockedObject import ClockedObject +from m5.objects.Device import ( + DmaDevice, + DmaVirtDevice, +) +from m5.objects.PciDevice import ( + PciDevice, + PciLegacyIoBar, + PciMemBar, + PciMemUpperBar, +) from m5.params import * from m5.proxy import * -from m5.objects.PciDevice import PciDevice -from m5.objects.PciDevice import PciMemBar, PciMemUpperBar, PciLegacyIoBar -from m5.objects.Device import DmaDevice, DmaVirtDevice -from m5.objects.ClockedObject import ClockedObject + # PCI device model for an AMD Vega 10 based GPU. The PCI codes and BARs # correspond to a Vega Frontier Edition hardware device. None of the PCI diff --git a/src/dev/amdgpu/SConscript b/src/dev/amdgpu/SConscript index 9f8eeacd00..b8ba454d48 100644 --- a/src/dev/amdgpu/SConscript +++ b/src/dev/amdgpu/SConscript @@ -39,6 +39,7 @@ SimObject('AMDGPU.py', sim_objects=['AMDGPUDevice', 'AMDGPUInterruptHandler', tags='x86 isa') Source('amdgpu_device.cc', tags='x86 isa') +Source('amdgpu_gfx.cc', tags='x86 isa') Source('amdgpu_nbio.cc', tags='x86 isa') Source('amdgpu_vm.cc', tags='x86 isa') Source('interrupt_handler.cc', tags='x86 isa') @@ -50,6 +51,7 @@ Source('system_hub.cc', tags='x86 isa') DebugFlag('AMDGPUDevice', tags='x86 isa') DebugFlag('AMDGPUMem', tags='x86 isa') +DebugFlag('AMDGPUSystemHub', tags='x86 isa') DebugFlag('PM4PacketProcessor', tags='x86 isa') DebugFlag('SDMAEngine', tags='x86 isa') DebugFlag('SDMAData', tags='x86 isa') diff --git a/src/dev/amdgpu/amdgpu_device.cc b/src/dev/amdgpu/amdgpu_device.cc index d1058f1606..48f450c2b2 100644 --- a/src/dev/amdgpu/amdgpu_device.cc +++ b/src/dev/amdgpu/amdgpu_device.cc @@ -216,11 +216,47 @@ AMDGPUDevice::getAddrRanges() const Tick AMDGPUDevice::readConfig(PacketPtr pkt) { - [[maybe_unused]] int offset = pkt->getAddr() & PCI_CONFIG_SIZE; - DPRINTF(AMDGPUDevice, "Read Config: from offset: %#x size: %#x " - "data: %#x\n", offset, pkt->getSize(), config.data[offset]); + int offset = pkt->getAddr() & PCI_CONFIG_SIZE; - Tick delay = PciDevice::readConfig(pkt); + if (offset < PCI_DEVICE_SPECIFIC) { + PciDevice::readConfig(pkt); + } else { + if (offset >= PXCAP_BASE && offset < (PXCAP_BASE + sizeof(PXCAP))) { + int pxcap_offset = offset - PXCAP_BASE; + + switch (pkt->getSize()) { + case sizeof(uint8_t): + pkt->setLE(pxcap.data[pxcap_offset]); + DPRINTF(AMDGPUDevice, + "Read PXCAP: dev %#x func %#x reg %#x 1 bytes: data " + "= %#x\n", _busAddr.dev, _busAddr.func, pxcap_offset, + (uint32_t)pkt->getLE()); + break; + case sizeof(uint16_t): + pkt->setLE( + *(uint16_t*)&pxcap.data[pxcap_offset]); + DPRINTF(AMDGPUDevice, + "Read PXCAP: dev %#x func %#x reg %#x 2 bytes: data " + "= %#x\n", _busAddr.dev, _busAddr.func, pxcap_offset, + (uint32_t)pkt->getLE()); + break; + case sizeof(uint32_t): + pkt->setLE( + *(uint32_t*)&pxcap.data[pxcap_offset]); + DPRINTF(AMDGPUDevice, + "Read PXCAP: dev %#x func %#x reg %#x 4 bytes: data " + "= %#x\n",_busAddr.dev, _busAddr.func, pxcap_offset, + (uint32_t)pkt->getLE()); + break; + default: + panic("Invalid access size (%d) for amdgpu PXCAP %#x\n", + pkt->getSize(), pxcap_offset); + } + pkt->makeAtomicResponse(); + } else { + warn("Device specific offset %d not implemented!\n", offset); + } + } // Before sending MMIOs the driver sends three interrupts in a row. // Use this to trigger creating a checkpoint to restore in timing mode. @@ -231,14 +267,14 @@ AMDGPUDevice::readConfig(PacketPtr pkt) if (offset == PCI0_INTERRUPT_PIN) { if (++init_interrupt_count == 3) { DPRINTF(AMDGPUDevice, "Checkpointing before first MMIO\n"); - exitSimLoop("checkpoint", 0, curTick() + delay + 1); + exitSimLoop("checkpoint", 0, curTick() + configDelay + 1); } } else { init_interrupt_count = 0; } } - return delay; + return configDelay; } Tick @@ -249,7 +285,24 @@ AMDGPUDevice::writeConfig(PacketPtr pkt) "data: %#x\n", offset, pkt->getSize(), pkt->getUintX(ByteOrder::little)); - return PciDevice::writeConfig(pkt); + if (offset < PCI_DEVICE_SPECIFIC) + return PciDevice::writeConfig(pkt); + + + if (offset >= PXCAP_BASE && offset < (PXCAP_BASE + sizeof(PXCAP))) { + uint8_t *pxcap_data = &(pxcap.data[0]); + int pxcap_offset = offset - PXCAP_BASE; + + DPRINTF(AMDGPUDevice, "Writing PXCAP offset %d size %d\n", + pxcap_offset, pkt->getSize()); + + memcpy(pxcap_data + pxcap_offset, pkt->getConstPtr(), + pkt->getSize()); + } + + pkt->makeAtomicResponse(); + + return configDelay; } void @@ -291,6 +344,7 @@ AMDGPUDevice::readFrame(PacketPtr pkt, Addr offset) system->getDeviceMemory(readPkt)->access(readPkt); pkt->setUintX(readPkt->getUintX(ByteOrder::little), ByteOrder::little); + delete readPkt; } void @@ -325,6 +379,9 @@ AMDGPUDevice::readMMIO(PacketPtr pkt, Addr offset) case GRBM_BASE: gpuvm.readMMIO(pkt, aperture_offset >> GRBM_OFFSET_SHIFT); break; + case GFX_BASE: + gfx.readMMIO(pkt, aperture_offset); + break; case MMHUB_BASE: gpuvm.readMMIO(pkt, aperture_offset >> MMHUB_OFFSET_SHIFT); break; @@ -409,7 +466,17 @@ AMDGPUDevice::writeDoorbell(PacketPtr pkt, Addr offset) panic("Write to unkown queue type!"); } } else { - warn("Unknown doorbell offset: %lx\n", offset); + warn("Unknown doorbell offset: %lx. Saving to pending doorbells.\n", + offset); + + // We have to ACK the PCI packet immediately, so create a copy of the + // packet here to send again. + RequestPtr pending_req(pkt->req); + PacketPtr pending_pkt = Packet::createWrite(pending_req); + uint8_t *pending_data = new uint8_t[pkt->getSize()]; + pending_pkt->dataDynamic(pending_data); + + pendingDoorbellPkts.emplace(offset, pending_pkt); } } @@ -453,6 +520,9 @@ AMDGPUDevice::writeMMIO(PacketPtr pkt, Addr offset) case NBIO_BASE: nbio.writeMMIO(pkt, aperture_offset); break; + case GFX_BASE: + gfx.writeMMIO(pkt, aperture_offset); + break; default: DPRINTF(AMDGPUDevice, "Unknown MMIO aperture for %#x\n", offset); break; @@ -529,6 +599,17 @@ AMDGPUDevice::write(PacketPtr pkt) return pioDelay; } +void +AMDGPUDevice::processPendingDoorbells(uint32_t offset) +{ + if (pendingDoorbellPkts.count(offset)) { + DPRINTF(AMDGPUDevice, "Sending pending doorbell %x\n", offset); + writeDoorbell(pendingDoorbellPkts[offset], offset); + delete pendingDoorbellPkts[offset]; + pendingDoorbellPkts.erase(offset); + } +} + bool AMDGPUDevice::haveRegVal(uint32_t addr) { @@ -597,10 +678,13 @@ AMDGPUDevice::serialize(CheckpointOut &cp) const uint64_t regs_size = regs.size(); uint64_t doorbells_size = doorbells.size(); uint64_t sdma_engs_size = sdmaEngs.size(); + uint64_t used_vmid_map_size = usedVMIDs.size(); SERIALIZE_SCALAR(regs_size); SERIALIZE_SCALAR(doorbells_size); SERIALIZE_SCALAR(sdma_engs_size); + // Save the number of vmids used + SERIALIZE_SCALAR(used_vmid_map_size); // Make a c-style array of the regs to serialize uint32_t reg_addrs[regs_size]; @@ -609,6 +693,9 @@ AMDGPUDevice::serialize(CheckpointOut &cp) const QueueType doorbells_queues[doorbells_size]; uint32_t sdma_engs_offset[sdma_engs_size]; int sdma_engs[sdma_engs_size]; + int used_vmids[used_vmid_map_size]; + int used_queue_id_sizes[used_vmid_map_size]; + std::vector used_vmid_sets; int idx = 0; for (auto & it : regs) { @@ -631,6 +718,20 @@ AMDGPUDevice::serialize(CheckpointOut &cp) const ++idx; } + idx = 0; + for (auto & it : usedVMIDs) { + used_vmids[idx] = it.first; + used_queue_id_sizes[idx] = it.second.size(); + std::vector set_vector(it.second.begin(), it.second.end()); + used_vmid_sets.insert(used_vmid_sets.end(), + set_vector.begin(), set_vector.end()); + ++idx; + } + + int num_queue_id = used_vmid_sets.size(); + int* vmid_array = new int[num_queue_id]; + std::copy(used_vmid_sets.begin(), used_vmid_sets.end(), vmid_array); + SERIALIZE_ARRAY(reg_addrs, sizeof(reg_addrs)/sizeof(reg_addrs[0])); SERIALIZE_ARRAY(reg_values, sizeof(reg_values)/sizeof(reg_values[0])); SERIALIZE_ARRAY(doorbells_offset, sizeof(doorbells_offset)/ @@ -640,10 +741,21 @@ AMDGPUDevice::serialize(CheckpointOut &cp) const SERIALIZE_ARRAY(sdma_engs_offset, sizeof(sdma_engs_offset)/ sizeof(sdma_engs_offset[0])); SERIALIZE_ARRAY(sdma_engs, sizeof(sdma_engs)/sizeof(sdma_engs[0])); + // Save the vmids used in an array + SERIALIZE_ARRAY(used_vmids, sizeof(used_vmids)/sizeof(used_vmids[0])); + // Save the size of the set of queue ids mapped to each vmid + SERIALIZE_ARRAY(used_queue_id_sizes, + sizeof(used_queue_id_sizes)/sizeof(used_queue_id_sizes[0])); + // Save all the queue ids used for all the vmids + SERIALIZE_ARRAY(vmid_array, num_queue_id); + // Save the total number of queue idsused + SERIALIZE_SCALAR(num_queue_id); // Serialize the device memory deviceMem.serializeSection(cp, "deviceMem"); gpuvm.serializeSection(cp, "GPUVM"); + + delete[] vmid_array; } void @@ -655,10 +767,13 @@ AMDGPUDevice::unserialize(CheckpointIn &cp) uint64_t regs_size = 0; uint64_t doorbells_size = 0; uint64_t sdma_engs_size = 0; + uint64_t used_vmid_map_size = 0; UNSERIALIZE_SCALAR(regs_size); UNSERIALIZE_SCALAR(doorbells_size); UNSERIALIZE_SCALAR(sdma_engs_size); + UNSERIALIZE_SCALAR(used_vmid_map_size); + if (regs_size > 0) { uint32_t reg_addrs[regs_size]; @@ -705,6 +820,33 @@ AMDGPUDevice::unserialize(CheckpointIn &cp) } } + if (used_vmid_map_size > 0) { + int used_vmids[used_vmid_map_size]; + int used_queue_id_sizes[used_vmid_map_size]; + int num_queue_id = 0; + std::vector used_vmid_sets; + // Extract the total number of queue ids used + UNSERIALIZE_SCALAR(num_queue_id); + int* vmid_array = new int[num_queue_id]; + // Extract the number of vmids used + UNSERIALIZE_ARRAY(used_vmids, used_vmid_map_size); + // Extract the size of the queue id set for each vmid + UNSERIALIZE_ARRAY(used_queue_id_sizes, used_vmid_map_size); + // Extract all the queue ids used + UNSERIALIZE_ARRAY(vmid_array, num_queue_id); + // Populate the usedVMIDs map with the queue ids per vm + int idx = 0; + for (int it = 0; it < used_vmid_map_size; it++) { + int vmid = used_vmids[it]; + int vmid_set_size = used_queue_id_sizes[it]; + for (int j = 0; j < vmid_set_size; j++) { + usedVMIDs[vmid].insert(vmid_array[idx + j]); + } + idx += vmid_set_size; + } + delete[] vmid_array; + } + // Unserialize the device memory deviceMem.unserializeSection(cp, "deviceMem"); gpuvm.unserializeSection(cp, "GPUVM"); @@ -752,6 +894,14 @@ AMDGPUDevice::deallocateAllQueues() for (auto& it : sdmaEngs) { it.second->deallocateRLCQueues(); } + + // "All" queues implicitly refers to all user queues. User queues begin at + // doorbell address 0x4000, so unmap any queue at or above that address. + for (auto [offset, vmid] : doorbellVMIDMap) { + if (offset >= 0x4000) { + doorbells.erase(offset); + } + } } void diff --git a/src/dev/amdgpu/amdgpu_device.hh b/src/dev/amdgpu/amdgpu_device.hh index 56ed2f4fa8..b6b6e2a81a 100644 --- a/src/dev/amdgpu/amdgpu_device.hh +++ b/src/dev/amdgpu/amdgpu_device.hh @@ -36,6 +36,7 @@ #include "base/bitunion.hh" #include "dev/amdgpu/amdgpu_defines.hh" +#include "dev/amdgpu/amdgpu_gfx.hh" #include "dev/amdgpu/amdgpu_nbio.hh" #include "dev/amdgpu/amdgpu_vm.hh" #include "dev/amdgpu/memory_manager.hh" @@ -89,6 +90,7 @@ class AMDGPUDevice : public PciDevice using GPURegMap = std::unordered_map; GPURegMap regs; std::unordered_map doorbells; + std::unordered_map pendingDoorbellPkts; /** * VGA ROM methods @@ -109,6 +111,7 @@ class AMDGPUDevice : public PciDevice * Blocks of the GPU */ AMDGPUNbio nbio; + AMDGPUGfx gfx; AMDGPUMemoryManager *gpuMemMgr; AMDGPUInterruptHandler *deviceIH; AMDGPUVM gpuvm; @@ -185,6 +188,7 @@ class AMDGPUDevice : public PciDevice * Set handles to GPU blocks. */ void setDoorbellType(uint32_t offset, QueueType qt); + void processPendingDoorbells(uint32_t offset); void setSDMAEngine(Addr offset, SDMAEngine *eng); /** diff --git a/src/dev/amdgpu/amdgpu_gfx.cc b/src/dev/amdgpu/amdgpu_gfx.cc new file mode 100644 index 0000000000..3d5b274b86 --- /dev/null +++ b/src/dev/amdgpu/amdgpu_gfx.cc @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dev/amdgpu/amdgpu_gfx.hh" + +#include "mem/packet_access.hh" +#include "sim/core.hh" + +namespace gem5 +{ + +void +AMDGPUGfx::readMMIO(PacketPtr pkt, Addr offset) +{ + switch (offset) { + case AMDGPU_MM_RLC_GPU_CLOCK_COUNT_LSB: + pkt->setLE(captured_clock_count); + break; + case AMDGPU_MM_RLC_GPU_CLOCK_COUNT_MSB: + pkt->setLE(captured_clock_count >> 32); + break; + default: + break; + } +} + +void +AMDGPUGfx::writeMMIO(PacketPtr pkt, Addr offset) +{ + switch (offset) { + case AMDGPU_MM_RLC_CAPTURE_GPU_CLOCK_COUNT: + // Use gem5 Ticks in nanoseconds are the counter. The first capture + // is expected to return zero. + if (captured_clock_count == 1) { + captured_clock_count = 0; + } else { + captured_clock_count = curTick() / sim_clock::as_int::ns; + } + break; + default: + break; + } +} + +} // namespace gem5 diff --git a/src/dev/amdgpu/amdgpu_gfx.hh b/src/dev/amdgpu/amdgpu_gfx.hh new file mode 100644 index 0000000000..c32b8624cf --- /dev/null +++ b/src/dev/amdgpu/amdgpu_gfx.hh @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023 Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DEV_AMDGPU_AMDGPU_GFX_HH__ +#define __DEV_AMDGPU_AMDGPU_GFX_HH__ + +#include "base/types.hh" +#include "mem/packet.hh" + +/** + * MMIO offsets for GFX. This class handles MMIO reads/writes to the GFX_BASE + * aperture which are generally read/written by the gfx driver source here: + * + * drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c + * https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/master/ + * drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c + * + * The MMIO addresses in the file are dword addresses. Here they are converted + * to byte addresses so gem5 does not need to shift the values. + */ + +// Registers used to read GPU clock count used in profiling +#define AMDGPU_MM_RLC_GPU_CLOCK_COUNT_LSB 0x13090 +#define AMDGPU_MM_RLC_GPU_CLOCK_COUNT_MSB 0x13094 +#define AMDGPU_MM_RLC_CAPTURE_GPU_CLOCK_COUNT 0x13098 + +namespace gem5 +{ + +class AMDGPUGfx +{ + public: + AMDGPUGfx() { } + + void readMMIO(PacketPtr pkt, Addr offset); + void writeMMIO(PacketPtr pkt, Addr offset); + + private: + /* + * GPU clock count at the time capture MMIO is received. + */ + uint64_t captured_clock_count = 1; +}; + +} // namespace gem5 + +#endif // __DEV_AMDGPU_AMDGPU_GFX_HH__ diff --git a/src/dev/amdgpu/pm4_packet_processor.cc b/src/dev/amdgpu/pm4_packet_processor.cc index e7b846529e..5f270a0c70 100644 --- a/src/dev/amdgpu/pm4_packet_processor.cc +++ b/src/dev/amdgpu/pm4_packet_processor.cc @@ -168,7 +168,7 @@ PM4PacketProcessor::decodeNext(PM4Queue *q) DPRINTF(PM4PacketProcessor, "PM4 decode queue %d rptr %p, wptr %p\n", q->id(), q->rptr(), q->wptr()); - if (q->rptr() < q->wptr()) { + if (q->rptr() != q->wptr()) { /* Additional braces here are needed due to a clang compilation bug falsely throwing a "suggest braces around initialization of subject" error. More info on this bug is available here: @@ -181,11 +181,28 @@ PM4PacketProcessor::decodeNext(PM4Queue *q) dmaReadVirt(getGARTAddr(q->rptr()), sizeof(uint32_t), cb, &cb->dmaBuffer); } else { + // Reached the end of processable data in the queue. Switch out of IB + // if this is an indirect buffer. + assert(q->rptr() == q->wptr()); q->processing(false); if (q->ib()) { q->ib(false); decodeNext(q); } + + // Write back rptr when the queue is empty. For static queues which + // are not unmapped, this is how the driver knows there is enough + // space in the queue to continue writing packets to the ring buffer. + if (q->getMQD()->aqlRptr) { + Addr addr = getGARTAddr(q->getMQD()->aqlRptr); + uint32_t *data = new uint32_t; + // gem5 stores rptr as a bytes offset while the driver expects + // a dword offset. Convert the offset to dword count. + *data = q->getRptr() >> 2; + auto cb = new DmaVirtCallback( + [data](const uint32_t &) { delete data; }); + dmaWriteVirt(addr, sizeof(uint32_t), cb, data); + } } } @@ -384,7 +401,10 @@ PM4PacketProcessor::mapQueues(PM4Queue *q, PM4MapQueues *pkt) "Mapping mqd from %p %p (vmid %d - last vmid %d).\n", addr, pkt->mqdAddr, pkt->vmid, gpuDevice->lastVMID()); - gpuDevice->mapDoorbellToVMID(pkt->doorbellOffset, + // The doorbellOffset is a dword address. We shift by two / multiply + // by four to get the byte address to match doorbell addresses in + // the GPU device. + gpuDevice->mapDoorbellToVMID(pkt->doorbellOffset << 2, gpuDevice->lastVMID()); QueueDesc *mqd = new QueueDesc(); @@ -444,6 +464,8 @@ PM4PacketProcessor::processMQD(PM4MapQueues *pkt, PM4Queue *q, Addr addr, DPRINTF(PM4PacketProcessor, "PM4 mqd read completed, base %p, mqd %p, " "hqdAQL %d.\n", mqd->base, mqd->mqdBase, mqd->aql); + + gpuDevice->processPendingDoorbells(offset); } void @@ -472,6 +494,8 @@ PM4PacketProcessor::processSDMAMQD(PM4MapQueues *pkt, PM4Queue *q, Addr addr, // Register doorbell with GPU device gpuDevice->setSDMAEngine(pkt->doorbellOffset << 2, sdma_eng); gpuDevice->setDoorbellType(pkt->doorbellOffset << 2, RLC); + + gpuDevice->processPendingDoorbells(pkt->doorbellOffset << 2); } void @@ -576,6 +600,7 @@ PM4PacketProcessor::unmapQueues(PM4Queue *q, PM4UnmapQueues *pkt) gpuDevice->deallocatePasid(pkt->pasid); break; case 2: + panic("Unmapping queue selection 2 unimplemented\n"); break; case 3: { auto &hsa_pp = gpuDevice->CP()->hsaPacketProc(); @@ -1044,6 +1069,7 @@ PM4PacketProcessor::serialize(CheckpointOut &cp) const int num_queues = queues.size(); Addr id[num_queues]; Addr mqd_base[num_queues]; + uint64_t mqd_read_index[num_queues]; Addr base[num_queues]; Addr rptr[num_queues]; Addr wptr[num_queues]; @@ -1060,6 +1086,7 @@ PM4PacketProcessor::serialize(CheckpointOut &cp) const uint32_t hqd_active[num_queues]; uint32_t hqd_vmid[num_queues]; Addr aql_rptr[num_queues]; + uint32_t aql[num_queues]; uint32_t doorbell[num_queues]; uint32_t hqd_pq_control[num_queues]; @@ -1068,9 +1095,10 @@ PM4PacketProcessor::serialize(CheckpointOut &cp) const PM4Queue *q = iter.second; id[i] = q->id(); mqd_base[i] = q->mqdBase(); + mqd_read_index[i] = q->getMQD()->mqdReadIndex; bool cur_state = q->ib(); q->ib(false); - base[i] = q->base() >> 8; + base[i] = q->base(); rptr[i] = q->getRptr(); wptr[i] = q->getWptr(); q->ib(true); @@ -1088,6 +1116,7 @@ PM4PacketProcessor::serialize(CheckpointOut &cp) const hqd_active[i] = q->getMQD()->hqd_active; hqd_vmid[i] = q->getMQD()->hqd_vmid; aql_rptr[i] = q->getMQD()->aqlRptr; + aql[i] = q->getMQD()->aql; doorbell[i] = q->getMQD()->doorbell; hqd_pq_control[i] = q->getMQD()->hqd_pq_control; i++; @@ -1096,6 +1125,7 @@ PM4PacketProcessor::serialize(CheckpointOut &cp) const SERIALIZE_SCALAR(num_queues); SERIALIZE_ARRAY(id, num_queues); SERIALIZE_ARRAY(mqd_base, num_queues); + SERIALIZE_ARRAY(mqd_read_index, num_queues); SERIALIZE_ARRAY(base, num_queues); SERIALIZE_ARRAY(rptr, num_queues); SERIALIZE_ARRAY(wptr, num_queues); @@ -1112,6 +1142,7 @@ PM4PacketProcessor::serialize(CheckpointOut &cp) const SERIALIZE_ARRAY(hqd_active, num_queues); SERIALIZE_ARRAY(hqd_vmid, num_queues); SERIALIZE_ARRAY(aql_rptr, num_queues); + SERIALIZE_ARRAY(aql, num_queues); SERIALIZE_ARRAY(doorbell, num_queues); SERIALIZE_ARRAY(hqd_pq_control, num_queues); } @@ -1127,6 +1158,7 @@ PM4PacketProcessor::unserialize(CheckpointIn &cp) Addr id[num_queues]; Addr mqd_base[num_queues]; + uint64_t mqd_read_index[num_queues]; Addr base[num_queues]; Addr rptr[num_queues]; Addr wptr[num_queues]; @@ -1143,11 +1175,13 @@ PM4PacketProcessor::unserialize(CheckpointIn &cp) uint32_t hqd_active[num_queues]; uint32_t hqd_vmid[num_queues]; Addr aql_rptr[num_queues]; + uint32_t aql[num_queues]; uint32_t doorbell[num_queues]; uint32_t hqd_pq_control[num_queues]; UNSERIALIZE_ARRAY(id, num_queues); UNSERIALIZE_ARRAY(mqd_base, num_queues); + UNSERIALIZE_ARRAY(mqd_read_index, num_queues); UNSERIALIZE_ARRAY(base, num_queues); UNSERIALIZE_ARRAY(rptr, num_queues); UNSERIALIZE_ARRAY(wptr, num_queues); @@ -1164,6 +1198,7 @@ PM4PacketProcessor::unserialize(CheckpointIn &cp) UNSERIALIZE_ARRAY(hqd_active, num_queues); UNSERIALIZE_ARRAY(hqd_vmid, num_queues); UNSERIALIZE_ARRAY(aql_rptr, num_queues); + UNSERIALIZE_ARRAY(aql, num_queues); UNSERIALIZE_ARRAY(doorbell, num_queues); UNSERIALIZE_ARRAY(hqd_pq_control, num_queues); @@ -1172,22 +1207,24 @@ PM4PacketProcessor::unserialize(CheckpointIn &cp) memset(mqd, 0, sizeof(QueueDesc)); mqd->mqdBase = mqd_base[i] >> 8; - mqd->base = base[i]; - mqd->rptr = rptr[i]; - mqd->ibBase = ib_base[i]; - mqd->ibRptr = ib_rptr[i]; + mqd->mqdReadIndex = mqd_read_index[i]; + mqd->base = base[i] >> 8; + mqd->aql = aql[i]; PM4MapQueues* pkt = new PM4MapQueues; memset(pkt, 0, sizeof(PM4MapQueues)); newQueue(mqd, offset[i], pkt, id[i]); - queues[id[i]]->ib(false); - queues[id[i]]->wptr(wptr[i]); - queues[id[i]]->ib(true); - queues[id[i]]->wptr(ib_wptr[i]); + if (ib[i]) { + queues[id[i]]->wptr(ib_wptr[i]); + queues[id[i]]->rptr(ib_rptr[i]); + } else { + queues[id[i]]->rptr(rptr[i]); + queues[id[i]]->wptr(wptr[i]); + } + queues[id[i]]->ib(ib[i]); queues[id[i]]->offset(offset[i]); queues[id[i]]->processing(processing[i]); - queues[id[i]]->ib(ib[i]); queues[id[i]]->setPkt(me[i], pipe[i], queue[i], privileged[i]); queues[id[i]]->getMQD()->hqd_active = hqd_active[i]; queues[id[i]]->getMQD()->hqd_vmid = hqd_vmid[i]; @@ -1195,6 +1232,14 @@ PM4PacketProcessor::unserialize(CheckpointIn &cp) queues[id[i]]->getMQD()->doorbell = doorbell[i]; queues[id[i]]->getMQD()->hqd_pq_control = hqd_pq_control[i]; + if (mqd->aql) { + int mqd_size = (1 << ((hqd_pq_control[i] & 0x3f) + 1)) * 4; + auto &hsa_pp = gpuDevice->CP()->hsaPacketProc(); + hsa_pp.setDeviceQueueDesc(aql_rptr[i], base[i], id[i], + mqd_size, 8, GfxVersion::gfx900, offset[i], + mqd_read_index[i]); + } + DPRINTF(PM4PacketProcessor, "PM4 queue %d, rptr: %p wptr: %p\n", queues[id[i]]->id(), queues[id[i]]->rptr(), queues[id[i]]->wptr()); diff --git a/src/dev/amdgpu/sdma_engine.cc b/src/dev/amdgpu/sdma_engine.cc index e99d694634..0202f583e6 100644 --- a/src/dev/amdgpu/sdma_engine.cc +++ b/src/dev/amdgpu/sdma_engine.cc @@ -510,9 +510,12 @@ SDMAEngine::decodeHeader(SDMAQueue *q, uint32_t header) dmaReadVirt(q->rptr(), sizeof(sdmaAtomic), cb, dmaBuffer); } break; case SDMA_OP_CONST_FILL: { - q->incRptr(sizeof(sdmaConstFill)); - warn("SDMA_OP_CONST_FILL not implemented"); - decodeNext(q); + DPRINTF(SDMAEngine, "SDMA Constant fill packet\n"); + dmaBuffer = new sdmaConstFill(); + cb = new DmaVirtCallback( + [ = ] (const uint64_t &) + { constFill(q, (sdmaConstFill *)dmaBuffer, header); }); + dmaReadVirt(q->rptr(), sizeof(sdmaConstFill), cb, dmaBuffer); } break; case SDMA_OP_PTEPDE: { DPRINTF(SDMAEngine, "SDMA PTEPDE packet\n"); @@ -1026,6 +1029,68 @@ SDMAEngine::atomicDone(SDMAQueue *q, sdmaAtomicHeader *header, sdmaAtomic *pkt, decodeNext(q); } +void +SDMAEngine::constFill(SDMAQueue *q, sdmaConstFill *pkt, uint32_t header) +{ + q->incRptr(sizeof(sdmaConstFill)); + + sdmaConstFillHeader fill_header; + fill_header.ordinal = header; + + DPRINTF(SDMAEngine, "ConstFill %lx srcData %x count %d size %d sw %d\n", + pkt->addr, pkt->srcData, pkt->count, fill_header.fillsize, + fill_header.sw); + + // Count is number of elements - 1. Size is log2 of byte size. + int fill_bytes = (pkt->count + 1) * (1 << fill_header.fillsize); + uint8_t *fill_data = new uint8_t[fill_bytes]; + + memset(fill_data, pkt->srcData, fill_bytes); + + Addr device_addr = getDeviceAddress(pkt->addr); + if (device_addr) { + DPRINTF(SDMAEngine, "ConstFill %d bytes of %x to device at %lx\n", + fill_bytes, pkt->srcData, pkt->addr); + + auto cb = new EventFunctionWrapper( + [ = ]{ constFillDone(q, pkt, fill_data); }, name()); + + // Copy the minimum page size at a time in case the physical addresses + // are not contiguous. + ChunkGenerator gen(pkt->addr, fill_bytes, AMDGPU_MMHUB_PAGE_SIZE); + for (; !gen.done(); gen.next()) { + Addr chunk_addr = getDeviceAddress(gen.addr()); + assert(chunk_addr); + + DPRINTF(SDMAEngine, "Copying chunk of %d bytes from %#lx (%#lx)\n", + gen.size(), gen.addr(), chunk_addr); + + gpuDevice->getMemMgr()->writeRequest(chunk_addr, fill_data, + gen.size(), 0, + gen.last() ? cb : nullptr); + fill_data += gen.size(); + } + } else { + DPRINTF(SDMAEngine, "ConstFill %d bytes of %x to host at %lx\n", + fill_bytes, pkt->srcData, pkt->addr); + + auto cb = new DmaVirtCallback( + [ = ] (const uint64_t &) + { constFillDone(q, pkt, fill_data); }); + dmaWriteVirt(pkt->addr, fill_bytes, cb, (void *)fill_data); + } +} + +void +SDMAEngine::constFillDone(SDMAQueue *q, sdmaConstFill *pkt, uint8_t *fill_data) +{ + DPRINTF(SDMAEngine, "ConstFill to %lx done\n", pkt->addr); + + delete fill_data; + delete pkt; + decodeNext(q); +} + AddrRangeList SDMAEngine::getAddrRanges() const { diff --git a/src/dev/amdgpu/sdma_engine.hh b/src/dev/amdgpu/sdma_engine.hh index bcbd497e8a..5abe63fcc6 100644 --- a/src/dev/amdgpu/sdma_engine.hh +++ b/src/dev/amdgpu/sdma_engine.hh @@ -245,6 +245,8 @@ class SDMAEngine : public DmaVirtDevice uint64_t *dmaBuffer); void atomicDone(SDMAQueue *q, sdmaAtomicHeader *header, sdmaAtomic *pkt, uint64_t *dmaBuffer); + void constFill(SDMAQueue *q, sdmaConstFill *pkt, uint32_t header); + void constFillDone(SDMAQueue *q, sdmaConstFill *pkt, uint8_t *fill_data); /** * Methods for getting SDMA MMIO base address and size. These are set by diff --git a/src/dev/amdgpu/sdma_packets.hh b/src/dev/amdgpu/sdma_packets.hh index 52a47d3a2d..07d3f12600 100644 --- a/src/dev/amdgpu/sdma_packets.hh +++ b/src/dev/amdgpu/sdma_packets.hh @@ -37,7 +37,7 @@ namespace gem5 { /** - * SDMA packets + * SDMA packets - see src/core/inc/sdma_registers.h in ROCR-Runtime */ typedef struct GEM5_PACKED { @@ -80,6 +80,23 @@ typedef struct GEM5_PACKED } sdmaConstFill; static_assert(sizeof(sdmaConstFill) == 16); +typedef struct GEM5_PACKED +{ + union + { + struct + { + uint32_t op : 8; + uint32_t sub_op : 8; + uint32_t sw : 2; + uint32_t res0 : 12; + uint32_t fillsize : 2; + }; + uint32_t ordinal; + }; +} sdmaConstFillHeader; +static_assert(sizeof(sdmaConstFillHeader) == 4); + typedef struct GEM5_PACKED { uint32_t key0; diff --git a/src/dev/amdgpu/system_hub.cc b/src/dev/amdgpu/system_hub.cc index 7a252ea0fa..892a7c4535 100644 --- a/src/dev/amdgpu/system_hub.cc +++ b/src/dev/amdgpu/system_hub.cc @@ -31,6 +31,8 @@ #include "dev/amdgpu/system_hub.hh" +#include "debug/AMDGPUSystemHub.hh" +#include "mem/packet_access.hh" #include "mem/port.hh" namespace gem5 @@ -39,16 +41,92 @@ namespace gem5 void AMDGPUSystemHub::sendRequest(PacketPtr pkt, Event *callback) { - ResponseEvent *dmaRespEvent = new ResponseEvent(callback); - Tick delay = 0; + // Some requests, in particular atomics, need to be sent in order + // to receive the correct values. If there is an atomic in progress + // we must block it until that request is complete. This is overly + // conservative and blocks reads/writes but this situation is rare + // so it should not impact simulated performance. + DeferredReq this_req(pkt, callback); + outstandingReqs[pkt->getAddr()].push_back(this_req); + + if (outstandingReqs[pkt->getAddr()].size () > 1) { + // There is another request in progress, Delay this one. + DPRINTF(AMDGPUSystemHub, "SystemHub deferring request for %#lx\n", + pkt->getAddr()); + } else { + // No other requests, we can send immediately. + sendDeferredRequest(this_req); + } +} + +void +AMDGPUSystemHub::sendDeferredRequest(DeferredReq& deferredReq) +{ + PacketPtr pkt = deferredReq.first; + Event *callback = deferredReq.second; + Tick delay = 0; + std::string req_type; + + if (pkt->isAtomicOp()) { + AtomicResponseEvent *atomicRespEvent = + new AtomicResponseEvent(*this, callback, pkt); + + // First read the value. The response event will do the atomic/write + // This places the current value in the packet, which is correct since + // atomics return the value prior to performing the atomic. + dmaRead(pkt->getAddr(), pkt->getSize(), atomicRespEvent, + pkt->getPtr(), 0, 0, delay); + + req_type = "Atomic"; + } else if (pkt->isWrite()) { + ResponseEvent *dmaRespEvent = + new ResponseEvent(*this, callback, pkt); - if (pkt->isWrite()) { dmaWrite(pkt->getAddr(), pkt->getSize(), dmaRespEvent, pkt->getPtr(), 0, 0, delay); + + req_type = "Write"; } else { + ResponseEvent *dmaRespEvent = + new ResponseEvent(*this, callback, pkt); + assert(pkt->isRead()); dmaRead(pkt->getAddr(), pkt->getSize(), dmaRespEvent, pkt->getPtr(), 0, 0, delay); + + req_type = "Read"; + } + + DPRINTF(AMDGPUSystemHub, "SystemHub %s request for %#lx size %d\n", + req_type.c_str(), pkt->getAddr(), pkt->getSize()); +} + +void +AMDGPUSystemHub::sendNextRequest(Addr addr, const PacketPtr donePkt) +{ + // Remove our request + assert(outstandingReqs.count(addr)); + + [[maybe_unused]] DeferredReq& frontPkt = outstandingReqs[addr].front(); + assert(frontPkt.first == donePkt); + + outstandingReqs[addr].pop_front(); + + // If there are no more requests this can be removed from the map. + // Otherwise issue the next request in the list + if (outstandingReqs[addr].empty()) { + DPRINTF(AMDGPUSystemHub, "SystemHub done with packets for addr %#lx\n", + donePkt->getAddr()); + + outstandingReqs.erase(addr); + } else { + DeferredReq& nextPkt = outstandingReqs[addr].front(); + + DPRINTF(AMDGPUSystemHub, "SystemHub sending deferred request for addr" + " %#lx size %d\n", nextPkt.first->getAddr(), + nextPkt.first->getSize()); + + sendDeferredRequest(nextPkt); } } @@ -57,8 +135,9 @@ AMDGPUSystemHub::dmaResponse(PacketPtr pkt) { } -AMDGPUSystemHub::ResponseEvent::ResponseEvent(Event *_callback) - : callback(_callback) +AMDGPUSystemHub::ResponseEvent::ResponseEvent( + AMDGPUSystemHub& _hub, Event *_callback, PacketPtr _pkt) + : systemHub(_hub), callback(_callback), pkt(_pkt) { // Delete this event after process is called setFlags(Event::AutoDelete); @@ -67,9 +146,62 @@ AMDGPUSystemHub::ResponseEvent::ResponseEvent(Event *_callback) void AMDGPUSystemHub::ResponseEvent::process() { + DPRINTF(AMDGPUSystemHub, "SystemHub response for addr %#lx size %d\n", + pkt->getAddr(), pkt->getSize()); + + systemHub.sendNextRequest(pkt->getAddr(), pkt); + callback->process(); } +AMDGPUSystemHub::AtomicResponseEvent::AtomicResponseEvent( + AMDGPUSystemHub& _hub, Event *_callback, PacketPtr _pkt) + : systemHub(_hub), callback(_callback), pkt(_pkt) +{ + // Delete this event after process is called + setFlags(Event::AutoDelete); +} + +void +AMDGPUSystemHub::AtomicResponseEvent::process() +{ + // Make a second response with the original sender's callback + ResponseEvent *dmaRespEvent = new ResponseEvent(systemHub, callback, pkt); + Tick delay = 0; + + // Create a new write packet which will be modifed then written + RequestPtr write_req = + std::make_shared(pkt->getAddr(), pkt->getSize(), 0, + pkt->requestorId()); + + PacketPtr write_pkt = Packet::createWrite(write_req); + uint8_t *write_data = new uint8_t[pkt->getSize()]; + std::memcpy(write_data, pkt->getPtr(), pkt->getSize()); + write_pkt->dataDynamic(write_data); + + // Perform the atomic on the write packet data. The atomic op is not + // copied from the original packet, so use the original packet. + assert(pkt->isAtomicOp()); + (*pkt->getAtomicOp())(write_pkt->getPtr()); + + // Write back the new value. The atomic is not considered done until + // this packet's response event is triggered. + systemHub.dmaWrite(write_pkt->getAddr(), write_pkt->getSize(), + dmaRespEvent, write_pkt->getPtr(), 0, 0, delay); + + // Atomics from the GPU are at most 64-bit and usually 32-bit. + // We can take a peek at the data for debugging purposes. + [[maybe_unused]] uint64_t req_data = 0x12345678; + if (write_pkt->getSize() == 8) { + req_data = write_pkt->getLE(); + } else if (pkt->getSize() == 4) { + req_data = write_pkt->getLE(); + } + + DPRINTF(AMDGPUSystemHub, "SystemHub atomic %#lx writing %lx size %d\n", + write_pkt->getAddr(), req_data, write_pkt->getSize()); +} + AddrRangeList AMDGPUSystemHub::getAddrRanges() const { diff --git a/src/dev/amdgpu/system_hub.hh b/src/dev/amdgpu/system_hub.hh index 0b48c3bc01..7955f5e694 100644 --- a/src/dev/amdgpu/system_hub.hh +++ b/src/dev/amdgpu/system_hub.hh @@ -63,16 +63,37 @@ class AMDGPUSystemHub : public DmaDevice AddrRangeList getAddrRanges() const override; private: + typedef std::pair DeferredReq; + typedef std::list DeferredReqList; + std::unordered_map outstandingReqs; + + void sendNextRequest(Addr addr, const PacketPtr donePkt); + void sendDeferredRequest(DeferredReq& deferredReq); class ResponseEvent : public Event { - Event *callback; + AMDGPUSystemHub &systemHub; + Event *callback; + PacketPtr pkt; - public: - ResponseEvent(Event *_callback); + public: + ResponseEvent(AMDGPUSystemHub& _hub, + Event *_callback, PacketPtr _pkt); void process(); + }; + class AtomicResponseEvent : public Event + { + AMDGPUSystemHub &systemHub; + Event *callback; + PacketPtr pkt; + + public: + AtomicResponseEvent(AMDGPUSystemHub& _hub, + Event *_callback, PacketPtr _pkt); + + void process(); }; }; diff --git a/src/dev/arm/Doorbell.py b/src/dev/arm/Doorbell.py index 106a184902..3832553924 100644 --- a/src/dev/arm/Doorbell.py +++ b/src/dev/arm/Doorbell.py @@ -33,8 +33,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * +from m5.SimObject import SimObject class Doorbell(SimObject): diff --git a/src/dev/arm/EnergyCtrl.py b/src/dev/arm/EnergyCtrl.py index 91296143c8..6a9cf99608 100644 --- a/src/dev/arm/EnergyCtrl.py +++ b/src/dev/arm/EnergyCtrl.py @@ -33,10 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.SimObject import SimObject from m5.objects.Device import BasicPioDevice +from m5.params import * from m5.proxy import * +from m5.SimObject import SimObject from m5.util.fdthelper import * diff --git a/src/dev/arm/FlashDevice.py b/src/dev/arm/FlashDevice.py index d5069d94ac..795e735d7c 100644 --- a/src/dev/arm/FlashDevice.py +++ b/src/dev/arm/FlashDevice.py @@ -33,10 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.AbstractNVM import * from m5.params import * from m5.proxy import * -from m5.objects.AbstractNVM import * # Distribution of the data. # sequential: sequential (address n+1 is likely to be on the same plane as n) diff --git a/src/dev/arm/GenericTimer.py b/src/dev/arm/GenericTimer.py index 4b104ade92..fe7c1f5988 100644 --- a/src/dev/arm/GenericTimer.py +++ b/src/dev/arm/GenericTimer.py @@ -33,12 +33,22 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.objects.Device import PioDevice -from m5.params import Param, MaxAddr, NULL, VectorParam +from m5.params import ( + NULL, + MaxAddr, + Param, + VectorParam, +) from m5.proxy import Parent +from m5.SimObject import SimObject from m5.util import fatal -from m5.util.fdthelper import FdtNode, FdtProperty, FdtPropertyWords, FdtState +from m5.util.fdthelper import ( + FdtNode, + FdtProperty, + FdtPropertyWords, + FdtState, +) class SystemCounter(SimObject): diff --git a/src/dev/arm/Gic.py b/src/dev/arm/Gic.py index 41d602b86a..6cb03c8cb9 100644 --- a/src/dev/arm/Gic.py +++ b/src/dev/arm/Gic.py @@ -33,14 +33,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.Device import ( + BasicPioDevice, + PioDevice, +) +from m5.objects.IntPin import IntSourcePin +from m5.objects.Platform import Platform from m5.params import * from m5.proxy import * -from m5.util.fdthelper import * from m5.SimObject import SimObject - -from m5.objects.Device import PioDevice, BasicPioDevice -from m5.objects.Platform import Platform -from m5.objects.IntPin import IntSourcePin +from m5.util.fdthelper import * class BaseGic(PioDevice): @@ -315,6 +317,15 @@ class Gicv3(BaseGic): gicv4 = Param.Bool(False, "GIC is GICv4 compatible") + reserved_is_res0 = Param.Bool( + True, + "According to the GIC specification (IHI0069) " + "reserved addresses in the GIC memory map are treated as RES0. " + "We allow to disable this behaviour and panic instead " + "(reserved_res0 = False) to catch development bugs " + "(in gem5 and in the guest SW)", + ) + def interruptCells(self, int_type, int_num, int_trigger, partition=None): """ Interupt cells generation helper: diff --git a/src/dev/arm/NoMali.py b/src/dev/arm/NoMali.py index c7d0d4259c..ea20cdf3a3 100644 --- a/src/dev/arm/NoMali.py +++ b/src/dev/arm/NoMali.py @@ -33,10 +33,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * - from m5.objects.Device import BasicPioDevice from m5.objects.Gic import * +from m5.params import * class NoMaliGpuType(Enum): diff --git a/src/dev/arm/RealView.py b/src/dev/arm/RealView.py index e71f6cee5a..a0540ec50c 100644 --- a/src/dev/arm/RealView.py +++ b/src/dev/arm/RealView.py @@ -37,48 +37,66 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.defines import buildEnv +from m5.objects.ArmSystem import ArmExtension +from m5.objects.CfiMemory import CfiMemory +from m5.objects.ClockDomain import ( + ClockDomain, + SrcClockDomain, +) +from m5.objects.ClockedObject import ClockedObject +from m5.objects.Device import ( + BadAddr, + BasicPioDevice, + DmaDevice, + IsaFake, + PioDevice, +) +from m5.objects.Display import ( + Display, + Display1080p, +) +from m5.objects.EnergyCtrl import EnergyCtrl +from m5.objects.Ethernet import ( + IGbE_e1000, + IGbE_igb, + NSGigE, +) +from m5.objects.GenericTimer import * +from m5.objects.Gic import * +from m5.objects.Graphics import ImageFormat +from m5.objects.Ide import * +from m5.objects.MHU import ( + MHU, + Ap2ScpDoorbell, + Scp2ApDoorbell, +) +from m5.objects.PciDevice import ( + PciIoBar, + PciLegacyIoBar, +) +from m5.objects.PciHost import * +from m5.objects.Platform import Platform +from m5.objects.PS2 import * +from m5.objects.Scmi import * +from m5.objects.SimpleMemory import SimpleMemory +from m5.objects.SMMUv3 import SMMUv3 +from m5.objects.SubSystem import SubSystem +from m5.objects.Terminal import Terminal +from m5.objects.Uart import Uart +from m5.objects.VirtIOMMIO import MmioVirtIO +from m5.objects.VoltageDomain import VoltageDomain from m5.params import * from m5.proxy import * from m5.util.fdthelper import * -from m5.objects.ArmSystem import ArmExtension -from m5.objects.ClockDomain import ClockDomain, SrcClockDomain -from m5.objects.VoltageDomain import VoltageDomain -from m5.objects.Device import ( - BasicPioDevice, - PioDevice, - IsaFake, - BadAddr, - DmaDevice, -) -from m5.objects.PciHost import * -from m5.objects.Ethernet import NSGigE, IGbE_igb, IGbE_e1000 -from m5.objects.Ide import * -from m5.objects.Platform import Platform -from m5.objects.Terminal import Terminal -from m5.objects.Uart import Uart -from m5.objects.SimpleMemory import SimpleMemory -from m5.objects.GenericTimer import * -from m5.objects.Gic import * -from m5.objects.MHU import MHU, Scp2ApDoorbell, Ap2ScpDoorbell -from m5.objects.EnergyCtrl import EnergyCtrl -from m5.objects.ClockedObject import ClockedObject -from m5.objects.SubSystem import SubSystem -from m5.objects.Graphics import ImageFormat -from m5.objects.ClockedObject import ClockedObject -from m5.objects.PS2 import * -from m5.objects.VirtIOMMIO import MmioVirtIO -from m5.objects.Display import Display, Display1080p -from m5.objects.Scmi import * -from m5.objects.SMMUv3 import SMMUv3 -from m5.objects.PciDevice import PciLegacyIoBar, PciIoBar - -from m5.objects.CfiMemory import CfiMemory # Platforms with KVM support should generally use in-kernel GIC # emulation. Use a GIC model that automatically switches between # gem5's GIC model and KVM's GIC model if KVM is available. try: - from m5.objects.KvmGic import MuxingKvmGicV2, MuxingKvmGicV3 + from m5.objects.KvmGic import ( + MuxingKvmGicV2, + MuxingKvmGicV3, + ) kvm_gicv2_class = MuxingKvmGicV2 kvm_gicv3_class = MuxingKvmGicV3 diff --git a/src/dev/arm/SMMUv3.py b/src/dev/arm/SMMUv3.py index 46fad3bf68..28c2c6fe24 100644 --- a/src/dev/arm/SMMUv3.py +++ b/src/dev/arm/SMMUv3.py @@ -33,11 +33,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * -from m5.util.fdthelper import * from m5.SimObject import * -from m5.objects.ClockedObject import ClockedObject +from m5.util.fdthelper import * class SMMUv3DeviceInterface(ClockedObject): diff --git a/src/dev/arm/UFSHostDevice.py b/src/dev/arm/UFSHostDevice.py index 46ed7ddd24..3de0684487 100644 --- a/src/dev/arm/UFSHostDevice.py +++ b/src/dev/arm/UFSHostDevice.py @@ -34,10 +34,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import sys + +from m5.objects.AbstractNVM import * +from m5.objects.Device import DmaDevice from m5.params import * from m5.proxy import * -from m5.objects.Device import DmaDevice -from m5.objects.AbstractNVM import * class UFSHostDevice(DmaDevice): diff --git a/src/dev/arm/VExpressFastmodel.py b/src/dev/arm/VExpressFastmodel.py index 0dfddb7735..1135bd8b92 100644 --- a/src/dev/arm/VExpressFastmodel.py +++ b/src/dev/arm/VExpressFastmodel.py @@ -23,9 +23,15 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects.FastModelGIC import FastModelGIC, SCFastModelGIC +from m5.objects.FastModelGIC import ( + FastModelGIC, + SCFastModelGIC, +) from m5.objects.Gic import ArmSPI -from m5.objects.RealView import VExpress_GEM5_Base, HDLcd +from m5.objects.RealView import ( + HDLcd, + VExpress_GEM5_Base, +) from m5.objects.SubSystem import SubSystem diff --git a/src/dev/arm/VirtIOMMIO.py b/src/dev/arm/VirtIOMMIO.py index eecd703e3a..d51f2aa69d 100644 --- a/src/dev/arm/VirtIOMMIO.py +++ b/src/dev/arm/VirtIOMMIO.py @@ -35,13 +35,15 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * -from m5.proxy import * - from m5.objects.Device import BasicPioDevice from m5.objects.Gic import ArmInterruptPin -from m5.objects.VirtIO import VirtIODeviceBase, VirtIODummyDevice +from m5.objects.VirtIO import ( + VirtIODeviceBase, + VirtIODummyDevice, +) +from m5.params import * +from m5.proxy import * +from m5.SimObject import SimObject class MmioVirtIO(BasicPioDevice): diff --git a/src/dev/arm/css/Scmi.py b/src/dev/arm/css/Scmi.py index 1246e69343..4ae616d209 100644 --- a/src/dev/arm/css/Scmi.py +++ b/src/dev/arm/css/Scmi.py @@ -33,12 +33,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.Doorbell import Doorbell +from m5.objects.Scp import Scp from m5.params import * from m5.proxy import * -from m5.objects.Scp import Scp -from m5.objects.Doorbell import Doorbell -from m5.util.fdthelper import * from m5.SimObject import SimObject +from m5.util.fdthelper import * class ScmiChannel(SimObject): diff --git a/src/dev/arm/gic_v3.hh b/src/dev/arm/gic_v3.hh index 2ea6a98b3b..7adb1d0f3f 100644 --- a/src/dev/arm/gic_v3.hh +++ b/src/dev/arm/gic_v3.hh @@ -167,6 +167,17 @@ class Gicv3 : public BaseGic, public Gicv3Registers Tick write(PacketPtr pkt) override; bool supportsVersion(GicVersion version) override; + template + void + reserved(const char* fmt, Args... args) const + { + if (params().reserved_is_res0) { + warn(fmt, args...); + } else { + panic(fmt, args...); + } + } + public: Gicv3(const Params &p); diff --git a/src/dev/arm/gic_v3_distributor.cc b/src/dev/arm/gic_v3_distributor.cc index 1cb485c5f5..af306929ff 100644 --- a/src/dev/arm/gic_v3_distributor.cc +++ b/src/dev/arm/gic_v3_distributor.cc @@ -505,8 +505,8 @@ Gicv3Distributor::read(Addr addr, size_t size, bool is_secure_access) return 0; // RES0 default: - panic("Gicv3Distributor::read(): invalid offset %#x\n", addr); - break; + gic->reserved("Gicv3Distributor::read(): invalid offset %#x\n", addr); + return 0; // RES0 } } @@ -999,7 +999,7 @@ Gicv3Distributor::write(Addr addr, uint64_t data, size_t size, } default: - panic("Gicv3Distributor::write(): invalid offset %#x\n", addr); + gic->reserved("Gicv3Distributor::write(): invalid offset %#x\n", addr); break; } } diff --git a/src/dev/arm/gic_v3_redistributor.cc b/src/dev/arm/gic_v3_redistributor.cc index e4380ef6f0..67d6e42e6b 100644 --- a/src/dev/arm/gic_v3_redistributor.cc +++ b/src/dev/arm/gic_v3_redistributor.cc @@ -377,8 +377,8 @@ Gicv3Redistributor::read(Addr addr, size_t size, bool is_secure_access) return 0; default: - panic("Gicv3Redistributor::read(): invalid offset %#x\n", addr); - break; + gic->reserved("Gicv3Redistributor::read(): invalid offset %#x\n", addr); + return 0; // RES0 } } @@ -704,7 +704,7 @@ Gicv3Redistributor::write(Addr addr, uint64_t data, size_t size, } default: - panic("Gicv3Redistributor::write(): invalid offset %#x\n", addr); + gic->reserved("Gicv3Redistributor::write(): invalid offset %#x\n", addr); break; } } diff --git a/src/dev/dma_device.hh b/src/dev/dma_device.hh index 92b44bf5f6..3fd77860f4 100644 --- a/src/dev/dma_device.hh +++ b/src/dev/dma_device.hh @@ -187,7 +187,7 @@ class DmaPort : public RequestPort, public Drainable /** Default substreamId */ const uint32_t defaultSSid; - const int cacheLineSize; + const Addr cacheLineSize; protected: @@ -257,7 +257,7 @@ class DmaDevice : public PioDevice void init() override; - unsigned int cacheBlockSize() const { return sys->cacheLineSize(); } + Addr cacheBlockSize() const { return sys->cacheLineSize(); } Port &getPort(const std::string &if_name, PortID idx=InvalidPortID) override; @@ -526,7 +526,7 @@ class DmaReadFifo : public Drainable, public Serializable DmaPort &port; - const int cacheLineSize; + const Addr cacheLineSize; private: class DmaDoneEvent : public Event diff --git a/src/dev/hsa/HSADevice.py b/src/dev/hsa/HSADevice.py index b22269d5bb..ed3b2d8086 100644 --- a/src/dev/hsa/HSADevice.py +++ b/src/dev/hsa/HSADevice.py @@ -27,11 +27,11 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * -from m5.proxy import * from m5.objects.Device import DmaVirtDevice from m5.objects.VegaGPUTLB import VegaPagetableWalker +from m5.params import * +from m5.proxy import * +from m5.SimObject import SimObject class HSAPacketProcessor(DmaVirtDevice): diff --git a/src/dev/hsa/HSADriver.py b/src/dev/hsa/HSADriver.py index c3e12df4d2..99c81ecc86 100644 --- a/src/dev/hsa/HSADriver.py +++ b/src/dev/hsa/HSADriver.py @@ -27,10 +27,10 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject +from m5.objects.Process import EmulatedDriver from m5.params import * from m5.proxy import * -from m5.objects.Process import EmulatedDriver +from m5.SimObject import SimObject class HSADriver(EmulatedDriver): diff --git a/src/dev/hsa/hsa_packet.hh b/src/dev/hsa/hsa_packet.hh index 8c7d694431..8eab8385a6 100644 --- a/src/dev/hsa/hsa_packet.hh +++ b/src/dev/hsa/hsa_packet.hh @@ -100,6 +100,14 @@ struct _hsa_barrier_or_packet_t uint64_t completion_signal; }; +struct _hsa_generic_vendor_pkt +{ + uint32_t padding[14]; + Addr completion_signal; +}; +// All HSA AQL packets are 64 bytes. Confirm that here. +static_assert(sizeof(_hsa_generic_vendor_pkt) == 64); + } // namespace gem5 #endif // __DEV_HSA_HSA_PACKET_HH__ diff --git a/src/dev/hsa/hsa_packet_processor.cc b/src/dev/hsa/hsa_packet_processor.cc index d0afcf816f..2064de41ce 100644 --- a/src/dev/hsa/hsa_packet_processor.cc +++ b/src/dev/hsa/hsa_packet_processor.cc @@ -389,20 +389,16 @@ HSAPacketProcessor::processPkt(void* pkt, uint32_t rl_idx, Addr host_pkt_addr) dep_sgnl_rd_st->resetSigVals(); // The completion signal is connected if (bar_and_pkt->completion_signal != 0) { - // HACK: The semantics of the HSA signal is to - // decrement the current signal value - // I'm going to cheat here and read out - // the value from main memory using functional - // access, and then just DMA the decremented value. - uint64_t signal_value = gpu_device->functionalReadHsaSignal(\ - bar_and_pkt->completion_signal); - + // The semantics of the HSA signal is to decrement the current + // signal value by one. Do this asynchronously via DMAs and + // callbacks as we can safely continue with this function + // while waiting for the next packet from the host. DPRINTF(HSAPacketProcessor, "Triggering barrier packet" \ " completion signal! Addr: %x\n", bar_and_pkt->completion_signal); - gpu_device->updateHsaSignal(bar_and_pkt->completion_signal, - signal_value - 1); + gpu_device->sendCompletionSignal( + bar_and_pkt->completion_signal); } } if (dep_sgnl_rd_st->pendingReads > 0) { diff --git a/src/dev/hsa/hsa_signal.hh b/src/dev/hsa/hsa_signal.hh index 6acbcb7e1b..7d1f316f04 100644 --- a/src/dev/hsa/hsa_signal.hh +++ b/src/dev/hsa/hsa_signal.hh @@ -69,6 +69,12 @@ typedef struct amd_signal_s uint32_t reserved3[2]; } amd_signal_t; +typedef struct +{ + uint64_t start_ts; + uint64_t end_ts; +} amd_event_t; + } // namespace gem5 #endif // DEV_HSA_HSA_SIGNAL_H diff --git a/src/dev/i2c/I2C.py b/src/dev/i2c/I2C.py index 1d3de26d29..df0743f5d8 100644 --- a/src/dev/i2c/I2C.py +++ b/src/dev/i2c/I2C.py @@ -33,9 +33,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * from m5.objects.Device import BasicPioDevice +from m5.params import * +from m5.SimObject import SimObject class I2CDevice(SimObject): diff --git a/src/dev/lupio/LupioBLK.py b/src/dev/lupio/LupioBLK.py index 786c2ccbc5..a41a6f249a 100644 --- a/src/dev/lupio/LupioBLK.py +++ b/src/dev/lupio/LupioBLK.py @@ -25,13 +25,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.objects.Device import DmaDevice - from m5.params import Param from m5.proxy import Parent class LupioBLK(DmaDevice): - type = "LupioBLK" cxx_class = "gem5::LupioBLK" cxx_header = "dev/lupio/lupio_blk.hh" diff --git a/src/dev/lupio/LupioPIC.py b/src/dev/lupio/LupioPIC.py index 7afa727e4b..40ea7c7f89 100644 --- a/src/dev/lupio/LupioPIC.py +++ b/src/dev/lupio/LupioPIC.py @@ -29,7 +29,6 @@ from m5.params import Param class LupioPIC(BasicPioDevice): - type = "LupioPIC" cxx_class = "gem5::LupioPIC" cxx_header = "dev/lupio/lupio_pic.hh" diff --git a/src/dev/lupio/LupioRNG.py b/src/dev/lupio/LupioRNG.py index d6b7b8a199..a1b93446a0 100644 --- a/src/dev/lupio/LupioRNG.py +++ b/src/dev/lupio/LupioRNG.py @@ -30,7 +30,6 @@ from m5.proxy import Parent class LupioRNG(BasicPioDevice): - type = "LupioRNG" cxx_class = "gem5::LupioRNG" cxx_header = "dev/lupio/lupio_rng.hh" diff --git a/src/dev/lupio/LupioTTY.py b/src/dev/lupio/LupioTTY.py index ff35004481..6c0f2efff5 100644 --- a/src/dev/lupio/LupioTTY.py +++ b/src/dev/lupio/LupioTTY.py @@ -24,11 +24,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.Device import BasicPioDevice from m5.params import Param from m5.proxy import Parent -from m5.objects.Device import BasicPioDevice - class LupioTTY(BasicPioDevice): type = "LupioTTY" diff --git a/src/dev/mips/Malta.py b/src/dev/mips/Malta.py index 9199bd5b3f..a8def0b0c0 100755 --- a/src/dev/mips/Malta.py +++ b/src/dev/mips/Malta.py @@ -24,13 +24,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * - from m5.objects.BadDevice import BadDevice from m5.objects.Device import BasicPioDevice from m5.objects.Platform import Platform from m5.objects.Uart import Uart8250 +from m5.params import * +from m5.proxy import * class MaltaCChip(BasicPioDevice): diff --git a/src/dev/net/Ethernet.py b/src/dev/net/Ethernet.py index 72f2061b2b..495e7da211 100644 --- a/src/dev/net/Ethernet.py +++ b/src/dev/net/Ethernet.py @@ -37,10 +37,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.defines import buildEnv -from m5.SimObject import SimObject +from m5.objects.PciDevice import ( + PciDevice, + PciIoBar, + PciMemBar, +) from m5.params import * from m5.proxy import * -from m5.objects.PciDevice import PciDevice, PciIoBar, PciMemBar +from m5.SimObject import SimObject ETHERNET_ROLE = "ETHERNET" Port.compat(ETHERNET_ROLE, ETHERNET_ROLE) diff --git a/src/dev/net/Kconfig b/src/dev/net/Kconfig new file mode 100644 index 0000000000..0e2c3eb551 --- /dev/null +++ b/src/dev/net/Kconfig @@ -0,0 +1,27 @@ +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +config HAVE_TUNTAP + def_bool $(HAVE_TUNTAP) diff --git a/src/dev/pci/CopyEngine.py b/src/dev/pci/CopyEngine.py index 821f7c5cf0..89d01e19b3 100644 --- a/src/dev/pci/CopyEngine.py +++ b/src/dev/pci/CopyEngine.py @@ -24,11 +24,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject +from m5.objects.PciDevice import ( + PciDevice, + PciMemBar, +) from m5.params import * from m5.proxy import * - -from m5.objects.PciDevice import PciDevice, PciMemBar +from m5.SimObject import SimObject class CopyEngine(PciDevice): diff --git a/src/dev/pci/PciDevice.py b/src/dev/pci/PciDevice.py index 8466101287..f8c0c1ba6e 100644 --- a/src/dev/pci/PciDevice.py +++ b/src/dev/pci/PciDevice.py @@ -36,11 +36,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * -from m5.proxy import * from m5.objects.Device import DmaDevice from m5.objects.PciHost import PciHost +from m5.params import * +from m5.proxy import * +from m5.SimObject import SimObject class PciBar(SimObject): diff --git a/src/dev/pci/PciHost.py b/src/dev/pci/PciHost.py index 007b17a30c..5fef19eb92 100644 --- a/src/dev/pci/PciHost.py +++ b/src/dev/pci/PciHost.py @@ -33,11 +33,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * -from m5.proxy import * from m5.objects.Device import PioDevice from m5.objects.Platform import Platform +from m5.params import * +from m5.proxy import * +from m5.SimObject import SimObject class PciHost(PioDevice): @@ -76,7 +76,6 @@ class GenericPciHost(PciHost): relocatable=0, addr=0, ): - busf = bus & 0xFF devicef = device & 0x1F functionf = function & 0x7 diff --git a/src/dev/pci/pcireg.h b/src/dev/pci/pcireg.h index ab5fea540c..e7794e4dc2 100644 --- a/src/dev/pci/pcireg.h +++ b/src/dev/pci/pcireg.h @@ -326,7 +326,7 @@ struct MSIXPbaEntry * Defines the PCI Express capability register and its associated bitfields * for a PCIe device. */ -struct PXCAP +union PXCAP { uint8_t data[48]; struct diff --git a/src/dev/ps2/PS2.py b/src/dev/ps2/PS2.py index 9a0b16495f..6aa3373535 100644 --- a/src/dev/ps2/PS2.py +++ b/src/dev/ps2/PS2.py @@ -33,9 +33,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * from m5.proxy import * +from m5.SimObject import SimObject class PS2Device(SimObject): diff --git a/src/dev/qemu/QemuFwCfg.py b/src/dev/qemu/QemuFwCfg.py index bb237b9bfc..a71a07e52f 100644 --- a/src/dev/qemu/QemuFwCfg.py +++ b/src/dev/qemu/QemuFwCfg.py @@ -23,9 +23,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.objects.SimObject import SimObject from m5.objects.Device import PioDevice +from m5.objects.SimObject import SimObject +from m5.params import * class QemuFwCfgItem(SimObject): diff --git a/src/dev/reg_bank.hh b/src/dev/reg_bank.hh index 3a89a00ab6..9f53c44e38 100644 --- a/src/dev/reg_bank.hh +++ b/src/dev/reg_bank.hh @@ -42,7 +42,9 @@ #include #include "base/bitfield.hh" +#include "base/debug.hh" #include "base/logging.hh" +#include "base/trace.hh" #include "base/types.hh" #include "sim/byteswap.hh" #include "sim/serialize_handlers.hh" @@ -861,9 +863,27 @@ class RegisterBank : public RegisterBankBase void reset() override { _resetter(*this); } }; + // Allow gem5 models to set a debug flag to the register bank for logging + // all full/partial read/write access to the registers. The register bank + // would not log if the flag is not set. + // + // The debug flag is the one declared in the SConscript + // + // DebugFlag('HelloExample') + // + // Then the flag can be set in the register bank with: + // + // setDebugFlag(::gem5::debug::HelloExample) + void + setDebugFlag(const ::gem5::debug::SimpleFlag& flag) + { + _debug_flag = &flag; + } + private: std::map> _offsetMap; + const ::gem5::debug::SimpleFlag* _debug_flag = nullptr; Addr _base = 0; Addr _size = 0; const std::string _name; @@ -956,45 +976,34 @@ class RegisterBank : public RegisterBankBase if (it == _offsetMap.end() || it->first > addr) it--; - if (it->first < addr) { - RegisterBase ® = it->second.get(); - // Skip at least the beginning of the first register. + std::ostringstream ss; + while (done != bytes) { + RegisterBase ® = it->second.get(); + const Addr reg_off = addr - it->first; + const Addr reg_size = reg.size() - reg_off; + const Addr reg_bytes = std::min(reg_size, bytes - done); - // Figure out what parts of it we're accessing. - const off_t reg_off = addr - it->first; - const size_t reg_bytes = std::min(reg.size() - reg_off, - bytes - done); + if (reg_bytes != reg.size()) { + if (_debug_flag) { + ccprintf(ss, "Read register %s, byte offset %d, size %d\n", + reg.name(), reg_off, reg_bytes); + } + reg.read(ptr + done, reg_off, reg_bytes); + } else { + if (_debug_flag) { + ccprintf(ss, "Read register %s\n", reg.name()); + } + reg.read(ptr + done); + } - // Actually do the access. - reg.read(ptr, reg_off, reg_bytes); - done += reg_bytes; - it++; - - // Was that everything? - if (done == bytes) - return; + done += reg_bytes; + addr += reg_bytes; + ++it; } - while (true) { - RegisterBase ® = it->second.get(); - - const size_t reg_size = reg.size(); - const size_t remaining = bytes - done; - - if (remaining == reg_size) { - // A complete register read, and then we're done. - reg.read(ptr + done); - return; - } else if (remaining > reg_size) { - // A complete register read, with more to go. - reg.read(ptr + done); - done += reg_size; - it++; - } else { - // Skip the end of the register, and then we're done. - reg.read(ptr + done, 0, remaining); - return; - } + if (_debug_flag) { + ::gem5::trace::getDebugLogger()->dprintf_flag( + curTick(), name(), _debug_flag->name(), "%s", ss.str()); } } @@ -1013,45 +1022,34 @@ class RegisterBank : public RegisterBankBase if (it == _offsetMap.end() || it->first > addr) it--; - if (it->first < addr) { + std::ostringstream ss; + while (done != bytes) { RegisterBase ® = it->second.get(); - // Skip at least the beginning of the first register. + const Addr reg_off = addr - it->first; + const Addr reg_size = reg.size() - reg_off; + const Addr reg_bytes = std::min(reg_size, bytes - done); - // Figure out what parts of it we're accessing. - const off_t reg_off = addr - it->first; - const size_t reg_bytes = std::min(reg.size() - reg_off, - bytes - done); + if (reg_bytes != reg.size()) { + if (_debug_flag) { + ccprintf(ss, "Write register %s, byte offset %d, size %d\n", + reg.name(), reg_off, reg_size); + } + reg.write(ptr + done, reg_off, reg_bytes); + } else { + if (_debug_flag) { + ccprintf(ss, "Write register %s\n", reg.name()); + } + reg.write(ptr + done); + } - // Actually do the access. - reg.write(ptr, reg_off, reg_bytes); done += reg_bytes; - it++; - - // Was that everything? - if (done == bytes) - return; + addr += reg_bytes; + ++it; } - while (true) { - RegisterBase ® = it->second.get(); - - const size_t reg_size = reg.size(); - const size_t remaining = bytes - done; - - if (remaining == reg_size) { - // A complete register write, and then we're done. - reg.write(ptr + done); - return; - } else if (remaining > reg_size) { - // A complete register write, with more to go. - reg.write(ptr + done); - done += reg_size; - it++; - } else { - // Skip the end of the register, and then we're done. - reg.write(ptr + done, 0, remaining); - return; - } + if (_debug_flag) { + ::gem5::trace::getDebugLogger()->dprintf_flag( + curTick(), name(), _debug_flag->name(), "%s", ss.str()); } } diff --git a/src/dev/riscv/HiFive.py b/src/dev/riscv/HiFive.py index 5bd6363363..04b2672ea8 100755 --- a/src/dev/riscv/HiFive.py +++ b/src/dev/riscv/HiFive.py @@ -35,19 +35,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects.Platform import Platform -from m5.objects.PMAChecker import PMAChecker from m5.objects.Clint import Clint +from m5.objects.PciHost import GenericPciHost +from m5.objects.Platform import Platform from m5.objects.Plic import Plic +from m5.objects.PMAChecker import PMAChecker from m5.objects.RTC import RiscvRTC -from m5.objects.Uart import RiscvUart8250 from m5.objects.Terminal import Terminal +from m5.objects.Uart import RiscvUart8250 from m5.params import * from m5.proxy import * from m5.util.fdthelper import * -from m5.objects.PciHost import GenericPciHost - class GenericRiscvPciHost(GenericPciHost): type = "GenericRiscvPciHost" @@ -251,7 +250,7 @@ class HiFive(HiFiveBase): def annotateCpuDeviceNode(self, cpu, state): cpu.append(FdtPropertyStrings("mmu-type", "riscv,sv48")) cpu.append(FdtPropertyStrings("status", "okay")) - cpu.append(FdtPropertyStrings("riscv,isa", "rv64imafdcsu")) + cpu.append(FdtPropertyStrings("riscv,isa", "rv64imafdc")) cpu.appendCompatible(["riscv"]) int_node = FdtNode("interrupt-controller") diff --git a/src/dev/riscv/RTC.py b/src/dev/riscv/RTC.py index a6559eaf48..66f691e79c 100644 --- a/src/dev/riscv/RTC.py +++ b/src/dev/riscv/RTC.py @@ -33,10 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.IntPin import IntSourcePin from m5.params import * from m5.proxy import * from m5.SimObject import SimObject -from m5.objects.IntPin import IntSourcePin class RiscvRTC(SimObject): diff --git a/src/dev/riscv/RiscvVirtIOMMIO.py b/src/dev/riscv/RiscvVirtIOMMIO.py index 17019502fa..7a343715ad 100644 --- a/src/dev/riscv/RiscvVirtIOMMIO.py +++ b/src/dev/riscv/RiscvVirtIOMMIO.py @@ -34,13 +34,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * -from m5.proxy import * -from m5.util.fdthelper import * - from m5.objects.PlicDevice import PlicIntDevice from m5.objects.VirtIO import VirtIODummyDevice +from m5.params import * +from m5.proxy import * +from m5.SimObject import SimObject +from m5.util.fdthelper import * class RiscvMmioVirtIO(PlicIntDevice): diff --git a/src/dev/serial/Terminal.py b/src/dev/serial/Terminal.py index a08a18fe1e..28abf7572a 100644 --- a/src/dev/serial/Terminal.py +++ b/src/dev/serial/Terminal.py @@ -36,11 +36,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject +from m5.objects.Serial import SerialDevice from m5.params import * from m5.proxy import * - -from m5.objects.Serial import SerialDevice +from m5.SimObject import SimObject class TerminalDump(ScopedEnum): diff --git a/src/dev/serial/Uart.py b/src/dev/serial/Uart.py index 2ca68b8f12..7de6582ba0 100644 --- a/src/dev/serial/Uart.py +++ b/src/dev/serial/Uart.py @@ -36,13 +36,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.defines import buildEnv +from m5.objects.Device import BasicPioDevice +from m5.objects.Serial import SerialDevice from m5.params import * from m5.proxy import * from m5.util.fdthelper import * -from m5.defines import buildEnv - -from m5.objects.Device import BasicPioDevice -from m5.objects.Serial import SerialDevice class Uart(BasicPioDevice): @@ -82,5 +81,5 @@ class RiscvUart8250(Uart8250): node.append(FdtPropertyWords("interrupts", [platform.uart_int_id])) node.append(FdtPropertyWords("clock-frequency", [0x384000])) node.append(FdtPropertyWords("interrupt-parent", state.phandle(plic))) - node.appendCompatible(["ns8250"]) + node.appendCompatible(["ns8250", "ns16550a"]) yield node diff --git a/src/dev/sparc/T1000.py b/src/dev/sparc/T1000.py index 9e473a395d..b4042ecb9f 100644 --- a/src/dev/sparc/T1000.py +++ b/src/dev/sparc/T1000.py @@ -24,13 +24,17 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * - -from m5.objects.Device import BasicPioDevice, PioDevice, IsaFake, BadAddr +from m5.objects.Device import ( + BadAddr, + BasicPioDevice, + IsaFake, + PioDevice, +) from m5.objects.Platform import Platform from m5.objects.Terminal import Terminal from m5.objects.Uart import Uart8250 +from m5.params import * +from m5.proxy import * class MmDisk(BasicPioDevice): @@ -151,6 +155,7 @@ class T1000(Platform): puart0 = Uart8250(pio_addr=0x1F10000000) iob = Iob() + # Attach I/O devices that are on chip def attachOnChipIO(self, bus): self.iob.pio = bus.mem_side_ports diff --git a/src/dev/storage/DiskImage.py b/src/dev/storage/DiskImage.py index e7657e556c..c02d65fbef 100644 --- a/src/dev/storage/DiskImage.py +++ b/src/dev/storage/DiskImage.py @@ -24,8 +24,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * +from m5.SimObject import SimObject class DiskImage(SimObject): diff --git a/src/dev/storage/Ide.py b/src/dev/storage/Ide.py index 7498a52ecb..348e9e3d7b 100644 --- a/src/dev/storage/Ide.py +++ b/src/dev/storage/Ide.py @@ -24,9 +24,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject +from m5.objects.PciDevice import ( + PciDevice, + PciIoBar, +) from m5.params import * -from m5.objects.PciDevice import PciDevice, PciIoBar +from m5.SimObject import SimObject class IdeID(Enum): diff --git a/src/dev/storage/SimpleDisk.py b/src/dev/storage/SimpleDisk.py index 252ce38a42..21ff037630 100644 --- a/src/dev/storage/SimpleDisk.py +++ b/src/dev/storage/SimpleDisk.py @@ -24,9 +24,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * from m5.proxy import * +from m5.SimObject import SimObject class SimpleDisk(SimObject): diff --git a/src/dev/virtio/VirtIO.py b/src/dev/virtio/VirtIO.py index 1d652bca64..a009745caf 100644 --- a/src/dev/virtio/VirtIO.py +++ b/src/dev/virtio/VirtIO.py @@ -35,11 +35,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject +from m5.objects.Device import PioDevice +from m5.objects.PciDevice import ( + PciDevice, + PciIoBar, +) from m5.params import * from m5.proxy import * -from m5.objects.Device import PioDevice -from m5.objects.PciDevice import PciDevice, PciIoBar +from m5.SimObject import SimObject class VirtIODeviceBase(SimObject): diff --git a/src/dev/virtio/VirtIO9P.py b/src/dev/virtio/VirtIO9P.py index b6611713b7..37c9e481cb 100644 --- a/src/dev/virtio/VirtIO9P.py +++ b/src/dev/virtio/VirtIO9P.py @@ -35,9 +35,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.VirtIO import VirtIODeviceBase from m5.params import * from m5.proxy import * -from m5.objects.VirtIO import VirtIODeviceBase class VirtIO9PBase(VirtIODeviceBase): diff --git a/src/dev/virtio/VirtIOBlock.py b/src/dev/virtio/VirtIOBlock.py index 6a75c00956..bc9394cf2f 100644 --- a/src/dev/virtio/VirtIOBlock.py +++ b/src/dev/virtio/VirtIOBlock.py @@ -35,9 +35,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.VirtIO import VirtIODeviceBase from m5.params import * from m5.proxy import * -from m5.objects.VirtIO import VirtIODeviceBase class VirtIOBlock(VirtIODeviceBase): diff --git a/src/dev/virtio/VirtIOConsole.py b/src/dev/virtio/VirtIOConsole.py index 72826aa6fb..b1f5e850e7 100644 --- a/src/dev/virtio/VirtIOConsole.py +++ b/src/dev/virtio/VirtIOConsole.py @@ -35,10 +35,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.Serial import SerialDevice +from m5.objects.VirtIO import VirtIODeviceBase from m5.params import * from m5.proxy import * -from m5.objects.VirtIO import VirtIODeviceBase -from m5.objects.Serial import SerialDevice class VirtIOConsole(VirtIODeviceBase): diff --git a/src/dev/virtio/VirtIORng 2.py b/src/dev/virtio/VirtIORng 2.py index 925fccdabe..5d5db04992 100644 --- a/src/dev/virtio/VirtIORng 2.py +++ b/src/dev/virtio/VirtIORng 2.py @@ -36,9 +36,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.VirtIO import VirtIODeviceBase from m5.params import * from m5.proxy import * -from m5.objects.VirtIO import VirtIODeviceBase class VirtIORng(VirtIODeviceBase): diff --git a/src/dev/virtio/VirtIORng.py b/src/dev/virtio/VirtIORng.py index 925fccdabe..5d5db04992 100644 --- a/src/dev/virtio/VirtIORng.py +++ b/src/dev/virtio/VirtIORng.py @@ -36,9 +36,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.VirtIO import VirtIODeviceBase from m5.params import * from m5.proxy import * -from m5.objects.VirtIO import VirtIODeviceBase class VirtIORng(VirtIODeviceBase): diff --git a/src/dev/x86/Cmos.py b/src/dev/x86/Cmos.py index ccc14de8c1..049cc4ea33 100644 --- a/src/dev/x86/Cmos.py +++ b/src/dev/x86/Cmos.py @@ -24,10 +24,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * from m5.objects.Device import BasicPioDevice from m5.objects.IntPin import IntSourcePin +from m5.params import * +from m5.proxy import * class Cmos(BasicPioDevice): diff --git a/src/dev/x86/I8042.py b/src/dev/x86/I8042.py index 0dae054588..e357ac91f3 100644 --- a/src/dev/x86/I8042.py +++ b/src/dev/x86/I8042.py @@ -24,11 +24,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * from m5.objects.Device import PioDevice from m5.objects.IntPin import IntSourcePin from m5.objects.PS2 import * +from m5.params import * +from m5.proxy import * class I8042(PioDevice): diff --git a/src/dev/x86/I82094AA.py b/src/dev/x86/I82094AA.py index 228bc5a5eb..45ee9a3d2b 100644 --- a/src/dev/x86/I82094AA.py +++ b/src/dev/x86/I82094AA.py @@ -24,10 +24,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * from m5.objects.Device import BasicPioDevice from m5.objects.IntPin import VectorIntSinkPin +from m5.params import * +from m5.proxy import * class I82094AA(BasicPioDevice): diff --git a/src/dev/x86/I8237.py b/src/dev/x86/I8237.py index b8a8a8ce51..60f472f48c 100644 --- a/src/dev/x86/I8237.py +++ b/src/dev/x86/I8237.py @@ -24,9 +24,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.Device import BasicPioDevice from m5.params import * from m5.proxy import * -from m5.objects.Device import BasicPioDevice class I8237(BasicPioDevice): diff --git a/src/dev/x86/I8254.py b/src/dev/x86/I8254.py index 545f13739a..660bee0e62 100644 --- a/src/dev/x86/I8254.py +++ b/src/dev/x86/I8254.py @@ -24,10 +24,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * from m5.objects.Device import BasicPioDevice from m5.objects.IntPin import IntSourcePin +from m5.params import * +from m5.proxy import * class I8254(BasicPioDevice): diff --git a/src/dev/x86/I8259.py b/src/dev/x86/I8259.py index 5fcef01f3c..6791c57be2 100644 --- a/src/dev/x86/I8259.py +++ b/src/dev/x86/I8259.py @@ -24,10 +24,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.Device import BasicPioDevice +from m5.objects.IntPin import ( + IntSourcePin, + VectorIntSinkPin, +) from m5.params import * from m5.proxy import * -from m5.objects.Device import BasicPioDevice -from m5.objects.IntPin import IntSourcePin, VectorIntSinkPin class X86I8259CascadeMode(Enum): diff --git a/src/dev/x86/Pc.py b/src/dev/x86/Pc.py index 0039d67230..fd51e14268 100644 --- a/src/dev/x86/Pc.py +++ b/src/dev/x86/Pc.py @@ -24,16 +24,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * - -from m5.objects.Device import IsaFake, BadAddr +from m5.objects.Device import ( + BadAddr, + IsaFake, +) +from m5.objects.PciHost import GenericPciHost from m5.objects.Platform import Platform from m5.objects.SouthBridge import SouthBridge from m5.objects.Terminal import Terminal from m5.objects.Uart import Uart8250 -from m5.objects.PciHost import GenericPciHost from m5.objects.XBar import IOXBar +from m5.params import * +from m5.proxy import * def x86IOAddress(port): diff --git a/src/dev/x86/PcSpeaker.py b/src/dev/x86/PcSpeaker.py index 3337b6a07b..1ca7d25967 100644 --- a/src/dev/x86/PcSpeaker.py +++ b/src/dev/x86/PcSpeaker.py @@ -24,9 +24,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.Device import BasicPioDevice from m5.params import * from m5.proxy import * -from m5.objects.Device import BasicPioDevice class PcSpeaker(BasicPioDevice): diff --git a/src/dev/x86/SouthBridge.py b/src/dev/x86/SouthBridge.py index 670f687175..ec117c917b 100644 --- a/src/dev/x86/SouthBridge.py +++ b/src/dev/x86/SouthBridge.py @@ -24,17 +24,20 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * from m5.objects.Cmos import Cmos from m5.objects.I8042 import I8042 -from m5.objects.I82094AA import I82094AA from m5.objects.I8237 import I8237 from m5.objects.I8254 import I8254 from m5.objects.I8259 import I8259 -from m5.objects.PciDevice import PciLegacyIoBar, PciIoBar +from m5.objects.I82094AA import I82094AA +from m5.objects.PciDevice import ( + PciIoBar, + PciLegacyIoBar, +) from m5.objects.PcSpeaker import PcSpeaker from m5.objects.X86Ide import X86IdeController +from m5.params import * +from m5.proxy import * from m5.SimObject import SimObject diff --git a/src/dev/x86/X86Ide.py b/src/dev/x86/X86Ide.py index 9ae0704503..50ac43e396 100644 --- a/src/dev/x86/X86Ide.py +++ b/src/dev/x86/X86Ide.py @@ -23,11 +23,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * from m5.objects.Ide import IdeController from m5.objects.IntPin import IntSourcePin from m5.objects.PciDevice import PciLegacyIoBar +from m5.params import * +from m5.SimObject import SimObject class X86IdeController(IdeController): diff --git a/src/dev/x86/X86QemuFwCfg.py b/src/dev/x86/X86QemuFwCfg.py index 4998f9a8b5..b9451f4f50 100644 --- a/src/dev/x86/X86QemuFwCfg.py +++ b/src/dev/x86/X86QemuFwCfg.py @@ -23,9 +23,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.E820 import X86E820Entry -from m5.objects.QemuFwCfg import QemuFwCfgIo, QemuFwCfgItem +from m5.objects.QemuFwCfg import ( + QemuFwCfgIo, + QemuFwCfgItem, +) +from m5.params import * def x86IOAddress(port): diff --git a/src/gpu-compute/GPU.py b/src/gpu-compute/GPU.py index c64a6b791d..3c294648a9 100644 --- a/src/gpu-compute/GPU.py +++ b/src/gpu-compute/GPU.py @@ -27,17 +27,17 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +from m5.citations import add_citation from m5.defines import buildEnv -from m5.params import * -from m5.proxy import * -from m5.SimObject import SimObject - from m5.objects.Bridge import Bridge from m5.objects.ClockedObject import ClockedObject from m5.objects.Device import DmaVirtDevice from m5.objects.LdsState import LdsState from m5.objects.Process import EmulatedDriver from m5.objects.VegaGPUTLB import VegaPagetableWalker +from m5.params import * +from m5.proxy import * +from m5.SimObject import SimObject class PrefetchType(Enum): @@ -130,7 +130,7 @@ class ComputeUnit(ClockedObject): # Wavefront size is 64. This is configurable, however changing # this value to anything other than 64 will likely cause errors. wf_size = Param.Int(64, "Wavefront size (in work items)") - num_barrier_slots = Param.Int(4, "Number of barrier slots in a CU") + num_barrier_slots = Param.Int(16, "Number of barrier slots in a CU") num_SIMDs = Param.Int(4, "number of SIMD units per CU") num_scalar_cores = Param.Int(1, "number of Scalar cores per CU") num_scalar_mem_pipes = Param.Int( @@ -356,3 +356,35 @@ class StorageClassType(Enum): "SC_ARG", "SC_NONE", ] + + +add_citation( + ComputeUnit, + """@inproceedings{Gutierrez:2018:amdgpu, + author = {Anthony Gutierrez and + Bradford M. Beckmann and + Alexandru Dutu and + Joseph Gross and + Michael LeBeane and + John Kalamatianos and + Onur Kayiran and + Matthew Poremba and + Brandon Potter and + Sooraj Puthoor and + Matthew D. Sinclair and + Mark Wyse and + Jieming Yin and + Xianwei Zhang and + Akshay Jain and + Timothy G. Rogers}, + title = {Lost in Abstraction: Pitfalls of Analyzing GPUs at the Intermediate + Language Level}, + booktitle = {{IEEE} International Symposium on High Performance Computer Architecture, + {HPCA} 2018, Vienna, Austria, February 24-28, 2018}, + pages = {608--619}, + publisher = {{IEEE} Computer Society}, + year = {2018}, + url = {https://doi.org/10.1109/HPCA.2018.00058}, + doi = {10.1109/HPCA.2018.00058} +}""", +) diff --git a/src/gpu-compute/GPUStaticInstFlags.py b/src/gpu-compute/GPUStaticInstFlags.py index b75e2c6c92..3a44d402be 100644 --- a/src/gpu-compute/GPUStaticInstFlags.py +++ b/src/gpu-compute/GPUStaticInstFlags.py @@ -54,6 +54,7 @@ class GPUStaticInstFlags(Enum): "MemoryRef", # References memory (load, store, or atomic) "Flat", # Flat memory op "FlatGlobal", # Global memory op + "FlatScratch", # Scratch memory op "Load", # Reads from memory "Store", # Writes to memory # Atomic ops diff --git a/src/gpu-compute/Kconfig b/src/gpu-compute/Kconfig new file mode 100644 index 0000000000..574f670833 --- /dev/null +++ b/src/gpu-compute/Kconfig @@ -0,0 +1,28 @@ +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +menuconfig BUILD_GPU + bool "Build the compute-GPU model" + default n diff --git a/src/gpu-compute/LdsState.py b/src/gpu-compute/LdsState.py index c81859331c..e57ed9cc5b 100644 --- a/src/gpu-compute/LdsState.py +++ b/src/gpu-compute/LdsState.py @@ -28,11 +28,10 @@ # POSSIBILITY OF SUCH DAMAGE. from m5.defines import buildEnv +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * -from m5.objects.ClockedObject import ClockedObject - class LdsState(ClockedObject): type = "LdsState" diff --git a/src/gpu-compute/compute_unit.cc b/src/gpu-compute/compute_unit.cc index 06fe28f5b8..8d6deeb85a 100644 --- a/src/gpu-compute/compute_unit.cc +++ b/src/gpu-compute/compute_unit.cc @@ -383,6 +383,13 @@ ComputeUnit::startWavefront(Wavefront *w, int waveId, LdsChunk *ldsChunk, stats.waveLevelParallelism.sample(activeWaves); activeWaves++; + + panic_if(w->wrGmReqsInPipe, "GM write counter for wavefront non-zero\n"); + panic_if(w->rdGmReqsInPipe, "GM read counter for wavefront non-zero\n"); + panic_if(w->wrLmReqsInPipe, "LM write counter for wavefront non-zero\n"); + panic_if(w->rdLmReqsInPipe, "GM read counter for wavefront non-zero\n"); + panic_if(w->outstandingReqs, + "Outstanding reqs counter for wavefront non-zero\n"); } /** @@ -1910,6 +1917,8 @@ ComputeUnit::updateInstStats(GPUDynInstPtr gpuDynInst) } } else if (gpuDynInst->isFlatGlobal()) { stats.flatVMemInsts++; + } else if (gpuDynInst->isFlatScratch()) { + stats.flatVMemInsts++; } else if (gpuDynInst->isLocalMem()) { stats.ldsNoFlatInsts++; } else if (gpuDynInst->isLoad()) { diff --git a/src/gpu-compute/dispatcher.cc b/src/gpu-compute/dispatcher.cc index 7b36bce591..8a72fd73f4 100644 --- a/src/gpu-compute/dispatcher.cc +++ b/src/gpu-compute/dispatcher.cc @@ -310,20 +310,10 @@ GPUDispatcher::notifyWgCompl(Wavefront *wf) gpuCmdProc->hsaPacketProc() .finishPkt(task->dispPktPtr(), task->queueId()); if (task->completionSignal()) { - /** - * HACK: The semantics of the HSA signal is to decrement - * the current signal value. We cheat here and read out - * he value from main memory using functional access and - * then just DMA the decremented value. - */ - uint64_t signal_value = - gpuCmdProc->functionalReadHsaSignal(task->completionSignal()); - DPRINTF(GPUDisp, "HSA AQL Kernel Complete with completion " "signal! Addr: %d\n", task->completionSignal()); - gpuCmdProc->updateHsaSignal(task->completionSignal(), - signal_value - 1); + gpuCmdProc->sendCompletionSignal(task->completionSignal()); } else { DPRINTF(GPUDisp, "HSA AQL Kernel Complete! No completion " "signal\n"); @@ -334,7 +324,7 @@ GPUDispatcher::notifyWgCompl(Wavefront *wf) DPRINTF(GPUKernelInfo, "Completed kernel %d\n", kern_id); if (kernelExitEvents) { - exitSimLoop("GPU Kernel Completed"); + shader->requestKernelExitEvent(); } } diff --git a/src/gpu-compute/gpu_command_processor.cc b/src/gpu-compute/gpu_command_processor.cc index 8f748bdc31..05c9a95eed 100644 --- a/src/gpu-compute/gpu_command_processor.cc +++ b/src/gpu-compute/gpu_command_processor.cc @@ -116,28 +116,52 @@ void GPUCommandProcessor::submitDispatchPkt(void *raw_pkt, uint32_t queue_id, Addr host_pkt_addr) { - static int dynamic_task_id = 0; _hsa_dispatch_packet_t *disp_pkt = (_hsa_dispatch_packet_t*)raw_pkt; assert(!(disp_pkt->kernel_object & (system()->cacheLineSize() - 1))); /** - * we need to read a pointer in the application's address - * space to pull out the kernel code descriptor. + * Need to use a raw pointer for DmaVirtDevice API. This is deleted + * in the dispatchKernelObject method. */ - auto *tc = sys->threads[0]; - - TranslatingPortProxy fs_proxy(tc); - SETranslatingPortProxy se_proxy(tc); - PortProxy &virt_proxy = FullSystem ? fs_proxy : se_proxy; + AMDKernelCode *akc = new AMDKernelCode; /** - * In full system mode, the page table entry may point to a system page - * or a device page. System pages use the proxy as normal, but a device - * page needs to be read from device memory. Check what type it is here. + * The kernel_object is a pointer to the machine code, whose entry + * point is an 'amd_kernel_code_t' type, which is included in the + * kernel binary, and describes various aspects of the kernel. The + * desired entry is the 'kernel_code_entry_byte_offset' field, + * which provides the byte offset (positive or negative) from the + * address of the amd_kernel_code_t to the start of the machine + * instructions. + * + * For SE mode we can read from the port proxy. In FS mode, we may need + * to wait for the guest OS to setup translations, especially when using + * the KVM CPU, so it is preferred to read the code object using a timing + * DMA request. */ - bool is_system_page = true; - Addr phys_addr = disp_pkt->kernel_object; - if (FullSystem) { + if (!FullSystem) { + /** + * we need to read a pointer in the application's address + * space to pull out the kernel code descriptor. + */ + auto *tc = sys->threads[0]; + SETranslatingPortProxy virt_proxy(tc); + + DPRINTF(GPUCommandProc, "reading kernel_object using proxy\n"); + virt_proxy.readBlob(disp_pkt->kernel_object, (uint8_t*)akc, + sizeof(AMDKernelCode)); + + dispatchKernelObject(akc, raw_pkt, queue_id, host_pkt_addr); + } else { + /** + * In full system mode, the page table entry may point to a system + * page or a device page. System pages use the proxy as normal, but + * a device page needs to be read from device memory. Check what type + * it is here. + */ + bool is_system_page = true; + Addr phys_addr = disp_pkt->kernel_object; + /** * Full system currently only supports running on single VMID (one * virtual memory space), i.e., one application running on GPU at a @@ -149,61 +173,68 @@ GPUCommandProcessor::submitDispatchPkt(void *raw_pkt, uint32_t queue_id, walker->startFunctional(gpuDevice->getVM().getPageTableBase(vmid), phys_addr, tmp_bytes, BaseMMU::Mode::Read, is_system_page); - } - DPRINTF(GPUCommandProc, "kernobj vaddr %#lx paddr %#lx size %d s:%d\n", - disp_pkt->kernel_object, phys_addr, sizeof(AMDKernelCode), - is_system_page); + DPRINTF(GPUCommandProc, "kernel_object vaddr %#lx paddr %#lx size %d" + " s:%d\n", disp_pkt->kernel_object, phys_addr, + sizeof(AMDKernelCode), is_system_page); - /** - * The kernel_object is a pointer to the machine code, whose entry - * point is an 'amd_kernel_code_t' type, which is included in the - * kernel binary, and describes various aspects of the kernel. The - * desired entry is the 'kernel_code_entry_byte_offset' field, - * which provides the byte offset (positive or negative) from the - * address of the amd_kernel_code_t to the start of the machine - * instructions. - */ - AMDKernelCode akc; - if (is_system_page) { - DPRINTF(GPUCommandProc, "kernel_object in system, using proxy\n"); - virt_proxy.readBlob(disp_pkt->kernel_object, (uint8_t*)&akc, - sizeof(AMDKernelCode)); - } else { - assert(FullSystem); - DPRINTF(GPUCommandProc, "kernel_object in device, using device mem\n"); + /** + * System objects use DMA device. Device objects need to use device + * memory. + */ + if (is_system_page) { + DPRINTF(GPUCommandProc, + "sending system DMA read for kernel_object\n"); - // Read from GPU memory manager one cache line at a time to prevent - // rare cases where the AKC spans two memory pages. - ChunkGenerator gen(disp_pkt->kernel_object, sizeof(AMDKernelCode), - system()->cacheLineSize()); - for (; !gen.done(); gen.next()) { - Addr chunk_addr = gen.addr(); - int vmid = 1; - unsigned dummy; - walker->startFunctional(gpuDevice->getVM().getPageTableBase(vmid), - chunk_addr, dummy, BaseMMU::Mode::Read, - is_system_page); + auto dma_callback = new DmaVirtCallback( + [=](const uint32_t&) { + dispatchKernelObject(akc, raw_pkt, queue_id, host_pkt_addr); + }); - Request::Flags flags = Request::PHYSICAL; - RequestPtr request = std::make_shared(chunk_addr, - system()->cacheLineSize(), flags, walker->getDevRequestor()); - Packet *readPkt = new Packet(request, MemCmd::ReadReq); - readPkt->dataStatic((uint8_t *)&akc + gen.complete()); - system()->getDeviceMemory(readPkt)->access(readPkt); - delete readPkt; + dmaReadVirt(disp_pkt->kernel_object, sizeof(AMDKernelCode), + dma_callback, (void *)akc); + } else { + DPRINTF(GPUCommandProc, + "kernel_object in device, using device mem\n"); + + // Read from GPU memory manager one cache line at a time to prevent + // rare cases where the AKC spans two memory pages. + ChunkGenerator gen(disp_pkt->kernel_object, sizeof(AMDKernelCode), + system()->cacheLineSize()); + for (; !gen.done(); gen.next()) { + Addr chunk_addr = gen.addr(); + int vmid = 1; + unsigned dummy; + walker->startFunctional( + gpuDevice->getVM().getPageTableBase(vmid), chunk_addr, + dummy, BaseMMU::Mode::Read, is_system_page); + + Request::Flags flags = Request::PHYSICAL; + RequestPtr request = std::make_shared(chunk_addr, + system()->cacheLineSize(), flags, + walker->getDevRequestor()); + Packet *readPkt = new Packet(request, MemCmd::ReadReq); + readPkt->dataStatic((uint8_t *)akc + gen.complete()); + system()->getDeviceMemory(readPkt)->access(readPkt); + delete readPkt; + } + + dispatchKernelObject(akc, raw_pkt, queue_id, host_pkt_addr); } } +} + +void +GPUCommandProcessor::dispatchKernelObject(AMDKernelCode *akc, void *raw_pkt, + uint32_t queue_id, Addr host_pkt_addr) +{ + _hsa_dispatch_packet_t *disp_pkt = (_hsa_dispatch_packet_t*)raw_pkt; DPRINTF(GPUCommandProc, "GPU machine code is %lli bytes from start of the " - "kernel object\n", akc.kernel_code_entry_byte_offset); - - DPRINTF(GPUCommandProc,"GPUCommandProc: Sending dispatch pkt to %lu\n", - (uint64_t)tc->cpuId()); - + "kernel object\n", akc->kernel_code_entry_byte_offset); Addr machine_code_addr = (Addr)disp_pkt->kernel_object - + akc.kernel_code_entry_byte_offset; + + akc->kernel_code_entry_byte_offset; DPRINTF(GPUCommandProc, "Machine code starts at addr: %#x\n", machine_code_addr); @@ -219,7 +250,7 @@ GPUCommandProcessor::submitDispatchPkt(void *raw_pkt, uint32_t queue_id, * APUs to implement asynchronous memcopy operations from 2 pointers in * host memory. I have no idea what BLIT stands for. * */ - if (akc.runtime_loader_kernel_symbol) { + if (akc->runtime_loader_kernel_symbol) { kernel_name = "Some kernel"; } else { kernel_name = "Blit kernel"; @@ -230,7 +261,7 @@ GPUCommandProcessor::submitDispatchPkt(void *raw_pkt, uint32_t queue_id, GfxVersion gfxVersion = FullSystem ? gpuDevice->getGfxVersion() : driver()->getGfxVersion(); HSAQueueEntry *task = new HSAQueueEntry(kernel_name, queue_id, - dynamic_task_id, raw_pkt, &akc, host_pkt_addr, machine_code_addr, + dynamic_task_id, raw_pkt, akc, host_pkt_addr, machine_code_addr, gfxVersion); DPRINTF(GPUCommandProc, "Task ID: %i Got AQL: wg size (%dx%dx%d), " @@ -248,6 +279,152 @@ GPUCommandProcessor::submitDispatchPkt(void *raw_pkt, uint32_t queue_id, initABI(task); ++dynamic_task_id; + + // The driver expects the start time to be in ns + Tick start_ts = curTick() / sim_clock::as_int::ns; + dispatchStartTime.insert({disp_pkt->completion_signal, start_ts}); + + delete akc; +} + +void +GPUCommandProcessor::sendCompletionSignal(Addr signal_handle) +{ + // Originally the completion signal was read functionally and written + // with a timing DMA. This can cause issues in FullSystem mode and + // cause translation failures. Therefore, in FullSystem mode everything + // is done in timing mode. + + if (!FullSystem) { + /** + * HACK: The semantics of the HSA signal is to decrement + * the current signal value. We cheat here and read out + * he value from main memory using functional access and + * then just DMA the decremented value. + */ + uint64_t signal_value = functionalReadHsaSignal(signal_handle); + + updateHsaSignal(signal_handle, signal_value - 1); + } else { + // The semantics of the HSA signal is to decrement the current + // signal value by one. Do this asynchronously via DMAs and + // callbacks as we can safely continue with this function + // while waiting for the next packet from the host. + updateHsaSignalAsync(signal_handle, -1); + } +} + +void +GPUCommandProcessor::updateHsaSignalAsync(Addr signal_handle, int64_t diff) +{ + Addr mailbox_addr = getHsaSignalMailboxAddr(signal_handle); + uint64_t *mailboxValue = new uint64_t; + auto cb2 = new DmaVirtCallback( + [ = ] (const uint64_t &) + { updateHsaMailboxData(signal_handle, mailboxValue); }); + dmaReadVirt(mailbox_addr, sizeof(uint64_t), cb2, (void *)mailboxValue); + DPRINTF(GPUCommandProc, "updateHsaSignalAsync reading mailbox addr %lx\n", + mailbox_addr); +} + +void +GPUCommandProcessor::updateHsaMailboxData(Addr signal_handle, + uint64_t *mailbox_value) +{ + Addr event_addr = getHsaSignalEventAddr(signal_handle); + + DPRINTF(GPUCommandProc, "updateHsaMailboxData read %ld\n", *mailbox_value); + if (*mailbox_value != 0) { + // This is an interruptible signal. Now, read the + // event ID and directly communicate with the driver + // about that event notification. + auto cb = new DmaVirtCallback( + [ = ] (const uint64_t &) + { updateHsaEventData(signal_handle, mailbox_value); }); + dmaReadVirt(event_addr, sizeof(uint64_t), cb, (void *)mailbox_value); + } else { + delete mailbox_value; + + Addr ts_addr = signal_handle + offsetof(amd_signal_t, start_ts); + + amd_event_t *event_ts = new amd_event_t; + event_ts->start_ts = dispatchStartTime[signal_handle]; + event_ts->end_ts = curTick() / sim_clock::as_int::ns; + auto cb = new DmaVirtCallback( + [ = ] (const uint64_t &) + { updateHsaEventTs(signal_handle, event_ts); }); + dmaWriteVirt(ts_addr, sizeof(amd_event_t), cb, (void *)event_ts); + DPRINTF(GPUCommandProc, "updateHsaMailboxData reading timestamp addr " + "%lx\n", ts_addr); + + dispatchStartTime.erase(signal_handle); + } +} + +void +GPUCommandProcessor::updateHsaEventData(Addr signal_handle, + uint64_t *event_value) +{ + Addr mailbox_addr = getHsaSignalMailboxAddr(signal_handle); + + DPRINTF(GPUCommandProc, "updateHsaEventData read %ld\n", *event_value); + // Write *event_value to the mailbox to clear the event + auto cb = new DmaVirtCallback( + [ = ] (const uint64_t &) + { updateHsaSignalDone(event_value); }, *event_value); + dmaWriteVirt(mailbox_addr, sizeof(uint64_t), cb, &cb->dmaBuffer, 0); + + Addr ts_addr = signal_handle + offsetof(amd_signal_t, start_ts); + + amd_event_t *event_ts = new amd_event_t; + event_ts->start_ts = dispatchStartTime[signal_handle]; + event_ts->end_ts = curTick() / sim_clock::as_int::ns; + auto cb2 = new DmaVirtCallback( + [ = ] (const uint64_t &) + { updateHsaEventTs(signal_handle, event_ts); }); + dmaWriteVirt(ts_addr, sizeof(amd_event_t), cb2, (void *)event_ts); + DPRINTF(GPUCommandProc, "updateHsaEventData reading timestamp addr %lx\n", + ts_addr); + + dispatchStartTime.erase(signal_handle); +} + +void +GPUCommandProcessor::updateHsaEventTs(Addr signal_handle, + amd_event_t *ts) +{ + delete ts; + + Addr value_addr = getHsaSignalValueAddr(signal_handle); + int64_t diff = -1; + + uint64_t *signalValue = new uint64_t; + auto cb = new DmaVirtCallback( + [ = ] (const uint64_t &) + { updateHsaSignalData(value_addr, diff, signalValue); }); + dmaReadVirt(value_addr, sizeof(uint64_t), cb, (void *)signalValue); + DPRINTF(GPUCommandProc, "updateHsaSignalAsync reading value addr %lx\n", + value_addr); +} + +void +GPUCommandProcessor::updateHsaSignalData(Addr value_addr, int64_t diff, + uint64_t *prev_value) +{ + // Reuse the value allocated for the read + DPRINTF(GPUCommandProc, "updateHsaSignalData read %ld, writing %ld\n", + *prev_value, *prev_value + diff); + *prev_value += diff; + auto cb = new DmaVirtCallback( + [ = ] (const uint64_t &) + { updateHsaSignalDone(prev_value); }); + dmaWriteVirt(value_addr, sizeof(uint64_t), cb, (void *)prev_value); +} + +void +GPUCommandProcessor::updateHsaSignalDone(uint64_t *signal_value) +{ + delete signal_value; } uint64_t @@ -329,18 +506,27 @@ GPUCommandProcessor::driver() */ /** - * TODO: For now we simply tell the HSAPP to finish the packet, - * however a future patch will update this method to provide - * the proper handling of any required vendor-specific packets. - * In the version of ROCm that is currently supported (1.6) - * the runtime will send packets that direct the CP to - * invalidate the GPUs caches. We do this automatically on - * each kernel launch in the CU, so this is safe for now. + * TODO: For now we simply tell the HSAPP to finish the packet and write a + * completion signal, if any. However, in the future proper handing may be + * required for vendor specific packets. + * + * In the version of ROCm that is currently supported the runtime will send + * packets that direct the CP to invalidate the GPU caches. We do this + * automatically on each kernel launch in the CU, so that situation is safe + * for now. */ void GPUCommandProcessor::submitVendorPkt(void *raw_pkt, uint32_t queue_id, Addr host_pkt_addr) { + auto vendor_pkt = (_hsa_generic_vendor_pkt *)raw_pkt; + + if (vendor_pkt->completion_signal) { + sendCompletionSignal(vendor_pkt->completion_signal); + } + + warn("Ignoring vendor packet\n"); + hsaPP->finishPkt(raw_pkt, queue_id); } diff --git a/src/gpu-compute/gpu_command_processor.hh b/src/gpu-compute/gpu_command_processor.hh index bafe733ee1..85b2a44494 100644 --- a/src/gpu-compute/gpu_command_processor.hh +++ b/src/gpu-compute/gpu_command_processor.hh @@ -46,6 +46,7 @@ #include #include +#include "arch/amdgpu/vega/gpu_registers.hh" #include "base/logging.hh" #include "base/trace.hh" #include "base/types.hh" @@ -98,6 +99,8 @@ class GPUCommandProcessor : public DmaVirtDevice Addr host_pkt_addr); void attachDriver(GPUComputeDriver *driver); + void dispatchKernelObject(AMDKernelCode *akc, void *raw_pkt, + uint32_t queue_id, Addr host_pkt_addr); void dispatchPkt(HSAQueueEntry *task); void signalWakeupEvent(uint32_t event_id); @@ -106,9 +109,17 @@ class GPUCommandProcessor : public DmaVirtDevice AddrRangeList getAddrRanges() const override; System *system(); + void sendCompletionSignal(Addr signal_handle); void updateHsaSignal(Addr signal_handle, uint64_t signal_value, HsaSignalCallbackFunction function = [] (const uint64_t &) { }); + void updateHsaSignalAsync(Addr signal_handle, int64_t diff); + void updateHsaSignalData(Addr value_addr, int64_t diff, + uint64_t *prev_value); + void updateHsaSignalDone(uint64_t *signal_value); + void updateHsaMailboxData(Addr signal_handle, uint64_t *mailbox_value); + void updateHsaEventData(Addr signal_handle, uint64_t *event_value); + void updateHsaEventTs(Addr signal_handle, amd_event_t *event_value); uint64_t functionalReadHsaSignal(Addr signal_handle); @@ -140,6 +151,12 @@ class GPUCommandProcessor : public DmaVirtDevice HSAPacketProcessor *hsaPP; TranslationGenPtr translate(Addr vaddr, Addr size) override; + // Running counter of dispatched tasks + int dynamic_task_id = 0; + + // Keep track of start times for task dispatches. + std::unordered_map dispatchStartTime; + /** * Perform a DMA read of the read_dispatch_id_field_base_byte_offset * field, which follows directly after the read_dispatch_id (the read @@ -199,7 +216,7 @@ class GPUCommandProcessor : public DmaVirtDevice * the signal is reset we should check that the runtime was * successful and then proceed to launch the kernel. */ - if (task->privMemPerItem() > + if ((task->privMemPerItem() * VegaISA::NumVecElemPerVecReg) > task->amdQueue.compute_tmpring_size_wavesize * 1024) { // TODO: Raising this signal will potentially nuke scratch // space for in-flight kernels that were launched from this diff --git a/src/gpu-compute/gpu_dyn_inst.cc b/src/gpu-compute/gpu_dyn_inst.cc index 3cbb6f1ff8..c59317d2c4 100644 --- a/src/gpu-compute/gpu_dyn_inst.cc +++ b/src/gpu-compute/gpu_dyn_inst.cc @@ -432,6 +432,12 @@ GPUDynInst::isFlatGlobal() const return _staticInst->isFlatGlobal(); } +bool +GPUDynInst::isFlatScratch() const +{ + return _staticInst->isFlatScratch(); +} + bool GPUDynInst::isLoad() const { @@ -576,6 +582,12 @@ GPUDynInst::readsFlatScratch() const return false; } +bool +GPUDynInst::needsToken() const +{ + return isGlobalMem() || isFlat() || isFlatGlobal() || isFlatScratch(); +} + bool GPUDynInst::isAtomicAnd() const { @@ -901,12 +913,12 @@ GPUDynInst::resolveFlatSegment(const VectorMask &mask) uint32_t numSgprs = wavefront()->maxSgprs; uint32_t physSgprIdx = wavefront()->computeUnit->registerManager->mapSgpr(wavefront(), - numSgprs - 3); + numSgprs - 4); uint32_t offset = wavefront()->computeUnit->srf[simdId]->read(physSgprIdx); physSgprIdx = wavefront()->computeUnit->registerManager->mapSgpr(wavefront(), - numSgprs - 4); + numSgprs - 3); uint32_t size = wavefront()->computeUnit->srf[simdId]->read(physSgprIdx); for (int lane = 0; lane < wavefront()->computeUnit->wfSize(); ++lane) { @@ -919,12 +931,12 @@ GPUDynInst::resolveFlatSegment(const VectorMask &mask) wavefront()->execUnitId = wavefront()->flatLmUnitId; wavefront()->decLGKMInstsIssued(); if (isLoad()) { - wavefront()->rdGmReqsInPipe--; + wavefront()->rdLmReqsInPipe--; } else if (isStore()) { - wavefront()->wrGmReqsInPipe--; + wavefront()->wrLmReqsInPipe--; } else if (isAtomic() || isMemSync()) { - wavefront()->rdGmReqsInPipe--; - wavefront()->wrGmReqsInPipe--; + wavefront()->wrLmReqsInPipe--; + wavefront()->rdLmReqsInPipe--; } else { panic("Invalid memory operation!\n"); } diff --git a/src/gpu-compute/gpu_dyn_inst.hh b/src/gpu-compute/gpu_dyn_inst.hh index e2884a012a..6551fa417a 100644 --- a/src/gpu-compute/gpu_dyn_inst.hh +++ b/src/gpu-compute/gpu_dyn_inst.hh @@ -234,6 +234,7 @@ class GPUDynInst : public GPUExecContext bool isMemRef() const; bool isFlat() const; bool isFlatGlobal() const; + bool isFlatScratch() const; bool isLoad() const; bool isStore() const; @@ -256,6 +257,7 @@ class GPUDynInst : public GPUExecContext bool writesFlatScratch() const; bool readsExecMask() const; bool writesExecMask() const; + bool needsToken() const; bool isAtomicAnd() const; bool isAtomicOr() const; diff --git a/src/gpu-compute/gpu_static_inst.hh b/src/gpu-compute/gpu_static_inst.hh index b86a507dce..156f0e529d 100644 --- a/src/gpu-compute/gpu_static_inst.hh +++ b/src/gpu-compute/gpu_static_inst.hh @@ -130,6 +130,7 @@ class GPUStaticInst : public GPUStaticInstFlags bool isMemRef() const { return _flags[MemoryRef]; } bool isFlat() const { return _flags[Flat]; } bool isFlatGlobal() const { return _flags[FlatGlobal]; } + bool isFlatScratch() const { return _flags[FlatScratch]; } bool isLoad() const { return _flags[Load]; } bool isStore() const { return _flags[Store]; } diff --git a/src/gpu-compute/hsa_queue_entry.hh b/src/gpu-compute/hsa_queue_entry.hh index 4083c1c85a..84ae139127 100644 --- a/src/gpu-compute/hsa_queue_entry.hh +++ b/src/gpu-compute/hsa_queue_entry.hh @@ -70,8 +70,6 @@ class HSAQueueEntry _gridSize{{(int)((_hsa_dispatch_packet_t*)disp_pkt)->grid_size_x, (int)((_hsa_dispatch_packet_t*)disp_pkt)->grid_size_y, (int)((_hsa_dispatch_packet_t*)disp_pkt)->grid_size_z}}, - numVgprs(akc->workitem_vgpr_count), - numSgprs(akc->wavefront_sgpr_count), _queueId(queue_id), _dispatchId(dispatch_id), dispPkt(disp_pkt), _hostDispPktAddr(host_pkt_addr), _completionSignal(((_hsa_dispatch_packet_t*)disp_pkt) @@ -88,40 +86,36 @@ class HSAQueueEntry _globalWgId(0), dispatchComplete(false) { - // Precompiled BLIT kernels actually violate the spec a bit - // and don't set many of the required akc fields. For these kernels, - // we need to rip register usage from the resource registers. - // - // We can't get an exact number of registers from the resource - // registers because they round, but we can get an upper bound on it. - // We determine the number of registers by solving for "vgprs_used" - // in the LLVM docs: https://www.llvm.org/docs/AMDGPUUsage.html + // Use the resource descriptors to determine number of GPRs. This will + // round up in some cases, however the exact number field in the AMD + // kernel code struct is not backwards compatible and that field is + // not populated in newer compiles. The resource descriptor dword must + // be backwards compatible, so use that always. + // LLVM docs: https://www.llvm.org/docs/AMDGPUUsage.html // #code-object-v3-kernel-descriptor + // // Currently, the only supported gfx version in gem5 that computes - // this differently is gfx90a. - if (!numVgprs) { - if (gfx_version == GfxVersion::gfx90a) { - numVgprs = (akc->granulated_workitem_vgpr_count + 1) * 8; - } else { - numVgprs = (akc->granulated_workitem_vgpr_count + 1) * 4; - } + // VGPR count differently is gfx90a. + if (gfx_version == GfxVersion::gfx90a) { + numVgprs = (akc->granulated_workitem_vgpr_count + 1) * 8; + } else { + numVgprs = (akc->granulated_workitem_vgpr_count + 1) * 4; } - if (!numSgprs || numSgprs == - std::numeric_limitswavefront_sgpr_count)>::max()) { - // Supported major generation numbers: 0 (BLIT kernels), 8, and 9 - uint16_t version = akc->amd_machine_version_major; - assert((version == 0) || (version == 8) || (version == 9)); - // SGPR allocation granularies: - // - GFX8: 8 - // - GFX9: 16 - // Source: https://llvm.org/docs/AMDGPUUsage.html - if ((version == 0) || (version == 8)) { - // We assume that BLIT kernels use the same granularity as GFX8 - numSgprs = (akc->granulated_wavefront_sgpr_count + 1) * 8; - } else if (version == 9) { - numSgprs = ((akc->granulated_wavefront_sgpr_count + 1) * 16)/2; - } + // SGPR allocation granularies: + // - GFX8: 8 + // - GFX9: 16 + // Source: https://llvm.org/docs/.html + if (gfx_version == GfxVersion::gfx801 || + gfx_version == GfxVersion::gfx803) { + numSgprs = (akc->granulated_wavefront_sgpr_count + 1) * 8; + } else if (gfx_version == GfxVersion::gfx900 || + gfx_version == GfxVersion::gfx902 || + gfx_version == GfxVersion::gfx908 || + gfx_version == GfxVersion::gfx90a) { + numSgprs = ((akc->granulated_wavefront_sgpr_count + 1) * 16)/2; + } else { + panic("Saw unknown gfx version setting up GPR counts\n"); } initialVgprState.reset(); diff --git a/src/gpu-compute/schedule_stage.cc b/src/gpu-compute/schedule_stage.cc index 4c4028b152..0d475c577e 100644 --- a/src/gpu-compute/schedule_stage.cc +++ b/src/gpu-compute/schedule_stage.cc @@ -579,7 +579,7 @@ ScheduleStage::fillDispatchList() // operation. GPUDynInstPtr mp = schIter->first; if (!mp->isMemSync() && !mp->isScalar() && - (mp->isGlobalMem() || mp->isFlat())) { + mp->needsToken()) { computeUnit.globalMemoryPipe.acqCoalescerToken(mp); } diff --git a/src/gpu-compute/scoreboard_check_stage.cc b/src/gpu-compute/scoreboard_check_stage.cc index 3d18260822..b618cab278 100644 --- a/src/gpu-compute/scoreboard_check_stage.cc +++ b/src/gpu-compute/scoreboard_check_stage.cc @@ -154,7 +154,8 @@ ScoreboardCheckStage::ready(Wavefront *w, nonrdytype_e *rdyStatus, if (!(ii->isBarrier() || ii->isNop() || ii->isReturn() || ii->isBranch() || ii->isALU() || ii->isLoad() || ii->isStore() || ii->isAtomic() || ii->isEndOfKernel() || ii->isMemSync() || ii->isFlat() || - ii->isFlatGlobal() || ii->isSleep() || ii->isLocalMem())) { + ii->isFlatGlobal() || ii->isFlatScratch() || ii->isSleep() || + ii->isLocalMem())) { panic("next instruction: %s is of unknown type\n", ii->disassemble()); } diff --git a/src/gpu-compute/shader.cc b/src/gpu-compute/shader.cc index 73d2366b74..e13e7c9cf4 100644 --- a/src/gpu-compute/shader.cc +++ b/src/gpu-compute/shader.cc @@ -41,6 +41,7 @@ #include "debug/GPUMem.hh" #include "debug/GPUShader.hh" #include "debug/GPUWgLatency.hh" +#include "dev/amdgpu/hwreg_defines.hh" #include "gpu-compute/dispatcher.hh" #include "gpu-compute/gpu_command_processor.hh" #include "gpu-compute/gpu_static_inst.hh" @@ -72,15 +73,25 @@ Shader::Shader(const Params &p) : ClockedObject(p), gpuCmdProc.setShader(this); _dispatcher.setShader(this); + // These apertures are set by the driver. In full system mode that is done + // using a PM4 packet but the emulated SE mode driver does not set them + // explicitly, so we need to define some reasonable defaults here. _gpuVmApe.base = ((Addr)1 << 61) + 0x1000000000000L; _gpuVmApe.limit = (_gpuVmApe.base & 0xFFFFFF0000000000UL) | 0xFFFFFFFFFFL; - _ldsApe.base = ((Addr)1 << 61) + 0x0; + _ldsApe.base = 0x1000000000000; _ldsApe.limit = (_ldsApe.base & 0xFFFFFFFF00000000UL) | 0xFFFFFFFF; - _scratchApe.base = ((Addr)1 << 61) + 0x100000000L; + _scratchApe.base = 0x2000000000000; _scratchApe.limit = (_scratchApe.base & 0xFFFFFFFF00000000UL) | 0xFFFFFFFF; + // The scratch and LDS address can be queried starting in gfx900. The + // base addresses are in the SH_MEM_BASES 32-bit register. The upper 16 + // bits are for the LDS address and the lower 16 bits are for scratch + // address. In both cases the 16 bits represent bits 63:48 of the address. + // This means bits 47:0 of the base address is always zero. + setHwReg(HW_REG_SH_MEM_BASES, 0x00010002); + shHiddenPrivateBaseVmid = 0; cuList.resize(n_cu); @@ -519,8 +530,14 @@ Shader::notifyCuSleep() { panic_if(_activeCus <= 0 || _activeCus > cuList.size(), "Invalid activeCu size\n"); _activeCus--; - if (!_activeCus) + if (!_activeCus) { stats.shaderActiveTicks += curTick() - _lastInactiveTick; + + if (kernelExitRequested) { + kernelExitRequested = false; + exitSimLoop("GPU Kernel Completed"); + } + } } /** diff --git a/src/gpu-compute/shader.hh b/src/gpu-compute/shader.hh index 08dfd24b76..32ddf3d15b 100644 --- a/src/gpu-compute/shader.hh +++ b/src/gpu-compute/shader.hh @@ -97,6 +97,10 @@ class Shader : public ClockedObject // Last tick that all CUs attached to this shader were inactive Tick _lastInactiveTick; + // If a kernel-based exit event was requested, wait for all CUs in the + // shader to complete before actually exiting so that stats are updated. + bool kernelExitRequested = false; + public: typedef ShaderParams Params; enum hsail_mode_e {SIMT,VECTOR_SCALAR}; @@ -314,6 +318,12 @@ class Shader : public ClockedObject stats.vectorInstDstOperand[num_operands]++; } + void + requestKernelExitEvent() + { + kernelExitRequested = true; + } + protected: struct ShaderStats : public statistics::Group { diff --git a/src/gpu-compute/wavefront.cc b/src/gpu-compute/wavefront.cc index 8a1adfe802..0bca152e08 100644 --- a/src/gpu-compute/wavefront.cc +++ b/src/gpu-compute/wavefront.cc @@ -1082,7 +1082,7 @@ Wavefront::exec() * we return here to avoid spurious errors related to flat insts * and their address segment resolution. */ - if (execMask().none() && ii->isFlat()) { + if (execMask().none() && ii->needsToken()) { computeUnit->getTokenManager()->recvTokens(1); return; } diff --git a/src/kern/linux/events.cc b/src/kern/linux/events.cc index 35767596af..721bac8a45 100644 --- a/src/kern/linux/events.cc +++ b/src/kern/linux/events.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016 ARM Limited + * Copyright (c) 2011, 2016, 2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -49,6 +49,7 @@ #include "kern/linux/helpers.hh" #include "kern/system_events.hh" #include "sim/core.hh" +#include "sim/sim_exit.hh" #include "sim/system.hh" namespace gem5 @@ -58,25 +59,22 @@ namespace linux { void -DmesgDump::process(ThreadContext *tc) +PanicOrOopsEvent::process(ThreadContext *tc) { - inform("Dumping kernel dmesg buffer to %s...\n", fname); - OutputStream *os = simout.create(fname); - dumpDmesg(tc, *os->stream()); - simout.close(os); - warn(descr()); -} -void -KernelPanic::process(ThreadContext *tc) -{ - inform("Dumping kernel dmesg buffer to %s...\n", fname); - OutputStream *os = simout.create(fname); - dumpDmesg(tc, *os->stream()); - simout.close(os); + if (behaviour != KernelPanicOopsBehaviour::Continue) { + inform("Dumping kernel dmesg buffer to %s...\n", fname); + OutputStream *os = simout.create(fname); + dumpDmesg(tc, *os->stream()); + simout.close(os); + } - panic(descr()); + if (behaviour == KernelPanicOopsBehaviour::DumpDmesgAndExit) { + exitSimLoop(descr(), static_cast(1), curTick(), false); + } else if (behaviour == KernelPanicOopsBehaviour::DumpDmesgAndPanic) { + panic(descr()); + } } void diff --git a/src/kern/linux/events.hh b/src/kern/linux/events.hh index 966c1ba075..2f1e60cd28 100644 --- a/src/kern/linux/events.hh +++ b/src/kern/linux/events.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 ARM Limited + * Copyright (c) 2016, 2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -47,6 +47,7 @@ #include "base/compiler.hh" #include "base/trace.hh" #include "debug/DebugPrintf.hh" +#include "enums/KernelPanicOopsBehaviour.hh" #include "kern/linux/printk.hh" #include "kern/system_events.hh" #include "mem/se_translating_port_proxy.hh" @@ -83,43 +84,25 @@ class DebugPrintk : public Base }; /** - * Dump the guest kernel's dmesg buffer to a file in gem5's output - * directory and print a warning. + * Specify what to do on a Linux Kernel Panic or Oops. * - * @warn This event uses linux::dumpDmesg() and comes with the same + * @warn This event may use linux::dumpDmesg() and comes with the same * limitations. Most importantly, the kernel's address mappings must * be available to the translating proxy. */ -class DmesgDump : public PCEvent +class PanicOrOopsEvent : public PCEvent { protected: std::string fname; + KernelPanicOopsBehaviour behaviour; public: - DmesgDump(PCEventScope *s, const std::string &desc, Addr addr, - const std::string &_fname) : - PCEvent(s, desc, addr), fname(_fname) - {} - void process(ThreadContext *tc) override; -}; - -/** - * Dump the guest kernel's dmesg buffer to a file in gem5's output - * directory and panic. - * - * @warn This event uses linux::dumpDmesg() and comes with the same - * limitations. Most importantly, the kernel's address mappings must - * be available to the translating proxy. - */ -class KernelPanic : public PCEvent -{ - protected: - std::string fname; - - public: - KernelPanic(PCEventScope *s, const std::string &desc, Addr addr, - const std::string &_fname) : - PCEvent(s, desc, addr), fname(_fname) + PanicOrOopsEvent(PCEventScope *s, const std::string &desc, Addr addr, + const std::string &_fname, + const KernelPanicOopsBehaviour _behaviour) + : PCEvent(s, desc, addr) + , fname(_fname) + , behaviour(_behaviour) {} void process(ThreadContext *tc) override; }; diff --git a/src/kern/linux/helpers.cc b/src/kern/linux/helpers.cc index b024226985..871492ed46 100644 --- a/src/kern/linux/helpers.cc +++ b/src/kern/linux/helpers.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022 Arm Limited + * Copyright (c) 2016, 2022-2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -37,7 +37,28 @@ #include "kern/linux/helpers.hh" +/* The following pragmas are to fix a bug in GCC \w the CPP standard library, + * outlined here: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105562. + * This ignores the 'maybe-unitialized' warning when importing regex for + * GCC 12.1. + */ + +// ignore 'maybe-uniitialized' warnings for GCC 12.1. +#if __GNUC__ && __GNUC__ == 12 && __GNUC_MINOR__ == 1 + #define SUPPRESSING_MAYBE_UNINITIALIZED_WARNING + // save diagnostic state. + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif +#include +#ifdef SUPPRESSING_MAYBE_UNINITIALIZED_WARNING + // restore the diagnostic state. + #pragma GCC diagnostic pop +#endif + +#include #include +#include #include "base/compiler.hh" #include "base/loader/object_file.hh" @@ -125,9 +146,9 @@ dumpDmesg(ThreadContext *tc, std::ostream &os) return; } - uint32_t log_buf_len = proxy.read(lb_len->address, bo); - uint32_t log_first_idx = proxy.read(first->address, bo); - uint32_t log_next_idx = proxy.read(next->address, bo); + uint32_t log_buf_len = proxy.read(lb_len->address(), bo); + uint32_t log_first_idx = proxy.read(first->address(), bo); + uint32_t log_next_idx = proxy.read(next->address(), bo); if (log_first_idx >= log_buf_len || log_next_idx >= log_buf_len) { warn("dmesg pointers/length corrupted\n"); @@ -143,7 +164,7 @@ dumpDmesg(ThreadContext *tc, std::ostream &os) warn("Unexpected dmesg buffer length\n"); return; } - proxy.readBlob(lb->address + log_first_idx, log_buf.data(), length); + proxy.readBlob(lb->address() + log_first_idx, log_buf.data(), length); } else { const int length_2 = log_buf_len - log_first_idx; if (length_2 < 0 || length_2 + log_next_idx > log_buf.size()) { @@ -151,8 +172,10 @@ dumpDmesg(ThreadContext *tc, std::ostream &os) return; } length = log_buf_len; - proxy.readBlob(lb->address + log_first_idx, log_buf.data(), length_2); - proxy.readBlob(lb->address, log_buf.data() + length_2, log_next_idx); + proxy.readBlob( + lb->address() + log_first_idx, log_buf.data(), length_2); + proxy.readBlob( + lb->address(), log_buf.data() + length_2, log_next_idx); } // Print dmesg buffer content @@ -261,6 +284,41 @@ struct GEM5_PACKED DmesgInfoRecord } }; +/** Metadata struct for Linux pre-v5.18.0 + * + */ +template +struct Metadata_Pre_v5_18_0 +{ + using atomic_var_t = AtomicVarType; + using guest_ptr_t = + typename std::make_unsigned_t; + unsigned int mask_bits; + guest_ptr_t metadata_ring_ptr; + guest_ptr_t info_ring_ptr; + atomic_var_t unused1; + atomic_var_t unused2; +}; + + +/** Metadata struct for Linux post-v5.18.0 + * + */ +template +struct Metadata_Post_v5_18_0 +{ + using atomic_var_t = AtomicVarType; + using guest_ptr_t = + typename std::make_unsigned_t; + unsigned int mask_bits; + guest_ptr_t metadata_ring_ptr; + guest_ptr_t info_ring_ptr; + atomic_var_t unused1; + atomic_var_t unused2; + atomic_var_t unused3; +}; + + /** Top-level ringbuffer record for the Linux dmesg ringbuffer, post-v5.10. * * Struct data members are compatible with the equivalent Linux data @@ -271,7 +329,7 @@ struct GEM5_PACKED DmesgInfoRecord * the gem5 world, and reading/generating appropriate masks. * */ -template +template struct GEM5_PACKED DmesgRingbuffer { static_assert( @@ -286,14 +344,9 @@ struct GEM5_PACKED DmesgRingbuffer using metadata_record_t = DmesgMetadataRecord; // Struct data members - struct - { - unsigned int mask_bits; - guest_ptr_t metadata_ring_ptr; - guest_ptr_t info_ring_ptr; - atomic_var_t unused1; - atomic_var_t unused2; - } metadata; + + // Metadata struct size depends on the Linux Kernel Version + MetadataStructType metadata; struct { unsigned int mask_bits; @@ -386,9 +439,16 @@ struct GEM5_PACKED DmesgRingbuffer } }; -// Aliases for the two types of Ringbuffer that could be used. -using Linux64_Ringbuffer = DmesgRingbuffer; -using Linux32_Ringbuffer = DmesgRingbuffer; +// Aliases for the types of Ringbuffer that could be used. +using Linux64_Ringbuffer_Pre_v5_18_0 = + DmesgRingbuffer>; +using Linux32_Ringbuffer_Pre_v5_18_0 = + DmesgRingbuffer>; + +using Linux64_Ringbuffer_Post_v5_18_0 = + DmesgRingbuffer>; +using Linux32_Ringbuffer_Post_v5_18_0 = + DmesgRingbuffer>; /** Print the record at the specified offset into the data ringbuffer, * and return the offset of the next entry in the data ringbuffer, @@ -498,7 +558,8 @@ dumpDmesgImpl(ThreadContext *tc, std::ostream &os) ringbuffer_t dynamic_rb; auto dynamic_rb_symbol = symtab.find("printk_rb_dynamic"); if (dynamic_rb_symbol != symtab_end_it) { - dynamic_rb = ringbuffer_t::read(proxy, dynamic_rb_symbol->address, bo); + dynamic_rb = + ringbuffer_t::read(proxy, dynamic_rb_symbol->address(), bo); } else { warn("Failed to find required dmesg symbols.\n"); return; @@ -508,7 +569,7 @@ dumpDmesgImpl(ThreadContext *tc, std::ostream &os) ringbuffer_t static_rb; auto static_rb_symbol = symtab.find("printk_rb_static"); if (static_rb_symbol != symtab_end_it) { - static_rb = ringbuffer_t::read(proxy, static_rb_symbol->address, bo); + static_rb = ringbuffer_t::read(proxy, static_rb_symbol->address(), bo); } else { warn("Failed to find required dmesg symbols.\n"); return; @@ -521,21 +582,22 @@ dumpDmesgImpl(ThreadContext *tc, std::ostream &os) auto active_ringbuffer_ptr_symbol = symtab.find("prb"); if (active_ringbuffer_ptr_symbol != symtab_end_it) { active_ringbuffer_ptr = - proxy.read(active_ringbuffer_ptr_symbol->address, bo); + proxy.read(active_ringbuffer_ptr_symbol->address(), + bo); } else { warn("Failed to find required dmesg symbols.\n"); return; } if (active_ringbuffer_ptr == 0 || - (active_ringbuffer_ptr != dynamic_rb_symbol->address && - active_ringbuffer_ptr != static_rb_symbol->address)) { + (active_ringbuffer_ptr != dynamic_rb_symbol->address() && + active_ringbuffer_ptr != static_rb_symbol->address())) { warn("Kernel Dmesg ringbuffer appears to be invalid.\n"); return; } ringbuffer_t & rb = - (active_ringbuffer_ptr == dynamic_rb_symbol->address) + (active_ringbuffer_ptr == dynamic_rb_symbol->address()) ? dynamic_rb : static_rb; atomic_var_t head_offset = rb.data.head_offset; @@ -581,6 +643,102 @@ dumpDmesgImpl(ThreadContext *tc, std::ostream &os) } } +/** Extract all null-terminated printable strings from a buffer and + * return them as a vector. + * + */ +std::vector +extract_printable_strings(const std::vector buffer) +{ + std::vector results; + std::string result; + bool reading_printable = false; + for (const uint8_t byte: buffer) { + if (std::isprint(byte)) { + result += static_cast(byte); + reading_printable = true; + } else if (reading_printable) { + if (byte == '\0') { + results.push_back(result); + } + result.clear(); + reading_printable = false; + } + } + return results; +} + +/** Try to extract the Linux Kernel Version. + * + * This function attempts to find the Kernel version by searching the + * `uts_namespace` struct at the exported symbol `init_uts_ns`. This + * structure contains the Kernel version as a string, and is + * referenced by `procfs` to return the Kernel version in a running + * system. + * + * Different versions of the Kernel use different layouts for this + * structure, and the top level structure is marked as + * `__randomize_layout`, so the exact layout in memory cannot be + * relied on. Because of this, `extract_kernel_version` takes the + * approach of searching the memory holding the structure for + * printable strings, and parsing the version from those strings. + * + * If a likely match is found, the version is packed into a uint32_t + * in the standard format used by the Linux kernel (which allows + * numerical comparison of version numbers) and returned. + * + * If no likely match is found, 0x0 is returned to indicate an error. + * + */ +uint32_t +extract_kernel_version(ThreadContext* tc) { + System *system = tc->getSystemPtr(); + const auto &symtab = system->workload->symtab(tc); + auto symtab_end_it = symtab.end(); + + auto symbol = symtab.find("init_uts_ns"); + if (symbol == symtab_end_it) { + return 0x0; + } + + // Use size of `init_uts_ns` in Linux v5.18.0 as a default. + // (e.g. for upgraded checkpoints.) + const size_t INIT_UTS_NS_SIZE_DEFAULT = 432; + const size_t BUFFER_SIZE = + symbol->sizeOrDefault(INIT_UTS_NS_SIZE_DEFAULT); + + TranslatingPortProxy proxy(tc); + std::vector buffer(BUFFER_SIZE); + proxy.readBlob( + symbol->address(), buffer.data(), buffer.size() * sizeof(uint8_t)); + auto strings = extract_printable_strings(buffer); + + const std::regex version_re {"^(\\d+)\\.(\\d+)\\.(\\d)+$"}; + std::smatch match; + for (const auto& string: strings) { + if (std::regex_search(string, match, version_re)) { + try { + int major = std::stoi(match[1]); + int minor = std::stoi(match[2]); + int point = std::stoi(match[3]); + return ( + (major & 0xFF) << 16 + | (minor & 0xFF) << 8 + | std::min(point, 255)); + } + catch (const std::invalid_argument &) { + // This shouldn't be possible if the regex matched. + continue; + } + catch (const std::out_of_range &) { + continue; + } + } + } + + return 0x0; +} + /** Dump the kernel Dmesg ringbuffer for Linux versions post-v5.10. * * Delegates to an architecture specific template funtion instance. @@ -591,11 +749,26 @@ dumpDmesg(ThreadContext *tc, std::ostream &os) { System *system = tc->getSystemPtr(); const bool os_is_64_bit = loader::archIs64Bit(system->workload->getArch()); + const uint32_t kernel_version = extract_kernel_version(tc); + const uint32_t KERNEL_5_18_0 = 0x00051200; - if (os_is_64_bit) { - dumpDmesgImpl(tc, os); + if (kernel_version == 0x0) { + warn("Could not determine Linux Kernel version. " + "Assuming post-v5.18.0\n"); + } + + if (kernel_version == 0x0 || kernel_version >= KERNEL_5_18_0) { + if (os_is_64_bit) { + dumpDmesgImpl(tc, os); + } else { + dumpDmesgImpl(tc, os); + } } else { - dumpDmesgImpl(tc, os); + if (os_is_64_bit) { + dumpDmesgImpl(tc, os); + } else { + dumpDmesgImpl(tc, os); + } } } diff --git a/src/kern/linux/linux.cc b/src/kern/linux/linux.cc index 1a54c9c53e..11b9fe0ee4 100644 --- a/src/kern/linux/linux.cc +++ b/src/kern/linux/linux.cc @@ -58,19 +58,19 @@ Linux::openSpecialFile(std::string path, Process *process, bool matched = false; std::string data; - if (path.compare(0, 13, "/proc/meminfo") == 0) { + if (path == "/proc/meminfo") { data = Linux::procMeminfo(process, tc); matched = true; - } else if (path.compare(0, 11, "/etc/passwd") == 0) { + } else if (path == "/etc/passwd") { data = Linux::etcPasswd(process, tc); matched = true; - } else if (path.compare(0, 15, "/proc/self/maps") == 0) { + } else if (path == "/proc/self/maps") { data = Linux::procSelfMaps(process, tc); matched = true; - } else if (path.compare(0, 30, "/sys/devices/system/cpu/online") == 0) { + } else if (path == "/sys/devices/system/cpu/online") { data = Linux::cpuOnline(process, tc); matched = true; - } else if (path.compare(0, 12 ,"/dev/urandom") == 0) { + } else if (path == "/dev/urandom") { data = Linux::devRandom(process, tc); matched = true; } diff --git a/src/learning_gem5/part2/HelloObject.py b/src/learning_gem5/part2/HelloObject.py index 6b9aa8f811..d2cf73e74f 100644 --- a/src/learning_gem5/part2/HelloObject.py +++ b/src/learning_gem5/part2/HelloObject.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2017 Jason Lowe-Power # All rights reserved. # diff --git a/src/learning_gem5/part2/SimpleCache.py b/src/learning_gem5/part2/SimpleCache.py index 1295e543fd..86c09fc09d 100644 --- a/src/learning_gem5/part2/SimpleCache.py +++ b/src/learning_gem5/part2/SimpleCache.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2017 Jason Lowe-Power # All rights reserved. # @@ -25,9 +24,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * -from m5.objects.ClockedObject import ClockedObject class SimpleCache(ClockedObject): diff --git a/src/learning_gem5/part2/SimpleMemobj.py b/src/learning_gem5/part2/SimpleMemobj.py index 2ab95ff76e..19b5ca7c7b 100644 --- a/src/learning_gem5/part2/SimpleMemobj.py +++ b/src/learning_gem5/part2/SimpleMemobj.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2017 Jason Lowe-Power # All rights reserved. # diff --git a/src/learning_gem5/part2/SimpleObject.py b/src/learning_gem5/part2/SimpleObject.py index 2acbc77759..82efc2c95a 100644 --- a/src/learning_gem5/part2/SimpleObject.py +++ b/src/learning_gem5/part2/SimpleObject.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2017 Jason Lowe-Power # All rights reserved. # diff --git a/src/learning_gem5/part2/simple_cache.hh b/src/learning_gem5/part2/simple_cache.hh index 25d195d4f1..1ca87dd126 100644 --- a/src/learning_gem5/part2/simple_cache.hh +++ b/src/learning_gem5/part2/simple_cache.hh @@ -267,7 +267,7 @@ class SimpleCache : public ClockedObject const Cycles latency; /// The block size for the cache - const unsigned blockSize; + const Addr blockSize; /// Number of blocks in the cache (size of cache / block size) const unsigned capacity; diff --git a/src/gpu-compute/SConsopts b/src/learning_gem5/part3/Kconfig similarity index 89% rename from src/gpu-compute/SConsopts rename to src/learning_gem5/part3/Kconfig index 251ac5d8cf..9d06122d2e 100644 --- a/src/gpu-compute/SConsopts +++ b/src/learning_gem5/part3/Kconfig @@ -1,4 +1,4 @@ -# Copyright 2020 Google, Inc. +# Copyright 2022 Google LLC # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -23,7 +23,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Import('*') +config PROTOCOL + default "MSI" if RUBY_PROTOCOL_MSI -sticky_vars.Add(BoolVariable('BUILD_GPU', 'Build the compute-GPU model', - False)) +cont_choice "Ruby protocol" + config RUBY_PROTOCOL_MSI + bool "MSI" +endchoice diff --git a/src/learning_gem5/part3/MSI-dir.sm b/src/learning_gem5/part3/MSI-dir.sm index ca5ea3e534..70d960114a 100644 --- a/src/learning_gem5/part3/MSI-dir.sm +++ b/src/learning_gem5/part3/MSI-dir.sm @@ -448,7 +448,7 @@ machine(MachineType:Directory, "Directory protocol") } action(popMemQueue, "pM", desc="Pop the memory queue") { - memQueue_in.dequeue(clockEdge()); + dequeueMemRespQueue(); } // Stalling actions diff --git a/src/learning_gem5/part3/SConsopts b/src/learning_gem5/part3/SConsopts index dabfd1e146..39968d7fdc 100644 --- a/src/learning_gem5/part3/SConsopts +++ b/src/learning_gem5/part3/SConsopts @@ -2,8 +2,5 @@ Import('*') # NOTE: All SLICC setup code found in src/mem/ruby/protocol/SConscript -# Register this protocol with gem5/SCons -main.Append(ALL_PROTOCOLS=['MSI']) - # Add this directory to the search path for SLICC main.Append(PROTOCOL_DIRS=[Dir('.')]) diff --git a/src/mem/AbstractMemory.py b/src/mem/AbstractMemory.py index 7ab24bc118..57e47adcb1 100644 --- a/src/mem/AbstractMemory.py +++ b/src/mem/AbstractMemory.py @@ -36,8 +36,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.ClockedObject import ClockedObject +from m5.params import * class AbstractMemory(ClockedObject): diff --git a/src/mem/AddrMapper.py b/src/mem/AddrMapper.py index 932fbf14e1..f1f3fbd6f5 100644 --- a/src/mem/AddrMapper.py +++ b/src/mem/AddrMapper.py @@ -36,6 +36,7 @@ from m5.params import * from m5.SimObject import SimObject + # An address mapper changes the packet addresses in going from the # response port side of the mapper to the request port side. When the # response port is queried for the address ranges, it also performs the diff --git a/src/mem/Bridge.py b/src/mem/Bridge.py index 8131d62ef8..ff89d742b6 100644 --- a/src/mem/Bridge.py +++ b/src/mem/Bridge.py @@ -36,8 +36,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.ClockedObject import ClockedObject +from m5.params import * class Bridge(ClockedObject): diff --git a/src/mem/CfiMemory.py b/src/mem/CfiMemory.py index c8de9e511e..5af534a6a9 100644 --- a/src/mem/CfiMemory.py +++ b/src/mem/CfiMemory.py @@ -36,9 +36,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.AbstractMemory import AbstractMemory -from m5.util.fdthelper import FdtNode, FdtPropertyWords +from m5.params import * +from m5.util.fdthelper import ( + FdtNode, + FdtPropertyWords, +) class CfiMemory(AbstractMemory): diff --git a/src/mem/CommMonitor.py b/src/mem/CommMonitor.py index ab946f1e91..739ecad8bf 100644 --- a/src/mem/CommMonitor.py +++ b/src/mem/CommMonitor.py @@ -33,11 +33,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.System import System from m5.params import * from m5.proxy import * -from m5.objects.System import System from m5.SimObject import SimObject + # The communication monitor will most typically be used in combination # with periodic dumping and resetting of stats using schedStatEvent class CommMonitor(SimObject): diff --git a/src/mem/DRAMSim2.py b/src/mem/DRAMSim2.py index 364a0d794b..032fd80a35 100644 --- a/src/mem/DRAMSim2.py +++ b/src/mem/DRAMSim2.py @@ -33,8 +33,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * +from citations import add_citation + from m5.objects.AbstractMemory import * +from m5.params import * + # A wrapper for DRAMSim2 multi-channel memory controller class DRAMSim2(AbstractMemory): @@ -56,3 +59,22 @@ class DRAMSim2(AbstractMemory): ) traceFile = Param.String("", "Output file for trace generation") enableDebug = Param.Bool(False, "Enable DRAMSim2 debug output") + + +add_citation( + DRAMSim2, + """@article{Rosenfeld:2011:dramsim2, + author = {Paul Rosenfeld and + Elliott Cooper{-}Balis and + Bruce L. Jacob}, + title = {DRAMSim2: {A} Cycle Accurate Memory System Simulator}, + journal = {{IEEE} Compututer Architecture Letters}, + volume = {10}, + number = {1}, + pages = {16--19}, + year = {2011}, + url = {https://doi.org/10.1109/L-CA.2011.4}, + doi = {10.1109/L-CA.2011.4} +} +""", +) diff --git a/src/mem/DRAMSys.py b/src/mem/DRAMSys.py index c7d69a0ae4..04c7a66487 100644 --- a/src/mem/DRAMSys.py +++ b/src/mem/DRAMSys.py @@ -24,12 +24,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import * +from m5.citations import add_citation +from m5.objects.AbstractMemory import * +from m5.objects.Tlm import TlmTargetSocket from m5.params import * from m5.proxy import * - -from m5.objects.Tlm import TlmTargetSocket -from m5.objects.AbstractMemory import * +from m5.SimObject import * class DRAMSys(AbstractMemory): @@ -41,3 +41,30 @@ class DRAMSys(AbstractMemory): configuration = Param.String("Path to the DRAMSys configuration") resource_directory = Param.String("Path to the DRAMSys resource directory") recordable = Param.Bool(True, "Whether DRAMSys should record a trace file") + + +add_citation( + DRAMSys, + r"""@inproceedings{Steiner:2020:dramsys4, + author = {Lukas Steiner and + Matthias Jung and + Felipe S. Prado and + Kirill Bykov and + Norbert Wehn}, + editor = {Alex Orailoglu and + Matthias Jung and + Marc Reichenbach}, + title = {DRAMSys4.0: {A} Fast and Cycle-Accurate SystemC/TLM-Based {DRAM} Simulator}, + booktitle = {Embedded Computer Systems: Architectures, Modeling, and Simulation + - 20th International Conference, {SAMOS} 2020, Samos, Greece, July + 5-9, 2020, Proceedings}, + series = {Lecture Notes in Computer Science}, + volume = {12471}, + pages = {110--126}, + publisher = {Springer}, + year = {2020}, + url = {https://doi.org/10.1007/978-3-030-60939-9\_8}, + doi = {10.1007/978-3-030-60939-9\_8} +} +""", +) diff --git a/src/mem/DRAMsim3.py b/src/mem/DRAMsim3.py index 40f61608d8..3500c18ecd 100644 --- a/src/mem/DRAMsim3.py +++ b/src/mem/DRAMsim3.py @@ -33,8 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * +from m5.citations import add_citation from m5.objects.AbstractMemory import * +from m5.params import * + # A wrapper for DRAMSim3 multi-channel memory controller class DRAMsim3(AbstractMemory): @@ -54,3 +56,24 @@ class DRAMsim3(AbstractMemory): filePath = Param.String( "ext/dramsim3/DRAMsim3/", "Directory to prepend to file names" ) + + +add_citation( + DRAMsim3, + """@article{Li:2020:dramsim3, + author = {Shang Li and + Zhiyuan Yang and + Dhiraj Reddy and + Ankur Srivastava and + Bruce L. Jacob}, + title = {DRAMsim3: {A} Cycle-Accurate, Thermal-Capable {DRAM} Simulator}, + journal = {{IEEE} Compututer Architecture Letters}, + volume = {19}, + number = {2}, + pages = {110--113}, + year = {2020}, + url = {https://doi.org/10.1109/LCA.2020.2973991}, + doi = {10.1109/LCA.2020.2973991} +} +""", +) diff --git a/src/mem/HBMCtrl.py b/src/mem/HBMCtrl.py index 45d89a76c9..292ad40ff0 100644 --- a/src/mem/HBMCtrl.py +++ b/src/mem/HBMCtrl.py @@ -24,9 +24,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.MemCtrl import * from m5.params import * from m5.proxy import * -from m5.objects.MemCtrl import * # HBMCtrl manages two pseudo channels of HBM2 diff --git a/src/mem/HMCController.py b/src/mem/HMCController.py index ba5495b71f..dfae48374b 100644 --- a/src/mem/HMCController.py +++ b/src/mem/HMCController.py @@ -36,8 +36,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.XBar import * +from m5.params import * # References: # [1] http://www.open-silicon.com/open-silicon-ips/hmc/ diff --git a/src/mem/HeteroMemCtrl.py b/src/mem/HeteroMemCtrl.py index 8bddc94086..f54da2ec91 100644 --- a/src/mem/HeteroMemCtrl.py +++ b/src/mem/HeteroMemCtrl.py @@ -38,9 +38,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.MemCtrl import * from m5.params import * from m5.proxy import * -from m5.objects.MemCtrl import * # HeteroMemCtrl controls a dram and an nvm interface diff --git a/src/mem/MemChecker.py b/src/mem/MemChecker.py index fcee653265..4fab7b9a7a 100644 --- a/src/mem/MemChecker.py +++ b/src/mem/MemChecker.py @@ -33,9 +33,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * from m5.proxy import * +from m5.SimObject import SimObject class MemChecker(SimObject): diff --git a/src/mem/MemCtrl.py b/src/mem/MemCtrl.py index 549616ccba..215ae271eb 100644 --- a/src/mem/MemCtrl.py +++ b/src/mem/MemCtrl.py @@ -38,9 +38,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.citations import add_citation +from m5.objects.QoSMemCtrl import * from m5.params import * from m5.proxy import * -from m5.objects.QoSMemCtrl import * + # Enum for memory scheduling algorithms, currently First-Come # First-Served and a First-Row Hit then First-Come First-Served @@ -99,3 +101,24 @@ class MemCtrl(QoSMemCtrl): command_window = Param.Latency("10ns", "Static backend latency") disable_sanity_check = Param.Bool(False, "Disable port resp Q size check") + + +add_citation( + MemCtrl, + """@inproceedings{Hansson:2014:dram-controller, + author = {Andreas Hansson and + Neha Agarwal and + Aasheesh Kolli and + Thomas F. Wenisch and + Aniruddha N. Udipi}, + title = {Simulating {DRAM} controllers for future system architecture exploration}, + booktitle = {2014 {IEEE} International Symposium on Performance Analysis of Systems + and Software, {ISPASS} 2014, Monterey, CA, USA, March 23-25, 2014}, + pages = {201--210}, + publisher = {{IEEE} Computer Society}, + year = {2014}, + url = {https://doi.org/10.1109/ISPASS.2014.6844484}, + doi = {10.1109/ISPASS.2014.6844484} +} +""", +) diff --git a/src/mem/MemDelay.py b/src/mem/MemDelay.py index eb4aaa7bf1..52124f0b56 100644 --- a/src/mem/MemDelay.py +++ b/src/mem/MemDelay.py @@ -33,8 +33,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.ClockedObject import ClockedObject +from m5.params import * class MemDelay(ClockedObject): diff --git a/src/mem/MemInterface.py b/src/mem/MemInterface.py index 60bf99bf47..512acab405 100644 --- a/src/mem/MemInterface.py +++ b/src/mem/MemInterface.py @@ -38,10 +38,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.AbstractMemory import AbstractMemory from m5.params import * from m5.proxy import * -from m5.objects.AbstractMemory import AbstractMemory # Enum for the address mapping. With Ch, Ra, Ba, Ro and Co denoting # channel, rank, bank, row and column, respectively, and going from diff --git a/src/mem/NVMInterface.py b/src/mem/NVMInterface.py index 841dc0c047..8289fd29c7 100644 --- a/src/mem/NVMInterface.py +++ b/src/mem/NVMInterface.py @@ -33,11 +33,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * +from m5.objects.DRAMInterface import AddrMap from m5.objects.MemCtrl import MemCtrl from m5.objects.MemInterface import MemInterface -from m5.objects.DRAMInterface import AddrMap +from m5.params import * +from m5.proxy import * + # The following interface aims to model byte-addressable NVM # The most important system-level performance effects of a NVM diff --git a/src/mem/SConscript b/src/mem/SConscript index 6e017e0397..70a5c43758 100644 --- a/src/mem/SConscript +++ b/src/mem/SConscript @@ -71,6 +71,7 @@ SimObject('ThreadBridge.py', sim_objects=['ThreadBridge']) Source('abstract_mem.cc') Source('addr_mapper.cc') +Source('backdoor_manager.cc') Source('bridge.cc') Source('coherent_xbar.cc') Source('cfi_mem.cc') @@ -105,6 +106,8 @@ Source('serial_link.cc') Source('mem_delay.cc') Source('port_terminator.cc') +GTest('backdoor_manager.test', 'backdoor_manager.test.cc', + 'backdoor_manager.cc', with_tag('gem5_trace')) GTest('translation_gen.test', 'translation_gen.test.cc') Source('translating_port_proxy.cc') @@ -124,6 +127,7 @@ if env['HAVE_DRAMSIM3']: if env['HAVE_DRAMSYS']: SimObject('DRAMSys.py', sim_objects=['DRAMSys']) Source('dramsys_wrapper.cc') + Source('dramsys.cc') SimObject('MemChecker.py', sim_objects=['MemChecker', 'MemCheckerMonitor']) Source('mem_checker.cc') diff --git a/src/mem/SerialLink.py b/src/mem/SerialLink.py index 6b767050d6..66d87786f2 100644 --- a/src/mem/SerialLink.py +++ b/src/mem/SerialLink.py @@ -37,8 +37,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.ClockedObject import ClockedObject +from m5.params import * # SerialLink is a simple variation of the Bridge class, with the ability to # account for the latency of packet serialization. diff --git a/src/mem/SharedMemoryServer.py b/src/mem/SharedMemoryServer.py index 97004224de..b0461b614d 100644 --- a/src/mem/SharedMemoryServer.py +++ b/src/mem/SharedMemoryServer.py @@ -1,6 +1,6 @@ -from m5.SimObject import SimObject from m5.params import Param from m5.proxy import Parent +from m5.SimObject import SimObject class SharedMemoryServer(SimObject): diff --git a/src/mem/SimpleMemory.py b/src/mem/SimpleMemory.py index fefda187f2..e0256d7df1 100644 --- a/src/mem/SimpleMemory.py +++ b/src/mem/SimpleMemory.py @@ -36,8 +36,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * from m5.objects.AbstractMemory import * +from m5.params import * class SimpleMemory(AbstractMemory): diff --git a/src/mem/ThreadBridge.py b/src/mem/ThreadBridge.py index f0ee0897ce..9f76655976 100644 --- a/src/mem/ThreadBridge.py +++ b/src/mem/ThreadBridge.py @@ -23,8 +23,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * +from m5.SimObject import SimObject class ThreadBridge(SimObject): diff --git a/src/mem/XBar.py b/src/mem/XBar.py index d0becc22a8..927d3bbe36 100644 --- a/src/mem/XBar.py +++ b/src/mem/XBar.py @@ -36,13 +36,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.ClockedObject import ClockedObject from m5.objects.System import System from m5.params import * from m5.proxy import * from m5.SimObject import SimObject -from m5.objects.ClockedObject import ClockedObject - class BaseXBar(ClockedObject): type = "BaseXBar" diff --git a/src/mem/addr_mapper.cc b/src/mem/addr_mapper.cc index 091b9d56aa..3c4054b6ef 100644 --- a/src/mem/addr_mapper.cc +++ b/src/mem/addr_mapper.cc @@ -84,6 +84,19 @@ AddrMapper::recvFunctionalSnoop(PacketPtr pkt) pkt->setAddr(orig_addr); } +void +AddrMapper::recvMemBackdoorReq(const MemBackdoorReq &req, + MemBackdoorPtr &backdoor) +{ + AddrRange remapped_req_range = AddrRange(remapAddr(req.range().start()), + remapAddr(req.range().end())); + MemBackdoorReq remapped_req(remapped_req_range, req.flags()); + memSidePort.sendMemBackdoorReq(remapped_req, backdoor); + if (backdoor != nullptr) { + backdoor = getRevertedBackdoor(backdoor, req.range()); + } +} + Tick AddrMapper::recvAtomic(PacketPtr pkt) { @@ -104,6 +117,19 @@ AddrMapper::recvAtomicSnoop(PacketPtr pkt) return ret_tick; } +Tick +AddrMapper::recvAtomicBackdoor(PacketPtr pkt, MemBackdoorPtr& backdoor) +{ + Addr orig_addr = pkt->getAddr(); + pkt->setAddr(remapAddr(orig_addr)); + Tick ret_tick = memSidePort.sendAtomicBackdoor(pkt, backdoor); + pkt->setAddr(orig_addr); + if (backdoor != nullptr) { + backdoor = getRevertedBackdoor(backdoor, pkt->getAddrRange()); + } + return ret_tick; +} + bool AddrMapper::recvTimingReq(PacketPtr pkt) { @@ -206,7 +232,8 @@ AddrMapper::recvRangeChange() RangeAddrMapper::RangeAddrMapper(const RangeAddrMapperParams &p) : AddrMapper(p), originalRanges(p.original_ranges), - remappedRanges(p.remapped_ranges) + remappedRanges(p.remapped_ranges), + backdoorManager(originalRanges, remappedRanges) { if (originalRanges.size() != remappedRanges.size()) fatal("AddrMapper: original and shadowed range list must " @@ -232,6 +259,13 @@ RangeAddrMapper::remapAddr(Addr addr) const return addr; } +MemBackdoorPtr +RangeAddrMapper::getRevertedBackdoor(MemBackdoorPtr &backdoor, + const AddrRange &range) +{ + return backdoorManager.getRevertedBackdoor(backdoor, range); +} + AddrRangeList RangeAddrMapper::getAddrRanges() const { diff --git a/src/mem/addr_mapper.hh b/src/mem/addr_mapper.hh index 40a0bb033b..41709f38ab 100644 --- a/src/mem/addr_mapper.hh +++ b/src/mem/addr_mapper.hh @@ -38,6 +38,10 @@ #ifndef __MEM_ADDR_MAPPER_HH__ #define __MEM_ADDR_MAPPER_HH__ +#include + +#include "mem/backdoor_manager.hh" +#include "mem/packet.hh" #include "mem/port.hh" #include "params/AddrMapper.hh" #include "params/RangeAddrMapper.hh" @@ -77,6 +81,20 @@ class AddrMapper : public SimObject */ virtual Addr remapAddr(Addr addr) const = 0; + /** + * This function returns a backdoor that fulfills the initiator request, + * based on the target backdoor at the first parameter. + * Note that this function should return a backdoor in original address + * space, while the target backdoor is in remapped address space. Address + * reverting logic is probably required in this function. + * + * @param backdoor the backdoor obtained from target + * @param range the initiator request to be fulfilled + * @return a backdoor that fulfill the initiator request + */ + virtual MemBackdoorPtr getRevertedBackdoor(MemBackdoorPtr &backdoor, + const AddrRange &range) = 0; + class AddrMapperSenderState : public Packet::SenderState { @@ -168,12 +186,24 @@ class AddrMapper : public SimObject mapper.recvFunctional(pkt); } + void recvMemBackdoorReq(const MemBackdoorReq &req, + MemBackdoorPtr &backdoor) override + { + mapper.recvMemBackdoorReq(req, backdoor); + } + Tick recvAtomic(PacketPtr pkt) override { return mapper.recvAtomic(pkt); } + Tick + recvAtomicBackdoor(PacketPtr pkt, MemBackdoorPtr& backdoor) override + { + return mapper.recvAtomicBackdoor(pkt, backdoor); + } + bool recvTimingReq(PacketPtr pkt) override { @@ -209,10 +239,15 @@ class AddrMapper : public SimObject void recvFunctionalSnoop(PacketPtr pkt); + void recvMemBackdoorReq(const MemBackdoorReq &req, + MemBackdoorPtr &backdoor); + Tick recvAtomic(PacketPtr pkt); Tick recvAtomicSnoop(PacketPtr pkt); + Tick recvAtomicBackdoor(PacketPtr pkt, MemBackdoorPtr& backdoor); + bool recvTimingReq(PacketPtr pkt); bool recvTimingResp(PacketPtr pkt); @@ -269,12 +304,19 @@ class RangeAddrMapper : public AddrMapper std::vector remappedRanges; Addr remapAddr(Addr addr) const override; + + MemBackdoorPtr getRevertedBackdoor(MemBackdoorPtr &backdoor, + const AddrRange &range) override; + void recvRangeChange() override { // TODO Check that our peer is actually expecting to receive accesses // in our output range(s). } + + private: + BackdoorManager backdoorManager; }; } // namespace gem5 diff --git a/src/mem/backdoor_manager.cc b/src/mem/backdoor_manager.cc new file mode 100644 index 0000000000..32d267c7a3 --- /dev/null +++ b/src/mem/backdoor_manager.cc @@ -0,0 +1,148 @@ +/* + * Copyright 2023 Google, Inc + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "base/logging.hh" +#include "mem/backdoor_manager.hh" + +namespace gem5 +{ + +BackdoorManager::BackdoorManager(const std::vector &original_ranges, + const std::vector &remapped_ranges) + : originalRanges(original_ranges), + remappedRanges(remapped_ranges), + backdoorLists(original_ranges.size()) +{ +} + +MemBackdoorPtr +BackdoorManager::getRevertedBackdoor(MemBackdoorPtr backdoor, + const AddrRange &pkt_range) +{ + MemBackdoorPtr reverted_backdoor = findBackdoor(pkt_range); + if (reverted_backdoor == nullptr) { + reverted_backdoor = createRevertedBackdoor(backdoor, pkt_range); + } + return reverted_backdoor; +} + +MemBackdoorPtr +BackdoorManager::createRevertedBackdoor(MemBackdoorPtr backdoor, + const AddrRange &pkt_range) +{ + std::unique_ptr reverted_backdoor = std::make_unique(); + reverted_backdoor->flags(backdoor->flags()); + reverted_backdoor->ptr(backdoor->ptr()); + + Addr addr = pkt_range.start(); + for (int i = 0; i < originalRanges.size(); ++i) { + if (originalRanges[i].contains(addr)) { + /** Does not support interleaved range backdoors. */ + if (originalRanges[i].interleaved() || + remappedRanges[i].interleaved()) { + return nullptr; + } + + /** Shrink the backdoor to fit inside address range. */ + AddrRange shrinked_backdoor_range = + backdoor->range() & remappedRanges[i]; + + Addr backdoor_offset = + shrinked_backdoor_range.start() - remappedRanges[i].start(); + Addr backdoor_size = shrinked_backdoor_range.size(); + + /** Create the backdoor in original address view. */ + reverted_backdoor->range(AddrRange( + originalRanges[i].start() + backdoor_offset, + originalRanges[i].start() + backdoor_offset + backdoor_size)); + + /** + * The backdoor pointer also needs to be shrinked to point to the + * beginning of the range. + */ + Addr shrinked_offset = + shrinked_backdoor_range.start() - backdoor->range().start(); + reverted_backdoor->ptr(backdoor->ptr() + shrinked_offset); + + /** + * Bind the life cycle of the created backdoor with the target + * backdoor. Invalid and delete the created backdoor when the + * target backdoor is invalidated. + */ + MemBackdoorPtr reverted_backdoor_raw_ptr = reverted_backdoor.get(); + auto it = backdoorLists[i].insert(backdoorLists[i].end(), + std::move(reverted_backdoor)); + backdoor->addInvalidationCallback( + [this, i, it](const MemBackdoor &backdoor) { + (*it)->invalidate(); // *it is unique_ptr reverted_backdoor + this->backdoorLists[i].erase(it); + }); + return reverted_backdoor_raw_ptr; + } + } + // Backdoor is not valid. Return an empty one. + panic("Target does not provide valid backdoor."); +} + +MemBackdoorPtr +BackdoorManager::findBackdoor(const AddrRange &pkt_range) const +{ + Addr addr = pkt_range.start(); + Addr size = pkt_range.size(); + for (int i = 0; i < originalRanges.size(); ++i) { + /** The original ranges should be disjoint, so at most one range + * contains the begin address. + */ + if (originalRanges[i].contains(addr)) { + if (!originalRanges[i].contains(addr + size - 1)) { + /** The request range doesn't fit in any address range. */ + return nullptr; + } + for (const auto &backdoor : backdoorLists[i]) { + if (backdoor->range().contains(addr) && + backdoor->range().contains(addr + size - 1)) { + return backdoor.get(); + } + } + } + } + return nullptr; +} + +} // namespace gem5 diff --git a/src/mem/backdoor_manager.hh b/src/mem/backdoor_manager.hh new file mode 100644 index 0000000000..676987c370 --- /dev/null +++ b/src/mem/backdoor_manager.hh @@ -0,0 +1,89 @@ +/* + * Copyright 2023 Google, Inc + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MEM_BACKDOOR_MANAGER_HH__ +#define __MEM_BACKDOOR_MANAGER_HH__ + +#include +#include +#include + +#include "mem/backdoor.hh" +#include "mem/packet.hh" + +namespace gem5 +{ + +/** + * This class manages the backdoors for RangeAddrMapper. It provides + * functionalities such as backdoor remapping, resource managing. + */ +class BackdoorManager +{ + public: + explicit BackdoorManager(const std::vector &original_ranges, + const std::vector &remapped_ranges); + + MemBackdoorPtr getRevertedBackdoor(MemBackdoorPtr backdoor, + const AddrRange &pkt_range); + + protected: + /** + * This function creates a new backdoor, whose address range contains the + * original request address. The address range is in initiator address + * view, and shouldn't exceed the original address range. + */ + MemBackdoorPtr createRevertedBackdoor(MemBackdoorPtr backdoor, + const AddrRange &pkt_range); + /** + * This function returns a created backdoor that fulfills the request, or + * returns nullptr if there's no. + */ + MemBackdoorPtr findBackdoor(const AddrRange &pkt_range) const; + + const std::vector &originalRanges; + const std::vector &remappedRanges; + + /** + * In this vector, each entry contains a list of backdoors that in the + * range in original address view. + */ + std::vector>> backdoorLists; +}; +} // namespace gem5 + +#endif //__MEM_BACKDOOR_MANAGER_HH__ diff --git a/src/mem/backdoor_manager.test.cc b/src/mem/backdoor_manager.test.cc new file mode 100644 index 0000000000..05abc50f2f --- /dev/null +++ b/src/mem/backdoor_manager.test.cc @@ -0,0 +1,140 @@ +/* + * Copyright 2023 Google, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "base/addr_range.hh" +#include "base/gtest/logging.hh" +#include "mem/backdoor.hh" +#include "mem/backdoor_manager.hh" + +namespace gem5 +{ +namespace backdoor_manager_test +{ +const std::vector kOriginalRange({AddrRange(0x0, 0x1000)}); +const std::vector kRemappedRange({AddrRange(0x1000, 0x2000)}); + +class BackdoorManagerTest : public BackdoorManager, public ::testing::Test +{ + public: + BackdoorManagerTest() : BackdoorManager(kOriginalRange, kRemappedRange) + { + } +}; + +TEST_F(BackdoorManagerTest, BasicRemapTest) +{ + /** + * The backdoor range is remappedRanges[0], and should be reverted into + * originalRanges[0]. + */ + AddrRange pkt_range = originalRanges[0]; + + uint8_t *ptr = nullptr; + MemBackdoor remapped_backdoor(remappedRanges[0], ptr, + MemBackdoor::Flags::Readable); + MemBackdoorPtr reverted_backdoor = + getRevertedBackdoor(&remapped_backdoor, pkt_range); + + EXPECT_EQ(reverted_backdoor->range(), originalRanges[0]); + EXPECT_EQ(reverted_backdoor->ptr(), ptr); + ASSERT_EQ(backdoorLists[0].size(), 1); + EXPECT_EQ(backdoorLists[0].begin()->get(), reverted_backdoor); + + /** + * After the target backdoor is invalidated, the new created backdoor should + * be freed and removed from the backdoor list. + */ + remapped_backdoor.invalidate(); + EXPECT_EQ(backdoorLists[0].size(), 0); +} + +TEST_F(BackdoorManagerTest, ShrinkTest) +{ + AddrRange pkt_range = originalRanges[0]; + + /** + * The backdoor range is larger than the address remapper's address range. + * Backdoor is expected to be shrinked. + */ + Addr diff = 0x1000; + AddrRange remapped_backdoor_range( + remappedRanges[0].start() - diff, // 0x0 + remappedRanges[0].end() + diff); // 0x3000 + + uint8_t *ptr = nullptr; + MemBackdoor remapped_backdoor(remapped_backdoor_range, ptr, + MemBackdoor::Flags::Readable); + MemBackdoorPtr reverted_backdoor = + getRevertedBackdoor(&remapped_backdoor, pkt_range); + + EXPECT_EQ(reverted_backdoor->range(), originalRanges[0]); + EXPECT_EQ(reverted_backdoor->ptr(), ptr + diff); + + remapped_backdoor.invalidate(); +} + +TEST_F(BackdoorManagerTest, ReuseTest) +{ + /** + * The two packets have different address range, but both contained in the + * original address range. + */ + Addr mid = originalRanges[0].start() + originalRanges[0].size() / 2; + AddrRange pkt_range_0 = AddrRange(originalRanges[0].start(), mid); + AddrRange pkt_range_1 = AddrRange(mid, originalRanges[0].end()); + + /** + * The address range of the backdoor covers the whole address range, so + * both packets can be fulfilled by this backdoor. + */ + uint8_t *ptr = nullptr; + MemBackdoor remapped_backdoor(remappedRanges[0], ptr, + MemBackdoor::Flags::Readable); + /** + * For the first packet, a new backdoor should be constructed. + */ + MemBackdoorPtr reverted_backdoor_0 = + getRevertedBackdoor(&remapped_backdoor, pkt_range_0); + EXPECT_EQ(backdoorLists[0].size(), 1); + + /** + * For the second packet, it should return the same backdoor as previous + * one, and no new backdoor should be constructed. + */ + MemBackdoorPtr reverted_backdoor_1 = + getRevertedBackdoor(&remapped_backdoor, pkt_range_1); + EXPECT_EQ(reverted_backdoor_0, reverted_backdoor_1); + EXPECT_EQ(backdoorLists[0].size(), 1); + + remapped_backdoor.invalidate(); +} + +} // namespace backdoor_manager_test +} // namespace gem5 diff --git a/src/mem/cache/Cache.py b/src/mem/cache/Cache.py index 49665dde91..c1b1b95648 100644 --- a/src/mem/cache/Cache.py +++ b/src/mem/cache/Cache.py @@ -1,4 +1,4 @@ -# Copyright (c) 2012-2013, 2015, 2018 ARM Limited +# Copyright (c) 2012-2013, 2015, 2018, 2023 ARM Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -36,15 +36,15 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * -from m5.SimObject import SimObject - from m5.objects.ClockedObject import ClockedObject from m5.objects.Compressors import BaseCacheCompressor from m5.objects.Prefetcher import BasePrefetcher from m5.objects.ReplacementPolicies import * from m5.objects.Tags import * +from m5.params import * +from m5.proxy import * +from m5.SimObject import SimObject + # Enum for cache clusivity, currently mostly inclusive or mostly # exclusive. @@ -106,13 +106,6 @@ class BaseCache(ClockedObject): is_read_only = Param.Bool(False, "Is this cache read only (e.g. inst)") prefetcher = Param.BasePrefetcher(NULL, "Prefetcher attached to cache") - prefetch_on_access = Param.Bool( - False, - "Notify the hardware prefetcher on every access (not just misses)", - ) - prefetch_on_pf_hit = Param.Bool( - False, "Notify the hardware prefetcher on hit on prefetched lines" - ) tags = Param.BaseTags(BaseSetAssoc(), "Tag store") replacement_policy = Param.BaseReplacementPolicy( diff --git a/src/mem/cache/base.cc b/src/mem/cache/base.cc index 87c44cefb7..e738167066 100644 --- a/src/mem/cache/base.cc +++ b/src/mem/cache/base.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013, 2018-2019 ARM Limited + * Copyright (c) 2012-2013, 2018-2019, 2023 ARM Limited * All rights reserved. * * The license below extends only to copyright in the software and shall @@ -81,6 +81,7 @@ BaseCache::BaseCache(const BaseCacheParams &p, unsigned blk_size) : ClockedObject(p), cpuSidePort (p.name + ".cpu_side_port", *this, "CpuSidePort"), memSidePort(p.name + ".mem_side_port", this, "MemSidePort"), + accessor(*this), mshrQueue("MSHRs", p.mshrs, 0, p.demand_mshr_reserve, p.name), writeBuffer("write buffer", p.write_buffers, p.mshrs, p.name), tags(p.tags), @@ -126,7 +127,7 @@ BaseCache::BaseCache(const BaseCacheParams &p, unsigned blk_size) tags->tagsInit(); if (prefetcher) - prefetcher->setCache(this); + prefetcher->setParentInfo(system, getProbeManager(), getBlockSize()); fatal_if(compressor && !dynamic_cast(tags), "The tags of compressed cache %s must derive from CompressedTags", @@ -448,7 +449,7 @@ BaseCache::recvTimingReq(PacketPtr pkt) if (satisfied) { // notify before anything else as later handleTimingReqHit might turn // the packet in a response - ppHit->notify(pkt); + ppHit->notify(CacheAccessProbeArg(pkt,accessor)); if (prefetcher && blk && blk->wasPrefetched()) { DPRINTF(Cache, "Hit on prefetch for addr %#x (%s)\n", @@ -460,7 +461,7 @@ BaseCache::recvTimingReq(PacketPtr pkt) } else { handleTimingReqMiss(pkt, blk, forward_time, request_time); - ppMiss->notify(pkt); + ppMiss->notify(CacheAccessProbeArg(pkt,accessor)); } if (prefetcher) { @@ -557,7 +558,7 @@ BaseCache::recvTimingResp(PacketPtr pkt) writeAllocator->allocate() : mshr->allocOnFill(); blk = handleFill(pkt, blk, writebacks, allocate); assert(blk != nullptr); - ppFill->notify(pkt); + ppFill->notify(CacheAccessProbeArg(pkt, accessor)); } // Don't want to promote the Locked RMW Read until @@ -771,7 +772,9 @@ void BaseCache::updateBlockData(CacheBlk *blk, const PacketPtr cpkt, bool has_old_data) { - DataUpdate data_update(regenerateBlkAddr(blk), blk->isSecure()); + CacheDataUpdateProbeArg data_update( + regenerateBlkAddr(blk), blk->isSecure(), + blk->getSrcRequestorId(), accessor); if (ppDataUpdate->hasListeners()) { if (has_old_data) { data_update.oldData = std::vector(blk->data, @@ -788,6 +791,7 @@ BaseCache::updateBlockData(CacheBlk *blk, const PacketPtr cpkt, if (cpkt) { data_update.newData = std::vector(blk->data, blk->data + (blkSize / sizeof(uint64_t))); + data_update.hwPrefetched = blk->wasPrefetched(); } ppDataUpdate->notify(data_update); } @@ -809,7 +813,9 @@ BaseCache::cmpAndSwap(CacheBlk *blk, PacketPtr pkt) assert(sizeof(uint64_t) >= pkt->getSize()); // Get a copy of the old block's contents for the probe before the update - DataUpdate data_update(regenerateBlkAddr(blk), blk->isSecure()); + CacheDataUpdateProbeArg data_update( + regenerateBlkAddr(blk), blk->isSecure(), blk->getSrcRequestorId(), + accessor); if (ppDataUpdate->hasListeners()) { data_update.oldData = std::vector(blk->data, blk->data + (blkSize / sizeof(uint64_t))); @@ -1106,7 +1112,9 @@ BaseCache::satisfyRequest(PacketPtr pkt, CacheBlk *blk, bool, bool) if (pkt->isAtomicOp()) { // Get a copy of the old block's contents for the probe before // the update - DataUpdate data_update(regenerateBlkAddr(blk), blk->isSecure()); + CacheDataUpdateProbeArg data_update( + regenerateBlkAddr(blk), blk->isSecure(), + blk->getSrcRequestorId(), accessor); if (ppDataUpdate->hasListeners()) { data_update.oldData = std::vector(blk->data, blk->data + (blkSize / sizeof(uint64_t))); @@ -1125,6 +1133,7 @@ BaseCache::satisfyRequest(PacketPtr pkt, CacheBlk *blk, bool, bool) if (ppDataUpdate->hasListeners()) { data_update.newData = std::vector(blk->data, blk->data + (blkSize / sizeof(uint64_t))); + data_update.hwPrefetched = blk->wasPrefetched(); ppDataUpdate->notify(data_update); } @@ -2507,11 +2516,15 @@ BaseCache::CacheStats::regStats() void BaseCache::regProbePoints() { - ppHit = new ProbePointArg(this->getProbeManager(), "Hit"); - ppMiss = new ProbePointArg(this->getProbeManager(), "Miss"); - ppFill = new ProbePointArg(this->getProbeManager(), "Fill"); + ppHit = new ProbePointArg( + this->getProbeManager(), "Hit"); + ppMiss = new ProbePointArg( + this->getProbeManager(), "Miss"); + ppFill = new ProbePointArg( + this->getProbeManager(), "Fill"); ppDataUpdate = - new ProbePointArg(this->getProbeManager(), "Data Update"); + new ProbePointArg( + this->getProbeManager(), "Data Update"); } /////////////// diff --git a/src/mem/cache/base.hh b/src/mem/cache/base.hh index 8a06ec2c42..c2d9bc8a7b 100644 --- a/src/mem/cache/base.hh +++ b/src/mem/cache/base.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013, 2015-2016, 2018-2019 ARM Limited + * Copyright (c) 2012-2013, 2015-2016, 2018-2019, 2023 ARM Limited * All rights reserved. * * The license below extends only to copyright in the software and shall @@ -59,6 +59,7 @@ #include "debug/CachePort.hh" #include "enums/Clusivity.hh" #include "mem/cache/cache_blk.hh" +#include "mem/cache/cache_probe_arg.hh" #include "mem/cache/compressors/base.hh" #include "mem/cache/mshr_queue.hh" #include "mem/cache/tags/base.hh" @@ -115,28 +116,6 @@ class BaseCache : public ClockedObject NUM_BLOCKED_CAUSES }; - /** - * A data contents update is composed of the updated block's address, - * the old contents, and the new contents. - * @sa ppDataUpdate - */ - struct DataUpdate - { - /** The updated block's address. */ - Addr addr; - /** Whether the block belongs to the secure address space. */ - bool isSecure; - /** The stale data contents. If zero-sized this update is a fill. */ - std::vector oldData; - /** The new data contents. If zero-sized this is an invalidation. */ - std::vector newData; - - DataUpdate(Addr _addr, bool is_secure) - : addr(_addr), isSecure(is_secure), oldData(), newData() - { - } - }; - protected: /** @@ -336,6 +315,30 @@ class BaseCache : public ClockedObject protected: + struct CacheAccessorImpl : CacheAccessor + { + BaseCache &cache; + + CacheAccessorImpl(BaseCache &_cache) :cache(_cache) {} + + bool inCache(Addr addr, bool is_secure) const override + { return cache.inCache(addr, is_secure); } + + bool hasBeenPrefetched(Addr addr, bool is_secure) const override + { return cache.hasBeenPrefetched(addr, is_secure); } + + bool hasBeenPrefetched(Addr addr, bool is_secure, + RequestorID requestor) const override + { return cache.hasBeenPrefetched(addr, is_secure, requestor); } + + bool inMissQueue(Addr addr, bool is_secure) const override + { return cache.inMissQueue(addr, is_secure); } + + bool coalesce() const override + { return cache.coalesce(); } + + } accessor; + /** Miss status registers */ MSHRQueue mshrQueue; @@ -352,20 +355,20 @@ class BaseCache : public ClockedObject prefetch::Base *prefetcher; /** To probe when a cache hit occurs */ - ProbePointArg *ppHit; + ProbePointArg *ppHit; /** To probe when a cache miss occurs */ - ProbePointArg *ppMiss; + ProbePointArg *ppMiss; /** To probe when a cache fill occurs */ - ProbePointArg *ppFill; + ProbePointArg *ppFill; /** * To probe when the contents of a block are updated. Content updates * include data fills, overwrites, and invalidations, which means that * this probe partially overlaps with other probes. */ - ProbePointArg *ppDataUpdate; + ProbePointArg *ppDataUpdate; /** * The writeAllocator drive optimizations for streaming writes. @@ -1278,11 +1281,14 @@ class BaseCache : public ClockedObject bool hasBeenPrefetched(Addr addr, bool is_secure) const { CacheBlk *block = tags->findBlock(addr, is_secure); - if (block) { - return block->wasPrefetched(); - } else { - return false; - } + return block && block->wasPrefetched(); + } + + bool hasBeenPrefetched(Addr addr, bool is_secure, + RequestorID requestor) const { + CacheBlk *block = tags->findBlock(addr, is_secure); + return block && block->wasPrefetched() && + (block->getSrcRequestorId() == requestor); } bool inMissQueue(Addr addr, bool is_secure) const { diff --git a/src/mem/cache/cache.cc b/src/mem/cache/cache.cc index 24b3fe7219..3357d5e1b2 100644 --- a/src/mem/cache/cache.cc +++ b/src/mem/cache/cache.cc @@ -339,13 +339,25 @@ Cache::handleTimingReqMiss(PacketPtr pkt, CacheBlk *blk, Tick forward_time, if (pkt->isWrite()) { allocateWriteBuffer(pkt, forward_time); } else { - assert(pkt->isRead()); - // uncacheable accesses always allocate a new MSHR // Here we are using forward_time, modelling the latency of // a miss (outbound) just as forwardLatency, neglecting the // lookupLatency component. + + // Here we allow allocating miss buffer for read requests + // and x86's clflush requests. A clflush request should be + // propagate through all levels of the cache system. + + // Doing clflush in uncacheable regions might sound contradictory; + // however, it is entirely possible due to how the Linux kernel + // handle page property changes. When a linux kernel wants to + // change a page property, it flushes the related cache lines. The + // kernel might change the page property before flushing the cache + // lines. This results in the clflush might occur in an uncacheable + // region, where the kernel marks a region uncacheable before + // flushing. clflush results in a CleanInvalidReq. + assert(pkt->isRead() || pkt->isCleanInvalidateRequest()); allocateMissBuffer(pkt, forward_time); } diff --git a/src/mem/cache/cache_blk.hh b/src/mem/cache/cache_blk.hh index 775efbe673..e476ab639d 100644 --- a/src/mem/cache/cache_blk.hh +++ b/src/mem/cache/cache_blk.hh @@ -515,6 +515,7 @@ class TempCacheBlk final : public CacheBlk data = new uint8_t[size]; } TempCacheBlk(const TempCacheBlk&) = delete; + using CacheBlk::operator=; TempCacheBlk& operator=(const TempCacheBlk&) = delete; ~TempCacheBlk() { delete [] data; }; diff --git a/src/mem/cache/cache_probe_arg.hh b/src/mem/cache/cache_probe_arg.hh new file mode 100644 index 0000000000..27c7bc4041 --- /dev/null +++ b/src/mem/cache/cache_probe_arg.hh @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2023 ARM Limited + * All rights reserved. + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef __MEM_CACHE_PROBE_ARG_HH__ +#define __MEM_CACHE_PROBE_ARG_HH__ + +#include "mem/packet.hh" + +namespace gem5 +{ + +/** + * Provides generic cache lookup functions. A cache may provide + * a CacheAccessor object to other components that need to perform + * a lookup outside the normal cache control flow. Currently this + * is used by prefetchers that perform lookups when notified by + * cache events. + */ +struct CacheAccessor +{ + /** Determine if address is in cache */ + virtual bool inCache(Addr addr, bool is_secure) const = 0; + + /** Determine if address has been prefetched */ + virtual bool hasBeenPrefetched(Addr addr, bool is_secure) const = 0; + + /** Determine if address has been prefetched by the requestor */ + virtual bool hasBeenPrefetched(Addr addr, bool is_secure, + RequestorID requestor) const = 0; + + /** Determine if address is in cache miss queue */ + virtual bool inMissQueue(Addr addr, bool is_secure) const = 0; + + /** Determine if cache is coalescing writes */ + virtual bool coalesce() const = 0; +}; + +/** + * Information provided to probes on a cache event. + * @sa ppHit, ppMiss, ppFill in gem5::BaseCache (src/mem/cache/base.hh) + */ +class CacheAccessProbeArg +{ + public: + /** Packet that triggered the cache access*/ + PacketPtr pkt; + /** Accessor for the cache */ + CacheAccessor &cache; + + CacheAccessProbeArg(PacketPtr _pkt, CacheAccessor &_cache) + :pkt(_pkt), cache(_cache) + { + } +}; + +/** + * A data contents update is composed of the updated block's address, + * the old contents, and the new contents. + * @sa ppDataUpdate in gem5::BaseCache (src/mem/cache/base.hh) + */ +struct CacheDataUpdateProbeArg +{ + /** The updated block's address. */ + Addr addr; + /** Whether the block belongs to the secure address space. */ + bool isSecure; + /** Block original requestor */ + const RequestorID requestorID; + /** The stale data contents. If zero-sized this update is a fill. */ + std::vector oldData; + /** The new data contents. If zero-sized this is an invalidation. */ + std::vector newData; + /** Set if the update is from a prefetch or evicting a prefetched + * block that was never used. */ + bool hwPrefetched; + /** Accessor for the cache */ + CacheAccessor &accessor; + + CacheDataUpdateProbeArg(Addr _addr, bool is_secure, + RequestorID _requestorID, + CacheAccessor &_accessor) + : addr(_addr), isSecure(is_secure), requestorID(_requestorID), + oldData(), newData(), accessor(_accessor) + { + } +}; + +} // namespace gem5 + +#endif //__MEM_CACHE_PROBE_ARG_HH__ diff --git a/src/mem/cache/compressors/Compressors.py b/src/mem/cache/compressors/Compressors.py index eef5f77a18..8bd9a1a6a2 100644 --- a/src/mem/cache/compressors/Compressors.py +++ b/src/mem/cache/compressors/Compressors.py @@ -24,13 +24,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.IndexingPolicies import * +from m5.objects.ReplacementPolicies import * from m5.params import * from m5.proxy import * from m5.SimObject import * -from m5.objects.IndexingPolicies import * -from m5.objects.ReplacementPolicies import * - class BaseCacheCompressor(SimObject): type = "BaseCacheCompressor" diff --git a/src/mem/cache/compressors/base.cc b/src/mem/cache/compressors/base.cc index df3020dbf8..6ab2831fc9 100644 --- a/src/mem/cache/compressors/base.cc +++ b/src/mem/cache/compressors/base.cc @@ -78,7 +78,7 @@ Base::CompressionData::getSizeBits() const std::size_t Base::CompressionData::getSize() const { - return std::ceil(_size/8); + return std::ceil(_size/(float)CHAR_BIT); } Base::Base(const Params &p) diff --git a/src/mem/cache/compressors/frequent_values.hh b/src/mem/cache/compressors/frequent_values.hh index e7eac2644f..41103ce594 100644 --- a/src/mem/cache/compressors/frequent_values.hh +++ b/src/mem/cache/compressors/frequent_values.hh @@ -37,6 +37,7 @@ #include "base/sat_counter.hh" #include "base/types.hh" #include "mem/cache/base.hh" +#include "mem/cache/cache_probe_arg.hh" #include "mem/cache/compressors/base.hh" #include "mem/cache/compressors/encoders/huffman.hh" #include "mem/cache/prefetch/associative_set.hh" @@ -63,7 +64,7 @@ class FrequentValues : public Base private: class CompData; - using DataUpdate = BaseCache::DataUpdate; + using DataUpdate = CacheDataUpdateProbeArg; class FrequentValuesListener : public ProbeListenerArgBase { diff --git a/src/mem/cache/prefetch/Prefetcher.py b/src/mem/cache/prefetch/Prefetcher.py index a350319258..335d6f6942 100644 --- a/src/mem/cache/prefetch/Prefetcher.py +++ b/src/mem/cache/prefetch/Prefetcher.py @@ -1,4 +1,4 @@ -# Copyright (c) 2012, 2014, 2019 ARM Limited +# Copyright (c) 2012, 2014, 2019, 2023 ARM Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -36,16 +36,15 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import * -from m5.params import * -from m5.proxy import * - from m5.objects.ClockedObject import ClockedObject from m5.objects.IndexingPolicies import * from m5.objects.ReplacementPolicies import * +from m5.params import * +from m5.proxy import * +from m5.SimObject import * -class HWPProbeEvent(object): +class HWPProbeEvent: def __init__(self, prefetcher, obj, *listOfNames): self.obj = obj self.prefetcher = prefetcher @@ -76,11 +75,11 @@ class BasePrefetcher(ClockedObject): on_data = Param.Bool(True, "Notify prefetcher on data accesses") on_inst = Param.Bool(True, "Notify prefetcher on instruction accesses") prefetch_on_access = Param.Bool( - Parent.prefetch_on_access, + False, "Notify the hardware prefetcher on every access (not just misses)", ) prefetch_on_pf_hit = Param.Bool( - Parent.prefetch_on_pf_hit, + False, "Notify the hardware prefetcher on hit on prefetched lines", ) use_virtual_addresses = Param.Bool( diff --git a/src/mem/cache/prefetch/access_map_pattern_matching.cc b/src/mem/cache/prefetch/access_map_pattern_matching.cc index 989f3c6be1..f53b77868f 100644 --- a/src/mem/cache/prefetch/access_map_pattern_matching.cc +++ b/src/mem/cache/prefetch/access_map_pattern_matching.cc @@ -156,7 +156,8 @@ AccessMapPatternMatching::setEntryState(AccessMapEntry &entry, void AccessMapPatternMatching::calculatePrefetch(const Base::PrefetchInfo &pfi, - std::vector &addresses) + std::vector &addresses, + const CacheAccessor &cache) { assert(addresses.empty()); @@ -262,9 +263,10 @@ AMPM::AMPM(const AMPMPrefetcherParams &p) void AMPM::calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) + std::vector &addresses, + const CacheAccessor &cache) { - ampm.calculatePrefetch(pfi, addresses); + ampm.calculatePrefetch(pfi, addresses, cache); } } // namespace prefetch diff --git a/src/mem/cache/prefetch/access_map_pattern_matching.hh b/src/mem/cache/prefetch/access_map_pattern_matching.hh index 893d30dec2..b98a241b02 100644 --- a/src/mem/cache/prefetch/access_map_pattern_matching.hh +++ b/src/mem/cache/prefetch/access_map_pattern_matching.hh @@ -190,7 +190,8 @@ class AccessMapPatternMatching : public ClockedObject void startup() override; void calculatePrefetch(const Base::PrefetchInfo &pfi, - std::vector &addresses); + std::vector &addresses, + const CacheAccessor &cache); }; class AMPM : public Queued @@ -201,7 +202,8 @@ class AMPM : public Queued ~AMPM() = default; void calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) override; + std::vector &addresses, + const CacheAccessor &cache) override; }; } // namespace prefetch diff --git a/src/mem/cache/prefetch/base.cc b/src/mem/cache/prefetch/base.cc index e3e4b24cf2..f9d2624e7a 100644 --- a/src/mem/cache/prefetch/base.cc +++ b/src/mem/cache/prefetch/base.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 ARM Limited + * Copyright (c) 2013-2014, 2023 ARM Limited * All rights reserved. * * The license below extends only to copyright in the software and shall @@ -83,18 +83,26 @@ Base::PrefetchInfo::PrefetchInfo(PrefetchInfo const &pfi, Addr addr) } void -Base::PrefetchListener::notify(const PacketPtr &pkt) +Base::PrefetchListener::notify(const CacheAccessProbeArg &arg) { if (isFill) { - parent.notifyFill(pkt); + parent.notifyFill(arg); } else { - parent.probeNotify(pkt, miss); + parent.probeNotify(arg, miss); } } +void +Base::PrefetchEvictListener::notify(const EvictionInfo &info) +{ + if (info.newData.empty()) + parent.notifyEvict(info); +} + Base::Base(const BasePrefetcherParams &p) - : ClockedObject(p), listeners(), cache(nullptr), blkSize(p.block_size), - lBlkSize(floorLog2(blkSize)), onMiss(p.on_miss), onRead(p.on_read), + : ClockedObject(p), listeners(), system(nullptr), probeManager(nullptr), + blkSize(p.block_size), lBlkSize(floorLog2(blkSize)), + onMiss(p.on_miss), onRead(p.on_read), onWrite(p.on_write), onData(p.on_data), onInst(p.on_inst), requestorId(p.sys->getRequestorId(this)), pageBytes(p.page_bytes), @@ -107,13 +115,13 @@ Base::Base(const BasePrefetcherParams &p) } void -Base::setCache(BaseCache *_cache) +Base::setParentInfo(System *sys, ProbeManager *pm, unsigned blk_size) { - assert(!cache); - cache = _cache; - + assert(!system && !probeManager); + system = sys; + probeManager = pm; // If the cache has a different block size from the system's, save it - blkSize = cache->getBlockSize(); + blkSize = blk_size; lBlkSize = floorLog2(blkSize); } @@ -157,7 +165,7 @@ Base::StatGroup::StatGroup(statistics::Group *parent) } bool -Base::observeAccess(const PacketPtr &pkt, bool miss) const +Base::observeAccess(const PacketPtr &pkt, bool miss, bool prefetched) const { bool fetch = pkt->req->isInstFetch(); bool read = pkt->isRead(); @@ -165,7 +173,7 @@ Base::observeAccess(const PacketPtr &pkt, bool miss) const if (!miss) { if (prefetchOnPfHit) - return hasBeenPrefetched(pkt->getAddr(), pkt->isSecure()); + return prefetched; if (!prefetchOnAccess) return false; } @@ -184,24 +192,6 @@ Base::observeAccess(const PacketPtr &pkt, bool miss) const return true; } -bool -Base::inCache(Addr addr, bool is_secure) const -{ - return cache->inCache(addr, is_secure); -} - -bool -Base::inMissQueue(Addr addr, bool is_secure) const -{ - return cache->inMissQueue(addr, is_secure); -} - -bool -Base::hasBeenPrefetched(Addr addr, bool is_secure) const -{ - return cache->hasBeenPrefetched(addr, is_secure); -} - bool Base::samePage(Addr a, Addr b) const { @@ -239,18 +229,24 @@ Base::pageIthBlockAddress(Addr page, uint32_t blockIndex) const } void -Base::probeNotify(const PacketPtr &pkt, bool miss) +Base::probeNotify(const CacheAccessProbeArg &acc, bool miss) { + const PacketPtr pkt = acc.pkt; + const CacheAccessor &cache = acc.cache; + // Don't notify prefetcher on SWPrefetch, cache maintenance // operations or for writes that we are coaslescing. if (pkt->cmd.isSWPrefetch()) return; if (pkt->req->isCacheMaintenance()) return; - if (pkt->isWrite() && cache != nullptr && cache->coalesce()) return; + if (pkt->isWrite() && cache.coalesce()) return; if (!pkt->req->hasPaddr()) { panic("Request must have a physical address"); } - if (hasBeenPrefetched(pkt->getAddr(), pkt->isSecure())) { + bool has_been_prefetched = + acc.cache.hasBeenPrefetched(pkt->getAddr(), pkt->isSecure(), + requestorId); + if (has_been_prefetched) { usefulPrefetches += 1; prefetchStats.pfUseful++; if (miss) @@ -260,13 +256,13 @@ Base::probeNotify(const PacketPtr &pkt, bool miss) } // Verify this access type is observed by prefetcher - if (observeAccess(pkt, miss)) { + if (observeAccess(pkt, miss, has_been_prefetched)) { if (useVirtualAddresses && pkt->req->hasVaddr()) { PrefetchInfo pfi(pkt, pkt->req->getVaddr(), miss); - notify(pkt, pfi); + notify(acc, pfi); } else if (!useVirtualAddresses) { PrefetchInfo pfi(pkt, pkt->req->getPaddr(), miss); - notify(pkt, pfi); + notify(acc, pfi); } } } @@ -279,14 +275,15 @@ Base::regProbeListeners() * parent cache using the probe "Miss". Also connect to "Hit", if the * cache is configured to prefetch on accesses. */ - if (listeners.empty() && cache != nullptr) { - ProbeManager *pm(cache->getProbeManager()); - listeners.push_back(new PrefetchListener(*this, pm, "Miss", false, - true)); - listeners.push_back(new PrefetchListener(*this, pm, "Fill", true, - false)); - listeners.push_back(new PrefetchListener(*this, pm, "Hit", false, - false)); + if (listeners.empty() && probeManager != nullptr) { + listeners.push_back(new PrefetchListener(*this, probeManager, + "Miss", false, true)); + listeners.push_back(new PrefetchListener(*this, probeManager, + "Fill", true, false)); + listeners.push_back(new PrefetchListener(*this, probeManager, + "Hit", false, false)); + listeners.push_back(new PrefetchEvictListener(*this, probeManager, + "Data Update")); } } diff --git a/src/mem/cache/prefetch/base.hh b/src/mem/cache/prefetch/base.hh index 6bae73519c..f4d5cb051b 100644 --- a/src/mem/cache/prefetch/base.hh +++ b/src/mem/cache/prefetch/base.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 ARM Limited + * Copyright (c) 2013-2014, 2023 ARM Limited * All rights reserved. * * The license below extends only to copyright in the software and shall @@ -52,7 +52,7 @@ #include "base/compiler.hh" #include "base/statistics.hh" #include "base/types.hh" -#include "mem/cache/cache_blk.hh" +#include "mem/cache/cache_probe_arg.hh" #include "mem/packet.hh" #include "mem/request.hh" #include "sim/byteswap.hh" @@ -62,15 +62,16 @@ namespace gem5 { -class BaseCache; struct BasePrefetcherParams; +class ProbeManager; +class System; namespace prefetch { class Base : public ClockedObject { - class PrefetchListener : public ProbeListenerArgBase + class PrefetchListener : public ProbeListenerArgBase { public: PrefetchListener(Base &_parent, ProbeManager *pm, @@ -78,14 +79,27 @@ class Base : public ClockedObject bool _miss = false) : ProbeListenerArgBase(pm, name), parent(_parent), isFill(_isFill), miss(_miss) {} - void notify(const PacketPtr &pkt) override; + void notify(const CacheAccessProbeArg &arg) override; protected: Base &parent; const bool isFill; const bool miss; }; - std::vector listeners; + using EvictionInfo = CacheDataUpdateProbeArg; + + class PrefetchEvictListener : public ProbeListenerArgBase + { + public: + PrefetchEvictListener(Base &_parent, ProbeManager *pm, + const std::string &name) + : ProbeListenerArgBase(pm, name), parent(_parent) {} + void notify(const EvictionInfo &info) override; + protected: + Base &parent; + }; + + std::vector listeners; public: @@ -262,8 +276,11 @@ class Base : public ClockedObject // PARAMETERS - /** Pointr to the parent cache. */ - BaseCache* cache; + /** Pointer to the parent system. */ + System* system; + + /** Pointer to the parent cache's probe manager. */ + ProbeManager *probeManager; /** The block size of the parent cache. */ unsigned blkSize; @@ -304,16 +321,9 @@ class Base : public ClockedObject * Determine if this access should be observed * @param pkt The memory request causing the event * @param miss whether this event comes from a cache miss + * @param prefetched on a hit, this indicates the block was prefetched */ - bool observeAccess(const PacketPtr &pkt, bool miss) const; - - /** Determine if address is in cache */ - bool inCache(Addr addr, bool is_secure) const; - - /** Determine if address is in cache miss queue */ - bool inMissQueue(Addr addr, bool is_secure) const; - - bool hasBeenPrefetched(Addr addr, bool is_secure) const; + bool observeAccess(const PacketPtr &pkt, bool miss, bool prefetched) const; /** Determine if addresses are on the same page */ bool samePage(Addr a, Addr b) const; @@ -370,16 +380,22 @@ class Base : public ClockedObject Base(const BasePrefetcherParams &p); virtual ~Base() = default; - virtual void setCache(BaseCache *_cache); + virtual void + setParentInfo(System *sys, ProbeManager *pm, unsigned blk_size); /** * Notify prefetcher of cache access (may be any access or just * misses, depending on cache parameters.) */ - virtual void notify(const PacketPtr &pkt, const PrefetchInfo &pfi) = 0; + virtual void + notify(const CacheAccessProbeArg &acc, const PrefetchInfo &pfi) = 0; /** Notify prefetcher of cache fill */ - virtual void notifyFill(const PacketPtr &pkt) + virtual void notifyFill(const CacheAccessProbeArg &acc) + {} + + /** Notify prefetcher of cache eviction */ + virtual void notifyEvict(const EvictionInfo &info) {} virtual PacketPtr getPacket() = 0; @@ -423,10 +439,10 @@ class Base : public ClockedObject /** * Process a notification event from the ProbeListener. - * @param pkt The memory request causing the event + * @param acc probe arg encapsulating the memory request causing the event * @param miss whether this event comes from a cache miss */ - void probeNotify(const PacketPtr &pkt, bool miss); + void probeNotify(const CacheAccessProbeArg &acc, bool miss); /** * Add a SimObject and a probe name to listen events from diff --git a/src/mem/cache/prefetch/bop.cc b/src/mem/cache/prefetch/bop.cc index ce2502bee6..d50e9460d4 100644 --- a/src/mem/cache/prefetch/bop.cc +++ b/src/mem/cache/prefetch/bop.cc @@ -227,7 +227,8 @@ BOP::bestOffsetLearning(Addr x) void BOP::calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) + std::vector &addresses, + const CacheAccessor &cache) { Addr addr = pfi.getAddr(); Addr tag_x = tag(addr); @@ -252,8 +253,10 @@ BOP::calculatePrefetch(const PrefetchInfo &pfi, } void -BOP::notifyFill(const PacketPtr& pkt) +BOP::notifyFill(const CacheAccessProbeArg &arg) { + const PacketPtr& pkt = arg.pkt; + // Only insert into the RR right way if it's the pkt is a HWP if (!pkt->cmd.isHWPrefetch()) return; diff --git a/src/mem/cache/prefetch/bop.hh b/src/mem/cache/prefetch/bop.hh index bb1b05dfa9..09ae4ea72d 100644 --- a/src/mem/cache/prefetch/bop.hh +++ b/src/mem/cache/prefetch/bop.hh @@ -148,7 +148,7 @@ class BOP : public Queued void bestOffsetLearning(Addr); /** Update the RR right table after a prefetch fill */ - void notifyFill(const PacketPtr& pkt) override; + void notifyFill(const CacheAccessProbeArg &arg) override; public: @@ -156,7 +156,8 @@ class BOP : public Queued ~BOP() = default; void calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) override; + std::vector &addresses, + const CacheAccessor &cache) override; }; } // namespace prefetch diff --git a/src/mem/cache/prefetch/delta_correlating_prediction_tables.cc b/src/mem/cache/prefetch/delta_correlating_prediction_tables.cc index b59394ce25..ea59bea3c0 100644 --- a/src/mem/cache/prefetch/delta_correlating_prediction_tables.cc +++ b/src/mem/cache/prefetch/delta_correlating_prediction_tables.cc @@ -125,7 +125,8 @@ DeltaCorrelatingPredictionTables::DCPTEntry::getCandidates( void DeltaCorrelatingPredictionTables::calculatePrefetch( const Base::PrefetchInfo &pfi, - std::vector &addresses) + std::vector &addresses, + const CacheAccessor &cache) { if (!pfi.hasPC()) { DPRINTF(HWPrefetch, "Ignoring request with no PC.\n"); @@ -156,9 +157,10 @@ DCPT::DCPT(const DCPTPrefetcherParams &p) void DCPT::calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) + std::vector &addresses, + const CacheAccessor &cache) { - dcpt.calculatePrefetch(pfi, addresses); + dcpt.calculatePrefetch(pfi, addresses, cache); } } // namespace prefetch diff --git a/src/mem/cache/prefetch/delta_correlating_prediction_tables.hh b/src/mem/cache/prefetch/delta_correlating_prediction_tables.hh index 0218e9138a..7280c96733 100644 --- a/src/mem/cache/prefetch/delta_correlating_prediction_tables.hh +++ b/src/mem/cache/prefetch/delta_correlating_prediction_tables.hh @@ -114,9 +114,11 @@ class DeltaCorrelatingPredictionTables : public SimObject * Computes the prefetch candidates given a prefetch event. * @param pfi The prefetch event information * @param addresses prefetch candidates generated + * @param cache accessor for cache lookups */ void calculatePrefetch(const Base::PrefetchInfo &pfi, - std::vector &addresses); + std::vector &addresses, + const CacheAccessor &cache); }; @@ -130,7 +132,8 @@ class DCPT : public Queued ~DCPT() = default; void calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) override; + std::vector &addresses, + const CacheAccessor &cache) override; }; } // namespace prefetch diff --git a/src/mem/cache/prefetch/indirect_memory.cc b/src/mem/cache/prefetch/indirect_memory.cc index ab84ce25a2..780879bc6a 100644 --- a/src/mem/cache/prefetch/indirect_memory.cc +++ b/src/mem/cache/prefetch/indirect_memory.cc @@ -56,7 +56,8 @@ IndirectMemory::IndirectMemory(const IndirectMemoryPrefetcherParams &p) void IndirectMemory::calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) + std::vector &addresses, + const CacheAccessor &cache) { // This prefetcher requires a PC if (!pfi.hasPC()) { diff --git a/src/mem/cache/prefetch/indirect_memory.hh b/src/mem/cache/prefetch/indirect_memory.hh index da3e894cfa..877e55d63d 100644 --- a/src/mem/cache/prefetch/indirect_memory.hh +++ b/src/mem/cache/prefetch/indirect_memory.hh @@ -201,7 +201,8 @@ class IndirectMemory : public Queued ~IndirectMemory() = default; void calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) override; + std::vector &addresses, + const CacheAccessor &cache) override; }; } // namespace prefetch diff --git a/src/mem/cache/prefetch/irregular_stream_buffer.cc b/src/mem/cache/prefetch/irregular_stream_buffer.cc index ce30b41aa6..bf81ebedc5 100644 --- a/src/mem/cache/prefetch/irregular_stream_buffer.cc +++ b/src/mem/cache/prefetch/irregular_stream_buffer.cc @@ -66,7 +66,8 @@ IrregularStreamBuffer::IrregularStreamBuffer( void IrregularStreamBuffer::calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) + std::vector &addresses, + const CacheAccessor &cache) { // This prefetcher requires a PC if (!pfi.hasPC()) { diff --git a/src/mem/cache/prefetch/irregular_stream_buffer.hh b/src/mem/cache/prefetch/irregular_stream_buffer.hh index 39373010bb..399268a5a4 100644 --- a/src/mem/cache/prefetch/irregular_stream_buffer.hh +++ b/src/mem/cache/prefetch/irregular_stream_buffer.hh @@ -137,7 +137,8 @@ class IrregularStreamBuffer : public Queued ~IrregularStreamBuffer() = default; void calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) override; + std::vector &addresses, + const CacheAccessor &cache) override; }; } // namespace prefetch diff --git a/src/mem/cache/prefetch/multi.cc b/src/mem/cache/prefetch/multi.cc index 1f7298f354..9cd40163e2 100644 --- a/src/mem/cache/prefetch/multi.cc +++ b/src/mem/cache/prefetch/multi.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019 ARM Limited + * Copyright (c) 2014, 2019, 2023 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -53,10 +53,10 @@ Multi::Multi(const MultiPrefetcherParams &p) } void -Multi::setCache(BaseCache *_cache) +Multi::setParentInfo(System *sys, ProbeManager *pm, unsigned blk_size) { for (auto pf : prefetchers) - pf->setCache(_cache); + pf->setParentInfo(sys, pm, blk_size); } Tick diff --git a/src/mem/cache/prefetch/multi.hh b/src/mem/cache/prefetch/multi.hh index 7890f090b5..74cdaec984 100644 --- a/src/mem/cache/prefetch/multi.hh +++ b/src/mem/cache/prefetch/multi.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019 ARM Limited + * Copyright (c) 2014, 2019, 2023 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -56,7 +56,8 @@ class Multi : public Base Multi(const MultiPrefetcherParams &p); public: - void setCache(BaseCache *_cache) override; + void + setParentInfo(System *sys, ProbeManager *pm, unsigned blk_size) override; PacketPtr getPacket() override; Tick nextPrefetchReadyTime() const override; @@ -65,8 +66,11 @@ class Multi : public Base * Ignore notifications since each sub-prefetcher already gets a * notification through their probes-based interface. */ - void notify(const PacketPtr &pkt, const PrefetchInfo &pfi) override {}; - void notifyFill(const PacketPtr &pkt) override {}; + void + notify(const CacheAccessProbeArg &arg, const PrefetchInfo &pfi) override + {}; + + void notifyFill(const CacheAccessProbeArg &arg) override {}; /** @} */ protected: diff --git a/src/mem/cache/prefetch/pif.cc b/src/mem/cache/prefetch/pif.cc index 79e8e6d747..581831491f 100644 --- a/src/mem/cache/prefetch/pif.cc +++ b/src/mem/cache/prefetch/pif.cc @@ -198,7 +198,8 @@ PIF::notifyRetiredInst(const Addr pc) void PIF::calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) + std::vector &addresses, + const CacheAccessor &cache) { if (!pfi.hasPC()) { return; diff --git a/src/mem/cache/prefetch/pif.hh b/src/mem/cache/prefetch/pif.hh index 296087e8e0..ecb97db78d 100644 --- a/src/mem/cache/prefetch/pif.hh +++ b/src/mem/cache/prefetch/pif.hh @@ -182,7 +182,8 @@ class PIF : public Queued ~PIF() = default; void calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses); + std::vector &addresses, + const CacheAccessor &cache); /** * Add a SimObject and a probe name to monitor the retired instructions diff --git a/src/mem/cache/prefetch/queued.cc b/src/mem/cache/prefetch/queued.cc index 1ab34d2e9b..e22d6f5eb8 100644 --- a/src/mem/cache/prefetch/queued.cc +++ b/src/mem/cache/prefetch/queued.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015 ARM Limited + * Copyright (c) 2014-2015, 2023 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -94,7 +94,7 @@ Queued::DeferredPacket::finish(const Fault &fault, assert(ongoingTranslation); ongoingTranslation = false; bool failed = (fault != NoFault); - owner->translationComplete(this, failed); + owner->translationComplete(this, failed, *cache); } Queued::Queued(const QueuedPrefetcherParams &p) @@ -169,10 +169,12 @@ Queued::getMaxPermittedPrefetches(size_t total) const } void -Queued::notify(const PacketPtr &pkt, const PrefetchInfo &pfi) +Queued::notify(const CacheAccessProbeArg &acc, const PrefetchInfo &pfi) { Addr blk_addr = blockAddress(pfi.getAddr()); bool is_secure = pfi.isSecure(); + const PacketPtr pkt = acc.pkt; + const CacheAccessor &cache = acc.cache; // Squash queued prefetches if demand miss to same line if (queueSquash) { @@ -195,7 +197,7 @@ Queued::notify(const PacketPtr &pkt, const PrefetchInfo &pfi) // Calculate prefetches given this access std::vector addresses; - calculatePrefetch(pfi, addresses); + calculatePrefetch(pfi, addresses, cache); // Get the maximu number of prefetches that we are allowed to generate size_t max_pfs = getMaxPermittedPrefetches(addresses.size()); @@ -210,7 +212,7 @@ Queued::notify(const PacketPtr &pkt, const PrefetchInfo &pfi) if (!samePage(addr_prio.first, pfi.getAddr())) { statsQueued.pfSpanPage += 1; - if (hasBeenPrefetched(pkt->getAddr(), pkt->isSecure())) { + if (cache.hasBeenPrefetched(pkt->getAddr(), pkt->isSecure())) { statsQueued.pfUsefulSpanPage += 1; } } @@ -222,7 +224,7 @@ Queued::notify(const PacketPtr &pkt, const PrefetchInfo &pfi) DPRINTF(HWPrefetch, "Found a pf candidate addr: %#x, " "inserting into prefetch queue.\n", new_pfi.getAddr()); // Create and insert the request - insert(pkt, new_pfi, addr_prio.second); + insert(pkt, new_pfi, addr_prio.second, cache); num_pfs += 1; if (num_pfs == max_pfs) { break; @@ -298,7 +300,8 @@ Queued::processMissingTranslations(unsigned max) } void -Queued::translationComplete(DeferredPacket *dp, bool failed) +Queued::translationComplete(DeferredPacket *dp, bool failed, + const CacheAccessor &cache) { auto it = pfqMissingTranslation.begin(); while (it != pfqMissingTranslation.end()) { @@ -315,8 +318,9 @@ Queued::translationComplete(DeferredPacket *dp, bool failed) it->translationRequest->getPaddr()); Addr target_paddr = it->translationRequest->getPaddr(); // check if this prefetch is already redundant - if (cacheSnoop && (inCache(target_paddr, it->pfInfo.isSecure()) || - inMissQueue(target_paddr, it->pfInfo.isSecure()))) { + if (cacheSnoop && + (cache.inCache(target_paddr, it->pfInfo.isSecure()) || + cache.inMissQueue(target_paddr, it->pfInfo.isSecure()))) { statsQueued.pfInCache++; DPRINTF(HWPrefetch, "Dropping redundant in " "cache/MSHR prefetch addr:%#x\n", target_paddr); @@ -382,7 +386,7 @@ Queued::createPrefetchRequest(Addr addr, PrefetchInfo const &pfi, void Queued::insert(const PacketPtr &pkt, PrefetchInfo &new_pfi, - int32_t priority) + int32_t priority, const CacheAccessor &cache) { if (queueFilter) { if (alreadyInQueue(pfq, new_pfi, priority)) { @@ -451,8 +455,8 @@ Queued::insert(const PacketPtr &pkt, PrefetchInfo &new_pfi, } } if (has_target_pa && cacheSnoop && - (inCache(target_paddr, new_pfi.isSecure()) || - inMissQueue(target_paddr, new_pfi.isSecure()))) { + (cache.inCache(target_paddr, new_pfi.isSecure()) || + cache.inMissQueue(target_paddr, new_pfi.isSecure()))) { statsQueued.pfInCache++; DPRINTF(HWPrefetch, "Dropping redundant in " "cache/MSHR prefetch addr:%#x\n", target_paddr); @@ -460,7 +464,7 @@ Queued::insert(const PacketPtr &pkt, PrefetchInfo &new_pfi, } /* Create the packet and find the spot to insert it */ - DeferredPacket dpp(this, new_pfi, 0, priority); + DeferredPacket dpp(this, new_pfi, 0, priority, cache); if (has_target_pa) { Tick pf_time = curTick() + clockPeriod() * latency; dpp.createPkt(target_paddr, blkSize, requestorId, tagPrefetch, @@ -472,7 +476,7 @@ Queued::insert(const PacketPtr &pkt, PrefetchInfo &new_pfi, } else { // Add the translation request and try to resolve it later dpp.setTranslationRequest(translation_req); - dpp.tc = cache->system->threads[translation_req->contextId()]; + dpp.tc = system->threads[translation_req->contextId()]; DPRINTF(HWPrefetch, "Prefetch queued with no translation. " "addr:%#x priority: %3d\n", new_pfi.getAddr(), priority); addToQueue(pfqMissingTranslation, dpp); diff --git a/src/mem/cache/prefetch/queued.hh b/src/mem/cache/prefetch/queued.hh index 1d1a3faef4..f3620bd826 100644 --- a/src/mem/cache/prefetch/queued.hh +++ b/src/mem/cache/prefetch/queued.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015 ARM Limited + * Copyright (c) 2014-2015, 2023 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -75,6 +75,7 @@ class Queued : public Base RequestPtr translationRequest; ThreadContext *tc; bool ongoingTranslation; + const CacheAccessor *cache; /** * Constructor @@ -85,9 +86,10 @@ class Queued : public Base * @param prio This prefetch priority */ DeferredPacket(Queued *o, PrefetchInfo const &pfi, Tick t, - int32_t prio) : owner(o), pfInfo(pfi), tick(t), pkt(nullptr), + int32_t prio, const CacheAccessor &_cache) + : owner(o), pfInfo(pfi), tick(t), pkt(nullptr), priority(prio), translationRequest(), tc(nullptr), - ongoingTranslation(false) { + ongoingTranslation(false), cache(&_cache) { } bool operator>(const DeferredPacket& that) const @@ -192,12 +194,15 @@ class Queued : public Base Queued(const QueuedPrefetcherParams &p); virtual ~Queued(); - void notify(const PacketPtr &pkt, const PrefetchInfo &pfi) override; + void + notify(const CacheAccessProbeArg &acc, const PrefetchInfo &pfi) override; - void insert(const PacketPtr &pkt, PrefetchInfo &new_pfi, int32_t priority); + void insert(const PacketPtr &pkt, PrefetchInfo &new_pfi, int32_t priority, + const CacheAccessor &cache); virtual void calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) = 0; + std::vector &addresses, + const CacheAccessor &cache) = 0; PacketPtr getPacket() override; Tick nextPrefetchReadyTime() const override @@ -231,8 +236,10 @@ class Queued : public Base * new prefetch request. * @param dp the deferred packet that has completed the translation request * @param failed whether the translation was successful + * @param cache accessor for lookups on the cache that originated this pkt */ - void translationComplete(DeferredPacket *dp, bool failed); + void translationComplete(DeferredPacket *dp, bool failed, + const CacheAccessor &cache); /** * Checks whether the specified prefetch request is already in the diff --git a/src/mem/cache/prefetch/sbooe.cc b/src/mem/cache/prefetch/sbooe.cc index 44a10c232d..7f4c5ee04d 100644 --- a/src/mem/cache/prefetch/sbooe.cc +++ b/src/mem/cache/prefetch/sbooe.cc @@ -91,8 +91,10 @@ SBOOE::access(Addr access_line) } void -SBOOE::notifyFill(const PacketPtr& pkt) +SBOOE::notifyFill(const CacheAccessProbeArg &arg) { + const PacketPtr& pkt = arg.pkt; + // (1) Look for the address in the demands list // (2) Calculate the elapsed cycles until it was filled (curTick) // (3) Insert the latency into the latency buffer (FIFO) @@ -117,7 +119,8 @@ SBOOE::notifyFill(const PacketPtr& pkt) void SBOOE::calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) + std::vector &addresses, + const CacheAccessor &cache) { const Addr pfi_addr = pfi.getAddr(); const Addr pfi_line = pfi_addr >> lBlkSize; diff --git a/src/mem/cache/prefetch/sbooe.hh b/src/mem/cache/prefetch/sbooe.hh index 7914b88f45..26384923cf 100644 --- a/src/mem/cache/prefetch/sbooe.hh +++ b/src/mem/cache/prefetch/sbooe.hh @@ -153,13 +153,14 @@ class SBOOE : public Queued bool access(Addr line); /** Update the latency buffer after a prefetch fill */ - void notifyFill(const PacketPtr& pkt) override; + void notifyFill(const CacheAccessProbeArg &arg) override; public: SBOOE(const SBOOEPrefetcherParams &p); void calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) override; + std::vector &addresses, + const CacheAccessor &cache) override; }; } // namespace prefetch diff --git a/src/mem/cache/prefetch/signature_path.cc b/src/mem/cache/prefetch/signature_path.cc index a36ef809ce..6d3f05bf17 100644 --- a/src/mem/cache/prefetch/signature_path.cc +++ b/src/mem/cache/prefetch/signature_path.cc @@ -227,7 +227,8 @@ SignaturePath::calculateLookaheadConfidence(PatternEntry const &sig, void SignaturePath::calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) + std::vector &addresses, + const CacheAccessor &cache) { Addr request_addr = pfi.getAddr(); Addr ppn = request_addr / pageBytes; diff --git a/src/mem/cache/prefetch/signature_path.hh b/src/mem/cache/prefetch/signature_path.hh index 9613fe0886..a561cda063 100644 --- a/src/mem/cache/prefetch/signature_path.hh +++ b/src/mem/cache/prefetch/signature_path.hh @@ -287,7 +287,8 @@ class SignaturePath : public Queued ~SignaturePath() = default; void calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) override; + std::vector &addresses, + const CacheAccessor &cache) override; }; } // namespace prefetch diff --git a/src/mem/cache/prefetch/slim_ampm.cc b/src/mem/cache/prefetch/slim_ampm.cc index 950994a4bd..8717d0314b 100644 --- a/src/mem/cache/prefetch/slim_ampm.cc +++ b/src/mem/cache/prefetch/slim_ampm.cc @@ -43,11 +43,12 @@ SlimAMPM::SlimAMPM(const SlimAMPMPrefetcherParams &p) void SlimAMPM::calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) + std::vector &addresses, + const CacheAccessor &cache) { - dcpt.calculatePrefetch(pfi, addresses); + dcpt.calculatePrefetch(pfi, addresses, cache); if (addresses.empty()) { - ampm.calculatePrefetch(pfi, addresses); + ampm.calculatePrefetch(pfi, addresses, cache); } } diff --git a/src/mem/cache/prefetch/slim_ampm.hh b/src/mem/cache/prefetch/slim_ampm.hh index 54f38d4885..04ef6ca921 100644 --- a/src/mem/cache/prefetch/slim_ampm.hh +++ b/src/mem/cache/prefetch/slim_ampm.hh @@ -62,7 +62,8 @@ class SlimAMPM : public Queued ~SlimAMPM() = default; void calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) override; + std::vector &addresses, + const CacheAccessor &cache) override; }; } // namespace prefetch diff --git a/src/mem/cache/prefetch/spatio_temporal_memory_streaming.cc b/src/mem/cache/prefetch/spatio_temporal_memory_streaming.cc index 0e3211579c..406e444be0 100644 --- a/src/mem/cache/prefetch/spatio_temporal_memory_streaming.cc +++ b/src/mem/cache/prefetch/spatio_temporal_memory_streaming.cc @@ -63,7 +63,7 @@ STeMS::STeMS(const STeMSPrefetcherParams &p) } void -STeMS::checkForActiveGenerationsEnd() +STeMS::checkForActiveGenerationsEnd(const CacheAccessor &cache) { // This prefetcher operates attached to the L1 and it observes all // accesses, this guarantees that no evictions are missed @@ -79,8 +79,8 @@ STeMS::checkForActiveGenerationsEnd() if (seq_entry.counter > 0) { Addr cache_addr = agt_entry.paddress + seq_entry.offset * blkSize; - if (!inCache(cache_addr, sr_is_secure) && - !inMissQueue(cache_addr, sr_is_secure)) { + if (!cache.inCache(cache_addr, sr_is_secure) && + !cache.inMissQueue(cache_addr, sr_is_secure)) { generation_ended = true; pst_addr = (agt_entry.pc << spatialRegionSizeBits) + seq_entry.offset; @@ -135,7 +135,8 @@ STeMS::addToRMOB(Addr sr_addr, Addr pst_addr, unsigned int delta) void STeMS::calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) + std::vector &addresses, + const CacheAccessor &cache) { if (!pfi.hasPC()) { DPRINTF(HWPrefetch, "Ignoring request with no PC.\n"); @@ -152,7 +153,7 @@ STeMS::calculatePrefetch(const PrefetchInfo &pfi, Addr sr_offset = (pfi.getAddr() % spatialRegionSize) / blkSize; // Check if any active generation has ended - checkForActiveGenerationsEnd(); + checkForActiveGenerationsEnd(cache); ActiveGenerationTableEntry *agt_entry = activeGenerationTable.findEntry(sr_addr, is_secure); diff --git a/src/mem/cache/prefetch/spatio_temporal_memory_streaming.hh b/src/mem/cache/prefetch/spatio_temporal_memory_streaming.hh index cdd2788104..c6cd2f72d1 100644 --- a/src/mem/cache/prefetch/spatio_temporal_memory_streaming.hh +++ b/src/mem/cache/prefetch/spatio_temporal_memory_streaming.hh @@ -181,7 +181,7 @@ class STeMS : public Queued unsigned int lastTriggerCounter; /** Checks if the active generations have ended */ - void checkForActiveGenerationsEnd(); + void checkForActiveGenerationsEnd(const CacheAccessor &cache); /** * Adds an entry to the RMOB * @param sr_addr Spatial region address @@ -206,7 +206,8 @@ class STeMS : public Queued ~STeMS() = default; void calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) override; + std::vector &addresses, + const CacheAccessor &cache) override; }; } // namespace prefetch diff --git a/src/mem/cache/prefetch/stride.cc b/src/mem/cache/prefetch/stride.cc index 0a77b28a1c..b2e4702f20 100644 --- a/src/mem/cache/prefetch/stride.cc +++ b/src/mem/cache/prefetch/stride.cc @@ -118,7 +118,8 @@ Stride::allocateNewContext(int context) void Stride::calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) + std::vector &addresses, + const CacheAccessor &cache) { if (!pfi.hasPC()) { DPRINTF(HWPrefetch, "Ignoring request with no PC.\n"); diff --git a/src/mem/cache/prefetch/stride.hh b/src/mem/cache/prefetch/stride.hh index 7e55abea21..41cadbe7d0 100644 --- a/src/mem/cache/prefetch/stride.hh +++ b/src/mem/cache/prefetch/stride.hh @@ -160,7 +160,8 @@ class Stride : public Queued Stride(const StridePrefetcherParams &p); void calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) override; + std::vector &addresses, + const CacheAccessor &cache) override; }; } // namespace prefetch diff --git a/src/mem/cache/prefetch/tagged.cc b/src/mem/cache/prefetch/tagged.cc index 0d4d79b006..e34e1a692d 100644 --- a/src/mem/cache/prefetch/tagged.cc +++ b/src/mem/cache/prefetch/tagged.cc @@ -49,7 +49,8 @@ Tagged::Tagged(const TaggedPrefetcherParams &p) void Tagged::calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) + std::vector &addresses, + const CacheAccessor &cache) { Addr blkAddr = blockAddress(pfi.getAddr()); diff --git a/src/mem/cache/prefetch/tagged.hh b/src/mem/cache/prefetch/tagged.hh index 5c91f654b1..1ed11e9355 100644 --- a/src/mem/cache/prefetch/tagged.hh +++ b/src/mem/cache/prefetch/tagged.hh @@ -55,7 +55,8 @@ class Tagged : public Queued ~Tagged() = default; void calculatePrefetch(const PrefetchInfo &pfi, - std::vector &addresses) override; + std::vector &addresses, + const CacheAccessor &cache) override; }; } // namespace prefetch diff --git a/src/mem/cache/tags/Tags.py b/src/mem/cache/tags/Tags.py index ade187fa39..2717373829 100644 --- a/src/mem/cache/tags/Tags.py +++ b/src/mem/cache/tags/Tags.py @@ -33,10 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * from m5.objects.ClockedObject import ClockedObject from m5.objects.IndexingPolicies import * +from m5.params import * +from m5.proxy import * class BaseTags(ClockedObject): diff --git a/src/mem/cache/tags/base.cc b/src/mem/cache/tags/base.cc index 560b041e45..8216f3dfe8 100644 --- a/src/mem/cache/tags/base.cc +++ b/src/mem/cache/tags/base.cc @@ -215,6 +215,15 @@ BaseTags::print() return str; } +void +BaseTags::forEachBlk(std::function visitor) +{ + anyBlk([visitor](CacheBlk &blk) { + visitor(blk); + return false; + }); +} + BaseTags::BaseTagStats::BaseTagStats(BaseTags &_tags) : statistics::Group(&_tags), tags(_tags), diff --git a/src/mem/cache/tags/base.hh b/src/mem/cache/tags/base.hh index e2702778b8..c49188151c 100644 --- a/src/mem/cache/tags/base.hh +++ b/src/mem/cache/tags/base.hh @@ -336,7 +336,7 @@ class BaseTags : public ClockedObject * * @param visitor Visitor to call on each block. */ - virtual void forEachBlk(std::function visitor) = 0; + void forEachBlk(std::function visitor); /** * Find if any of the blocks satisfies a condition diff --git a/src/mem/cache/tags/base_set_assoc.hh b/src/mem/cache/tags/base_set_assoc.hh index 22695d2010..8ffb7189b7 100644 --- a/src/mem/cache/tags/base_set_assoc.hh +++ b/src/mem/cache/tags/base_set_assoc.hh @@ -233,12 +233,6 @@ class BaseSetAssoc : public BaseTags return indexingPolicy->regenerateAddr(blk->getTag(), blk); } - void forEachBlk(std::function visitor) override { - for (CacheBlk& blk : blks) { - visitor(blk); - } - } - bool anyBlk(std::function visitor) override { for (CacheBlk& blk : blks) { if (visitor(blk)) { diff --git a/src/mem/cache/tags/compressed_tags.cc b/src/mem/cache/tags/compressed_tags.cc index 32d7401550..c84718f5f6 100644 --- a/src/mem/cache/tags/compressed_tags.cc +++ b/src/mem/cache/tags/compressed_tags.cc @@ -163,14 +163,6 @@ CompressedTags::findVictim(Addr addr, const bool is_secure, return victim; } -void -CompressedTags::forEachBlk(std::function visitor) -{ - for (CompressionBlk& blk : blks) { - visitor(blk); - } -} - bool CompressedTags::anyBlk(std::function visitor) { diff --git a/src/mem/cache/tags/compressed_tags.hh b/src/mem/cache/tags/compressed_tags.hh index b54efb05d4..6e5b62d3e8 100644 --- a/src/mem/cache/tags/compressed_tags.hh +++ b/src/mem/cache/tags/compressed_tags.hh @@ -108,16 +108,6 @@ class CompressedTags : public SectorTags const std::size_t compressed_size, std::vector& evict_blks) override; - /** - * Visit each sub-block in the tags and apply a visitor. - * - * The visitor should be a std::function that takes a cache block. - * reference as its parameter. - * - * @param visitor Visitor to call on each block. - */ - void forEachBlk(std::function visitor) override; - /** * Find if any of the sub-blocks satisfies a condition. * diff --git a/src/mem/cache/tags/fa_lru.hh b/src/mem/cache/tags/fa_lru.hh index deffd72015..dba89f809d 100644 --- a/src/mem/cache/tags/fa_lru.hh +++ b/src/mem/cache/tags/fa_lru.hh @@ -85,6 +85,7 @@ class FALRUBlk : public CacheBlk { public: FALRUBlk() : CacheBlk(), prev(nullptr), next(nullptr), inCachesMask(0) {} + using CacheBlk::operator=; /** The previous block in LRU order. */ FALRUBlk *prev; @@ -253,12 +254,6 @@ class FALRU : public BaseTags return blk->getTag(); } - void forEachBlk(std::function visitor) override { - for (int i = 0; i < numBlocks; i++) { - visitor(blks[i]); - } - } - bool anyBlk(std::function visitor) override { for (int i = 0; i < numBlocks; i++) { if (visitor(blks[i])) { diff --git a/src/mem/cache/tags/sector_blk.hh b/src/mem/cache/tags/sector_blk.hh index dae8741e39..fbfea64d93 100644 --- a/src/mem/cache/tags/sector_blk.hh +++ b/src/mem/cache/tags/sector_blk.hh @@ -64,6 +64,7 @@ class SectorSubBlk : public CacheBlk public: SectorSubBlk() : CacheBlk(), _sectorBlk(nullptr), _sectorOffset(0) {} SectorSubBlk(const SectorSubBlk&) = delete; + using CacheBlk::operator=; SectorSubBlk& operator=(const SectorSubBlk&) = delete; SectorSubBlk(SectorSubBlk&&) = delete; /** diff --git a/src/mem/cache/tags/sector_tags.cc b/src/mem/cache/tags/sector_tags.cc index cb121ebd9a..6a9ffd02ed 100644 --- a/src/mem/cache/tags/sector_tags.cc +++ b/src/mem/cache/tags/sector_tags.cc @@ -359,14 +359,6 @@ SectorTags::SectorTagsStats::regStats() } } -void -SectorTags::forEachBlk(std::function visitor) -{ - for (SectorSubBlk& blk : blks) { - visitor(blk); - } -} - bool SectorTags::anyBlk(std::function visitor) { diff --git a/src/mem/cache/tags/sector_tags.hh b/src/mem/cache/tags/sector_tags.hh index bad132158c..035b085962 100644 --- a/src/mem/cache/tags/sector_tags.hh +++ b/src/mem/cache/tags/sector_tags.hh @@ -193,16 +193,6 @@ class SectorTags : public BaseTags */ Addr regenerateBlkAddr(const CacheBlk* blk) const override; - /** - * Visit each sub-block in the tags and apply a visitor. - * - * The visitor should be a std::function that takes a cache block. - * reference as its parameter. - * - * @param visitor Visitor to call on each block. - */ - void forEachBlk(std::function visitor) override; - /** * Find if any of the sub-blocks satisfies a condition. * diff --git a/src/mem/coherent_xbar.cc b/src/mem/coherent_xbar.cc index 8163299a09..74ef1ead36 100644 --- a/src/mem/coherent_xbar.cc +++ b/src/mem/coherent_xbar.cc @@ -159,7 +159,7 @@ CoherentXBar::recvTimingReq(PacketPtr pkt, PortID cpu_side_port_id) assert(is_express_snoop == cache_responding); // determine the destination based on the destination address range - PortID mem_side_port_id = findPort(pkt->getAddrRange()); + PortID mem_side_port_id = findPort(pkt); // test if the crossbar should be considered occupied for the current // port, and exclude express snoops from the check @@ -563,7 +563,7 @@ CoherentXBar::recvTimingSnoopReq(PacketPtr pkt, PortID mem_side_port_id) // device responsible for the address range something is // wrong, hence there is nothing further to do as the packet // would be going back to where it came from - assert(findPort(pkt->getAddrRange()) == mem_side_port_id); + assert(findPort(pkt) == mem_side_port_id); } bool @@ -799,7 +799,7 @@ CoherentXBar::recvAtomicBackdoor(PacketPtr pkt, PortID cpu_side_port_id, // even if we had a snoop response, we must continue and also // perform the actual request at the destination - PortID mem_side_port_id = findPort(pkt->getAddrRange()); + PortID mem_side_port_id = findPort(pkt); if (sink_packet) { DPRINTF(CoherentXBar, "%s: Not forwarding %s\n", __func__, @@ -1035,7 +1035,7 @@ CoherentXBar::recvFunctional(PacketPtr pkt, PortID cpu_side_port_id) } } - PortID dest_id = findPort(pkt->getAddrRange()); + PortID dest_id = findPort(pkt); memSidePorts[dest_id]->sendFunctional(pkt); } diff --git a/src/mem/dram_interface.cc b/src/mem/dram_interface.cc index 65e06db4d3..fca3282f27 100644 --- a/src/mem/dram_interface.cc +++ b/src/mem/dram_interface.cc @@ -579,7 +579,7 @@ DRAMInterface::doBurstAccess(MemPacket* mem_pkt, Tick next_burst_at, stats.readBursts++; if (row_hit) stats.readRowHits++; - stats.bytesRead += burstSize; + stats.dramBytesRead += burstSize; stats.perBankRdBursts[mem_pkt->bankId]++; // Update latency stats @@ -608,7 +608,7 @@ DRAMInterface::doBurstAccess(MemPacket* mem_pkt, Tick next_burst_at, stats.writeBursts++; if (row_hit) stats.writeRowHits++; - stats.bytesWritten += burstSize; + stats.dramBytesWritten += burstSize; stats.perBankWrBursts[mem_pkt->bankId]++; } @@ -1885,9 +1885,9 @@ DRAMInterface::DRAMStats::DRAMStats(DRAMInterface &_dram) ADD_STAT(bytesPerActivate, statistics::units::Byte::get(), "Bytes accessed per row activation"), - ADD_STAT(bytesRead, statistics::units::Byte::get(), + ADD_STAT(dramBytesRead, statistics::units::Byte::get(), "Total bytes read"), - ADD_STAT(bytesWritten, statistics::units::Byte::get(), + ADD_STAT(dramBytesWritten, statistics::units::Byte::get(), "Total bytes written"), ADD_STAT(avgRdBW, statistics::units::Rate< @@ -1948,8 +1948,8 @@ DRAMInterface::DRAMStats::regStats() readRowHitRate = (readRowHits / readBursts) * 100; writeRowHitRate = (writeRowHits / writeBursts) * 100; - avgRdBW = (bytesRead / 1000000) / simSeconds; - avgWrBW = (bytesWritten / 1000000) / simSeconds; + avgRdBW = (dramBytesRead / 1000000) / simSeconds; + avgWrBW = (dramBytesWritten / 1000000) / simSeconds; peakBW = (sim_clock::Frequency / dram.burstDelay()) * dram.bytesPerBurst() / 1000000; diff --git a/src/mem/dram_interface.hh b/src/mem/dram_interface.hh index e20e33faf9..2070a1773b 100644 --- a/src/mem/dram_interface.hh +++ b/src/mem/dram_interface.hh @@ -610,8 +610,8 @@ class DRAMInterface : public MemInterface statistics::Formula writeRowHitRate; statistics::Histogram bytesPerActivate; // Number of bytes transferred to/from DRAM - statistics::Scalar bytesRead; - statistics::Scalar bytesWritten; + statistics::Scalar dramBytesRead; + statistics::Scalar dramBytesWritten; // Average bandwidth statistics::Formula avgRdBW; diff --git a/src/mem/dramsys.cc b/src/mem/dramsys.cc new file mode 100644 index 0000000000..68fe983d30 --- /dev/null +++ b/src/mem/dramsys.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2023 Fraunhofer IESE + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dramsys.hh" + +namespace gem5 +{ + +namespace memory +{ + +DRAMSys::DRAMSys(Params const& params) : + AbstractMemory(params), + tlmWrapper(dramSysWrapper.tSocket, params.name + ".tlm", InvalidPortID), + config(::DRAMSys::Config::from_path(params.configuration, + params.resource_directory)), + dramSysWrapper( + params.name.c_str(), config, params.recordable, params.range) +{ + dramSysWrapper.dramsys->registerIdleCallback( + [this] + { + if (dramSysWrapper.dramsys->idle()) + { + signalDrainDone(); + } + }); +} + +gem5::Port& DRAMSys::getPort(const std::string& if_name, PortID idx) +{ + if (if_name != "tlm") + { + return AbstractMemory::getPort(if_name, idx); + } + + return tlmWrapper; +} + +DrainState DRAMSys::drain() +{ + return dramSysWrapper.dramsys->idle() ? DrainState::Drained + : DrainState::Draining; +} + +void DRAMSys::serialize(CheckpointOut& cp) const +{ + std::filesystem::path checkpointPath = CheckpointIn::dir(); + + auto topLevelObjects = sc_core::sc_get_top_level_objects(); + for (auto const* object : topLevelObjects) + { + std::function serialize; + serialize = + [&serialize, &checkpointPath](sc_core::sc_object const* object) + { + auto const* serializableObject = + dynamic_cast<::DRAMSys::Serialize const*>(object); + + if (serializableObject != nullptr) + { + std::string dumpFileName(object->name()); + dumpFileName += ".pmem"; + std::ofstream stream(checkpointPath / dumpFileName, + std::ios::binary); + serializableObject->serialize(stream); + } + + for (auto const* childObject : object->get_child_objects()) + { + serialize(childObject); + } + }; + + serialize(object); + } +} + +void DRAMSys::unserialize(CheckpointIn& cp) +{ + std::filesystem::path checkpointPath = CheckpointIn::dir(); + + auto topLevelObjects = sc_core::sc_get_top_level_objects(); + for (auto* object : topLevelObjects) + { + std::function deserialize; + deserialize = + [&deserialize, &checkpointPath](sc_core::sc_object* object) + { + auto* deserializableObject = + dynamic_cast<::DRAMSys::Deserialize*>(object); + + if (deserializableObject != nullptr) + { + std::string dumpFileName(object->name()); + dumpFileName += ".pmem"; + std::ifstream stream(checkpointPath / dumpFileName, + std::ios::binary); + deserializableObject->deserialize(stream); + } + + for (auto* childObject : object->get_child_objects()) + { + deserialize(childObject); + } + }; + + deserialize(object); + } +} + +} // namespace memory +} // namespace gem5 diff --git a/src/mem/dramsys.hh b/src/mem/dramsys.hh index d4d9ab8859..8530f2c563 100644 --- a/src/mem/dramsys.hh +++ b/src/mem/dramsys.hh @@ -29,7 +29,7 @@ #ifndef __MEM_DRAMSYS_H__ #define __MEM_DRAMSYS_H__ -#include "DRAMSysConfiguration.h" +#include "DRAMSys/config/DRAMSysConfiguration.h" #include "mem/abstract_mem.hh" #include "mem/dramsys_wrapper.hh" #include "params/DRAMSys.hh" @@ -43,36 +43,20 @@ namespace memory class DRAMSys : public AbstractMemory { PARAMS(DRAMSys); - sc_gem5::TlmTargetWrapper<32> tlmWrapper; + sc_gem5::TlmTargetWrapper<> tlmWrapper; public: - DRAMSys(Params const ¶ms) - : AbstractMemory(params), - tlmWrapper(dramSysWrapper.tSocket, - params.name + ".tlm", - InvalidPortID), - config(DRAMSysConfiguration::from_path( - params.configuration, - params.resource_directory)), - dramSysWrapper(params.name.c_str(), - config, - params.recordable, - params.range) - { - } + DRAMSys(Params const& params); - gem5::Port &getPort(const std::string &if_name, PortID idx) override - { - if (if_name != "tlm") - { - return AbstractMemory::getPort(if_name, idx); - } + gem5::Port& getPort(const std::string& if_name, PortID idx) override; - return tlmWrapper; - } + DrainState drain() override; + + void serialize(CheckpointOut& cp) const override; + void unserialize(CheckpointIn& cp) override; private: - DRAMSysConfiguration::Configuration config; + ::DRAMSys::Config::Configuration config; DRAMSysWrapper dramSysWrapper; }; diff --git a/src/mem/dramsys_wrapper.cc b/src/mem/dramsys_wrapper.cc index afa67f3bf2..2decec42a2 100644 --- a/src/mem/dramsys_wrapper.cc +++ b/src/mem/dramsys_wrapper.cc @@ -36,7 +36,7 @@ namespace memory DRAMSysWrapper::DRAMSysWrapper( sc_core::sc_module_name name, - DRAMSysConfiguration::Configuration const &config, + ::DRAMSys::Config::Configuration const &config, bool recordable, AddrRange range) : sc_core::sc_module(name), @@ -44,28 +44,41 @@ DRAMSysWrapper::DRAMSysWrapper( range(range) { tSocket.register_nb_transport_fw(this, &DRAMSysWrapper::nb_transport_fw); - tSocket.register_transport_dbg(this, &DRAMSysWrapper::transport_dbg); iSocket.register_nb_transport_bw(this, &DRAMSysWrapper::nb_transport_bw); + + tSocket.register_b_transport(this, &DRAMSysWrapper::b_transport); + + tSocket.register_transport_dbg(this, &DRAMSysWrapper::transport_dbg); iSocket.bind(dramsys->tSocket); // Register a callback to compensate for the destructor not // being called. registerExitCallback( - [this]() + []() { // Workaround for BUG GEM5-1233 sc_gem5::Kernel::stop(); }); } -std::shared_ptr<::DRAMSys> +std::shared_ptr<::DRAMSys::DRAMSys> DRAMSysWrapper::instantiateDRAMSys( bool recordable, - DRAMSysConfiguration::Configuration const &config) + ::DRAMSys::Config::Configuration const &config) { return recordable - ? std::make_shared<::DRAMSysRecordable>("DRAMSys", config) - : std::make_shared<::DRAMSys>("DRAMSys", config); + ? std::make_shared<::DRAMSys::DRAMSysRecordable>("DRAMSys", config) + : std::make_shared<::DRAMSys::DRAMSys>("DRAMSys", config); +} + +void DRAMSysWrapper::b_transport( + tlm::tlm_generic_payload &payload, + sc_core::sc_time &delay) +{ + // Subtract base address offset + payload.set_address(payload.get_address() - range.start()); + + iSocket->b_transport(payload, delay); } tlm::tlm_sync_enum DRAMSysWrapper::nb_transport_fw( diff --git a/src/mem/dramsys_wrapper.hh b/src/mem/dramsys_wrapper.hh index f1437cb761..26d552fd2f 100644 --- a/src/mem/dramsys_wrapper.hh +++ b/src/mem/dramsys_wrapper.hh @@ -32,13 +32,14 @@ #include #include -#include "DRAMSysConfiguration.h" +#include "DRAMSys/config/DRAMSysConfiguration.h" +#include "DRAMSys/simulation/DRAMSysRecordable.h" #include "mem/abstract_mem.hh" #include "params/DRAMSys.hh" #include "sim/core.hh" -#include "simulation/DRAMSysRecordable.h" #include "systemc/core/kernel.hh" #include "systemc/ext/core/sc_module_name.hh" + #include "systemc/ext/systemc" #include "systemc/ext/tlm" #include "systemc/ext/tlm_utils/simple_target_socket.h" @@ -57,14 +58,14 @@ class DRAMSysWrapper : public sc_core::sc_module public: SC_HAS_PROCESS(DRAMSysWrapper); DRAMSysWrapper(sc_core::sc_module_name name, - DRAMSysConfiguration::Configuration const &config, + ::DRAMSys::Config::Configuration const &config, bool recordable, AddrRange range); private: - static std::shared_ptr<::DRAMSys> + static std::shared_ptr<::DRAMSys::DRAMSys> instantiateDRAMSys(bool recordable, - DRAMSysConfiguration::Configuration const &config); + ::DRAMSys::Config::Configuration const &config); tlm::tlm_sync_enum nb_transport_fw(tlm::tlm_generic_payload &payload, tlm::tlm_phase &phase, @@ -74,12 +75,15 @@ class DRAMSysWrapper : public sc_core::sc_module tlm::tlm_phase &phase, sc_core::sc_time &bwDelay); + void b_transport(tlm::tlm_generic_payload &payload, + sc_core::sc_time &delay); + unsigned int transport_dbg(tlm::tlm_generic_payload &trans); tlm_utils::simple_initiator_socket iSocket; tlm_utils::simple_target_socket tSocket; - std::shared_ptr<::DRAMSys> dramsys; + std::shared_ptr<::DRAMSys::DRAMSys> dramsys; AddrRange range; }; diff --git a/src/mem/mem_ctrl.cc b/src/mem/mem_ctrl.cc index 9a3600f331..97c7741abd 100644 --- a/src/mem/mem_ctrl.cc +++ b/src/mem/mem_ctrl.cc @@ -1496,6 +1496,11 @@ MemCtrl::MemoryPort::recvFunctional(PacketPtr pkt) // calls recvAtomic() and throws away the latency; we can save a // little here by just not calculating the latency. ctrl.recvFunctional(pkt); + } else { + // The packet's request is satisfied by the queue, but queue + // does not call makeResponse. + // Here, change the packet to the corresponding response + pkt->makeResponse(); } pkt->popLabel(); diff --git a/src/mem/noncoherent_xbar.cc b/src/mem/noncoherent_xbar.cc index 0a378e2c63..12b62974b7 100644 --- a/src/mem/noncoherent_xbar.cc +++ b/src/mem/noncoherent_xbar.cc @@ -107,8 +107,8 @@ NoncoherentXBar::recvTimingReq(PacketPtr pkt, PortID cpu_side_port_id) // we should never see express snoops on a non-coherent crossbar assert(!pkt->isExpressSnoop()); - // determine the destination based on the address - PortID mem_side_port_id = findPort(pkt->getAddrRange()); + // determine the destination port + PortID mem_side_port_id = findPort(pkt); // test if the layer should be considered occupied for the current // port @@ -255,7 +255,7 @@ NoncoherentXBar::recvAtomicBackdoor(PacketPtr pkt, PortID cpu_side_port_id, unsigned int pkt_cmd = pkt->cmdToIndex(); // determine the destination port - PortID mem_side_port_id = findPort(pkt->getAddrRange()); + PortID mem_side_port_id = findPort(pkt); // stats updates for the request pktCount[cpu_side_port_id][mem_side_port_id]++; @@ -316,7 +316,7 @@ NoncoherentXBar::recvFunctional(PacketPtr pkt, PortID cpu_side_port_id) } // determine the destination port - PortID dest_id = findPort(pkt->getAddrRange()); + PortID dest_id = findPort(pkt); // forward the request to the appropriate destination memSidePorts[dest_id]->sendFunctional(pkt); diff --git a/src/mem/nvm_interface.cc b/src/mem/nvm_interface.cc index 366f71d56a..d9996f0ec9 100644 --- a/src/mem/nvm_interface.cc +++ b/src/mem/nvm_interface.cc @@ -538,7 +538,7 @@ NVMInterface::doBurstAccess(MemPacket* pkt, Tick next_burst_at, // Update the stats if (pkt->isRead()) { stats.readBursts++; - stats.bytesRead += burstSize; + stats.nvmBytesRead += burstSize; stats.perBankRdBursts[pkt->bankId]++; stats.pendingReads.sample(numPendingReads); @@ -548,7 +548,7 @@ NVMInterface::doBurstAccess(MemPacket* pkt, Tick next_burst_at, stats.totQLat += cmd_at - pkt->entryTime; } else { stats.writeBursts++; - stats.bytesWritten += burstSize; + stats.nvmBytesWritten += burstSize; stats.perBankWrBursts[pkt->bankId]++; } @@ -650,6 +650,11 @@ NVMInterface::NVMStats::NVMStats(NVMInterface &_nvm) statistics::units::Tick, statistics::units::Count>::get(), "Average memory access latency per NVM burst"), + ADD_STAT(nvmBytesRead, statistics::units::Byte::get(), + "Total bytes read"), + ADD_STAT(nvmBytesWritten, statistics::units::Byte::get(), + "Total bytes written"), + ADD_STAT(avgRdBW, statistics::units::Rate< statistics::units::Byte, statistics::units::Second>::get(), "Average DRAM read bandwidth in MiBytes/s"), @@ -715,8 +720,8 @@ NVMInterface::NVMStats::regStats() avgBusLat = totBusLat / readBursts; avgMemAccLat = totMemAccLat / readBursts; - avgRdBW = (bytesRead / 1000000) / simSeconds; - avgWrBW = (bytesWritten / 1000000) / simSeconds; + avgRdBW = (nvmBytesRead / 1000000) / simSeconds; + avgWrBW = (nvmBytesWritten / 1000000) / simSeconds; peakBW = (sim_clock::Frequency / nvm.tBURST) * nvm.burstSize / 1000000; diff --git a/src/mem/nvm_interface.hh b/src/mem/nvm_interface.hh index cc41587fe0..547fdbb5f8 100644 --- a/src/mem/nvm_interface.hh +++ b/src/mem/nvm_interface.hh @@ -125,8 +125,8 @@ class NVMInterface : public MemInterface statistics::Formula avgBusLat; statistics::Formula avgMemAccLat; - statistics::Scalar bytesRead; - statistics::Scalar bytesWritten; + statistics::Scalar nvmBytesRead; + statistics::Scalar nvmBytesWritten; // Average bandwidth statistics::Formula avgRdBW; diff --git a/src/mem/packet.hh b/src/mem/packet.hh index ed7a94f4fb..df2a8165fc 100644 --- a/src/mem/packet.hh +++ b/src/mem/packet.hh @@ -1156,7 +1156,7 @@ class Packet : public Printable, public Extensible public: /** * @{ - * @name Data accessor mehtods + * @name Data accessor methods */ /** @@ -1437,6 +1437,15 @@ class Packet : public Printable, public Extensible return cmd == MemCmd::CleanEvict || cmd == MemCmd::WritebackClean; } + /** + * Is this packet a clean invalidate request, e.g., clflush/clflushopt? + */ + bool + isCleanInvalidateRequest() const + { + return cmd == MemCmd::CleanInvalidReq; + } + bool isMaskedWrite() const { diff --git a/src/mem/port.cc b/src/mem/port.cc index 2f754c89df..e597255dfb 100644 --- a/src/mem/port.cc +++ b/src/mem/port.cc @@ -197,7 +197,7 @@ RequestPort::addTrace(PacketPtr pkt) const ext = std::make_shared(); pkt->setExtension(ext); } - ext->add(name(), _responsePort->name()); + ext->add(name(), _responsePort->name(), pkt->getAddr()); } void diff --git a/src/mem/port.hh b/src/mem/port.hh index 2555706444..5f977aaab8 100644 --- a/src/mem/port.hh +++ b/src/mem/port.hh @@ -47,6 +47,7 @@ #define __MEM_PORT_HH__ #include +#include #include #include @@ -85,9 +86,9 @@ class TracingExtension : public gem5::Extension } void - add(std::string request_port, std::string response_port) + add(std::string request_port, std::string response_port, gem5::Addr addr) { - trace_.push(request_port); + trace_.push(request_port + csprintf(" addr=%#llx", addr)); trace_.push(response_port); } @@ -100,6 +101,21 @@ class TracingExtension : public gem5::Extension bool empty() { return trace_.empty(); } std::stack& getTrace() { return trace_; } + std::string getTraceInString() + { + std::stringstream port_trace; + std::stack copy_stack = trace_; + port_trace << "Port trace of the Packet (" << std::endl + << "[Destination] "; + while (!copy_stack.empty()) { + if (copy_stack.size() == 1) + port_trace << "[Source] "; + port_trace << copy_stack.top() << std::endl; + copy_stack.pop(); + } + port_trace << ")"; + return port_trace.str(); + } private: std::stack trace_; diff --git a/src/mem/port_proxy.cc b/src/mem/port_proxy.cc index 19e1a53e84..a3c82452e2 100644 --- a/src/mem/port_proxy.cc +++ b/src/mem/port_proxy.cc @@ -44,19 +44,19 @@ namespace gem5 { -PortProxy::PortProxy(ThreadContext *tc, unsigned int cache_line_size) : +PortProxy::PortProxy(ThreadContext *tc, Addr cache_line_size) : PortProxy([tc](PacketPtr pkt)->void { tc->sendFunctional(pkt); }, cache_line_size) {} -PortProxy::PortProxy(const RequestPort &port, unsigned int cache_line_size) : +PortProxy::PortProxy(const RequestPort &port, Addr cache_line_size) : PortProxy([&port](PacketPtr pkt)->void { port.sendFunctional(pkt); }, cache_line_size) {} void PortProxy::readBlobPhys(Addr addr, Request::Flags flags, - void *p, int size) const + void *p, uint64_t size) const { for (ChunkGenerator gen(addr, size, _cacheLineSize); !gen.done(); gen.next()) { @@ -73,7 +73,7 @@ PortProxy::readBlobPhys(Addr addr, Request::Flags flags, void PortProxy::writeBlobPhys(Addr addr, Request::Flags flags, - const void *p, int size) const + const void *p, uint64_t size) const { for (ChunkGenerator gen(addr, size, _cacheLineSize); !gen.done(); gen.next()) { @@ -90,7 +90,7 @@ PortProxy::writeBlobPhys(Addr addr, Request::Flags flags, void PortProxy::memsetBlobPhys(Addr addr, Request::Flags flags, - uint8_t v, int size) const + uint8_t v, uint64_t size) const { // quick and dirty... uint8_t *buf = new uint8_t[size]; diff --git a/src/mem/port_proxy.hh b/src/mem/port_proxy.hh index 29f6ba60a4..49c6d6f811 100644 --- a/src/mem/port_proxy.hh +++ b/src/mem/port_proxy.hh @@ -92,7 +92,7 @@ class PortProxy : FunctionalRequestProtocol SendFunctionalFunc sendFunctional; /** Granularity of any transactions issued through this proxy. */ - const unsigned int _cacheLineSize; + const Addr _cacheLineSize; void recvFunctionalSnoop(PacketPtr pkt) override @@ -103,13 +103,13 @@ class PortProxy : FunctionalRequestProtocol } public: - PortProxy(SendFunctionalFunc func, unsigned int cache_line_size) : + PortProxy(SendFunctionalFunc func, Addr cache_line_size) : sendFunctional(func), _cacheLineSize(cache_line_size) {} // Helpers which create typical SendFunctionalFunc-s from other objects. - PortProxy(ThreadContext *tc, unsigned int cache_line_size); - PortProxy(const RequestPort &port, unsigned int cache_line_size); + PortProxy(ThreadContext *tc, Addr cache_line_size); + PortProxy(const RequestPort &port, Addr cache_line_size); virtual ~PortProxy() {} @@ -120,19 +120,19 @@ class PortProxy : FunctionalRequestProtocol * Read size bytes memory at physical address and store in p. */ void readBlobPhys(Addr addr, Request::Flags flags, - void *p, int size) const; + void *p, uint64_t size) const; /** * Write size bytes from p to physical address. */ void writeBlobPhys(Addr addr, Request::Flags flags, - const void *p, int size) const; + const void *p, uint64_t size) const; /** * Fill size bytes starting at physical addr with byte value val. */ void memsetBlobPhys(Addr addr, Request::Flags flags, - uint8_t v, int size) const; + uint8_t v, uint64_t size) const; @@ -143,7 +143,7 @@ class PortProxy : FunctionalRequestProtocol * Returns true on success and false on failure. */ virtual bool - tryReadBlob(Addr addr, void *p, int size) const + tryReadBlob(Addr addr, void *p, uint64_t size) const { readBlobPhys(addr, 0, p, size); return true; @@ -154,7 +154,7 @@ class PortProxy : FunctionalRequestProtocol * Returns true on success and false on failure. */ virtual bool - tryWriteBlob(Addr addr, const void *p, int size) const + tryWriteBlob(Addr addr, const void *p, uint64_t size) const { writeBlobPhys(addr, 0, p, size); return true; @@ -165,7 +165,7 @@ class PortProxy : FunctionalRequestProtocol * Returns true on success and false on failure. */ virtual bool - tryMemsetBlob(Addr addr, uint8_t val, int size) const + tryMemsetBlob(Addr addr, uint8_t val, uint64_t size) const { memsetBlobPhys(addr, 0, val, size); return true; @@ -179,7 +179,7 @@ class PortProxy : FunctionalRequestProtocol * Same as tryReadBlob, but insists on success. */ void - readBlob(Addr addr, void *p, int size) const + readBlob(Addr addr, void *p, uint64_t size) const { if (!tryReadBlob(addr, p, size)) fatal("readBlob(%#x, ...) failed", addr); @@ -189,7 +189,7 @@ class PortProxy : FunctionalRequestProtocol * Same as tryWriteBlob, but insists on success. */ void - writeBlob(Addr addr, const void *p, int size) const + writeBlob(Addr addr, const void *p, uint64_t size) const { if (!tryWriteBlob(addr, p, size)) fatal("writeBlob(%#x, ...) failed", addr); @@ -199,7 +199,7 @@ class PortProxy : FunctionalRequestProtocol * Same as tryMemsetBlob, but insists on success. */ void - memsetBlob(Addr addr, uint8_t v, int size) const + memsetBlob(Addr addr, uint8_t v, uint64_t size) const { if (!tryMemsetBlob(addr, v, size)) fatal("memsetBlob(%#x, ...) failed", addr); diff --git a/src/mem/probes/MemFootprintProbe.py b/src/mem/probes/MemFootprintProbe.py index 707a8b688f..46831415f5 100644 --- a/src/mem/probes/MemFootprintProbe.py +++ b/src/mem/probes/MemFootprintProbe.py @@ -34,11 +34,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.BaseMemProbe import BaseMemProbe from m5.params import * from m5.proxy import * -from m5.objects.BaseMemProbe import BaseMemProbe - class MemFootprintProbe(BaseMemProbe): type = "MemFootprintProbe" diff --git a/src/mem/probes/MemTraceProbe.py b/src/mem/probes/MemTraceProbe.py index d848e9ed61..6820e0732e 100644 --- a/src/mem/probes/MemTraceProbe.py +++ b/src/mem/probes/MemTraceProbe.py @@ -33,9 +33,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.BaseMemProbe import BaseMemProbe from m5.params import * from m5.proxy import * -from m5.objects.BaseMemProbe import BaseMemProbe class MemTraceProbe(BaseMemProbe): diff --git a/src/mem/probes/StackDistProbe.py b/src/mem/probes/StackDistProbe.py index 5b44d9d333..a62fb5a5b5 100644 --- a/src/mem/probes/StackDistProbe.py +++ b/src/mem/probes/StackDistProbe.py @@ -33,9 +33,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.BaseMemProbe import BaseMemProbe from m5.params import * from m5.proxy import * -from m5.objects.BaseMemProbe import BaseMemProbe class StackDistProbe(BaseMemProbe): diff --git a/src/mem/qos/QoSMemCtrl.py b/src/mem/qos/QoSMemCtrl.py index 3028b439d5..280d1d1e1b 100644 --- a/src/mem/qos/QoSMemCtrl.py +++ b/src/mem/qos/QoSMemCtrl.py @@ -33,10 +33,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * from m5.objects.ClockedObject import ClockedObject from m5.objects.QoSTurnaround import * +from m5.params import * +from m5.proxy import * + # QoS Queue Selection policy used to select packets among same-QoS queues class QoSQPolicy(Enum): diff --git a/src/mem/qos/QoSMemSinkCtrl.py b/src/mem/qos/QoSMemSinkCtrl.py index dac0fb5be6..8eed316d28 100644 --- a/src/mem/qos/QoSMemSinkCtrl.py +++ b/src/mem/qos/QoSMemSinkCtrl.py @@ -35,9 +35,9 @@ # # Author: Matteo Andreozzi -from m5.params import * from m5.objects.QoSMemCtrl import * from m5.objects.QoSMemSinkInterface import * +from m5.params import * class QoSMemSinkCtrl(QoSMemCtrl): diff --git a/src/mem/qos/QoSPolicy.py b/src/mem/qos/QoSPolicy.py index 2dfc974f43..0736d0367d 100644 --- a/src/mem/qos/QoSPolicy.py +++ b/src/mem/qos/QoSPolicy.py @@ -33,8 +33,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import * from m5.params import * +from m5.SimObject import * + # QoS scheduler policy used to serve incoming transaction class QoSPolicy(SimObject): diff --git a/src/mem/qos/QoSTurnaround.py b/src/mem/qos/QoSTurnaround.py index f356635a57..26794c3f49 100644 --- a/src/mem/qos/QoSTurnaround.py +++ b/src/mem/qos/QoSTurnaround.py @@ -35,6 +35,7 @@ from m5.SimObject import SimObject + # QoS Turnaround policy used to select bus state - READ or WRITE class QoSTurnaroundPolicy(SimObject): type = "QoSTurnaroundPolicy" diff --git a/src/mem/request.hh b/src/mem/request.hh index 491aad0241..df249ac249 100644 --- a/src/mem/request.hh +++ b/src/mem/request.hh @@ -757,6 +757,13 @@ class Request : public Extensible return atomicOpFunctor.get(); } + void + setAtomicOpFunctor(AtomicOpFunctorPtr amo_op) + { + atomicOpFunctor = std::move(amo_op); + } + + /** * Accessor for hardware transactional memory abort cause. */ diff --git a/src/mem/ruby/Kconfig b/src/mem/ruby/Kconfig new file mode 100644 index 0000000000..362174d5b7 --- /dev/null +++ b/src/mem/ruby/Kconfig @@ -0,0 +1,50 @@ +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +menu "Ruby" + menuconfig RUBY + bool "Enable" + + if RUBY + config PROTOCOL + string + + config NEED_PARTIAL_FUNC_READS + bool + + choice "Ruby protocol" + prompt "Ruby protocol" + endchoice + + config SLICC_HTML + bool 'Create HTML files' + + config NUMBER_BITS_PER_SET + int 'Max elements in set' + default 64 + endif +endmenu + +rsource "protocol/Kconfig" diff --git a/src/mem/ruby/SConscript b/src/mem/ruby/SConscript index 1e386f922d..ae2e20c4a3 100644 --- a/src/mem/ruby/SConscript +++ b/src/mem/ruby/SConscript @@ -1,6 +1,6 @@ # -*- mode:python -*- -# Copyright (c) 2021 Arm Limited +# Copyright (c) 2021,2023 Arm Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -49,7 +49,7 @@ from gem5_scons import Transform Import('*') -if env['CONF']['PROTOCOL'] == 'None': +if not env['CONF']['RUBY']: Return() DebugFlag('ProtocolTrace') @@ -111,8 +111,9 @@ MakeInclude('structures/DirectoryMemory.hh') MakeInclude('structures/PerfectCacheMemory.hh') MakeInclude('structures/PersistentTable.hh') MakeInclude('structures/RubyPrefetcher.hh') +MakeInclude('structures/RubyPrefetcherProxy.hh') MakeInclude('structures/TBEStorage.hh') -if env['PROTOCOL'] == 'CHI': +if env['CONF']['PROTOCOL'] == 'CHI': MakeInclude('structures/MN_TBEStorage.hh') MakeInclude('structures/MN_TBETable.hh') MakeInclude('structures/TBETable.hh') diff --git a/src/mem/ruby/SConsopts b/src/mem/ruby/SConsopts index f26b6d05f8..7c4a1a9ea9 100644 --- a/src/mem/ruby/SConsopts +++ b/src/mem/ruby/SConsopts @@ -25,12 +25,4 @@ Import('*') -main.SetDefault(ALL_PROTOCOLS=[], PROTOCOL_DIRS=[], SLICC_INCLUDES=[]) - -def add_protocols_var(): - sticky_vars.Add(EnumVariable('PROTOCOL', 'Coherence protocol for Ruby', - 'None', main['ALL_PROTOCOLS'])) -AfterSConsopts(add_protocols_var) - -sticky_vars.Add(('NUMBER_BITS_PER_SET', 'Max elements in set (default 64)', - 64)) +main.SetDefault(PROTOCOL_DIRS=[], SLICC_INCLUDES=[]) diff --git a/src/mem/ruby/common/DataBlock.cc b/src/mem/ruby/common/DataBlock.cc index f70aa79fd4..8f47d0026b 100644 --- a/src/mem/ruby/common/DataBlock.cc +++ b/src/mem/ruby/common/DataBlock.cc @@ -51,9 +51,19 @@ namespace ruby DataBlock::DataBlock(const DataBlock &cp) { - m_data = new uint8_t[RubySystem::getBlockSizeBytes()]; - memcpy(m_data, cp.m_data, RubySystem::getBlockSizeBytes()); + uint8_t *block_update; + size_t block_bytes = RubySystem::getBlockSizeBytes(); + m_data = new uint8_t[block_bytes]; + memcpy(m_data, cp.m_data, block_bytes); m_alloc = true; + // If this data block is involved in an atomic operation, the effect + // of applying the atomic operations on the data block are recorded in + // m_atomicLog. If so, we must copy over every entry in the change log + for (size_t i = 0; i < cp.m_atomicLog.size(); i++) { + block_update = new uint8_t[block_bytes]; + memcpy(block_update, cp.m_atomicLog[i], block_bytes); + m_atomicLog.push_back(block_update); + } } void @@ -73,7 +83,20 @@ DataBlock::clear() bool DataBlock::equal(const DataBlock& obj) const { - return !memcmp(m_data, obj.m_data, RubySystem::getBlockSizeBytes()); + size_t block_bytes = RubySystem::getBlockSizeBytes(); + // Check that the block contents match + if (memcmp(m_data, obj.m_data, block_bytes)) { + return false; + } + if (m_atomicLog.size() != obj.m_atomicLog.size()) { + return false; + } + for (size_t i = 0; i < m_atomicLog.size(); i++) { + if (memcmp(m_atomicLog[i], obj.m_atomicLog[i], block_bytes)) { + return false; + } + } + return true; } void @@ -87,12 +110,13 @@ DataBlock::copyPartial(const DataBlock &dblk, const WriteMask &mask) } void -DataBlock::atomicPartial(const DataBlock &dblk, const WriteMask &mask) +DataBlock::atomicPartial(const DataBlock &dblk, const WriteMask &mask, + bool isAtomicNoReturn) { for (int i = 0; i < RubySystem::getBlockSizeBytes(); i++) { m_data[i] = dblk.m_data[i]; } - mask.performAtomic(m_data); + mask.performAtomic(m_data, m_atomicLog, isAtomicNoReturn); } void @@ -107,6 +131,28 @@ DataBlock::print(std::ostream& out) const out << std::dec << "]" << std::flush; } +int +DataBlock::numAtomicLogEntries() const +{ + return m_atomicLog.size(); +} +uint8_t* +DataBlock::popAtomicLogEntryFront() +{ + assert(m_atomicLog.size() > 0); + auto ret = m_atomicLog.front(); + m_atomicLog.pop_front(); + return ret; +} +void +DataBlock::clearAtomicLogEntries() +{ + for (auto log : m_atomicLog) { + delete [] log; + } + m_atomicLog.clear(); +} + const uint8_t* DataBlock::getData(int offset, int len) const { @@ -137,7 +183,18 @@ DataBlock::setData(PacketPtr pkt) DataBlock & DataBlock::operator=(const DataBlock & obj) { - memcpy(m_data, obj.m_data, RubySystem::getBlockSizeBytes()); + uint8_t *block_update; + size_t block_bytes = RubySystem::getBlockSizeBytes(); + // Copy entire block contents from obj to current block + memcpy(m_data, obj.m_data, block_bytes); + // If this data block is involved in an atomic operation, the effect + // of applying the atomic operations on the data block are recorded in + // m_atomicLog. If so, we must copy over every entry in the change log + for (size_t i = 0; i < obj.m_atomicLog.size(); i++) { + block_update = new uint8_t[block_bytes]; + memcpy(block_update, obj.m_atomicLog[i], block_bytes); + m_atomicLog.push_back(block_update); + } return *this; } diff --git a/src/mem/ruby/common/DataBlock.hh b/src/mem/ruby/common/DataBlock.hh index e147d701c5..7456a25f3f 100644 --- a/src/mem/ruby/common/DataBlock.hh +++ b/src/mem/ruby/common/DataBlock.hh @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -71,6 +72,12 @@ class DataBlock { if (m_alloc) delete [] m_data; + + // If data block involved in atomic + // operations, free all meta data + for (auto log : m_atomicLog) { + delete [] log; + } } DataBlock& operator=(const DataBlock& obj); @@ -80,13 +87,17 @@ class DataBlock void clear(); uint8_t getByte(int whichByte) const; const uint8_t *getData(int offset, int len) const; + uint8_t* popAtomicLogEntryFront(); + int numAtomicLogEntries() const; + void clearAtomicLogEntries(); uint8_t *getDataMod(int offset); void setByte(int whichByte, uint8_t data); void setData(const uint8_t *data, int offset, int len); void setData(PacketPtr pkt); void copyPartial(const DataBlock &dblk, int offset, int len); void copyPartial(const DataBlock &dblk, const WriteMask &mask); - void atomicPartial(const DataBlock & dblk, const WriteMask & mask); + void atomicPartial(const DataBlock & dblk, const WriteMask & mask, + bool isAtomicNoReturn=true); bool equal(const DataBlock& obj) const; void print(std::ostream& out) const; @@ -94,6 +105,9 @@ class DataBlock void alloc(); uint8_t *m_data; bool m_alloc; + + // Tracks block changes when atomic ops are applied + std::deque m_atomicLog; }; inline void diff --git a/src/mem/ruby/common/SConscript b/src/mem/ruby/common/SConscript index 9f683cbaf1..9c1fc795a6 100644 --- a/src/mem/ruby/common/SConscript +++ b/src/mem/ruby/common/SConscript @@ -28,7 +28,7 @@ Import('*') -if env['CONF']['PROTOCOL'] == 'None': +if not env['CONF']['RUBY']: Return() env.Append(CPPDEFINES={'NUMBER_BITS_PER_SET': diff --git a/src/mem/ruby/common/WriteMask.cc b/src/mem/ruby/common/WriteMask.cc index 4c24a64706..1fa03c951e 100644 --- a/src/mem/ruby/common/WriteMask.cc +++ b/src/mem/ruby/common/WriteMask.cc @@ -55,5 +55,29 @@ WriteMask::print(std::ostream& out) const << std::flush; } +void +WriteMask::performAtomic(uint8_t * p, + std::deque& log, bool isAtomicNoReturn) const +{ + int offset; + uint8_t *block_update; + // Here, operations occur in FIFO order from the mAtomicOp + // vector. This is done to match the ordering of packets + // that was seen when the initial coalesced request was created. + for (int i = 0; i < mAtomicOp.size(); i++) { + if (!isAtomicNoReturn) { + // Save the old value of the data block in case a + // return value is needed + block_update = new uint8_t[mSize]; + std::memcpy(block_update, p, mSize); + log.push_back(block_update); + } + // Perform the atomic operation + offset = mAtomicOp[i].first; + AtomicOpFunctor *fnctr = mAtomicOp[i].second; + (*fnctr)(&p[offset]); + } +} + } // namespace ruby } // namespace gem5 diff --git a/src/mem/ruby/common/WriteMask.hh b/src/mem/ruby/common/WriteMask.hh index 2de21da79b..8c6b8ce976 100644 --- a/src/mem/ruby/common/WriteMask.hh +++ b/src/mem/ruby/common/WriteMask.hh @@ -222,26 +222,16 @@ class WriteMask void print(std::ostream& out) const; - void - performAtomic(uint8_t * p) const - { - for (int i = 0; i < mAtomicOp.size(); i++) { - int offset = mAtomicOp[i].first; - AtomicOpFunctor *fnctr = mAtomicOp[i].second; - (*fnctr)(&p[offset]); - } - } - - void - performAtomic(DataBlock & blk) const - { - for (int i = 0; i < mAtomicOp.size(); i++) { - int offset = mAtomicOp[i].first; - uint8_t *p = blk.getDataMod(offset); - AtomicOpFunctor *fnctr = mAtomicOp[i].second; - (*fnctr)(p); - } - } + /* + * Performs atomic operations on the data block pointed to by p. The + * atomic operations to perform are in the vector mAtomicOp. The + * effect of each atomic operation is pushed to the atomicChangeLog + * so that each individual atomic requestor may see the results of their + * specific atomic operation. + */ + void performAtomic(uint8_t * p, + std::deque& atomicChangeLog, + bool isAtomicNoReturn=true) const; const AtomicOpVector& getAtomicOps() const diff --git a/src/mem/ruby/network/BasicRouter.py b/src/mem/ruby/network/BasicRouter.py index 933470daec..ee74eaa1f4 100644 --- a/src/mem/ruby/network/BasicRouter.py +++ b/src/mem/ruby/network/BasicRouter.py @@ -24,9 +24,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * - from m5.objects.ClockedObject import ClockedObject +from m5.params import * class BasicRouter(ClockedObject): diff --git a/src/mem/ruby/network/MessageBuffer.cc b/src/mem/ruby/network/MessageBuffer.cc index 9a6500978e..9a4439a538 100644 --- a/src/mem/ruby/network/MessageBuffer.cc +++ b/src/mem/ruby/network/MessageBuffer.cc @@ -62,7 +62,8 @@ MessageBuffer::MessageBuffer(const Params &p) m_max_dequeue_rate(p.max_dequeue_rate), m_dequeues_this_cy(0), m_time_last_time_size_checked(0), m_time_last_time_enqueue(0), m_time_last_time_pop(0), - m_last_arrival_time(0), m_strict_fifo(p.ordered), + m_last_arrival_time(0), m_last_message_strict_fifo_bypassed(false), + m_strict_fifo(p.ordered), m_randomization(p.randomization), m_allow_zero_latency(p.allow_zero_latency), m_routing_priority(p.routing_priority), @@ -214,7 +215,8 @@ random_time() } void -MessageBuffer::enqueue(MsgPtr message, Tick current_time, Tick delta) +MessageBuffer::enqueue(MsgPtr message, Tick current_time, Tick delta, + bool bypassStrictFIFO) { // record current time incase we have a pop that also adjusts my size if (m_time_last_time_enqueue < current_time) { @@ -252,7 +254,8 @@ MessageBuffer::enqueue(MsgPtr message, Tick current_time, Tick delta) // Check the arrival time assert(arrival_time >= current_time); - if (m_strict_fifo) { + if (m_strict_fifo && + !(bypassStrictFIFO || m_last_message_strict_fifo_bypassed)) { if (arrival_time < m_last_arrival_time) { panic("FIFO ordering violated: %s name: %s current time: %d " "delta: %d arrival_time: %d last arrival_time: %d\n", @@ -266,6 +269,8 @@ MessageBuffer::enqueue(MsgPtr message, Tick current_time, Tick delta) m_last_arrival_time = arrival_time; } + m_last_message_strict_fifo_bypassed = bypassStrictFIFO; + // compute the delay cycles and set enqueue time Message* msg_ptr = message.get(); assert(msg_ptr != NULL); diff --git a/src/mem/ruby/network/MessageBuffer.hh b/src/mem/ruby/network/MessageBuffer.hh index 279599340a..03a0454433 100644 --- a/src/mem/ruby/network/MessageBuffer.hh +++ b/src/mem/ruby/network/MessageBuffer.hh @@ -123,7 +123,8 @@ class MessageBuffer : public SimObject const MsgPtr &peekMsgPtr() const { return m_prio_heap.front(); } - void enqueue(MsgPtr message, Tick curTime, Tick delta); + void enqueue(MsgPtr message, Tick curTime, Tick delta, + bool bypassStrictFIFO = false); // Defer enqueueing a message to a later cycle by putting it aside and not // enqueueing it in this cycle @@ -271,6 +272,9 @@ class MessageBuffer : public SimObject uint64_t m_msg_counter; int m_priority_rank; + + bool m_last_message_strict_fifo_bypassed; + const bool m_strict_fifo; const MessageRandomization m_randomization; const bool m_allow_zero_latency; diff --git a/src/mem/ruby/network/MessageBuffer.py b/src/mem/ruby/network/MessageBuffer.py index bd20239cea..270dbb1ba1 100644 --- a/src/mem/ruby/network/MessageBuffer.py +++ b/src/mem/ruby/network/MessageBuffer.py @@ -40,6 +40,7 @@ from m5.params import * from m5.proxy import * from m5.SimObject import SimObject + # A MessageBuffer inserts random delays to enqueued messages when the # randomization param is set to 'enabled' or when globally enabled for the # RubySystem and the param is set to 'ruby_system' (default). 'disabled' diff --git a/src/mem/ruby/network/Network.py b/src/mem/ruby/network/Network.py index 3e9f549f89..72934efa30 100644 --- a/src/mem/ruby/network/Network.py +++ b/src/mem/ruby/network/Network.py @@ -24,10 +24,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.BasicLink import BasicLink +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * -from m5.objects.ClockedObject import ClockedObject -from m5.objects.BasicLink import BasicLink class RubyNetwork(ClockedObject): diff --git a/src/mem/ruby/network/SConscript b/src/mem/ruby/network/SConscript index b5a4e7950f..e9a79746d7 100644 --- a/src/mem/ruby/network/SConscript +++ b/src/mem/ruby/network/SConscript @@ -28,7 +28,7 @@ Import('*') -if env['CONF']['PROTOCOL'] == 'None': +if not env['CONF']['RUBY']: Return() SimObject('BasicLink.py', sim_objects=[ diff --git a/src/mem/ruby/network/fault_model/SConscript b/src/mem/ruby/network/fault_model/SConscript index bd55947795..e6ff61aed8 100644 --- a/src/mem/ruby/network/fault_model/SConscript +++ b/src/mem/ruby/network/fault_model/SConscript @@ -33,7 +33,7 @@ Import('*') -if env['CONF']['PROTOCOL'] == 'None': +if not env['CONF']['RUBY']: Return() SimObject('FaultModel.py', sim_objects=['FaultModel']) diff --git a/src/mem/ruby/network/garnet/GarnetLink.py b/src/mem/ruby/network/garnet/GarnetLink.py index 3cc44e4614..5cd9abe71f 100644 --- a/src/mem/ruby/network/garnet/GarnetLink.py +++ b/src/mem/ruby/network/garnet/GarnetLink.py @@ -25,10 +25,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.BasicLink import ( + BasicExtLink, + BasicIntLink, +) +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * -from m5.objects.ClockedObject import ClockedObject -from m5.objects.BasicLink import BasicIntLink, BasicExtLink class CDCType(Enum): diff --git a/src/mem/ruby/network/garnet/GarnetNetwork.py b/src/mem/ruby/network/garnet/GarnetNetwork.py index 128118309c..b735260b4d 100644 --- a/src/mem/ruby/network/garnet/GarnetNetwork.py +++ b/src/mem/ruby/network/garnet/GarnetNetwork.py @@ -28,11 +28,12 @@ # Author: Tushar Krishna # -from m5.params import * -from m5.proxy import * -from m5.objects.Network import RubyNetwork +from m5.citations import add_citation from m5.objects.BasicRouter import BasicRouter from m5.objects.ClockedObject import ClockedObject +from m5.objects.Network import RubyNetwork +from m5.params import * +from m5.proxy import * class GarnetNetwork(RubyNetwork): @@ -83,3 +84,40 @@ class GarnetRouter(BasicRouter): width = Param.UInt32( Parent.ni_flit_size, "bit width supported by the router" ) + + +add_citation( + GarnetNetwork, + """@inproceedings{Bharadwaj:2020:kite, + author = {Srikant Bharadwaj and + Jieming Yin and + Bradford M. Beckmann and + Tushar Krishna}, + title = {Kite: {A} Family of Heterogeneous Interposer Topologies Enabled via + Accurate Interconnect Modeling}, + booktitle = {57th {ACM/IEEE} Design Automation Conference, {DAC} 2020, San Francisco, + CA, USA, July 20-24, 2020}, + pages = {1--6}, + publisher = {{IEEE}}, + year = {2020}, + url = {https://doi.org/10.1109/DAC18072.2020.9218539}, + doi = {10.1109/DAC18072.2020.9218539} +} +@inproceedings{Agarwal:2009:garnet, + author = {Niket Agarwal and + Tushar Krishna and + Li{-}Shiuan Peh and + Niraj K. Jha}, + title = {{GARNET:} {A} detailed on-chip network model inside a full-system + simulator}, + booktitle = {{IEEE} International Symposium on Performance Analysis of Systems + and Software, {ISPASS} 2009, April 26-28, 2009, Boston, Massachusetts, + USA, Proceedings}, + pages = {33--42}, + publisher = {{IEEE} Computer Society}, + year = {2009}, + url = {https://doi.org/10.1109/ISPASS.2009.4919636}, + doi = {10.1109/ISPASS.2009.4919636} +} +""", +) diff --git a/src/mem/ruby/network/garnet/SConscript b/src/mem/ruby/network/garnet/SConscript index 9e6e19e9b6..b0dcfe690c 100644 --- a/src/mem/ruby/network/garnet/SConscript +++ b/src/mem/ruby/network/garnet/SConscript @@ -28,7 +28,7 @@ Import('*') -if env['CONF']['PROTOCOL'] == 'None': +if not env['CONF']['RUBY']: Return() SimObject('GarnetLink.py', enums=['CDCType'], sim_objects=[ diff --git a/src/mem/ruby/network/simple/SConscript b/src/mem/ruby/network/simple/SConscript index 97055f5080..011552afae 100644 --- a/src/mem/ruby/network/simple/SConscript +++ b/src/mem/ruby/network/simple/SConscript @@ -40,7 +40,7 @@ Import('*') -if env['CONF']['PROTOCOL'] == 'None': +if not env['CONF']['RUBY']: Return() SimObject('SimpleLink.py', sim_objects=['SimpleExtLink', 'SimpleIntLink']) diff --git a/src/mem/ruby/network/simple/SimpleLink.py b/src/mem/ruby/network/simple/SimpleLink.py index 0d3f20698d..83aa2fbe71 100644 --- a/src/mem/ruby/network/simple/SimpleLink.py +++ b/src/mem/ruby/network/simple/SimpleLink.py @@ -36,12 +36,15 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.BasicLink import ( + BasicExtLink, + BasicIntLink, +) +from m5.objects.MessageBuffer import MessageBuffer from m5.params import * from m5.proxy import * -from m5.util import fatal from m5.SimObject import SimObject -from m5.objects.BasicLink import BasicIntLink, BasicExtLink -from m5.objects.MessageBuffer import MessageBuffer +from m5.util import fatal class SimpleExtLink(BasicExtLink): diff --git a/src/mem/ruby/network/simple/SimpleNetwork.py b/src/mem/ruby/network/simple/SimpleNetwork.py index aa553deee3..e52333b24d 100644 --- a/src/mem/ruby/network/simple/SimpleNetwork.py +++ b/src/mem/ruby/network/simple/SimpleNetwork.py @@ -36,14 +36,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * - -from m5.util import fatal -from m5.SimObject import SimObject -from m5.objects.Network import RubyNetwork from m5.objects.BasicRouter import BasicRouter from m5.objects.MessageBuffer import MessageBuffer +from m5.objects.Network import RubyNetwork +from m5.params import * +from m5.proxy import * +from m5.SimObject import SimObject +from m5.util import fatal class SimpleNetwork(RubyNetwork): diff --git a/src/mem/ruby/profiler/SConscript b/src/mem/ruby/profiler/SConscript index 0c493e9be3..ff9cc099f2 100644 --- a/src/mem/ruby/profiler/SConscript +++ b/src/mem/ruby/profiler/SConscript @@ -28,7 +28,7 @@ Import('*') -if env['CONF']['PROTOCOL'] == 'None': +if not env['CONF']['RUBY']: Return() Source('AccessTraceForAddress.cc') diff --git a/src/mem/ruby/protocol/GPU_VIPER-SQC.sm b/src/mem/ruby/protocol/GPU_VIPER-SQC.sm index 28bddf5ba4..bdc5d73f20 100644 --- a/src/mem/ruby/protocol/GPU_VIPER-SQC.sm +++ b/src/mem/ruby/protocol/GPU_VIPER-SQC.sm @@ -1,5 +1,6 @@ /* * Copyright (c) 2012-2015 Advanced Micro Devices, Inc. + * Copyright (c) 2023 Matthew D. Sinclair * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,6 +48,9 @@ machine(MachineType:SQC, "GPU SQC (L1 I Cache)") { state_declaration(State, desc="SQC Cache States", default="SQC_State_I") { I, AccessPermission:Invalid, desc="Invalid"; + // Note: currently IV in the TCP is only for pending loads to a given cache + // line. Since the SQC is read only, there are no stores. + IV, AccessPermission:Invalid, desc="Going from I to V, waiting on TCC data"; V, AccessPermission:Read_Only, desc="Valid"; } @@ -74,7 +78,7 @@ machine(MachineType:SQC, "GPU SQC (L1 I Cache)") } structure(TBE, desc="...") { - State TBEState, desc="Transient state"; + State TBEState, desc="Transient state"; DataBlock DataBlk, desc="data for the block, required for concurrent writebacks"; bool Dirty, desc="Is the data dirty (different than memory)?"; int NumPendingMsgs, desc="Number of acks/data messages that this processor is waiting for"; @@ -97,6 +101,7 @@ machine(MachineType:SQC, "GPU SQC (L1 I Cache)") void unset_tbe(); void wakeUpAllBuffers(); void wakeUpBuffers(Addr a); + void wakeUpAllBuffers(Addr a); Cycles curCycle(); // Internal functions @@ -269,6 +274,21 @@ machine(MachineType:SQC, "GPU SQC (L1 I Cache)") } } + action(t_allocateTBE, "t", desc="allocate TBE Entry") { + check_allocate(TBEs); + TBEs.allocate(address); + set_tbe(TBEs.lookup(address)); + } + + action(d_deallocateTBE, "d", desc="Deallocate TBE") { + TBEs.deallocate(address); + unset_tbe(); + } + + action(st_stallAndWaitRequest, "st", desc="Stall and wait on the address") { + stall_and_wait(mandatoryQueue_in, address); + } + action(p_popMandatoryQueue, "pm", desc="Pop Mandatory Queue") { mandatoryQueue_in.dequeue(clockEdge()); } @@ -277,6 +297,10 @@ machine(MachineType:SQC, "GPU SQC (L1 I Cache)") responseToSQC_in.dequeue(clockEdge()); } + action(wada_wakeUpAllDependentsAddr, "wada", desc="Wake up any requests waiting for this address") { + wakeUpAllBuffers(address); + } + action(l_loadDoneHit, "ldh", desc="local load done (hits in SQC)") { assert(is_valid(cache_entry)); sequencer.readCallback(address, cache_entry.DataBlk, true, MachineType:L1Cache); @@ -297,6 +321,10 @@ machine(MachineType:SQC, "GPU SQC (L1 I Cache)") } } + action(mru_updateMRU, "mru", desc="Touch block for replacement policy") { + L1cache.setMRU(address); + } + // added for profiling action(uu_profileDataMiss, "\udm", desc="Profile SQC demand miss"){ L1cache.profileDemandMiss(); @@ -308,22 +336,52 @@ machine(MachineType:SQC, "GPU SQC (L1 I Cache)") // Transitions + // if another request arrives for the same cache line that has a pending + // load, put it on the wakeup buffer. This reduced resource contention since + // they won't try again every cycle and will instead only try again once woken + // up + transition(IV, {Fetch}) { + st_stallAndWaitRequest; + } + // transitions from base - transition({I, V}, Repl, I) {TagArrayRead, TagArrayWrite} { + transition({I, IV, V}, Repl, I) {TagArrayRead, TagArrayWrite} { // since we're evicting something, don't bother classifying as hit/miss ic_invCache; } - transition(I, Data, V) {TagArrayRead, TagArrayWrite, DataArrayRead} { + // if we got a response for a load where the line is in I, then + // another request must have come in that replaced the line in question in + // the cache. Thus, complete this request without allocating the line, but + // still deallocate TBE and wakeup any dependent addresses. + transition(I, Data) {TagArrayRead, TagArrayWrite, DataArrayRead} { + // don't profile this as a hit/miss since it's a reponse from L2, + // so we already counted it + l_loadDoneMiss; + wada_wakeUpAllDependentsAddr; + d_deallocateTBE; + pr_popResponseQueue; + } + + // if line is currently in IV, then Data is returning the data for a + // pending load, so transition to V, deallocate TBE, and wakeup any dependent + // requests so they will be replayed now that this request has returned. + transition(IV, Data, V) {TagArrayRead, TagArrayWrite, DataArrayRead} { a_allocate; // don't profile this as a hit/miss since it's a reponse from L2, // so we already counted it w_writeCache; l_loadDoneMiss; + wada_wakeUpAllDependentsAddr; + d_deallocateTBE; pr_popResponseQueue; } - transition(I, Fetch) {TagArrayRead, TagArrayWrite} { + // if we have a load that misses, allocate TBE entry and transition to IV + // to prevent subsequent requests to same cache line from also going to TCC + // while this request is pending + transition(I, Fetch, IV) {TagArrayRead, TagArrayWrite} { + t_allocateTBE; nS_issueRdBlkS; uu_profileDataMiss; // since line wasn't in SQC, we missed p_popMandatoryQueue; @@ -332,6 +390,7 @@ machine(MachineType:SQC, "GPU SQC (L1 I Cache)") // simple hit transitions transition(V, Fetch) {TagArrayRead, DataArrayRead} { l_loadDoneHit; + mru_updateMRU; uu_profileDataHit; // line was in SQC, so we hit p_popMandatoryQueue; } diff --git a/src/mem/ruby/protocol/GPU_VIPER-TCC.sm b/src/mem/ruby/protocol/GPU_VIPER-TCC.sm index a59589870d..e599d2f937 100644 --- a/src/mem/ruby/protocol/GPU_VIPER-TCC.sm +++ b/src/mem/ruby/protocol/GPU_VIPER-TCC.sm @@ -36,6 +36,7 @@ machine(MachineType:TCC, "TCC Cache") bool WB; /*is this cache Writeback?*/ Cycles l2_request_latency := 50; Cycles l2_response_latency := 20; + Cycles glc_atomic_latency := 0; // From the TCPs or SQCs MessageBuffer * requestFromTCP, network="From", virtual_network="1", vnet_type="request"; @@ -60,10 +61,13 @@ machine(MachineType:TCC, "TCC Cache") WrVicBlk, desc="L1 Write Through"; WrVicBlkBack, desc="L1 Write Through(dirty cache)"; WrVicBlkEvict, desc="L1 Write Through(dirty cache) and evict"; + AtomicWait, desc="Atomic Op that must wait for pending loads"; Atomic, desc="Atomic Op"; + AtomicPassOn, desc="Atomic Op Passed on to Directory"; AtomicDone, desc="AtomicOps Complete"; AtomicNotDone, desc="AtomicOps not Complete"; - Data, desc="data messgae"; + Data, desc="Data message"; + Flush, desc="Flush cache entry"; // Coming from this TCC L2_Repl, desc="L2 Replacement"; // Probes @@ -81,6 +85,7 @@ machine(MachineType:TCC, "TCC Cache") I, AccessPermission:Invalid, desc="Invalid"; IV, AccessPermission:Busy, desc="Waiting for Data"; WI, AccessPermission:Busy, desc="Waiting on Writethrough Ack"; + WIB, AccessPermission:Busy, desc="Waiting on Writethrough Ack; Will be Bypassed"; A, AccessPermission:Busy, desc="Invalid waiting on atomici Data"; } @@ -89,6 +94,7 @@ machine(MachineType:TCC, "TCC Cache") DataArrayWrite, desc="Write the data array"; TagArrayRead, desc="Read the data array"; TagArrayWrite, desc="Write the data array"; + AtomicALUOperation, desc="Atomic ALU operation"; } @@ -102,16 +108,18 @@ machine(MachineType:TCC, "TCC Cache") } structure(TBE, desc="...") { - State TBEState, desc="Transient state"; - DataBlock DataBlk, desc="data for the block"; - bool Dirty, desc="Is the data dirty?"; - bool Shared, desc="Victim hit by shared probe"; - MachineID From, desc="Waiting for writeback from..."; - NetDest Destination, desc="Data destination"; - int numAtomics, desc="number remaining atomics"; - int atomicDoneCnt, desc="number AtomicDones triggered"; - bool isGLCSet, desc="Bypass L1 Cache"; - bool isSLCSet, desc="Bypass L1 and L2 Cache"; + State TBEState, desc="Transient state"; + DataBlock DataBlk, desc="data for the block"; + bool Dirty, desc="Is the data dirty?"; + bool Shared, desc="Victim hit by shared probe"; + MachineID From, desc="Waiting for writeback from..."; + NetDest Destination, desc="Data destination"; + int numPending, desc="num pending requests"; + int numPendingDirectoryAtomics, desc="number of pending atomics to be performed in directory"; + int atomicDoneCnt, desc="number AtomicDones triggered"; + bool isGLCSet, desc="Bypass L1 Cache"; + bool isSLCSet, desc="Bypass L1 and L2 Cache"; + WriteMask atomicWriteMask, desc="Atomic write mask"; } structure(TBETable, external="yes") { @@ -218,6 +226,8 @@ machine(MachineType:TCC, "TCC Cache") L2cache.recordRequestType(CacheRequestType:TagArrayRead, addr); } else if (request_type == RequestType:TagArrayWrite) { L2cache.recordRequestType(CacheRequestType:TagArrayWrite, addr); + } else if (request_type == RequestType:AtomicALUOperation) { + L2cache.recordRequestType(CacheRequestType:AtomicALUOperation, addr); } } @@ -230,6 +240,8 @@ machine(MachineType:TCC, "TCC Cache") return L2cache.checkResourceAvailable(CacheResourceType:TagArray, addr); } else if (request_type == RequestType:TagArrayWrite) { return L2cache.checkResourceAvailable(CacheResourceType:TagArray, addr); + } else if (request_type == RequestType:AtomicALUOperation) { + return L2cache.checkResourceAvailable(CacheResourceType:AtomicALUArray, addr); } else { error("Invalid RequestType type in checkResourceAvailable"); return true; @@ -253,21 +265,22 @@ machine(MachineType:TCC, "TCC Cache") // request queue going to NB // - -// ** IN_PORTS ** + // ** IN_PORTS ** in_port(triggerQueue_in, TriggerMsg, triggerQueue) { if (triggerQueue_in.isReady(clockEdge())) { peek(triggerQueue_in, TriggerMsg) { TBE tbe := TBEs.lookup(in_msg.addr); Entry cache_entry := getCacheEntry(in_msg.addr); + // The trigger queue applies only to atomics performed in the directory. + // There is a possible race where multiple AtomicDone triggers can be // sent if another Atomic to the same address is issued after the // AtomicDone is triggered but before the message arrives here. For // that case we count the number of AtomicDones in flight for this // address and only call AtomicDone to deallocate the TBE when it is // the last in flight message. - if (tbe.numAtomics == 0 && tbe.atomicDoneCnt == 1) { + if (tbe.numPendingDirectoryAtomics == 0 && tbe.atomicDoneCnt == 1) { trigger(Event:AtomicDone, in_msg.addr, cache_entry, tbe); } else { trigger(Event:AtomicNotDone, in_msg.addr, cache_entry, tbe); @@ -276,32 +289,45 @@ machine(MachineType:TCC, "TCC Cache") } } - - + // handle responses from directory here in_port(responseFromNB_in, ResponseMsg, responseFromNB) { if (responseFromNB_in.isReady(clockEdge())) { peek(responseFromNB_in, ResponseMsg, block_on="addr") { TBE tbe := TBEs.lookup(in_msg.addr); Entry cache_entry := getCacheEntry(in_msg.addr); - bool is_slc_set := false; + /* + MOESI_AMD_Base-dir acts as the directory, and it always passes + SLC information back to L2 because of races at L2 with requests + from different CUs sending requests to same cache line in parallel. + If these requests have different GLC/SLC settings, the L2 TBE may + not have the correct GLC/SLC information for a given request. + */ + bool is_slc_set := in_msg.isSLCSet; - if (!is_invalid(tbe)) { - is_slc_set := tbe.isSLCSet; - } - - if (is_slc_set) { - // If the SLC bit is set, the response needs to bypass the cache - // and should not be allocated an entry. - trigger(Event:Bypass, in_msg.addr, cache_entry, tbe); - } else if (in_msg.Type == CoherenceResponseType:NBSysResp) { - if(presentOrAvail(in_msg.addr)) { - trigger(Event:Data, in_msg.addr, cache_entry, tbe); - } else { - Addr victim := L2cache.cacheProbe(in_msg.addr); - trigger(Event:L2_Repl, victim, getCacheEntry(victim), TBEs.lookup(victim)); - } - } else if (in_msg.Type == CoherenceResponseType:NBSysWBAck) { + // Whether the SLC bit is set or not, WB acks should invoke the + // WBAck event. For cases where a read response will follow a + // WBAck (A read bypass evict on a dirty line), the line's TLB + // will not be deallocated on WBAck, and the SLC bit will be + // checked when the read response is received. + if (in_msg.Type == CoherenceResponseType:NBSysWBAck) { trigger(Event:WBAck, in_msg.addr, cache_entry, tbe); + } else if(in_msg.Type == CoherenceResponseType:NBSysResp) { + // If the SLC bit is set or the cache is write-through and + // we're receiving modified data (such as from an atomic), + // the response needs to bypass the cache and should not be + // allocated an entry. + if(is_slc_set || (!WB && in_msg.State == CoherenceState:Modified)) { + trigger(Event:Bypass, in_msg.addr, cache_entry, tbe); + } else { + if(presentOrAvail(in_msg.addr)) { + // Responses with atomic data will only reach here if the + // SLC bit isn't set and the cache is WB + trigger(Event:Data, in_msg.addr, cache_entry, tbe); + } else { + Addr victim := L2cache.cacheProbe(in_msg.addr); + trigger(Event:L2_Repl, victim, getCacheEntry(victim), TBEs.lookup(victim)); + } + } } else { error("Unexpected Response Message to Core"); } @@ -348,12 +374,33 @@ machine(MachineType:TCC, "TCC Cache") } else { trigger(Event:WrVicBlk, in_msg.addr, cache_entry, tbe); } - } else if (in_msg.Type == CoherenceRequestType:Atomic) { - // Currently the Atomic requests do not have GLC/SLC bit handing - // support. The assert ensures that the requests do not have - // these set, and therefore do not expect to bypass the cache - assert(!in_msg.isSLCSet); - trigger(Event:Atomic, in_msg.addr, cache_entry, tbe); + } else if (in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn) { + /* + If there are pending requests for this line already and those + requests are not atomics, because we can't easily differentiate + between different request types on return and because decrementing + the atomic count assumes all returned requests in the A state are + atomics, we will need to put this atomic to sleep and wake it up + when the loads return. + */ + if (is_valid(tbe) && (tbe.numPending > 0) && + (tbe.numPendingDirectoryAtomics == 0)) { + trigger(Event:AtomicWait, in_msg.addr, cache_entry, tbe); + } else { + // If the request is system-level, if the address isn't in the cache, + // or if this cache is write-through, then send the request to the + // directory. Since non-SLC atomics won't be performed by the directory, + // TCC will perform the atomic on the return path on Event:Data. + // The action will invalidate the cache line if SLC is set and the address is + // in the cache. + if(in_msg.isSLCSet || !WB) { + trigger(Event:AtomicPassOn, in_msg.addr, cache_entry, tbe); + } else { + trigger(Event:Atomic, in_msg.addr, cache_entry, tbe); + } + } } else if (in_msg.Type == CoherenceRequestType:RdBlk) { if (in_msg.isSLCSet) { // If SLC bit is set, the request needs to go directly to memory. @@ -362,6 +409,8 @@ machine(MachineType:TCC, "TCC Cache") } else { trigger(Event:RdBlk, in_msg.addr, cache_entry, tbe); } + } else if (in_msg.Type == CoherenceRequestType:WriteFlush) { + trigger(Event:Flush, in_msg.addr, cache_entry, tbe); } else { DPRINTF(RubySlicc, "%s\n", in_msg); error("Unexpected Response Message to Core"); @@ -402,24 +451,35 @@ machine(MachineType:TCC, "TCC Cache") out_msg.addr := address; out_msg.Type := CoherenceResponseType:TDSysResp; out_msg.Sender := machineID; - out_msg.Destination := tbe.Destination; - out_msg.DataBlk := cache_entry.DataBlk; out_msg.MessageSize := MessageSizeType:Response_Data; out_msg.Dirty := false; out_msg.State := CoherenceState:Shared; - DPRINTF(RubySlicc, "%s\n", out_msg); peek(responseFromNB_in, ResponseMsg) { - out_msg.isGLCSet := tbe.isGLCSet; - out_msg.isSLCSet := tbe.isSLCSet; + // if line state is Invalid, then we must be doing the transition(I, Data) + // so use the DataBlk from the incoming message + if ((getAccessPermission(address) == AccessPermission:NotPresent) || + (getAccessPermission(address) == AccessPermission:Invalid)) { + out_msg.DataBlk := in_msg.DataBlk; + } else { + out_msg.DataBlk := cache_entry.DataBlk; + } + out_msg.isGLCSet := in_msg.isGLCSet; + out_msg.isSLCSet := in_msg.isSLCSet; + // reuse CURequestor field to allow multiple concurrent loads and + // track where they should go back to (since TBE can't distinguish + // destinations) + out_msg.Destination.clear(); + out_msg.Destination.add(in_msg.CURequestor); } + DPRINTF(RubySlicc, "%s\n", out_msg); } enqueue(unblockToNB_out, UnblockMsg, 1) { out_msg.addr := address; out_msg.Destination.add(mapAddressToMachine(address, MachineType:Directory)); out_msg.MessageSize := MessageSizeType:Unblock_Control; peek(responseFromNB_in, ResponseMsg) { - out_msg.isGLCSet := tbe.isGLCSet; - out_msg.isSLCSet := tbe.isSLCSet; + out_msg.isGLCSet := in_msg.isGLCSet; + out_msg.isSLCSet := in_msg.isSLCSet; } DPRINTF(RubySlicc, "%s\n", out_msg); } @@ -431,13 +491,17 @@ machine(MachineType:TCC, "TCC Cache") out_msg.addr := address; out_msg.Type := CoherenceResponseType:TDSysResp; out_msg.Sender := machineID; - out_msg.Destination := tbe.Destination; + // reuse CURequestor field to allow multiple concurrent loads and + // track where they should go back to (since TBE can't distinguish + // destinations) + out_msg.Destination.clear(); + out_msg.Destination.add(in_msg.CURequestor); out_msg.DataBlk := in_msg.DataBlk; out_msg.MessageSize := MessageSizeType:Response_Data; out_msg.Dirty := false; out_msg.State := CoherenceState:Shared; - out_msg.isGLCSet := tbe.isGLCSet; - out_msg.isSLCSet := tbe.isSLCSet; + out_msg.isGLCSet := in_msg.isGLCSet; + out_msg.isSLCSet := in_msg.isSLCSet; DPRINTF(RubySlicc, "%s\n", out_msg); } enqueue(unblockToNB_out, UnblockMsg, 1) { @@ -450,19 +514,25 @@ machine(MachineType:TCC, "TCC Cache") } action(rd_requestData, "r", desc="Miss in L2, pass on") { - if(tbe.Destination.count()==1){ - peek(coreRequestNetwork_in, CPURequestMsg) { - enqueue(requestToNB_out, CPURequestMsg, l2_request_latency) { - out_msg.addr := address; - out_msg.Type := in_msg.Type; - out_msg.Requestor := machineID; - out_msg.Destination.add(mapAddressToMachine(address, MachineType:Directory)); - out_msg.Shared := false; // unneeded for this request - out_msg.MessageSize := in_msg.MessageSize; - out_msg.isGLCSet := tbe.isGLCSet; - out_msg.isSLCSet := tbe.isSLCSet; - DPRINTF(RubySlicc, "%s\n", out_msg); - } + peek(coreRequestNetwork_in, CPURequestMsg) { + DPRINTF(RubySlicc, "in_msg: %s\n", in_msg); + enqueue(requestToNB_out, CPURequestMsg, l2_request_latency) { + out_msg.addr := address; + out_msg.Type := in_msg.Type; + out_msg.Requestor := machineID; + /* + To allow multiple concurrent requests from different CUs, we pass + the orgin information along to the directory, which stores it in its + TBE as appropriate before passing it back to the TCC on the return + path. + */ + out_msg.CURequestor := in_msg.Requestor; + out_msg.Destination.add(mapAddressToMachine(address, MachineType:Directory)); + out_msg.Shared := false; // unneeded for this request + out_msg.MessageSize := in_msg.MessageSize; + out_msg.isGLCSet := in_msg.isGLCSet; + out_msg.isSLCSet := in_msg.isSLCSet; + DPRINTF(RubySlicc, "out_msg: %s\n", out_msg); } } } @@ -473,7 +543,7 @@ machine(MachineType:TCC, "TCC Cache") out_msg.addr := address; out_msg.Type := CoherenceResponseType:TDSysWBAck; out_msg.Destination.clear(); - out_msg.Destination.add(in_msg.WTRequestor); + out_msg.Destination.add(in_msg.CURequestor); out_msg.Sender := machineID; out_msg.MessageSize := MessageSizeType:Writeback_Control; out_msg.instSeqNum := in_msg.instSeqNum; @@ -495,17 +565,64 @@ machine(MachineType:TCC, "TCC Cache") } } + action(fw_sendFlushResponse, "fw", desc="send Flush Response") { + peek(coreRequestNetwork_in, CPURequestMsg) { + enqueue(responseToCore_out, ResponseMsg, l2_response_latency) { + out_msg.addr := address; + out_msg.Type := CoherenceResponseType:TDSysWBAck; + out_msg.Destination.clear(); + out_msg.Destination.add(in_msg.Requestor); + out_msg.Sender := machineID; + out_msg.MessageSize := MessageSizeType:Writeback_Control; + out_msg.instSeqNum := in_msg.instSeqNum; + } + } + } + action(ar_sendAtomicResponse, "ar", desc="send Atomic Ack") { + peek(coreRequestNetwork_in, CPURequestMsg) { + enqueue(responseToCore_out, ResponseMsg, l2_response_latency + glc_atomic_latency, true) { + out_msg.addr := address; + out_msg.Type := CoherenceResponseType:TDSysResp; + out_msg.Destination.clear(); + out_msg.Destination.add(in_msg.Requestor); + out_msg.Sender := machineID; + out_msg.MessageSize := MessageSizeType:Response_Data; + out_msg.DataBlk := cache_entry.DataBlk; + out_msg.isGLCSet := in_msg.isGLCSet; + out_msg.isSLCSet := in_msg.isSLCSet; + } + } + cache_entry.DataBlk.clearAtomicLogEntries(); + } + + action(baplr_sendBypassedAtomicPerformedLocallyResponse, "barplr", desc="send locally-performed bypassed Atomic Ack") { peek(responseFromNB_in, ResponseMsg) { enqueue(responseToCore_out, ResponseMsg, l2_response_latency) { out_msg.addr := address; out_msg.Type := CoherenceResponseType:TDSysResp; - out_msg.Destination.add(in_msg.WTRequestor); + out_msg.Destination.add(in_msg.CURequestor); + out_msg.Sender := machineID; + out_msg.MessageSize := in_msg.MessageSize; + out_msg.DataBlk := cache_entry.DataBlk; + out_msg.isGLCSet := tbe.isGLCSet; + out_msg.isSLCSet := tbe.isSLCSet; + } + } + cache_entry.DataBlk.clearAtomicLogEntries(); + } + + action(bapdr_sendBypassedAtomicPerformedInDirectoryResponse, "bapdr", desc="send bypassed Atomic Ack") { + peek(responseFromNB_in, ResponseMsg) { + enqueue(responseToCore_out, ResponseMsg, l2_response_latency) { + out_msg.addr := address; + out_msg.Type := CoherenceResponseType:TDSysResp; + out_msg.Destination.add(in_msg.CURequestor); out_msg.Sender := machineID; out_msg.MessageSize := in_msg.MessageSize; out_msg.DataBlk := in_msg.DataBlk; - out_msg.isGLCSet := tbe.isGLCSet; - out_msg.isSLCSet := tbe.isSLCSet; + out_msg.isGLCSet := in_msg.isGLCSet; + out_msg.isSLCSet := in_msg.isSLCSet; } } } @@ -531,24 +648,51 @@ machine(MachineType:TCC, "TCC Cache") TBEs.allocate(address); set_tbe(TBEs.lookup(address)); tbe.Destination.clear(); - tbe.numAtomics := 0; + tbe.numPendingDirectoryAtomics := 0; tbe.atomicDoneCnt := 0; + tbe.numPending := 0; } + // each pending requests increments this count by 1 + tbe.numPending := tbe.numPending + 1; if (coreRequestNetwork_in.isReady(clockEdge())) { peek(coreRequestNetwork_in, CPURequestMsg) { - if(in_msg.Type == CoherenceRequestType:RdBlk || in_msg.Type == CoherenceRequestType:Atomic){ + if(in_msg.Type == CoherenceRequestType:RdBlk || + in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn){ tbe.Destination.add(in_msg.Requestor); } + /* + If there are multiple concurrent requests to the same cache line, each + one will overwrite the previous ones GLC/SLC information here. + If these requests have different GLC/SLC information, this causes + a segfault. Hence, currently the support relies on the directory to + pass back the GLC/SLC information instead of relying on L2 TBE to be + correct. + + This message is left here as an FYI for future developers. + */ tbe.isGLCSet := in_msg.isGLCSet; tbe.isSLCSet := in_msg.isSLCSet; + if(in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn){ + tbe.atomicWriteMask.clear(); + tbe.atomicWriteMask.orMask(in_msg.writeMask); + } } } } action(dt_deallocateTBE, "dt", desc="Deallocate TBE entry") { - tbe.Destination.clear(); - TBEs.deallocate(address); - unset_tbe(); + // since we may have multiple destinations, can't deallocate if we aren't + // last one + tbe.numPending := tbe.numPending - 1; + if (tbe.numPending == 0) { + tbe.Destination.clear(); + TBEs.deallocate(address); + unset_tbe(); + } } action(wcb_writeCacheBlock, "wcb", desc="write data to TCC") { @@ -566,12 +710,26 @@ machine(MachineType:TCC, "TCC Cache") } } + action(wardb_writeAtomicResponseDirtyBytes, "wardb", desc="write data to TCC") { + peek(responseFromNB_in, ResponseMsg) { + cache_entry.DataBlk := in_msg.DataBlk; + cache_entry.writeMask.orMask(tbe.atomicWriteMask); + DPRINTF(RubySlicc, "Writing to TCC: %s\n", in_msg); + } + } + + action(owm_orWriteMask, "owm", desc="or TCCs write mask") { + peek(coreRequestNetwork_in, CPURequestMsg) { + cache_entry.writeMask.orMask(in_msg.writeMask); + } + } + action(wt_writeThrough, "wt", desc="write back data") { peek(coreRequestNetwork_in, CPURequestMsg) { enqueue(requestToNB_out, CPURequestMsg, l2_request_latency) { out_msg.addr := address; out_msg.Requestor := machineID; - out_msg.WTRequestor := in_msg.Requestor; + out_msg.CURequestor := in_msg.Requestor; out_msg.Destination.add(mapAddressToMachine(address, MachineType:Directory)); out_msg.MessageSize := MessageSizeType:Data; out_msg.Type := CoherenceRequestType:WriteThrough; @@ -579,6 +737,8 @@ machine(MachineType:TCC, "TCC Cache") out_msg.DataBlk := in_msg.DataBlk; out_msg.writeMask.orMask(in_msg.writeMask); out_msg.instSeqNum := in_msg.instSeqNum; + out_msg.isGLCSet := in_msg.isGLCSet; + out_msg.isSLCSet := in_msg.isSLCSet; } } } @@ -587,7 +747,7 @@ machine(MachineType:TCC, "TCC Cache") enqueue(requestToNB_out, CPURequestMsg, l2_request_latency) { out_msg.addr := address; out_msg.Requestor := machineID; - out_msg.WTRequestor := machineID; + out_msg.CURequestor := machineID; out_msg.Destination.add(mapAddressToMachine(address, MachineType:Directory)); out_msg.MessageSize := MessageSizeType:Data; out_msg.Type := CoherenceRequestType:WriteThrough; @@ -597,17 +757,37 @@ machine(MachineType:TCC, "TCC Cache") } } + action(f_flush, "f", desc="write back data") { + peek(coreRequestNetwork_in, CPURequestMsg) { + enqueue(requestToNB_out, CPURequestMsg, l2_request_latency) { + out_msg.addr := address; + out_msg.Requestor := machineID; + out_msg.CURequestor := in_msg.Requestor; + out_msg.Destination.add(mapAddressToMachine(address, MachineType:Directory)); + out_msg.MessageSize := MessageSizeType:Data; + out_msg.Type := CoherenceRequestType:WriteFlush; + out_msg.Dirty := true; + out_msg.DataBlk := cache_entry.DataBlk; + out_msg.writeMask.orMask(cache_entry.writeMask); + out_msg.isGLCSet := in_msg.isGLCSet; + out_msg.isSLCSet := in_msg.isSLCSet; + } + } + } + action(at_atomicThrough, "at", desc="write back data") { peek(coreRequestNetwork_in, CPURequestMsg) { enqueue(requestToNB_out, CPURequestMsg, l2_request_latency) { out_msg.addr := address; out_msg.Requestor := machineID; - out_msg.WTRequestor := in_msg.Requestor; + out_msg.CURequestor := in_msg.Requestor; out_msg.Destination.add(mapAddressToMachine(address, MachineType:Directory)); out_msg.MessageSize := MessageSizeType:Data; - out_msg.Type := CoherenceRequestType:Atomic; + out_msg.Type := in_msg.Type; out_msg.Dirty := true; out_msg.writeMask.orMask(in_msg.writeMask); + out_msg.isGLCSet := in_msg.isGLCSet; + out_msg.isSLCSet := in_msg.isSLCSet; } } } @@ -649,26 +829,40 @@ machine(MachineType:TCC, "TCC Cache") wakeUpAllBuffers(address); } + /* + Currently z_stall is unused because it can lead to Protocol Stalls that + eventually lead to deadlock. Instead, it is recommended to use + st_stallAndWaitRequest in combination with a wakeupBuffer call (e.g., + wada_wakeUpAllDependentsAddr) to put the pending requests to sleep instead of + them causing head of line blocking -- wada_wakeUpAllDependentsAddr should wake + the request up once the request preventing it from completing is done. action(z_stall, "z", desc="stall") { // built-in } + */ - action(ina_incrementNumAtomics, "ina", desc="inc num atomics") { - tbe.numAtomics := tbe.numAtomics + 1; + action(inpa_incrementNumPendingDirectoryAtomics, "inpa", desc="inc num atomics") { + // Only increment number of atomics if they will actually be performed in directory + // That is, if the SLC bit is set or if the cache is write through + peek(coreRequestNetwork_in, CPURequestMsg) { + if (in_msg.isSLCSet || !WB) { + tbe.numPendingDirectoryAtomics := tbe.numPendingDirectoryAtomics + 1; + } + } } - action(dna_decrementNumAtomics, "dna", desc="inc num atomics") { - tbe.numAtomics := tbe.numAtomics - 1; - if (tbe.numAtomics==0) { + action(dnpa_decrementNumPendingDirectoryAtomics, "dnpa", desc="dec num atomics") { + tbe.numPendingDirectoryAtomics := tbe.numPendingDirectoryAtomics - 1; + if (tbe.numPendingDirectoryAtomics==0) { enqueue(triggerQueue_out, TriggerMsg, 1) { tbe.atomicDoneCnt := tbe.atomicDoneCnt + 1; out_msg.addr := address; out_msg.Type := TriggerType:AtomicDone; peek(responseFromNB_in, ResponseMsg) { - out_msg.isGLCSet := tbe.isGLCSet; - out_msg.isSLCSet := tbe.isSLCSet; + out_msg.isGLCSet := in_msg.isGLCSet; + out_msg.isSLCSet := in_msg.isSLCSet; } } } @@ -682,6 +876,19 @@ machine(MachineType:TCC, "TCC Cache") triggerQueue_in.dequeue(clockEdge()); } + action(pa_performAtomic, "pa", desc="Perform atomic") { + peek(coreRequestNetwork_in, CPURequestMsg) { + if (in_msg.Type == CoherenceRequestType:AtomicReturn) { + cache_entry.DataBlk.atomicPartial(cache_entry.DataBlk, cache_entry.writeMask, false); + } else { + // Set the isAtomicNoReturn flag to ensure that logs are not + // generated erroneously + assert(in_msg.Type == CoherenceRequestType:AtomicNoReturn); + cache_entry.DataBlk.atomicPartial(cache_entry.DataBlk, cache_entry.writeMask, true); + } + } + } + // END ACTIONS // BEGIN TRANSITIONS @@ -693,37 +900,71 @@ machine(MachineType:TCC, "TCC Cache") // Stalling transitions do NOT check the tag array...and if they do, // they can cause a resource stall deadlock! - transition(WI, {RdBlk, WrVicBlk, Atomic, WrVicBlkBack}) { //TagArrayRead} { - // by putting the stalled requests in a buffer, we reduce resource contention - // since they won't try again every cycle and will instead only try again once - // woken up + transition(WI, {RdBlk, WrVicBlk, Atomic, AtomicPassOn, WrVicBlkBack}) { //TagArrayRead} { + // don't profile as hit or miss since it will be tried again + /* + By putting the stalled requests in a buffer, we reduce resource contention + since they won't try again every cycle and will instead only try again once + woken up. + */ + st_stallAndWaitRequest; + } + transition(WIB, {RdBlk, WrVicBlk, Atomic, WrVicBlkBack}) { //TagArrayRead} { + // don't profile as hit or miss since it will be tried again + /* + By putting the stalled requests in a buffer, we reduce resource contention + since they won't try again every cycle and will instead only try again once + woken up. + */ st_stallAndWaitRequest; } transition(A, {RdBlk, WrVicBlk, WrVicBlkBack}) { //TagArrayRead} { - // by putting the stalled requests in a buffer, we reduce resource contention - // since they won't try again every cycle and will instead only try again once - // woken up + // don't profile as hit or miss since it will be tried again + /* + By putting the stalled requests in a buffer, we reduce resource contention + since they won't try again every cycle and will instead only try again once + woken up. + */ st_stallAndWaitRequest; } - transition(IV, {WrVicBlk, Atomic, WrVicBlkBack}) { //TagArrayRead} { - // by putting the stalled requests in a buffer, we reduce resource contention - // since they won't try again every cycle and will instead only try again once - // woken up + + transition(IV, {WrVicBlk, Atomic, AtomicPassOn, WrVicBlkBack}) { //TagArrayRead} { + // don't profile as hit or miss since it will be tried again + /* + By putting the stalled requests in a buffer, we reduce resource contention + since they won't try again every cycle and will instead only try again once + woken up. + */ st_stallAndWaitRequest; } + + transition({I, IV, V, WI}, AtomicWait) { + // don't profile as hit or miss since it will be tried again + /* + By putting the stalled requests in a buffer, we reduce resource contention + since they won't try again every cycle and will instead only try again once + woken up. + */ + st_stallAndWaitRequest; + } + transition({M, V}, RdBlk) {TagArrayRead, DataArrayRead} { p_profileHit; sd_sendData; ut_updateTag; p_popRequestQueue; } + transition(W, RdBlk, WI) {TagArrayRead, DataArrayRead} { + // don't profile as hit or miss since it will be tried again t_allocateTBE; wb_writeBack; - // need to try this request again after writing back the current entry -- to - // do so, put it with other stalled requests in a buffer to reduce resource - // contention since they won't try again every cycle and will instead only - // try again once woken up + /* + Need to try this request again after writing back the current entry -- to + do so, put it with other stalled requests in a buffer to reduce resource + contention since they won't try again every cycle and will instead only + try again once woken up. + */ st_stallAndWaitRequest; } @@ -748,10 +989,10 @@ machine(MachineType:TCC, "TCC Cache") p_popRequestQueue; } -// Transition to be called when a read request with SLC flag set arrives at -// entry in state W. It evicts and invalidates the cache entry before -// forwarding the request to global memory - transition(W, RdBypassEvict, I) {TagArrayRead} { + // Transition to be called when a read request with SLC flag set arrives at + // entry in state W. It evicts and invalidates the cache entry before + // forwarding the request to global memory + transition(W, RdBypassEvict, WIB) {TagArrayRead} { p_profileMiss; t_allocateTBE; wb_writeBack; @@ -760,10 +1001,10 @@ machine(MachineType:TCC, "TCC Cache") p_popRequestQueue; } -// Transition to be called when a read request with SLC flag set arrives at -// entry in state M. It evicts and invalidates the cache entry before -// forwarding the request to global memory to main memory - transition(M, RdBypassEvict, I) {TagArrayRead} { + // Transition to be called when a read request with SLC flag set arrives at + // entry in state M. It evicts and invalidates the cache entry before + // forwarding the request to global memory to main memory + transition(M, RdBypassEvict, WIB) {TagArrayRead} { p_profileMiss; t_allocateTBE; wb_writeBack; @@ -772,9 +1013,9 @@ machine(MachineType:TCC, "TCC Cache") p_popRequestQueue; } -// Transition to be called when a read request with SLC flag set arrives at -// entry in state V. It invalidates the cache entry before forwarding the -// request to global memory. + // Transition to be called when a read request with SLC flag set arrives at + // entry in state V. It invalidates the cache entry before forwarding the + // request to global memory. transition(V, RdBypassEvict, I) {TagArrayRead} { p_profileMiss; t_allocateTBE; @@ -783,39 +1024,78 @@ machine(MachineType:TCC, "TCC Cache") p_popRequestQueue; } -// Transition to be called when a read request with SLC flag arrives at entry -// in transient state. The request stalls until the pending transition is complete. - transition({WI, IV}, RdBypassEvict) { + // Transition to be called when a read request with SLC flag arrives at entry + // in transient state. The request stalls until the pending transition is complete. + transition({WI, WIB, IV}, RdBypassEvict) { + // don't profile as hit or miss since it will be tried again st_stallAndWaitRequest; } - transition(V, Atomic, A) {TagArrayRead} { + transition(V, Atomic, M) {TagArrayRead, TagArrayWrite, DataArrayWrite, AtomicALUOperation} { p_profileHit; - i_invL2; - t_allocateTBE; - at_atomicThrough; - ina_incrementNumAtomics; + ut_updateTag; + owm_orWriteMask; + pa_performAtomic; + ar_sendAtomicResponse; p_popRequestQueue; } -transition(I, Atomic, A) {TagArrayRead} { - p_profileMiss; - i_invL2; - t_allocateTBE; - at_atomicThrough; - ina_incrementNumAtomics; - p_popRequestQueue; - } - - transition(A, Atomic) { - p_profileMiss; + transition(A, {Atomic, AtomicWait}) { + // don't profile as hit or miss since it will be tried again // by putting the stalled requests in a buffer, we reduce resource contention // since they won't try again every cycle and will instead only try again once // woken up st_stallAndWaitRequest; } - transition({M, W}, Atomic, WI) {TagArrayRead} { + transition(W, Atomic, WI) { + t_allocateTBE; + wb_writeBack; + // need to try this request again after writing back the current entry -- to + // do so, put it with other stalled requests in a buffer to reduce resource + // contention since they won't try again every cycle and will instead only + // try again once woken up + st_stallAndWaitRequest; + } + + transition(M, Atomic) {TagArrayRead, DataArrayWrite, AtomicALUOperation} { + p_profileHit; + owm_orWriteMask; + pa_performAtomic; + ar_sendAtomicResponse; + p_popRequestQueue; + } + + // The following atomic pass on actions will send the request to the directory, + // and are triggered when an atomic request is received that is not in TCC, + // and/or if SLC is set. + transition(V, AtomicPassOn, A) {TagArrayRead} { + p_profileHit; + i_invL2; + t_allocateTBE; + at_atomicThrough; + inpa_incrementNumPendingDirectoryAtomics; + p_popRequestQueue; + } + + transition(I, {Atomic, AtomicPassOn}, A) {TagArrayRead} { + p_profileMiss; + i_invL2; + t_allocateTBE; + at_atomicThrough; + inpa_incrementNumPendingDirectoryAtomics; + p_popRequestQueue; + } + + transition(A, AtomicPassOn) { + // don't profile as hit or miss since it will be tried again + // by putting the stalled requests in a buffer, we reduce resource contention + // since they won't try again every cycle and will instead only try again once + // woken up + st_stallAndWaitRequest; + } + + transition({M, W}, AtomicPassOn, WI) {TagArrayRead, DataArrayRead} { t_allocateTBE; wb_writeBack; // after writing back the current line, we need to wait for it to be done @@ -865,9 +1145,9 @@ transition(I, Atomic, A) {TagArrayRead} { p_popRequestQueue; } -// Transition to be called when a write request with SLC bit set arrives at an -// entry with state V. The entry has to be evicted and invalidated before the -// request is forwarded to global memory + // Transition to be called when a write request with SLC bit set arrives at an + // entry with state V. The entry has to be evicted and invalidated before the + // request is forwarded to global memory transition(V, WrVicBlkEvict, I) {TagArrayRead, TagArrayWrite, DataArrayWrite} { p_profileMiss; ut_updateTag; @@ -877,9 +1157,9 @@ transition(I, Atomic, A) {TagArrayRead} { p_popRequestQueue; } -// Transition to be called when a write request with SLC bit set arrives at an -// entry with state W. The entry has to be evicted and invalidated before the -// request is forwarded to global memory. + // Transition to be called when a write request with SLC bit set arrives at an + // entry with state W. The entry has to be evicted and invalidated before the + // request is forwarded to global memory. transition(W, WrVicBlkEvict, I) {TagArrayRead, TagArrayWrite, DataArrayWrite} { p_profileMiss; ut_updateTag; @@ -900,7 +1180,7 @@ transition(I, Atomic, A) {TagArrayRead} { i_invL2; } - transition({A, IV, WI}, L2_Repl) { + transition({A, IV, WI, WIB}, L2_Repl) { i_invL2; } @@ -919,14 +1199,14 @@ transition(I, Atomic, A) {TagArrayRead} { pp_popProbeQueue; } - transition({A, IV, WI}, PrbInv) { + transition({A, IV, WI, WIB}, PrbInv) { pi_sendProbeResponseInv; pp_popProbeQueue; } -// Transition to be called when the response for a request with SLC bit set -// arrives. The request has to be forwarded to the core that needs it while -// making sure no entry is allocated. + // Transition to be called when the response for a request with SLC bit set + // arrives. The request has to be forwarded to the core that needs it while + // making sure no entry is allocated. transition(I, Bypass, I) { rb_bypassDone; pr_popResponseQueue; @@ -934,20 +1214,66 @@ transition(I, Atomic, A) {TagArrayRead} { dt_deallocateTBE; } + transition(A, Bypass) {TagArrayRead, TagArrayWrite} { + bapdr_sendBypassedAtomicPerformedInDirectoryResponse; + dnpa_decrementNumPendingDirectoryAtomics; + pr_popResponseQueue; + } + + transition(WI, Bypass, I) { + pr_popResponseQueue; + wada_wakeUpAllDependentsAddr; + dt_deallocateTBE; + } + transition(IV, Data, V) {TagArrayRead, TagArrayWrite, DataArrayWrite} { a_allocateBlock; ut_updateTag; wcb_writeCacheBlock; sdr_sendDataResponse; - pr_popResponseQueue; wada_wakeUpAllDependentsAddr; dt_deallocateTBE; + pr_popResponseQueue; } - transition(A, Data) {TagArrayRead, TagArrayWrite, DataArrayWrite} { + /* + Since the L2 now allows multiple loads from different CUs to proceed in + parallel to the directory, we may get Event:Data back when the line is + already in V. In this case, send the response to the appropriate TCP + and update MRU/data in TCC, but don't need to allocate line. + */ + transition(V, Data) {TagArrayRead, TagArrayWrite, DataArrayWrite} { + ut_updateTag; + wcb_writeCacheBlock; + sdr_sendDataResponse; + wada_wakeUpAllDependentsAddr; + // tracks # pending requests, so need to decrement here too + dt_deallocateTBE; + pr_popResponseQueue; + } + + /* + Since the L2 now allows multiple loads from different CUs to proceed in + parallel to the directory, we may get Event:Data back when the line is + now in I because it has been evicted by an intervening request to the same + set index. In this case, send the response to the appropriate TCP without + affecting the TCC (essentially, treat it similar to a bypass request except + we also send the unblock back to the directory). + */ + transition(I, Data) { + sdr_sendDataResponse; + wada_wakeUpAllDependentsAddr; + // tracks # pending requests, so need to decrement here too + dt_deallocateTBE; + pr_popResponseQueue; + } + + transition(A, Data, M) {TagArrayRead, TagArrayWrite, DataArrayWrite, AtomicALUOperation} { a_allocateBlock; - ar_sendAtomicResponse; - dna_decrementNumAtomics; + wardb_writeAtomicResponseDirtyBytes; + pa_performAtomic; + baplr_sendBypassedAtomicPerformedLocallyResponse; + dt_deallocateTBE; pr_popResponseQueue; } @@ -974,4 +1300,25 @@ transition(I, Atomic, A) {TagArrayRead} { wada_wakeUpAllDependentsAddr; pr_popResponseQueue; } + + transition(WIB, WBAck,I) { + pr_popResponseQueue; + } + + transition({A, IV, WI, WIB}, Flush) { + st_stallAndWaitRequest; + } + + transition(I, Flush) { + fw_sendFlushResponse; + p_popRequestQueue; + } + + transition({V, W}, Flush, I) {TagArrayRead, TagArrayWrite} { + t_allocateTBE; + ut_updateTag; + f_flush; + i_invL2; + p_popRequestQueue; + } } diff --git a/src/mem/ruby/protocol/GPU_VIPER-TCP.sm b/src/mem/ruby/protocol/GPU_VIPER-TCP.sm index 7e0ad4ed96..97997a12b5 100644 --- a/src/mem/ruby/protocol/GPU_VIPER-TCP.sm +++ b/src/mem/ruby/protocol/GPU_VIPER-TCP.sm @@ -1,5 +1,6 @@ /* * Copyright (c) 2011-2015 Advanced Micro Devices, Inc. + * Copyright (c) 2023 Matthew D. Sinclair * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -52,28 +53,34 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") { state_declaration(State, desc="TCP Cache States", default="TCP_State_I") { - I, AccessPermission:Invalid, desc="Invalid"; + I, AccessPermission:Invalid, desc="Invalid"; + // Note: currently IV in the TCP is only for pending loads to a given cache + // line. Since the TCP is write through, stores should be allowed to pass + // through without requiring them to wait. + IV, AccessPermission:Invalid, desc="Going from I to V, waiting on TCC data"; V, AccessPermission:Read_Only, desc="Valid"; - A, AccessPermission:Invalid, desc="Waiting on Atomic"; + A, AccessPermission:Invalid, desc="Waiting on Atomic"; + + F, AccessPermission:Invalid, desc="Flushing; Waiting for Ack"; } enumeration(Event, desc="TCP Events") { // Core initiated - Load, desc="Load"; + Load, desc="Load"; LoadBypassEvict, desc="Bypass L1 on a load. Evict if cache block already allocated"; - Store, desc="Store to L1 (L1 is dirty)"; - StoreThrough, desc="Store directly to L2(L1 is clean)"; - Atomic, desc="Atomic"; - Flush, desc="Flush if dirty(wbL1 for Store Release)"; - Evict, desc="Evict if clean(invL1 for Load Acquire)"; + Store, desc="Store to L1 (L1 is dirty)"; + StoreThrough, desc="Store directly to L2(L1 is clean)"; + Atomic, desc="Atomic"; + Flush, desc="Flush if dirty(wbL1 for Store Release)"; + Evict, desc="Evict if clean(invL1 for Load Acquire)"; // Mem sys initiated - Repl, desc="Replacing block from cache"; + Repl, desc="Replacing block from cache"; // TCC initiated - TCC_Ack, desc="TCC Ack to Core Request"; - TCC_AckWB, desc="TCC Ack for WB"; + TCC_Ack, desc="TCC Ack to Core Request"; + TCC_AckWB, desc="TCC Ack for WB"; // Disable L1 cache - Bypass, desc="Bypass the entire L1 cache"; + Bypass, desc="Bypass the entire L1 cache"; } enumeration(RequestType, @@ -100,6 +107,8 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") bool Dirty, desc="Is the data dirty (different than memory)?"; int NumPendingMsgs,desc="Number of acks/data messages that this processor is waiting for"; bool Shared, desc="Victim hit by shared probe"; + bool isGLCSet, desc="Bypass L1 Cache"; + bool isSLCSet, desc="Bypass L1 and L2 Cache"; } structure(TBETable, external="yes") { @@ -121,6 +130,7 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") void unset_tbe(); void wakeUpAllBuffers(); void wakeUpBuffers(Addr a); + void wakeUpAllBuffers(Addr a); Cycles curCycle(); // Internal functions @@ -256,6 +266,8 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") peek(responseToTCP_in, ResponseMsg, block_on="addr") { Entry cache_entry := getCacheEntry(in_msg.addr); TBE tbe := TBEs.lookup(in_msg.addr); + DPRINTF(RubySlicc, "In responseToTCP_in with %s\n", in_msg); + if (in_msg.Type == CoherenceResponseType:TDSysResp) { if (disableL1 || in_msg.isGLCSet || in_msg.isSLCSet) { // If L1 is disabled or requests have GLC or SLC flag set, @@ -273,6 +285,7 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") } else if (in_msg.Type == CoherenceResponseType:TDSysWBAck || in_msg.Type == CoherenceResponseType:NBSysWBAck) { trigger(Event:TCC_AckWB, in_msg.addr, cache_entry, tbe); + DPRINTF(RubySlicc, "Issuing TCC_AckWB\n"); } else { error("Unexpected Response Message to Core"); } @@ -287,10 +300,13 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") TBE tbe := TBEs.lookup(in_msg.LineAddress); DPRINTF(RubySlicc, "%s\n", in_msg); if (in_msg.Type == RubyRequestType:LD) { - if ((in_msg.isGLCSet || in_msg.isSLCSet) && is_valid(cache_entry)) { - // Read requests with GLC or SLC bit set should not cache in the L1. - // They need to bypass the L1 and go to the L2. If an entry exists - // in the L1, it needs to be evicted + // Read requests with GLC or SLC bit set should not cache in the L1. + // They need to bypass the L1 and go to the L2. If an entry exists in + // the L1, it needs to be evicted, and if no entry or invalid entry in + // the L1, still need to bypass. The LoadBypassEvict Event handles + // both cases in its transitions below, so call LoadBypassEvict for + // both. + if ((in_msg.isGLCSet || in_msg.isSLCSet)) { trigger(Event:LoadBypassEvict, in_msg.LineAddress, cache_entry, tbe); } else { @@ -436,10 +452,15 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") out_msg.Destination.add(mapAddressToRange(address,MachineType:TCC, TCC_select_low_bit, TCC_select_num_bits)); out_msg.MessageSize := MessageSizeType:Data; - out_msg.Type := CoherenceRequestType:Atomic; out_msg.InitialRequestTime := curCycle(); out_msg.Shared := false; peek(mandatoryQueue_in, RubyRequest) { + if (in_msg.Type == RubyRequestType:ATOMIC_RETURN) { + out_msg.Type := CoherenceRequestType:AtomicReturn; + } else { + assert(in_msg.Type == RubyRequestType:ATOMIC_NO_RETURN); + out_msg.Type := CoherenceRequestType:AtomicNoReturn; + } out_msg.instSeqNum := in_msg.instSeqNum; out_msg.isGLCSet := in_msg.isGLCSet; out_msg.isSLCSet := in_msg.isSLCSet; @@ -459,6 +480,15 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") check_allocate(TBEs); TBEs.allocate(address); set_tbe(TBEs.lookup(address)); + + // pass GLC/SLC information along + if (mandatoryQueue_in.isReady(clockEdge())) { + peek(mandatoryQueue_in, RubyRequest) { + DPRINTF(RubySlicc, "Address: %p\n", address); + tbe.isGLCSet := in_msg.isGLCSet; + tbe.isSLCSet := in_msg.isSLCSet; + } + } } action(d_deallocateTBE, "d", desc="Deallocate TBE") { @@ -469,6 +499,24 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") action(sf_setFlush, "sf", desc="set flush") { inFlush := true; APPEND_TRANSITION_COMMENT(" inFlush is true"); + enqueue(requestNetwork_out, CPURequestMsg, issue_latency) { + out_msg.addr := address; + out_msg.Requestor := machineID; + assert(is_valid(cache_entry)); + out_msg.DataBlk := cache_entry.DataBlk; + out_msg.writeMask.clear(); + out_msg.writeMask.orMask(cache_entry.writeMask); + out_msg.Destination.add(mapAddressToRange(address,MachineType:TCC, + TCC_select_low_bit, TCC_select_num_bits)); + out_msg.MessageSize := MessageSizeType:Data; + out_msg.Type := CoherenceRequestType:WriteFlush; + out_msg.InitialRequestTime := curCycle(); + out_msg.Shared := false; + out_msg.isSLCSet := false; + peek(mandatoryQueue_in, RubyRequest) { + out_msg.instSeqNum := in_msg.instSeqNum; + } + } } action(p_popMandatoryQueue, "pm", desc="Pop Mandatory Queue") { @@ -479,6 +527,10 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") responseToTCP_in.dequeue(clockEdge()); } + action(st_stallAndWaitRequest, "st", desc="Stall and wait on the address") { + stall_and_wait(mandatoryQueue_in, address); + } + action(l_loadDoneHit, "ldh", desc="local load done (hits in TCP)") { assert(is_valid(cache_entry)); if (use_seq_not_coal) { @@ -497,6 +549,20 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") } } + action(ldmi_loadDoneMissInv, "ldmi", + desc="local load done (misses in TCP and line was evicted)") { + // since line was evicted, can't rely on data from cache entry, so use from + // the response message + peek(responseToTCP_in, ResponseMsg) { + DataBlock tmp:= in_msg.DataBlk; + if (use_seq_not_coal) { + sequencer.readCallback(address, tmp, false, MachineType:L1Cache); + } else { + coalescer.readCallback(address, MachineType:L1Cache, tmp); + } + } + } + action(ad_atomicDone, "ad", desc="atomic done") { assert(is_valid(cache_entry)); coalescer.atomicCallback(address, MachineType:L1Cache, cache_entry.DataBlk); @@ -524,6 +590,16 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") cache_entry.Dirty := true; } + action(f_flushDone, "f", desc="flush done") { + assert(is_valid(cache_entry)); + + if (use_seq_not_coal) { + sequencer.writeCallback(address, cache_entry.DataBlk, false, MachineType:L1Cache); + } else { + coalescer.writeCallback(address, MachineType:L1Cache, cache_entry.DataBlk); + } + } + action(inv_invDone, "inv", desc="local inv done") { if (use_seq_not_coal) { DPRINTF(RubySlicc, "Sequencer does not define invCallback!\n"); @@ -563,6 +639,10 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") L1cache.setMRU(address); } + action(wada_wakeUpAllDependentsAddr, "wada", desc="Wake up any requests waiting for this address") { + wakeUpAllBuffers(address); + } + // action(zz_recycleMandatoryQueue, "\z", desc="recycle mandatory queue") { // mandatoryQueue_in.recycle(clockEdge(), cyclesToTicks(recycle_latency)); // } @@ -580,7 +660,6 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") L1cache.profileDemandHit(); } - // Transitions // ArrayRead/Write assumptions: // All requests read Tag Array @@ -592,11 +671,19 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") // Stalling transitions do NOT check the tag array...and if they do, // they can cause a resource stall deadlock! - transition({A}, {Load, Atomic, StoreThrough}) { //TagArrayRead} { - z_stall; + // if another request arrives for the same cache line that has a pending + // atomic or load, put it on the wakeup buffer instead of z_stall'ing it. By + // doing so we reduce resource contention since they won't try again every cycle + // and will instead only try again once woken up + transition({A, IV}, {Load, LoadBypassEvict, Atomic, Store, StoreThrough, Flush}) { + st_stallAndWaitRequest; } - transition(I, Load) {TagArrayRead} { + // if we have a load that misses, allocate TBE entry and transition to IV + // to prevent subsequent requests to same cache line from also going to TCC + // while this request is pending + transition(I, Load, IV) {TagArrayRead} { + t_allocateTBE; n_issueRdBlk; uu_profileDataMiss; p_popMandatoryQueue; @@ -654,14 +741,38 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") p_popMandatoryQueue; } - transition(I, TCC_Ack, V) {TagArrayRead, TagArrayWrite, DataArrayRead, DataArrayWrite} { - a_allocate; - w_writeCache; - l_loadDoneMiss; + // if we got a response for a load where the line is in I, then + // another request must have come in that replaced the line in question in + // the cache. Thus, complete this request without allocating the line, but + // still deallocate TBE and wakeup any dependent addresses. + // (Note: this assumes TCC_AckWB is what stores use) + transition(I, TCC_Ack) {TagArrayRead, TagArrayWrite} { + wada_wakeUpAllDependentsAddr; + // NOTE: Because we invalidated the cache line, the assert in l_loadDoneMiss + // will fail -- unlike atomics that automatically go to I when the line returns + // loads do not automatically go to I. Resolve this by passing data from + // message. + ldmi_loadDoneMissInv; + d_deallocateTBE; pr_popResponseQueue; } - transition(I, Bypass, I) { + // if line is currently in IV, then TCC_Ack is returning the data for a + // pending load, so transition to V, deallocate TBE, and wakeup any dependent + // requests so they will be replayed now that this request has returned. + transition(IV, TCC_Ack, V) {TagArrayRead, TagArrayWrite, DataArrayRead, DataArrayWrite} { + a_allocate; + w_writeCache; + wada_wakeUpAllDependentsAddr; + l_loadDoneMiss; + d_deallocateTBE; + pr_popResponseQueue; + } + + // if a bypass request arrives back at the TCP, regardless of whether the line + // is in I (from the bypass request) or IV (from a subsequent non-bypassing + // load), retain the current state and complete the bypassing request. + transition({I, IV}, Bypass) { rb_bypassDone; pr_popResponseQueue; } @@ -673,12 +784,13 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") } transition(A, TCC_Ack, I) {TagArrayRead, DataArrayRead, DataArrayWrite} { - d_deallocateTBE; a_allocate; w_writeCache; ad_atomicDone; - pr_popResponseQueue; ic_invCache; + wada_wakeUpAllDependentsAddr; + d_deallocateTBE; + pr_popResponseQueue; } transition(V, TCC_Ack, V) {TagArrayRead, DataArrayRead, DataArrayWrite} { @@ -695,15 +807,22 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") ic_invCache; } - transition({V, I, A},Flush) {TagArrayFlash} { + // if a line with a pending load gets evicted, transition the line to I and + // invalidate it. + transition(IV, Repl, I) {TagArrayRead, TagArrayWrite} { + ic_invCache; + } + + transition({V,I}, Flush, F) {TagArrayFlash} { + a_allocate; sf_setFlush; p_popMandatoryQueue; } transition({I, V}, Evict, I) {TagArrayFlash} { inv_invDone; - p_popMandatoryQueue; ic_invCache; + p_popMandatoryQueue; } transition(A, Evict) {TagArrayFlash} { @@ -711,9 +830,18 @@ machine(MachineType:TCP, "GPU TCP (L1 Data Cache)") p_popMandatoryQueue; } + // if a line is in IV and a TCC_AckWB comes back, we must have had a WT + // store followed by a load. Thus, complete the store without affecting + // TBE or line state. // TCC_AckWB only snoops TBE - transition({V, I, A}, TCC_AckWB) { + transition({V, I, IV, A}, TCC_AckWB) { wd_wtDone; pr_popResponseQueue; } + + transition(F, TCC_AckWB, I) { + f_flushDone; + pr_popResponseQueue; + ic_invCache; + } } diff --git a/src/mem/ruby/protocol/GPU_VIPER.slicc b/src/mem/ruby/protocol/GPU_VIPER.slicc index 196058b0d8..8a09fb0580 100644 --- a/src/mem/ruby/protocol/GPU_VIPER.slicc +++ b/src/mem/ruby/protocol/GPU_VIPER.slicc @@ -8,4 +8,4 @@ include "GPU_VIPER-msg.sm"; include "GPU_VIPER-TCP.sm"; include "GPU_VIPER-SQC.sm"; include "GPU_VIPER-TCC.sm"; -include "MOESI_AMD_Base-L3cache.sm"; +include "MOESI_AMD_Base-L3cache.sm"; // unused in GPU_VIPER; see git blame for discussion diff --git a/src/mem/ruby/protocol/Kconfig b/src/mem/ruby/protocol/Kconfig new file mode 100644 index 0000000000..bed47a1108 --- /dev/null +++ b/src/mem/ruby/protocol/Kconfig @@ -0,0 +1,62 @@ +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +config PROTOCOL + default "GPU_VIPER" if RUBY_PROTOCOL_GPU_VIPER + default "MOESI_AMD_Base" if RUBY_PROTOCOL_MOESI_AMD_BASE + default "MESI_Two_Level" if RUBY_PROTOCOL_MESI_TWO_LEVEL + default "MESI_Three_Level" if RUBY_PROTOCOL_MESI_THREE_LEVEL + default "MESI_Three_Level_HTM" if RUBY_PROTOCOL_MESI_THREE_LEVEL_HTM + default "MI_example" if RUBY_PROTOCOL_MI_EXAMPLE + default "MOESI_CMP_directory" if RUBY_PROTOCOL_MOESI_CMP_DIRECTORY + default "MOESI_CMP_token" if RUBY_PROTOCOL_MOESI_CMP_TOKEN + default "MOESI_hammer" if RUBY_PROTOCOL_MOESI_HAMMER + default "Garnet_standalone" if RUBY_PROTOCOL_GARNET_STANDALONE + +cont_choice "Ruby protocol" + config RUBY_PROTOCOL_GPU_VIPER + bool "GPU VIPER" + depends on BUILD_GPU + config RUBY_PROTOCOL_MOESI_AMD_BASE + bool "MOESI AMD base" + config RUBY_PROTOCOL_MESI_TWO_LEVEL + bool "MESI two level" + config RUBY_PROTOCOL_MESI_THREE_LEVEL + bool "MESI three level" + config RUBY_PROTOCOL_MESI_THREE_LEVEL_HTM + bool "MESI three level HTM" + config RUBY_PROTOCOL_MI_EXAMPLE + bool "MI example" + config RUBY_PROTOCOL_MOESI_CMP_DIRECTORY + bool "MOESI CMP directory" + config RUBY_PROTOCOL_MOESI_CMP_TOKEN + bool "MOESI CMP token" + config RUBY_PROTOCOL_MOESI_HAMMER + bool "MOESI hammer" + config RUBY_PROTOCOL_GARNET_STANDALONE + bool "Garnet standalone" +endchoice + +rsource "chi/Kconfig" diff --git a/src/mem/ruby/protocol/MESI_Two_Level-dir.sm b/src/mem/ruby/protocol/MESI_Two_Level-dir.sm index 9d6975570c..84ec578788 100644 --- a/src/mem/ruby/protocol/MESI_Two_Level-dir.sm +++ b/src/mem/ruby/protocol/MESI_Two_Level-dir.sm @@ -299,7 +299,7 @@ machine(MachineType:Directory, "MESI Two Level directory protocol") } action(l_popMemQueue, "q", desc="Pop off-chip request queue") { - memQueue_in.dequeue(clockEdge()); + dequeueMemRespQueue(); } action(kd_wakeUpDependents, "kd", desc="wake-up dependents") { @@ -365,7 +365,7 @@ machine(MachineType:Directory, "MESI Two Level directory protocol") desc="Queue off-chip writeback request") { peek(requestNetwork_in, RequestMsg) { enqueue(memQueue_out, MemoryMsg, to_mem_ctrl_latency) { - out_msg.addr := address; + out_msg.addr := in_msg.addr; out_msg.Type := MemoryRequestType:MEMORY_WB; out_msg.Sender := machineID; out_msg.MessageSize := MessageSizeType:Writeback_Data; diff --git a/src/mem/ruby/protocol/MI_example-dir.sm b/src/mem/ruby/protocol/MI_example-dir.sm index 11d2862b91..bbaa7d0789 100644 --- a/src/mem/ruby/protocol/MI_example-dir.sm +++ b/src/mem/ruby/protocol/MI_example-dir.sm @@ -523,7 +523,7 @@ machine(MachineType:Directory, "Directory protocol") } action(l_popMemQueue, "q", desc="Pop off-chip request queue") { - memQueue_in.dequeue(clockEdge()); + dequeueMemRespQueue(); } // TRANSITIONS diff --git a/src/mem/ruby/protocol/MOESI_AMD_Base-Region-dir.sm b/src/mem/ruby/protocol/MOESI_AMD_Base-Region-dir.sm index f16c2576a2..2f5103f846 100644 --- a/src/mem/ruby/protocol/MOESI_AMD_Base-Region-dir.sm +++ b/src/mem/ruby/protocol/MOESI_AMD_Base-Region-dir.sm @@ -180,6 +180,9 @@ machine(MachineType:Directory, "AMD_Base-like protocol") bool MemData, desc="Got MemData?",default="false"; bool wtData, desc="Got write through data?",default="false"; bool atomicData, desc="Got Atomic op?",default="false"; + // Note, protocol invariant: atomicData = atomicDataReturn || atomicDataNoReturn; + bool atomicDataReturn, desc="Got Atomic op and need return value?",default="false"; + bool atomicDataNoReturn, desc="Got Atomic op and don't need return value?",default="false"; Cycles InitialRequestTime, desc="..."; Cycles ForwardRequestTime, desc="..."; Cycles ProbeRequestStartTime, desc="..."; @@ -436,7 +439,9 @@ machine(MachineType:Directory, "AMD_Base-like protocol") trigger(Event:RdBlkS, in_msg.addr, entry, tbe); } else if (in_msg.Type == CoherenceRequestType:RdBlkM) { trigger(Event:RdBlkM, in_msg.addr, entry, tbe); - } else if (in_msg.Type == CoherenceRequestType:Atomic) { + } else if (in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn) { trigger(Event:Atomic, in_msg.addr, entry, tbe); } else if (in_msg.Type == CoherenceRequestType:WriteThrough) { trigger(Event:WriteThrough, in_msg.addr, entry, tbe); @@ -474,7 +479,9 @@ machine(MachineType:Directory, "AMD_Base-like protocol") trigger(Event:RdBlkSP, in_msg.addr, entry, tbe); } else if (in_msg.Type == CoherenceRequestType:RdBlkM) { trigger(Event:RdBlkMP, in_msg.addr, entry, tbe); - } else if (in_msg.Type == CoherenceRequestType:Atomic) { + } else if (in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn) { trigger(Event:AtomicP, in_msg.addr, entry, tbe); } else if (in_msg.Type == CoherenceRequestType:WriteThrough) { trigger(Event:WriteThroughP, in_msg.addr, entry, tbe); @@ -670,7 +677,9 @@ machine(MachineType:Directory, "AMD_Base-like protocol") out_msg.DemandRequest := false; } } else { - assert(in_msg.Type == CoherenceRequestType:Atomic); + assert(in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn); enqueue(responseNetwork_out, ResponseMsg, response_latency) { out_msg.addr := address; out_msg.Type := CoherenceResponseType:NBSysResp; @@ -977,10 +986,18 @@ machine(MachineType:Directory, "AMD_Base-like protocol") tbe.WTRequestor := in_msg.WTRequestor; tbe.LastSender := in_msg.Requestor; } - if (in_msg.Type == CoherenceRequestType:Atomic) { + if (in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn) { tbe.writeMask.clear(); tbe.writeMask.orMask(in_msg.writeMask); tbe.atomicData := true; + if (in_msg.Type == CoherenceRequestType:AtomicReturn) { + tbe.atomicDataReturn := true; + } else { + assert(in_msg.Type == CoherenceRequestType:AtomicNoReturn); + tbe.atomicDataNoReturn := true; + } tbe.WTRequestor := in_msg.WTRequestor; tbe.LastSender := in_msg.Requestor; } @@ -1012,10 +1029,18 @@ machine(MachineType:Directory, "AMD_Base-like protocol") tbe.WTRequestor := in_msg.WTRequestor; tbe.LastSender := in_msg.Requestor; } - if (in_msg.Type == CoherenceRequestType:Atomic) { + if (in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn) { tbe.writeMask.clear(); tbe.writeMask.orMask(in_msg.writeMask); tbe.atomicData := true; + if (in_msg.Type == CoherenceRequestType:AtomicReturn) { + tbe.atomicDataReturn := true; + } else { + assert(in_msg.Type == CoherenceRequestType:AtomicNoReturn); + tbe.atomicDataNoReturn := true; + } tbe.WTRequestor := in_msg.WTRequestor; tbe.LastSender := in_msg.Requestor; } @@ -1062,8 +1087,15 @@ machine(MachineType:Directory, "AMD_Base-like protocol") tbe.DataBlkAux.copyPartial(in_msg.DataBlk,in_msg.writeMask); getDirectoryEntry(address).DataBlk := tbe.DataBlkAux; } else{ - assert(in_msg.Type == CoherenceRequestType:Atomic); - tbe.DataBlkAux.atomicPartial(getDirectoryEntry(address).DataBlk,in_msg.writeMask); + assert(in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn) { + if (in_msg.Type == CoherenceRequestType:AtomicReturn) { + tbe.DataBlkAux.atomicPartial(getDirectoryEntry(address).DataBlk,in_msg.writeMask, false); + } else { + assert(in_msg.Type == CoherenceRequestType:AtomicNoReturn); + tbe.DataBlkAux.atomicPartial(getDirectoryEntry(address).DataBlk,in_msg.writeMask, true); + } getDirectoryEntry(address).DataBlk := tbe.DataBlkAux; } } @@ -1076,7 +1108,12 @@ machine(MachineType:Directory, "AMD_Base-like protocol") tbe.DataBlk := tmp; getDirectoryEntry(address).DataBlk := tbe.DataBlk; } else if (tbe.atomicData) { - tbe.DataBlk.atomicPartial(getDirectoryEntry(address).DataBlk,tbe.writeMask); + if (tbe.atomicDataReturn) { + tbe.DataBlk.atomicPartial(getDirectoryEntry(address).DataBlk,tbe.writeMask, false); + } else { + assert(tbe.atomicDataNoReturn); + tbe.DataBlk.atomicPartial(getDirectoryEntry(address).DataBlk,tbe.writeMask, true); + } getDirectoryEntry(address).DataBlk := tbe.DataBlk; } else if (tbe.Dirty == true) { APPEND_TRANSITION_COMMENT(" Wrote data back "); @@ -1137,6 +1174,7 @@ machine(MachineType:Directory, "AMD_Base-like protocol") tbe.DataBlk := tmp; } else if (tbe.Dirty) { if(tbe.atomicData == false && tbe.wtData == false) { + assert(tbe.atomicDataReturn == false && tbe.atomicDataNoReturn); DPRINTF(RubySlicc, "Got double data for %s from %s\n", address, in_msg.Sender); assert(tbe.DataBlk == in_msg.DataBlk); // in case of double data } @@ -1397,7 +1435,7 @@ machine(MachineType:Directory, "AMD_Base-like protocol") } action(pm_popMemQueue, "pm", desc="pop mem queue") { - memQueue_in.dequeue(clockEdge()); + dequeueMemRespQueue(); } action(pt_popTriggerQueue, "pt", desc="pop trigger queue") { diff --git a/src/mem/ruby/protocol/MOESI_AMD_Base-RegionBuffer.sm b/src/mem/ruby/protocol/MOESI_AMD_Base-RegionBuffer.sm index 5987d7cf76..5d85ad2fc6 100644 --- a/src/mem/ruby/protocol/MOESI_AMD_Base-RegionBuffer.sm +++ b/src/mem/ruby/protocol/MOESI_AMD_Base-RegionBuffer.sm @@ -458,7 +458,9 @@ machine(MachineType:RegionBuffer, "Region Buffer for AMD_Base-like protocol") trigger(Event:CPUWrite, in_msg.addr, cache_entry, tbe); } else if (in_msg.Type == CoherenceRequestType:WriteThrough ) { trigger(Event:CPUWrite, in_msg.addr, cache_entry, tbe); - } else if (in_msg.Type == CoherenceRequestType:Atomic ) { + } else if (in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn) { trigger(Event:CPUWrite, in_msg.addr, cache_entry, tbe); } else { if (in_msg.Type == CoherenceRequestType:VicDirty || @@ -523,9 +525,11 @@ machine(MachineType:RegionBuffer, "Region Buffer for AMD_Base-like protocol") assert(in_msg.Type == CoherenceRequestType:RdBlkM || in_msg.Type == CoherenceRequestType:RdBlkS); } APPEND_TRANSITION_COMMENT(cache_entry.NumOutstandingReqs); - if (in_msg.Type == CoherenceRequestType:RdBlkM || in_msg.Type == CoherenceRequestType:Atomic || - in_msg.Type == CoherenceRequestType:WriteThrough ) - { + if (in_msg.Type == CoherenceRequestType:RdBlkM || + in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn || + in_msg.Type == CoherenceRequestType:WriteThrough) { cache_entry.dirty := true; } if (in_msg.Type == CoherenceRequestType:VicDirty || diff --git a/src/mem/ruby/protocol/MOESI_AMD_Base-dir.sm b/src/mem/ruby/protocol/MOESI_AMD_Base-dir.sm index 3b38e3b1ff..c36fc9ec93 100644 --- a/src/mem/ruby/protocol/MOESI_AMD_Base-dir.sm +++ b/src/mem/ruby/protocol/MOESI_AMD_Base-dir.sm @@ -39,6 +39,7 @@ machine(MachineType:Directory, "AMD Baseline protocol") bool GPUonly := "False"; int TCC_select_num_bits; bool useL3OnWT := "False"; + bool L2isWB; Cycles to_memory_controller_latency := 1; // DMA @@ -83,6 +84,8 @@ machine(MachineType:Directory, "AMD Baseline protocol") BM_Pm, AccessPermission:Backing_Store, desc="blocked waiting for probes, already got memory"; B_Pm, AccessPermission:Backing_Store, desc="blocked waiting for probes, already got memory"; B, AccessPermission:Backing_Store, desc="sent response, Blocked til ack"; + + F, AccessPermission:Busy, desc="sent Flus, blocked till ack"; } // Events @@ -120,6 +123,9 @@ machine(MachineType:Directory, "AMD Baseline protocol") // DMA DmaRead, desc="DMA read"; DmaWrite, desc="DMA write"; + + // Flush + Flush, desc="Flush entry"; } enumeration(RequestType, desc="To communicate stats from transitions to recordStats") { @@ -148,11 +154,14 @@ machine(MachineType:Directory, "AMD Baseline protocol") bool Dirty, desc="Is the data dirty?"; int NumPendingAcks, desc="num acks expected"; MachineID OriginalRequestor, desc="Original Requestor"; - MachineID WTRequestor, desc="WT Requestor"; + MachineID CURequestor, desc="CU that initiated the request"; bool Cached, desc="data hit in Cache"; bool MemData, desc="Got MemData?",default="false"; bool wtData, desc="Got write through data?",default="false"; bool atomicData, desc="Got Atomic op?",default="false"; + // Note, protocol invariant: atomicData = atomicDataReturn || atomicDataNoReturn; + bool atomicDataReturn, desc="Got Atomic op and need return value?",default="false"; + bool atomicDataNoReturn, desc="Got Atomic op and don't need return value?",default="false"; Cycles InitialRequestTime, desc="..."; Cycles ForwardRequestTime, desc="..."; Cycles ProbeRequestStartTime, desc="..."; @@ -161,6 +170,9 @@ machine(MachineType:Directory, "AMD Baseline protocol") uint64_t probe_id, desc="probe id for lifetime profiling"; WriteMask writeMask, desc="outstanding write through mask"; int Len, desc="Length of memory request for DMA"; + // GLC is passed along because it is needed in the return path + bool isGLCSet, desc="Bypass GPU L1 Cache"; + bool isSLCSet, desc="Bypass GPU L1 and L2 Cache"; } structure(TBETable, external="yes") { @@ -392,7 +404,9 @@ machine(MachineType:Directory, "AMD Baseline protocol") trigger(Event:RdBlkM, in_msg.addr, entry, tbe); } else if (in_msg.Type == CoherenceRequestType:WriteThrough) { trigger(Event:WriteThrough, in_msg.addr, entry, tbe); - } else if (in_msg.Type == CoherenceRequestType:Atomic) { + } else if (in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn) { trigger(Event:Atomic, in_msg.addr, entry, tbe); } else if (in_msg.Type == CoherenceRequestType:VicDirty) { if (getDirectoryEntry(in_msg.addr).VicDirtyIgnore.isElement(in_msg.Requestor)) { @@ -410,6 +424,9 @@ machine(MachineType:Directory, "AMD Baseline protocol") DPRINTF(RubySlicc, "Got VicClean from %s on %s\n", in_msg.Requestor, in_msg.addr); trigger(Event:VicClean, in_msg.addr, entry, tbe); } + } else if (in_msg.Type == CoherenceRequestType:WriteFlush) { + DPRINTF(RubySlicc, "Got Flush from %s on %s\n", in_msg.Requestor, in_msg.addr); + trigger(Event:Flush, in_msg.addr, entry, tbe); } else { error("Bad request message type"); } @@ -455,6 +472,7 @@ machine(MachineType:Directory, "AMD Baseline protocol") out_msg.ForwardRequestTime := tbe.ForwardRequestTime; out_msg.ProbeRequestStartTime := tbe.ProbeRequestStartTime; out_msg.OriginalResponder := tbe.LastSender; + out_msg.CURequestor := tbe.CURequestor; out_msg.L3Hit := tbe.L3Hit; DPRINTF(RubySlicc, "%s\n", out_msg); } @@ -483,6 +501,9 @@ machine(MachineType:Directory, "AMD Baseline protocol") out_msg.ProbeRequestStartTime := tbe.ProbeRequestStartTime; out_msg.OriginalResponder := tbe.LastSender; out_msg.L3Hit := tbe.L3Hit; + out_msg.isGLCSet := tbe.isGLCSet; + out_msg.isSLCSet := tbe.isSLCSet; + out_msg.CURequestor := tbe.CURequestor; DPRINTF(RubySlicc, "%s\n", out_msg); } } @@ -512,9 +533,11 @@ machine(MachineType:Directory, "AMD Baseline protocol") out_msg.ForwardRequestTime := tbe.ForwardRequestTime; out_msg.ProbeRequestStartTime := tbe.ProbeRequestStartTime; out_msg.OriginalResponder := tbe.LastSender; - if(tbe.atomicData){ - out_msg.WTRequestor := tbe.WTRequestor; - } + out_msg.isGLCSet := tbe.isGLCSet; + out_msg.isSLCSet := tbe.isSLCSet; + if(tbe.atomicData){ + out_msg.CURequestor := tbe.CURequestor; + } out_msg.L3Hit := tbe.L3Hit; DPRINTF(RubySlicc, "%s\n", out_msg); } @@ -540,6 +563,8 @@ machine(MachineType:Directory, "AMD Baseline protocol") out_msg.InitialRequestTime := tbe.InitialRequestTime; out_msg.ForwardRequestTime := curCycle(); out_msg.ProbeRequestStartTime := tbe.ProbeRequestStartTime; + out_msg.isGLCSet := tbe.isGLCSet; + out_msg.isSLCSet := tbe.isSLCSet; DPRINTF(RubySlicc, "%s\n", out_msg); } } @@ -550,13 +575,32 @@ machine(MachineType:Directory, "AMD Baseline protocol") out_msg.addr := address; out_msg.Type := CoherenceResponseType:NBSysWBAck; out_msg.Destination.add(in_msg.Requestor); - out_msg.WTRequestor := in_msg.WTRequestor; + out_msg.CURequestor := in_msg.CURequestor; out_msg.Sender := machineID; out_msg.MessageSize := MessageSizeType:Writeback_Control; out_msg.InitialRequestTime := in_msg.InitialRequestTime; out_msg.ForwardRequestTime := curCycle(); out_msg.ProbeRequestStartTime := curCycle(); out_msg.instSeqNum := in_msg.instSeqNum; + out_msg.isGLCSet := in_msg.isGLCSet; + out_msg.isSLCSet := in_msg.isSLCSet; + } + } + } + + action(rf_sendResponseFlush, "rf", desc="send Flush Ack") { + peek(memQueue_in, MemoryMsg) { + enqueue(responseNetwork_out, ResponseMsg, 1) { + out_msg.addr := address; + out_msg.Type := CoherenceResponseType:NBSysWBAck; + out_msg.Destination.add(tbe.OriginalRequestor); + out_msg.CURequestor := tbe.CURequestor; + out_msg.Sender := machineID; + out_msg.MessageSize := MessageSizeType:Writeback_Control; + out_msg.InitialRequestTime := tbe.InitialRequestTime; + out_msg.ForwardRequestTime := curCycle(); + out_msg.ProbeRequestStartTime := curCycle(); + //out_msg.instSeqNum := in_msg.instSeqNum; } } } @@ -716,7 +760,9 @@ machine(MachineType:Directory, "AMD Baseline protocol") // CPU + GPU or GPU only system if ((in_msg.Type != CoherenceRequestType:WriteThrough && - in_msg.Type != CoherenceRequestType:Atomic) || + in_msg.Type != CoherenceRequestType:Atomic && + in_msg.Type != CoherenceRequestType:AtomicReturn && + in_msg.Type != CoherenceRequestType:AtomicNoReturn) || !in_msg.NoWriteConflict) { if (noTCCdir) { probe_dests.add(mapAddressToRange(address, MachineType:TCC, @@ -739,6 +785,8 @@ machine(MachineType:Directory, "AMD Baseline protocol") out_msg.MessageSize := MessageSizeType:Control; out_msg.Destination := probe_dests; tbe.NumPendingAcks := out_msg.Destination.count(); + out_msg.isGLCSet := in_msg.isGLCSet; + out_msg.isSLCSet := in_msg.isSLCSet; DPRINTF(RubySlicc, "%s\n", out_msg); APPEND_TRANSITION_COMMENT(" dc: Acks remaining: "); APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks); @@ -843,6 +891,8 @@ machine(MachineType:Directory, "AMD Baseline protocol") out_msg.MessageSize := MessageSizeType:Control; out_msg.Destination := probe_dests; tbe.NumPendingAcks := out_msg.Destination.count(); + out_msg.isGLCSet := in_msg.isGLCSet; + out_msg.isSLCSet := in_msg.isSLCSet; DPRINTF(RubySlicc, "%s\n", (out_msg)); APPEND_TRANSITION_COMMENT(" sc: Acks remaining: "); APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks); @@ -897,6 +947,8 @@ machine(MachineType:Directory, "AMD Baseline protocol") out_msg.ReturnData := false; out_msg.MessageSize := MessageSizeType:Control; out_msg.Destination := probe_dests; + out_msg.isGLCSet := in_msg.isGLCSet; + out_msg.isSLCSet := in_msg.isSLCSet; tbe.NumPendingAcks := out_msg.Destination.count(); APPEND_TRANSITION_COMMENT(" ic: Acks remaining: "); APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks); @@ -932,6 +984,23 @@ machine(MachineType:Directory, "AMD Baseline protocol") } } + action(f_writeFlushDataToMemory, "f", desc="Write flush data to memory") { + peek(requestNetwork_in, CPURequestMsg) { + enqueue(memQueue_out, MemoryMsg, to_memory_controller_latency) { + out_msg.addr := address; + out_msg.Type := MemoryRequestType:MEMORY_WB; + out_msg.Sender := machineID; + out_msg.MessageSize := MessageSizeType:Writeback_Data; + out_msg.DataBlk := in_msg.DataBlk; + } + if (tbe.Dirty == false) { + // have to update the TBE, too, because of how this + // directory deals with functional writes + tbe.DataBlk := in_msg.DataBlk; + } + } + } + action(atd_allocateTBEforDMA, "atd", desc="allocate TBE Entry for DMA") { check_allocate(TBEs); peek(dmaRequestQueue_in, DMARequestMsg) { @@ -966,15 +1035,28 @@ machine(MachineType:Directory, "AMD Baseline protocol") tbe.writeMask.clear(); tbe.writeMask.orMask(in_msg.writeMask); tbe.wtData := true; - tbe.WTRequestor := in_msg.WTRequestor; + tbe.CURequestor := in_msg.CURequestor; tbe.LastSender := in_msg.Requestor; } - if (in_msg.Type == CoherenceRequestType:Atomic) { + if (in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn) { tbe.writeMask.clear(); tbe.writeMask.orMask(in_msg.writeMask); tbe.atomicData := true; - tbe.WTRequestor := in_msg.WTRequestor; + if (in_msg.Type == CoherenceRequestType:AtomicReturn) { + tbe.atomicDataReturn := true; + } else { + assert(in_msg.Type == CoherenceRequestType:AtomicNoReturn); + tbe.atomicDataNoReturn := true; + } + tbe.CURequestor := in_msg.CURequestor; tbe.LastSender := in_msg.Requestor; + tbe.isSLCSet := in_msg.isSLCSet; + } + // GPU read requests also need to track where the requestor came from + if (in_msg.Type == CoherenceRequestType:RdBlk) { + tbe.CURequestor := in_msg.CURequestor; } tbe.Dirty := false; if (in_msg.Type == CoherenceRequestType:WriteThrough) { @@ -985,6 +1067,9 @@ machine(MachineType:Directory, "AMD Baseline protocol") tbe.NumPendingAcks := 0; tbe.Cached := in_msg.ForceShared; tbe.InitialRequestTime := in_msg.InitialRequestTime; + tbe.isGLCSet := in_msg.isGLCSet; + tbe.isSLCSet := in_msg.isSLCSet; + DPRINTF(RubySlicc, "t_allocateTBE in_msg: %s, tbe: %s\n", in_msg, tbe.CURequestor); } } @@ -995,8 +1080,15 @@ machine(MachineType:Directory, "AMD Baseline protocol") action(wd_writeBackData, "wd", desc="Write back data if needed") { if (tbe.wtData || tbe.atomicData || tbe.Dirty == false) { - if (tbe.atomicData) { - tbe.DataBlk.atomicPartial(tbe.DataBlk, tbe.writeMask); + // Only perform atomics in the directory if the SLC bit is set, or + // if the L2 is WT + if (tbe.atomicData && (tbe.isSLCSet || !L2isWB)) { + if (tbe.atomicDataReturn) { + tbe.DataBlk.atomicPartial(tbe.DataBlk, tbe.writeMask, false); + } else { + assert(tbe.atomicDataNoReturn); + tbe.DataBlk.atomicPartial(tbe.DataBlk, tbe.writeMask, true); + } } enqueue(memQueue_out, MemoryMsg, to_memory_controller_latency) { out_msg.addr := address; @@ -1036,6 +1128,7 @@ machine(MachineType:Directory, "AMD Baseline protocol") tbe.writeMask.fillMask(); } else if (tbe.Dirty) { if(tbe.atomicData == false && tbe.wtData == false) { + assert(tbe.atomicDataReturn == false && tbe.atomicDataNoReturn == false); DPRINTF(RubySlicc, "Got double data for %s from %s\n", address, in_msg.Sender); assert(tbe.DataBlk == in_msg.DataBlk); // in case of double data } @@ -1169,7 +1262,7 @@ machine(MachineType:Directory, "AMD Baseline protocol") } action(pm_popMemQueue, "pm", desc="pop mem queue") { - memQueue_in.dequeue(clockEdge()); + dequeueMemRespQueue(); } action(pt_popTriggerQueue, "pt", desc="pop trigger queue") { @@ -1209,11 +1302,20 @@ machine(MachineType:Directory, "AMD Baseline protocol") } action(wada_wakeUpAllDependentsAddr, "wada", desc="Wake up any requests waiting for this address") { + DPRINTF(RubySlicc, "wada wakeup: 0x%x\n", address); wakeUpAllBuffers(address); } + /* + Currently z_stall is unused because it can lead to Protocol Stalls that + eventually lead to deadlock. Instead, it is recommended to use + st_stallAndWaitRequest in combination with a wakeupBuffer call (e.g., + wada_wakeUpAllDependentsAddr) to put the pending requests to sleep instead of + them causing head of line blocking -- wada_wakeUpAllDependentsAddr should wake + the request up once the request preventing it from completing is done. action(z_stall, "z", desc="...") { } + */ // TRANSITIONS transition({BL, BDR_M, BDW_M, BS_M, BM_M, B_M, BP, BDR_PM, BDW_PM, BS_PM, BM_PM, B_PM, BDR_Pm, BDW_Pm, BS_Pm, BM_Pm, B_Pm, B}, {RdBlkS, RdBlkM, RdBlk, CtoD}) { @@ -1315,19 +1417,19 @@ machine(MachineType:Directory, "AMD Baseline protocol") d_writeDataToMemory; al_allocateL3Block; pr_profileL3HitMiss; //Must come after al_allocateL3Block and before dt_deallocateTBE - wad_wakeUpDependents; + wada_wakeUpAllDependentsAddr; dt_deallocateTBE; pr_popResponseQueue; } transition(BL, StaleWB, U) {L3TagArrayWrite} { dt_deallocateTBE; - wa_wakeUpAllDependents; + wada_wakeUpAllDependentsAddr; pr_popResponseQueue; } transition({B, BDR_M, BDW_M, BS_M, BM_M, B_M, BP, BDR_PM, BDW_PM, BS_PM, BM_PM, B_PM, BDR_Pm, BDW_Pm, BS_Pm, BM_Pm, B_Pm}, {VicDirty, VicClean}) { - z_stall; + st_stallAndWaitRequest; } transition({U, BL, BDR_M, BDW_M, BS_M, BM_M, B_M, BP, BDR_PM, BDW_PM, BS_PM, BM_PM, B_PM, BDR_Pm, BDW_Pm, BS_Pm, BM_Pm, B_Pm, B}, WBAck) { @@ -1405,8 +1507,8 @@ machine(MachineType:Directory, "AMD Baseline protocol") transition(BDW_M, MemData, U) { mt_writeMemDataToTBE; - da_sendResponseDmaAck; wd_writeBackData; + da_sendResponseDmaAck; wada_wakeUpAllDependentsAddr; dt_deallocateTBE; pm_popMemQueue; @@ -1414,8 +1516,8 @@ machine(MachineType:Directory, "AMD Baseline protocol") transition(BS_M, MemData, B){L3TagArrayWrite, L3DataArrayWrite} { mt_writeMemDataToTBE; - s_sendResponseS; wd_writeBackData; + s_sendResponseS; alwt_allocateL3BlockOnWT; dt_deallocateTBE; pm_popMemQueue; @@ -1423,8 +1525,8 @@ machine(MachineType:Directory, "AMD Baseline protocol") transition(BM_M, MemData, B){L3TagArrayWrite, L3DataArrayWrite} { mt_writeMemDataToTBE; - m_sendResponseM; wd_writeBackData; + m_sendResponseM; alwt_allocateL3BlockOnWT; dt_deallocateTBE; pm_popMemQueue; @@ -1432,32 +1534,32 @@ machine(MachineType:Directory, "AMD Baseline protocol") transition(B_M, MemData, B){L3TagArrayWrite, L3DataArrayWrite} { mt_writeMemDataToTBE; - es_sendResponseES; wd_writeBackData; + es_sendResponseES; alwt_allocateL3BlockOnWT; dt_deallocateTBE; pm_popMemQueue; } transition(BS_M, L3Hit, B) {L3TagArrayWrite, L3DataArrayWrite} { - s_sendResponseS; wd_writeBackData; + s_sendResponseS; alwt_allocateL3BlockOnWT; dt_deallocateTBE; ptl_popTriggerQueue; } transition(BM_M, L3Hit, B) {L3DataArrayWrite, L3TagArrayWrite} { - m_sendResponseM; wd_writeBackData; + m_sendResponseM; alwt_allocateL3BlockOnWT; dt_deallocateTBE; ptl_popTriggerQueue; } transition(B_M, L3Hit, B) {L3DataArrayWrite, L3TagArrayWrite} { - es_sendResponseES; wd_writeBackData; + es_sendResponseES; alwt_allocateL3BlockOnWT; dt_deallocateTBE; ptl_popTriggerQueue; @@ -1503,8 +1605,8 @@ machine(MachineType:Directory, "AMD Baseline protocol") } transition(BDW_Pm, ProbeAcksComplete, U) { - da_sendResponseDmaAck; wd_writeBackData; + da_sendResponseDmaAck; // Check for pending requests from the core we put to sleep while waiting // for a response wada_wakeUpAllDependentsAddr; @@ -1514,8 +1616,8 @@ machine(MachineType:Directory, "AMD Baseline protocol") transition(BS_Pm, ProbeAcksComplete, B){L3DataArrayWrite, L3TagArrayWrite} { sf_setForwardReqTime; - s_sendResponseS; wd_writeBackData; + s_sendResponseS; alwt_allocateL3BlockOnWT; dt_deallocateTBE; pt_popTriggerQueue; @@ -1523,8 +1625,8 @@ machine(MachineType:Directory, "AMD Baseline protocol") transition(BM_Pm, ProbeAcksComplete, B){L3DataArrayWrite, L3TagArrayWrite} { sf_setForwardReqTime; - m_sendResponseM; wd_writeBackData; + m_sendResponseM; alwt_allocateL3BlockOnWT; dt_deallocateTBE; pt_popTriggerQueue; @@ -1532,8 +1634,8 @@ machine(MachineType:Directory, "AMD Baseline protocol") transition(B_Pm, ProbeAcksComplete, B){L3DataArrayWrite, L3TagArrayWrite} { sf_setForwardReqTime; - es_sendResponseES; wd_writeBackData; + es_sendResponseES; alwt_allocateL3BlockOnWT; dt_deallocateTBE; pt_popTriggerQueue; @@ -1541,10 +1643,23 @@ machine(MachineType:Directory, "AMD Baseline protocol") transition(BP, ProbeAcksComplete, B){L3TagArrayWrite, L3TagArrayWrite} { sf_setForwardReqTime; - c_sendResponseCtoD; wd_writeBackData; + c_sendResponseCtoD; alwt_allocateL3BlockOnWT; dt_deallocateTBE; pt_popTriggerQueue; } + + transition(U, Flush, F) {L3TagArrayRead, L3TagArrayWrite} { + t_allocateTBE; + f_writeFlushDataToMemory; + w_sendResponseWBAck; + p_popRequestQueue; + } + + transition(F, WBAck, U) { + pm_popMemQueue; + dt_deallocateTBE; + } + } diff --git a/src/mem/ruby/protocol/MOESI_AMD_Base-msg.sm b/src/mem/ruby/protocol/MOESI_AMD_Base-msg.sm index bb3a013325..b860ff1681 100644 --- a/src/mem/ruby/protocol/MOESI_AMD_Base-msg.sm +++ b/src/mem/ruby/protocol/MOESI_AMD_Base-msg.sm @@ -39,6 +39,8 @@ enumeration(CoherenceRequestType, desc="Coherence Request Types") { VicClean, desc="L2 clean eviction"; VicDirty, desc="L2 dirty eviction"; Atomic, desc="Upper level atomic"; + AtomicReturn, desc="Upper level atomic"; + AtomicNoReturn, desc="Upper level atomic"; AtomicWriteBack, desc="Upper level atomic"; WriteThrough, desc="Ordered WriteThrough w/Data"; WriteThroughFifo, desc="WriteThrough with no data"; @@ -132,14 +134,14 @@ structure(CPURequestMsg, desc="...", interface="Message") { int Acks, default="0", desc="Acks that the dir (mem ctrl) should expect to receive"; CoherenceRequestType OriginalType, default="CoherenceRequestType_NA", desc="Type of request from core fwded through region buffer"; WriteMask writeMask, desc="Write Through Data"; - MachineID WTRequestor, desc="Node who initiated the write through"; + MachineID CURequestor, desc="Node who initiated the request"; int wfid, default="0", desc="wavefront id"; uint64_t instSeqNum, desc="instruction sequence number"; bool NoWriteConflict, default="true", desc="write collided with CAB entry"; int ProgramCounter, desc="PC that accesses to this block"; - bool isGLCSet, default="false", desc="GLC flag value in the request"; - bool isSLCSet, default="false", desc="SLC flag value in the request"; + bool isGLCSet, default="false", desc="GLC flag value in the request"; + bool isSLCSet, default="false", desc="SLC flag value in the request"; bool functionalRead(Packet *pkt) { // Only PUTX messages contains the data block @@ -168,6 +170,8 @@ structure(NBProbeRequestMsg, desc="...", interface="Message") { MachineID Requestor, desc="Requestor id for 3-hop requests"; bool NoAckNeeded, default="false", desc="For short circuting acks"; int ProgramCounter, desc="PC that accesses to this block"; + bool isGLCSet, default="false", desc="GLC flag value in the request"; + bool isSLCSet, default="false", desc="SLC flag value in the request"; bool functionalRead(Packet *pkt) { return false; @@ -238,7 +242,7 @@ structure(ResponseMsg, desc="...", interface="Message") { bool L3Hit, default="false", desc="Did memory or L3 supply the data?"; MachineID OriginalResponder, desc="Mach which wrote the data to the L3"; - MachineID WTRequestor, desc="Node who started the writethrough"; + MachineID CURequestor, desc="Node who started the access"; bool NotCached, default="false", desc="True when the Region buffer has already evicted the line"; diff --git a/src/mem/ruby/protocol/MOESI_AMD_Base-probeFilter.sm b/src/mem/ruby/protocol/MOESI_AMD_Base-probeFilter.sm index 8608608590..4e9e9597aa 100644 --- a/src/mem/ruby/protocol/MOESI_AMD_Base-probeFilter.sm +++ b/src/mem/ruby/protocol/MOESI_AMD_Base-probeFilter.sm @@ -170,6 +170,9 @@ machine(MachineType:Directory, "AMD Baseline protocol") bool MemData, desc="Got MemData?",default="false"; bool wtData, desc="Got write through data?",default="false"; bool atomicData, desc="Got Atomic op?",default="false"; + // Note, protocol invariant: atomicData = atomicDataReturn || atomicDataNoReturn; + bool atomicDataReturn, desc="Got Atomic op and need return value?",default="false"; + bool atomicDataNoReturn, desc="Got Atomic op and don't need return value?",default="false"; Cycles InitialRequestTime, desc="..."; Cycles ForwardRequestTime, desc="..."; Cycles ProbeRequestStartTime, desc="..."; @@ -451,7 +454,9 @@ machine(MachineType:Directory, "AMD Baseline protocol") trigger(Event:RdBlkM, in_msg.addr, entry, tbe); } else if (in_msg.Type == CoherenceRequestType:WriteThrough) { trigger(Event:WriteThrough, in_msg.addr, entry, tbe); - } else if (in_msg.Type == CoherenceRequestType:Atomic) { + } else if (in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn) { trigger(Event:Atomic, in_msg.addr, entry, tbe); } else if (in_msg.Type == CoherenceRequestType:VicDirty) { if (getDirectoryEntry(in_msg.addr).VicDirtyIgnore.isElement(in_msg.Requestor)) { @@ -656,7 +661,9 @@ machine(MachineType:Directory, "AMD Baseline protocol") // add relevant TCC node to list. This replaces all TCPs and SQCs if(isGPUSharer(address)) { if ((in_msg.Type == CoherenceRequestType:WriteThrough || - in_msg.Type == CoherenceRequestType:Atomic) && + in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn) && in_msg.NoWriteConflict) { // Don't Include TCCs unless there was write-CAB conflict in the TCC } else if(noTCCdir) { @@ -814,6 +821,8 @@ machine(MachineType:Directory, "AMD Baseline protocol") tbe.writeMask.clear(); tbe.wtData := false; tbe.atomicData := false; + tbe.atomicDataReturn := false; + tbe.atomicDataNoReturn := false; tbe.DataBlk := getDirectoryEntry(address).DataBlk; // Data only for WBs tbe.Dirty := false; tbe.NumPendingAcks := 0; @@ -831,10 +840,18 @@ machine(MachineType:Directory, "AMD Baseline protocol") tbe.WTRequestor := in_msg.WTRequestor; tbe.LastSender := in_msg.Requestor; } - if (in_msg.Type == CoherenceRequestType:Atomic) { + if (in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn) { tbe.writeMask.clear(); tbe.writeMask.orMask(in_msg.writeMask); tbe.atomicData := true; + if (in_msg.Type == CoherenceRequestType:AtomicReturn) { + tbe.atomicDataReturn = true; + } else { + assert(in_msg.Type == CoherenceRequestType:AtomicNoReturn); + tbe.atomicDataNoReturn = true; + } tbe.WTRequestor := in_msg.WTRequestor; tbe.LastSender := in_msg.Requestor; } @@ -866,8 +883,14 @@ machine(MachineType:Directory, "AMD Baseline protocol") tbe.DataBlk := tmp; getDirectoryEntry(address).DataBlk := tbe.DataBlk; } else if (tbe.atomicData) { - tbe.DataBlk.atomicPartial(getDirectoryEntry(address).DataBlk, - tbe.writeMask); + if (tbe.atomicDataReturn) { + tbe.DataBlk.atomicPartial(getDirectoryEntry(address).DataBlk, + tbe.writeMask, false); + } else { + assert(tbe.atomicDataNoReturn); + tbe.DataBlk.atomicPartial(getDirectoryEntry(address).DataBlk, + tbe.writeMask, true); + } getDirectoryEntry(address).DataBlk := tbe.DataBlk; } else if (tbe.Dirty == false) { getDirectoryEntry(address).DataBlk := tbe.DataBlk; @@ -896,6 +919,7 @@ machine(MachineType:Directory, "AMD Baseline protocol") tbe.DataBlk := tmp; } else if (tbe.Dirty) { if(tbe.atomicData == false && tbe.wtData == false) { + assert(atomicDataReturn == false && atomicDataNoReturn); DPRINTF(RubySlicc, "Got double data for %s from %s\n", address, in_msg.Sender); assert(tbe.DataBlk == in_msg.DataBlk); // in case of double data } @@ -1050,7 +1074,9 @@ machine(MachineType:Directory, "AMD Baseline protocol") entry.pfState := ProbeFilterState:T; entry.isOnCPU := false; entry.isOnGPU := false; - } else if (in_msg.Type == CoherenceRequestType:Atomic) { + } else if (in_msg.Type == CoherenceRequestType:Atomic || + in_msg.Type == CoherenceRequestType:AtomicReturn || + in_msg.Type == CoherenceRequestType:AtomicNoReturn) { entry.pfState := ProbeFilterState:T; entry.isOnCPU := false; entry.isOnGPU := false; @@ -1103,7 +1129,7 @@ machine(MachineType:Directory, "AMD Baseline protocol") } action(pm_popMemQueue, "pm", desc="pop mem queue") { - memQueue_in.dequeue(clockEdge()); + dequeueMemRespQueue(); } action(pt_popTriggerQueue, "pt", desc="pop trigger queue") { diff --git a/src/mem/ruby/protocol/MOESI_CMP_directory-dir.sm b/src/mem/ruby/protocol/MOESI_CMP_directory-dir.sm index 3b4a8012c5..4a513d6d3f 100644 --- a/src/mem/ruby/protocol/MOESI_CMP_directory-dir.sm +++ b/src/mem/ruby/protocol/MOESI_CMP_directory-dir.sm @@ -598,7 +598,7 @@ machine(MachineType:Directory, "Directory protocol") } action(q_popMemQueue, "q", desc="Pop off-chip request queue") { - memQueue_in.dequeue(clockEdge()); + dequeueMemRespQueue(); } action(qf_queueMemoryFetchRequest, "qf", desc="Queue off-chip fetch request") { @@ -1014,6 +1014,31 @@ machine(MachineType:Directory, "Directory protocol") i_popIncomingRequestQueue; } + //this happens when there is race between FwdGetX + //and PUTX on owner. Owner in this case hands off + //ownership to GetX requestor and PUTX still goes + //through. But since owner has changed, state should + //go back to M and PUTX is essentially trashed. + transition(MI, Unblock, M) { + w_deallocateTBE; + j_popIncomingUnblockQueue; + } + + transition(MIS, Unblock, M) { + w_deallocateTBE; + j_popIncomingUnblockQueue; + } + + transition(OS, Unblock, O) { + //In OS state there is no TBE for some reason + // w_deallocateTBE; + j_popIncomingUnblockQueue; + } + + transition(OSS, Unblock, O) { + j_popIncomingUnblockQueue; + } + transition(WBI, Memory_Ack, I) { clearWBAck; w_deallocateTBE; diff --git a/src/mem/ruby/protocol/MOESI_CMP_token-dir.sm b/src/mem/ruby/protocol/MOESI_CMP_token-dir.sm index 97ea292eb7..7f2bdf94e0 100644 --- a/src/mem/ruby/protocol/MOESI_CMP_token-dir.sm +++ b/src/mem/ruby/protocol/MOESI_CMP_token-dir.sm @@ -821,7 +821,7 @@ machine(MachineType:Directory, "Token protocol") } action(l_popMemQueue, "q", desc="Pop off-chip request queue") { - memQueue_in.dequeue(clockEdge()); + dequeueMemRespQueue(); } action(r_bounceResponse, "r", desc="Bounce response to starving processor") { diff --git a/src/mem/ruby/protocol/MOESI_hammer-dir.sm b/src/mem/ruby/protocol/MOESI_hammer-dir.sm index 8fd447fdf4..833ccd3b18 100644 --- a/src/mem/ruby/protocol/MOESI_hammer-dir.sm +++ b/src/mem/ruby/protocol/MOESI_hammer-dir.sm @@ -1141,7 +1141,7 @@ machine(MachineType:Directory, "AMD Hammer-like protocol") } action(l_popMemQueue, "q", desc="Pop off-chip request queue") { - memQueue_in.dequeue(clockEdge()); + dequeueMemRespQueue(); } action(g_popTriggerQueue, "g", desc="Pop trigger queue") { diff --git a/src/mem/ruby/protocol/RubySlicc_Defines.sm b/src/mem/ruby/protocol/RubySlicc_Defines.sm index 6ae3a6cb12..590a134ef9 100644 --- a/src/mem/ruby/protocol/RubySlicc_Defines.sm +++ b/src/mem/ruby/protocol/RubySlicc_Defines.sm @@ -37,3 +37,5 @@ Cycles recycle_latency; // memory controllers. void functionalMemoryRead(Packet *pkt); bool functionalMemoryWrite(Packet *pkt); + +void dequeueMemRespQueue(); diff --git a/src/mem/ruby/protocol/RubySlicc_Exports.sm b/src/mem/ruby/protocol/RubySlicc_Exports.sm index a32983ada4..ca44fd3780 100644 --- a/src/mem/ruby/protocol/RubySlicc_Exports.sm +++ b/src/mem/ruby/protocol/RubySlicc_Exports.sm @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 ARM Limited + * Copyright (c) 2020-2021,2023 ARM Limited * All rights reserved. * * The license below extends only to copyright in the software and shall @@ -51,6 +51,8 @@ external_type(Addr, primitive="yes"); external_type(Cycles, primitive="yes", default="Cycles(0)"); external_type(Tick, primitive="yes", default="0"); external_type(RequestPtr, primitive="yes", default="nullptr"); +external_type(RequestorID, primitive="yes"); +external_type(prefetch::Base, primitive="yes"); structure(WriteMask, external="yes", desc="...") { void clear(); @@ -77,6 +79,9 @@ structure(DataBlock, external = "yes", desc="..."){ void copyPartial(DataBlock, int, int); void copyPartial(DataBlock, WriteMask); void atomicPartial(DataBlock, WriteMask); + void atomicPartial(DataBlock, WriteMask, bool); + int numAtomicLogEntries(); + void clearAtomicLogEntries(); } bool testAndRead(Addr addr, DataBlock datablk, Packet *pkt); @@ -228,11 +233,13 @@ enumeration(CacheRequestType, desc="...", default="CacheRequestType_NULL") { DataArrayWrite, desc="Write access to the cache's data array"; TagArrayRead, desc="Read access to the cache's tag array"; TagArrayWrite, desc="Write access to the cache's tag array"; + AtomicALUOperation, desc="Atomic ALU operation"; } enumeration(CacheResourceType, desc="...", default="CacheResourceType_NULL") { DataArray, desc="Access to the cache's data array"; TagArray, desc="Access to the cache's tag array"; + AtomicALUArray, desc="Access to the cache's atomic ALU array"; } enumeration(DirectoryRequestType, desc="...", default="DirectoryRequestType_NULL") { diff --git a/src/mem/ruby/protocol/RubySlicc_Types.sm b/src/mem/ruby/protocol/RubySlicc_Types.sm index 8ba9d935ff..2206effa29 100644 --- a/src/mem/ruby/protocol/RubySlicc_Types.sm +++ b/src/mem/ruby/protocol/RubySlicc_Types.sm @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 ARM Limited + * Copyright (c) 2020-2021,2023 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -139,6 +139,13 @@ structure (Sequencer, external = "yes") { Cycles, Cycles, Cycles); void writeUniqueCallback(Addr, DataBlock); + void atomicCallback(Addr, DataBlock); + void atomicCallback(Addr, DataBlock, bool); + void atomicCallback(Addr, DataBlock, bool, MachineType); + void atomicCallback(Addr, DataBlock, bool, MachineType, + Cycles, Cycles, Cycles); + + void unaddressedCallback(Addr, RubyRequestType); void unaddressedCallback(Addr, RubyRequestType, MachineType); void unaddressedCallback(Addr, RubyRequestType, MachineType, @@ -252,3 +259,14 @@ structure (RubyPrefetcher, external = "yes") { void observePfHit(Addr); void observePfMiss(Addr); } + +structure(RubyPrefetcherProxy, external = "yes") { + void notifyPfHit(RequestPtr, bool, DataBlock); + void notifyPfMiss(RequestPtr, bool, DataBlock); + void notifyPfFill(RequestPtr, DataBlock, bool); + void notifyPfEvict(Addr, bool, RequestorID); + void completePrefetch(Addr); + // SLICC controller must define its own regProbePoints and call + // this for every RubyPrefetcherProxy object present + void regProbePoints(); +} diff --git a/src/mem/ruby/protocol/RubySlicc_Util.sm b/src/mem/ruby/protocol/RubySlicc_Util.sm index 3079f20f23..104c7c034c 100644 --- a/src/mem/ruby/protocol/RubySlicc_Util.sm +++ b/src/mem/ruby/protocol/RubySlicc_Util.sm @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 ARM Limited + * Copyright (c) 2021,2023 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -60,3 +60,4 @@ Addr makeNextStrideAddress(Addr addr, int stride); structure(BoolVec, external="yes") { } int countBoolVec(BoolVec bVec); +RequestorID getRequestorID(RequestPtr req); diff --git a/src/mem/ruby/protocol/SConscript b/src/mem/ruby/protocol/SConscript index 07545c3ae0..7369c0c193 100644 --- a/src/mem/ruby/protocol/SConscript +++ b/src/mem/ruby/protocol/SConscript @@ -36,7 +36,7 @@ from gem5_scons import Transform Import('*') -if env['CONF']['PROTOCOL'] == 'None': +if not env['CONF']['RUBY']: Return() output_dir = Dir('.') diff --git a/src/mem/ruby/protocol/SConsopts b/src/mem/ruby/protocol/SConsopts index 2fcc57a5f5..bcbc83ba9b 100644 --- a/src/mem/ruby/protocol/SConsopts +++ b/src/mem/ruby/protocol/SConsopts @@ -30,23 +30,6 @@ import os Import('*') -main.Append(ALL_PROTOCOLS=[ - 'GPU_VIPER', - 'MOESI_AMD_Base', - 'MESI_Two_Level', - 'MESI_Three_Level', - 'MESI_Three_Level_HTM', - 'MI_example', - 'MOESI_CMP_directory', - 'MOESI_CMP_token', - 'MOESI_hammer', - 'Garnet_standalone', - 'None' - ]) - -opt = BoolVariable('SLICC_HTML', 'Create HTML files', False) -sticky_vars.Add(opt) - main.Append(PROTOCOL_DIRS=[Dir('.')]) protocol_base = Dir('.') diff --git a/src/mem/ruby/protocol/chi/CHI-cache-actions.sm b/src/mem/ruby/protocol/chi/CHI-cache-actions.sm index d18c600516..07344718df 100644 --- a/src/mem/ruby/protocol/chi/CHI-cache-actions.sm +++ b/src/mem/ruby/protocol/chi/CHI-cache-actions.sm @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 ARM Limited + * Copyright (c) 2021-2023 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -63,6 +63,7 @@ action(AllocateTBE_Request, desc="") { out_msg.usesTxnId := false; out_msg.event := Event:SendRetryAck; out_msg.retryDest := in_msg.requestor; + out_msg.txnId := in_msg.txnId; retryQueue.emplace(in_msg.addr,false,in_msg.requestor); } } @@ -145,16 +146,24 @@ action(AllocateTBE_SeqRequest, desc="") { assert(in_msg.Prefetch == PrefetchBit:No); out_msg.is_local_pf := false; out_msg.is_remote_pf := false; + out_msg.txnId := max_outstanding_transactions; + + out_msg.atomic_op.clear(); + out_msg.atomic_op.orMask(in_msg.writeMask); if ((in_msg.Type == RubyRequestType:LD) || (in_msg.Type == RubyRequestType:IFETCH)) { out_msg.type := CHIRequestType:Load; - } else if (in_msg.Type == RubyRequestType:ST) { + } else if (in_msg.Type == RubyRequestType:ST) { if (in_msg.Size == blockSize) { out_msg.type := CHIRequestType:StoreLine; } else { out_msg.type := CHIRequestType:Store; } + } else if (in_msg.Type == RubyRequestType:ATOMIC_RETURN) { + out_msg.type := CHIRequestType:AtomicLoad; + } else if (in_msg.Type == RubyRequestType:ATOMIC_NO_RETURN){ + out_msg.type := CHIRequestType:AtomicStore; } else { error("Invalid RubyRequestType"); } @@ -598,10 +607,13 @@ action(Initiate_ReadUnique_AutoUpgrade, desc="") { action(Initiate_ReadUnique_Upgrade, desc="") { // must use the transitions with auto upgrade otherwise assert(is_HN == false); - assert(tbe.use_DCT == false); assert((tbe.dataValid && tbe.dataUnique) == false); assert((tbe.dir_ownerExists && tbe.dir_ownerIsExcl) == false); + // CompData or CompUC will always be send by us after permission is received + // from downstream + tbe.use_DCT := false; + tbe.actions.push(Event:ReadMissPipe); if (tbe.dataMaybeDirtyUpstream) { tbe.actions.push(Event:SendSnpUnique); @@ -764,6 +776,148 @@ action(Initiate_StoreMiss, desc="") { } } +action(Initiate_Atomic_UC, desc="") { + if ((policy_type == 0) || // ALL NEAR + (policy_type == 1) || // UNIQUE NEAR + (policy_type == 2) // PRESENT NEAR + ){ + tbe.actions.push(Event:DataArrayRead); + tbe.actions.push(Event:DelayAtomic); + tbe.actions.push(Event:AtomicHit); + tbe.actions.pushNB(Event:DataArrayWrite); + tbe.actions.pushNB(Event:TagArrayWrite); + } else { + error("Invalid policy type"); + } +} + +action(Initiate_Atomic_UD, desc="") { + if ((policy_type == 0) || // ALL NEAR + (policy_type == 1) || // UNIQUE NEAR + (policy_type == 2) // PRESENT NEAR + ){ + tbe.actions.push(Event:DataArrayRead); + tbe.actions.push(Event:DelayAtomic); + tbe.actions.push(Event:AtomicHit); + tbe.actions.pushNB(Event:DataArrayWrite); + tbe.actions.pushNB(Event:TagArrayWrite); + } else { + error("Invalid policy type"); + } +} + +action(Initiate_AtomicReturn_I, desc="") { + if (policy_type == 0){ // ALL NEAR + tbe.actions.push(Event:SendReadUnique); + tbe.actions.push(Event:WriteFEPipe); + tbe.actions.push(Event:CheckCacheFill); + tbe.actions.push(Event:WriteBEPipe); + tbe.actions.push(Event:TagArrayWrite); + tbe.atomic_to_be_done := true; + } else if ((policy_type == 1) || // UNIQUE NEAR + (policy_type == 2)) { // PRESENT NEAR + tbe.actions.push(Event:SendAtomicReturn_NoWait); + tbe.dataToBeInvalid := true; + tbe.doCacheFill := false; + tbe.atomic_to_be_done := false; + } else { + error("Invalid policy type"); + } +} + +action(Initiate_AtomicNoReturn_I, desc="") { + if (policy_type == 0){ // ALL NEAR + tbe.actions.push(Event:SendReadUnique); + tbe.actions.push(Event:WriteFEPipe); + tbe.actions.push(Event:CheckCacheFill); + tbe.actions.push(Event:WriteBEPipe); + tbe.actions.push(Event:TagArrayWrite); + tbe.atomic_to_be_done := true; + } else if (policy_type == 1) { // UNIQUE NEAR + tbe.actions.push(Event:SendAtomicNoReturn); + tbe.actions.push(Event:SendANRData); + tbe.dataToBeInvalid := true; + tbe.doCacheFill := false; + tbe.atomic_to_be_done := false; + } else { + error("Invalid policy type"); + } +} + +action(Initiate_AtomicReturn_SD, desc="") { + if (policy_type == 0){ // ALL NEAR + tbe.actions.push(Event:SendReadUnique); + tbe.actions.push(Event:WriteFEPipe); + tbe.actions.push(Event:CheckCacheFill); + tbe.actions.push(Event:WriteBEPipe); + tbe.actions.push(Event:TagArrayWrite); + tbe.atomic_to_be_done := true; + } else if (policy_type == 1) { // UNIQUE NEAR + tbe.actions.push(Event:SendAtomicReturn_NoWait); + tbe.dataToBeInvalid := true; + tbe.doCacheFill := false; + tbe.atomic_to_be_done := false; + } else { + error("Invalid policy type"); + } +} + +action(Initiate_AtomicNoReturn_SD, desc="") { + if (policy_type == 0){ // ALL NEAR + tbe.actions.push(Event:SendReadUnique); + tbe.actions.push(Event:WriteFEPipe); + tbe.actions.push(Event:CheckCacheFill); + tbe.actions.push(Event:WriteBEPipe); + tbe.actions.push(Event:TagArrayWrite); + tbe.atomic_to_be_done := true; + } else if (policy_type == 1) { // UNIQUE NEAR + tbe.actions.push(Event:SendAtomicNoReturn); + tbe.actions.push(Event:SendANRData); + tbe.dataToBeInvalid := true; + tbe.doCacheFill := false; + tbe.atomic_to_be_done := false; + } else { + error("Invalid policy type"); + } +} + +action(Initiate_AtomicReturn_SC, desc="") { + if (policy_type == 0){ // ALL NEAR + tbe.actions.push(Event:SendReadUnique); + tbe.actions.push(Event:WriteFEPipe); + tbe.actions.push(Event:CheckCacheFill); + tbe.actions.push(Event:WriteBEPipe); + tbe.actions.push(Event:TagArrayWrite); + tbe.atomic_to_be_done := true; + } else if (policy_type == 1) { // UNIQUE NEAR + tbe.actions.push(Event:SendAtomicReturn_NoWait); + tbe.dataToBeInvalid := true; + tbe.doCacheFill := false; + tbe.atomic_to_be_done := false; + } else { + error("Invalid policy type"); + } +} + +action(Initiate_AtomicNoReturn_SC, desc="") { + if (policy_type == 0){ // ALL NEAR + tbe.actions.push(Event:SendReadUnique); + tbe.actions.push(Event:WriteFEPipe); + tbe.actions.push(Event:CheckCacheFill); + tbe.actions.push(Event:WriteBEPipe); + tbe.actions.push(Event:TagArrayWrite); + tbe.atomic_to_be_done := true; + } else if (policy_type == 1) { // UNIQUE NEAR + tbe.actions.push(Event:SendAtomicNoReturn); + tbe.actions.push(Event:SendANRData); + tbe.dataToBeInvalid := true; + tbe.doCacheFill := false; + tbe.atomic_to_be_done := false; + } else { + error("Invalid policy type"); + } +} + action(Initiate_StoreUpgrade, desc="") { assert(tbe.dataValid); assert(is_valid(cache_entry)); @@ -860,8 +1014,111 @@ action(Initiate_WriteUnique_Forward, desc="") { tbe.actions.pushNB(Event:TagArrayWrite); } +action(Initiate_AtomicReturn_LocalWrite, desc="") { + if ((tbe.dir_sharers.count() > 0) && tbe.dataMaybeDirtyUpstream) { + tbe.actions.push(Event:SendSnpUnique); + } else if (tbe.dir_sharers.count() > 0){ + // no one will send us data unless we explicitly ask + tbe.actions.push(Event:SendSnpUniqueRetToSrc); + } + tbe.actions.push(Event:SendDBIDResp_AR); + tbe.actions.pushNB(Event:WriteFEPipe); + tbe.actions.pushNB(Event:SendCompData_AR); + tbe.actions.push(Event:WriteFEPipe); + tbe.actions.push(Event:CheckCacheFill); + tbe.actions.push(Event:DelayAtomic); + tbe.actions.push(Event:WriteBEPipe); + tbe.actions.push(Event:TagArrayWrite); +} +action(Initiate_AtomicNoReturn_LocalWrite, desc="") { + if ((tbe.dir_sharers.count() > 0) && tbe.dataMaybeDirtyUpstream) { + tbe.actions.push(Event:SendSnpUnique); + } else if (tbe.dir_sharers.count() > 0){ + // no one will send us data unless we explicitly ask + tbe.actions.push(Event:SendSnpUniqueRetToSrc); + } + if (comp_anr) { + tbe.actions.push(Event:SendDBIDResp_ANR); + tbe.actions.pushNB(Event:WriteFEPipe); + tbe.actions.pushNB(Event:SendComp_ANR); + } else { + tbe.actions.push(Event:SendCompDBIDResp_ANR); + tbe.actions.pushNB(Event:WriteFEPipe); + } + tbe.actions.push(Event:WriteFEPipe); + tbe.actions.push(Event:CheckCacheFill); + tbe.actions.push(Event:DelayAtomic); + tbe.actions.push(Event:WriteBEPipe); + tbe.actions.push(Event:TagArrayWrite); +} + + +action(Initiate_AtomicReturn_Forward, desc="") { + if ((tbe.dir_sharers.count() > 0) && + (tbe.dir_sharers.isElement(tbe.requestor))){ + tbe.dir_sharers.remove(tbe.requestor); + } + tbe.actions.push(Event:SendAtomicReturn); + tbe.actions.push(Event:SendCompData_AR); + tbe.actions.pushNB(Event:TagArrayWrite); + + tbe.dataToBeInvalid := true; +} + +action(Initiate_AtomicNoReturn_Forward, desc="") { + if ((tbe.dir_sharers.count() > 0) && + (tbe.dir_sharers.isElement(tbe.requestor))){ + tbe.dir_sharers.remove(tbe.requestor); + } + if (comp_anr) { + tbe.actions.push(Event:SendAtomicNoReturn); + tbe.actions.push(Event:SendDBIDResp_ANR); + tbe.actions.pushNB(Event:SendComp_ANR); + } else { + tbe.actions.push(Event:SendAtomicNoReturn); + tbe.actions.push(Event:SendCompDBIDResp_ANR); + } + tbe.actions.push(Event:WriteBEPipe); + tbe.actions.push(Event:SendANRData); + tbe.actions.pushNB(Event:TagArrayWrite); + + tbe.dataToBeInvalid := true; +} + +action(Initiate_AtomicReturn_Miss, desc="") { + tbe.actions.push(Event:SendReadNoSnp); + tbe.actions.pushNB(Event:WriteFEPipe); + tbe.actions.push(Event:SendDBIDResp_AR); + tbe.actions.pushNB(Event:WriteFEPipe); + tbe.actions.pushNB(Event:SendCompData_AR); + tbe.actions.push(Event:WriteFEPipe); + tbe.actions.push(Event:CheckCacheFill); + tbe.actions.push(Event:DelayAtomic); + tbe.actions.push(Event:WriteBEPipe); + tbe.actions.push(Event:TagArrayWrite); +} + +action(Initiate_AtomicNoReturn_Miss, desc="") { + assert(is_HN); + tbe.actions.push(Event:SendReadNoSnp); + if (comp_anr) { + tbe.actions.push(Event:SendDBIDResp_ANR); + tbe.actions.pushNB(Event:WriteFEPipe); + tbe.actions.pushNB(Event:SendComp_ANR); + } else { + tbe.actions.push(Event:SendCompDBIDResp_ANR); + tbe.actions.pushNB(Event:WriteFEPipe); + } + + tbe.actions.push(Event:WriteFEPipe); + tbe.actions.push(Event:CheckCacheFill); + tbe.actions.push(Event:DelayAtomic); + tbe.actions.push(Event:WriteBEPipe); + tbe.actions.push(Event:TagArrayWrite); +} + action(Initiate_CopyBack, desc="") { // expect to receive this data after Send_CompDBIDResp if (tbe.reqType == CHIRequestType:WriteBackFull) { @@ -923,8 +1180,8 @@ action(Initiate_Evict, desc="") { tbe.actions.push(Event:WriteBEPipe); tbe.actions.push(Event:SendWBData); } else { - tbe.actions.push(Event:SendCompIResp); tbe.actions.push(Event:SendEvict); + tbe.actions.push(Event:SendCompIResp); } } else { tbe.actions.push(Event:SendCompIResp); @@ -1152,7 +1409,9 @@ action(Send_ReadShared, desc="") { action(Send_ReadNoSnp, desc="") { assert(is_HN); - assert(tbe.use_DMT == false); + assert((tbe.use_DMT == false) || + ((tbe.reqType == CHIRequestType:AtomicReturn) || + (tbe.reqType == CHIRequestType:AtomicNoReturn))); clearExpectedReqResp(tbe); tbe.expected_req_resp.addExpectedDataType(CHIDataType:CompData_UC); @@ -1363,6 +1622,45 @@ action(Send_WriteUnique, desc="") { tbe.expected_req_resp.addExpectedCount(1); } +action(Send_AtomicReturn, desc="") { + assert(is_valid(tbe)); + + enqueue(reqOutPort, CHIRequestMsg, request_latency) { + prepareRequestAtomic(tbe, CHIRequestType:AtomicReturn, out_msg); + out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); + allowRequestRetry(tbe, out_msg); + } + clearExpectedReqResp(tbe); + tbe.expected_req_resp.addExpectedRespType(CHIResponseType:DBIDResp); + tbe.expected_req_resp.addExpectedCount(1); +} + +action(Send_AtomicReturn_NoWait, desc="") { + assert(is_valid(tbe)); + + enqueue(reqOutPort, CHIRequestMsg, request_latency) { + prepareRequestAtomic(tbe, CHIRequestType:AtomicReturn, out_msg); + out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); + allowRequestRetry(tbe, out_msg); + } + + tbe.dataAMOValid := false; +} + +action(Send_AtomicNoReturn, desc="") { + assert(is_valid(tbe)); + + enqueue(reqOutPort, CHIRequestMsg, request_latency) { + prepareRequestAtomic(tbe, CHIRequestType:AtomicNoReturn, out_msg); + out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); + allowRequestRetry(tbe, out_msg); + } + tbe.expected_req_resp.addExpectedRespType(CHIResponseType:CompDBIDResp); + tbe.expected_req_resp.addExpectedRespType(CHIResponseType:DBIDResp); + tbe.expected_req_resp.addExpectedCount(1); +} + + action(Send_SnpCleanInvalid, desc="") { assert(is_valid(tbe)); assert(tbe.expected_snp_resp.hasExpected() == false); @@ -1631,6 +1929,20 @@ action(ExpectNCBWrData, desc="") { tbe.dataBlkValid.setMask(addressOffset(tbe.accAddr, tbe.addr), tbe.accSize, false); } +action(ExpectNCBWrData_A, desc="") { + // Expected data + int num_msgs := tbe.accSize / data_channel_size; + if ((tbe.accSize % data_channel_size) != 0) { + num_msgs := num_msgs + 1; + } + tbe.expected_req_resp.clear(num_msgs); + tbe.expected_req_resp.addExpectedDataType(CHIDataType:NCBWrData); + tbe.expected_req_resp.setExpectedCount(1); + + // In atomic operations we do not expect real data for the current block + // Thus the mask bits do not care +} + action(ExpectCompAck, desc="") { assert(is_valid(tbe)); tbe.expected_req_resp.addExpectedRespType(CHIResponseType:CompAck); @@ -1653,7 +1965,22 @@ action(Receive_ReqDataResp, desc="") { } // Copy data to tbe only if we didn't have valid data or the received // data is dirty - if ((tbe.dataBlkValid.isFull() == false) || + if ((in_msg.type == CHIDataType:NCBWrData) && + ((tbe.reqType == CHIRequestType:AtomicReturn) || + (tbe.reqType == CHIRequestType:AtomicNoReturn))){ + // DO NOTHING + } else if ((in_msg.type == CHIDataType:CompData_I) && + ((tbe.reqType == CHIRequestType:AtomicReturn) || + (tbe.reqType == CHIRequestType:AtomicLoad))) { + if(tbe.dataBlkValid.isFull()){ + tbe.dataBlkValid.clear(); + } + tbe.oldDataBlk.copyPartial(in_msg.dataBlk, in_msg.bitMask); + assert(tbe.dataBlkValid.isOverlap(in_msg.bitMask) == false); + tbe.dataBlkValid.orMask(in_msg.bitMask); + DPRINTF(RubySlicc, "Received %s\n", tbe.oldDataBlk); + DPRINTF(RubySlicc, "dataBlkValid = %s\n", tbe.dataBlkValid); + } else if ((tbe.dataBlkValid.isFull() == false) || (in_msg.type == CHIDataType:CompData_UD_PD) || (in_msg.type == CHIDataType:CompData_SD_PD) || (in_msg.type == CHIDataType:CBWrData_UD_PD) || @@ -1678,7 +2005,8 @@ action(Receive_RespSepDataFromCompData, desc="") { if (tbe.expected_req_resp.receiveResp(CHIResponseType:RespSepData) == false) { error("Received unexpected message"); } - if (is_HN == false) { + if ((is_HN == false) && (tbe.reqType != CHIRequestType:AtomicReturn) && + ((tbe.reqType != CHIRequestType:AtomicLoad) || (tbe.atomic_to_be_done == true))){ // must now ack the responder tbe.actions.pushFrontNB(Event:SendCompAck); } @@ -1900,6 +2228,7 @@ action(UpdateDataState_FromReqDataResp, desc="") { } else if (in_msg.type == CHIDataType:CompData_I) { tbe.dataValid := true; + tbe.dataAMOValid := true; tbe.dataToBeInvalid := true; assert(tbe.dataMaybeDirtyUpstream == false); @@ -1941,7 +2270,9 @@ action(UpdateDataState_FromReqDataResp, desc="") { action(UpdateDataState_FromWUDataResp, desc="") { assert(is_valid(tbe)); - if (tbe.expected_req_resp.hasReceivedData()) { + if (tbe.expected_req_resp.hasReceivedData() && + (tbe.reqType != CHIRequestType:AtomicReturn) && + (tbe.reqType != CHIRequestType:AtomicNoReturn)) { assert(tbe.dataBlkValid.test(addressOffset(tbe.accAddr, tbe.addr))); assert(tbe.dataBlkValid.test(addressOffset(tbe.accAddr, tbe.addr) + tbe.accSize - 1)); @@ -1959,6 +2290,23 @@ action(UpdateDataState_FromWUDataResp, desc="") { printTBEState(tbe); } +action(UpdateDataState_FromADataResp, desc="") { + assert(is_valid(tbe)); + if (is_HN && (tbe.expected_req_resp.hasReceivedData()) && + ((tbe.reqType == CHIRequestType:AtomicReturn) || + (tbe.reqType == CHIRequestType:AtomicNoReturn))) { + DPRINTF(RubySlicc, "Atomic before %s\n", tbe.dataBlk); + + tbe.oldDataBlk := tbe.dataBlk; + tbe.dataBlk.atomicPartial(tbe.dataBlk, tbe.atomic_op); + tbe.dataBlk.clearAtomicLogEntries(); + tbe.dataDirty := true; + + DPRINTF(RubySlicc, "Atomic after %s\n", tbe.dataBlk); + } + printTBEState(tbe); +} + action(UpdateDataState_FromCUResp, desc="") { assert(is_valid(tbe)); peek(rspInPort, CHIResponseMsg) { @@ -2122,6 +2470,10 @@ action(Receive_ReqResp_WUNeedComp, desc="") { tbe.defer_expected_comp := true; } +action(Receive_ReqResp_AR, desc="") { + tbe.actions.pushFrontNB(Event:SendARData); +} + action(Receive_ReqResp_WUComp, desc="") { if (tbe.defer_expected_comp) { tbe.defer_expected_comp := false; @@ -2130,6 +2482,16 @@ action(Receive_ReqResp_WUComp, desc="") { } } +action(Receive_ReqResp_CopyDBID, desc="Copy the rsp DBID into the TBE") { + if (tbe.expected_req_resp.receivedRespType(CHIResponseType:DBIDResp) == false && + tbe.expected_req_resp.receivedRespType(CHIResponseType:CompDBIDResp) == false) { + error("Received unexpected message"); + } + peek(rspInPort, CHIResponseMsg) { + tbe.txnId := in_msg.dbid; + } +} + action(Receive_SnpResp, desc="") { assert(tbe.expected_snp_resp.hasExpected()); peek(rspInPort, CHIResponseMsg) { @@ -2305,6 +2667,36 @@ action(CheckWUComp, desc="") { } } +action(Send_ARData, desc="") { + assert(is_valid(tbe)); + tbe.snd_msgType := CHIDataType:NCBWrData; + tbe.snd_destination := mapAddressToDownstreamMachine(tbe.addr); + setupPendingAtomicSend(tbe); +} + +action(Send_ANRData, desc="") { + assert(is_valid(tbe)); + tbe.snd_msgType := CHIDataType:NCBWrData; + tbe.snd_destination := mapAddressToDownstreamMachine(tbe.addr); + setupPendingAtomicSend(tbe); +} + +action(CheckARComp, desc="") { + assert(is_valid(tbe)); + tbe.expected_req_resp.addExpectedDataType(CHIDataType:CompData_I); + tbe.expected_req_resp.addExpectedRespType(CHIResponseType:RespSepData); + tbe.expected_req_resp.addExpectedCount(2); +} + +action(CheckANRComp, desc="") { + assert(is_valid(tbe)); + if (tbe.defer_expected_comp) { + tbe.defer_expected_comp := false; + tbe.expected_req_resp.addExpectedCount(1); + tbe.expected_req_resp.addExpectedRespType(CHIResponseType:Comp); + } +} + action(Send_SnpRespData, desc="") { assert(is_HN == false); assert(is_valid(tbe)); @@ -2316,7 +2708,7 @@ action(Send_SnpRespData, desc="") { ((tbe.dataDirty || tbe.dataUnique) && (tbe.reqType == CHIRequestType:SnpShared)) || ((tbe.dataDirty || tbe.dataUnique) && (tbe.reqType == CHIRequestType:SnpUnique))); - if (tbe.dataToBeInvalid) { + if (tbe.dataToBeInvalid && tbe.dir_sharers.isEmpty()) { assert(tbe.dataMaybeDirtyUpstream == false); if (tbe.dataDirty) { tbe.snd_msgType := CHIDataType:SnpRespData_I_PD; @@ -2505,6 +2897,7 @@ action(Send_Data, desc="") { enqueue(datOutPort, CHIDataMsg, data_latency) { out_msg.addr := tbe.addr; out_msg.type := tbe.snd_msgType; + out_msg.txnId := tbe.txnId; int offset := tbe.snd_pendBytes.firstBitSet(true); assert(offset < blockSize); @@ -2515,7 +2908,12 @@ action(Send_Data, desc="") { } tbe.snd_pendBytes.setMask(offset, range, false); - out_msg.dataBlk := tbe.dataBlk; + if (tbe.reqType == CHIRequestType:AtomicReturn){ + out_msg.dataBlk := tbe.oldDataBlk; + } else { + out_msg.dataBlk := tbe.dataBlk; + } + out_msg.bitMask.setMask(offset, range); out_msg.responder := machineID; @@ -2551,6 +2949,8 @@ action(Send_CompI, desc="") { out_msg.type := CHIResponseType:Comp_I; out_msg.responder := machineID; out_msg.Destination.add(tbe.requestor); + out_msg.txnId := tbe.txnId; + out_msg.dbid := tbe.txnId; } } @@ -2561,6 +2961,8 @@ action(Send_CompUC, desc="") { out_msg.type := CHIResponseType:Comp_UC; out_msg.responder := machineID; out_msg.Destination.add(tbe.requestor); + out_msg.txnId := tbe.txnId; + out_msg.dbid := tbe.txnId; } } @@ -2571,6 +2973,8 @@ action(Send_CompUC_Stale, desc="") { out_msg.type := CHIResponseType:Comp_UC; out_msg.responder := machineID; out_msg.Destination.add(tbe.requestor); + out_msg.txnId := tbe.txnId; + out_msg.dbid := tbe.txnId; // We don't know if this is a stale clean unique or a bug, so flag the // reponse so the requestor can make further checks out_msg.stale := true; @@ -2584,6 +2988,7 @@ action(Send_CompAck, desc="") { out_msg.type := CHIResponseType:CompAck; out_msg.responder := machineID; out_msg.Destination.add(mapAddressToDownstreamMachine(tbe.addr)); + out_msg.txnId := tbe.txnId; } } @@ -2594,6 +2999,7 @@ action(Send_CompI_Stale, desc="") { out_msg.type := CHIResponseType:Comp_I; out_msg.responder := machineID; out_msg.Destination.add(tbe.requestor); + out_msg.dbid := tbe.txnId; // We don't know if this is a stale writeback or a bug, so flag the // reponse so the requestor can make further checks out_msg.stale := true; @@ -2607,6 +3013,8 @@ action(Send_CompDBIDResp, desc="") { out_msg.type := CHIResponseType:CompDBIDResp; out_msg.responder := machineID; out_msg.Destination.add(tbe.requestor); + out_msg.txnId := tbe.txnId; + out_msg.dbid := tbe.txnId; } } @@ -2617,6 +3025,8 @@ action(Send_CompDBIDResp_Stale, desc="") { out_msg.type := CHIResponseType:CompDBIDResp; out_msg.responder := machineID; out_msg.Destination.add(tbe.requestor); + out_msg.txnId := tbe.txnId; + out_msg.dbid := tbe.txnId; // We don't know if this is a stale writeback or a bug, so flag the // reponse so the requestor can make further checks out_msg.stale := true; @@ -2630,6 +3040,7 @@ action(Send_DBIDResp, desc="") { out_msg.type := CHIResponseType:DBIDResp; out_msg.responder := machineID; out_msg.Destination.add(tbe.requestor); + out_msg.dbid := tbe.txnId; } } @@ -2643,6 +3054,36 @@ action(Send_Comp_WU, desc="") { } } + +action(Send_CompData_AR, desc="") { + assert(is_valid(tbe)); + assert(tbe.dataValid); + + if (is_HN) { + tbe.oldDataBlk := tbe.dataBlk; + } + + tbe.snd_msgType := CHIDataType:CompData_I; + tbe.dataMaybeDirtyUpstream := false; + tbe.requestorToBeExclusiveOwner := false; + tbe.requestorToBeOwner := false; + tbe.snd_destination := tbe.requestor; + setupPendingSend(tbe); + printTBEState(tbe); + +} + +action(Send_Comp_ANR, desc="") { + assert(is_valid(tbe)); + enqueue(rspOutPort, CHIResponseMsg, comp_anr_latency + response_latency) { + out_msg.addr := address; + out_msg.type := CHIResponseType:Comp; + out_msg.responder := machineID; + out_msg.Destination.add(tbe.requestor); + } +} + + action(Send_SnpRespI, desc="") { enqueue(rspOutPort, CHIResponseMsg, response_latency) { out_msg.addr := address; @@ -2664,6 +3105,7 @@ action(Send_RetryAck, desc="") { out_msg.type := CHIResponseType:RetryAck; out_msg.responder := machineID; out_msg.Destination.add(in_msg.retryDest); + out_msg.txnId := in_msg.txnId; } } } @@ -2972,6 +3414,23 @@ action(Callback_StoreHit, desc="") { } } +action(Callback_AtomicHit, desc="") { + assert(is_valid(tbe)); + assert(tbe.dataValid); + assert((tbe.reqType == CHIRequestType:AtomicLoad) || + (tbe.reqType == CHIRequestType:AtomicStore)); + DPRINTF(RubySlicc, "Atomic before %s\n", tbe.dataBlk); + + DataBlock oldDataBlk; + oldDataBlk := tbe.dataBlk; + tbe.dataBlk.atomicPartial(tbe.dataBlk, tbe.atomic_op); + tbe.dataBlk.clearAtomicLogEntries(); + + sequencer.atomicCallback(tbe.addr, oldDataBlk, false); + DPRINTF(RubySlicc, "Atomic after %s\n", tbe.dataBlk); + tbe.dataDirty := true; +} + action(Callback_ExpressPrefetchHit, desc="") { // have not allocated TBE, but must clear the reservation assert(is_invalid(tbe)); @@ -2982,7 +3441,7 @@ action(Callback_ExpressPrefetchHit, desc="") { cache.profilePrefetchHit(); peek(reqRdyPort, CHIRequestMsg) { assert(in_msg.is_local_pf); - notifyPfComplete(in_msg.addr); + pfProxy.completePrefetch(in_msg.addr); } } @@ -2993,7 +3452,7 @@ action(Callback_Miss, desc="") { assert(is_valid(tbe)); if (tbe.dataValid && tbe.is_local_pf) { assert(use_prefetcher); - notifyPfComplete(tbe.addr); + pfProxy.completePrefetch(tbe.addr); } else if (tbe.dataValid && (tbe.reqType == CHIRequestType:Load)) { DPRINTF(RubySlicc, "Read data %s\n", tbe.dataBlk); @@ -3020,6 +3479,26 @@ action(Callback_Miss, desc="") { // also decay the timeout scLockDecayLatency(); } + } else if (tbe.dataValid && tbe.atomic_to_be_done && + ((tbe.reqType == CHIRequestType:AtomicLoad) || + (tbe.reqType == CHIRequestType:AtomicStore))){ + assert(is_valid(tbe)); + assert(tbe.dataValid); + assert((tbe.reqType == CHIRequestType:AtomicLoad) || + (tbe.reqType == CHIRequestType:AtomicStore)); + DPRINTF(RubySlicc, "Atomic before %s\n", tbe.dataBlk); + + DataBlock oldDataBlk; + oldDataBlk := tbe.dataBlk; + tbe.dataBlk.atomicPartial(tbe.dataBlk, tbe.atomic_op); + tbe.dataBlk.clearAtomicLogEntries(); + + sequencer.atomicCallback(tbe.addr, oldDataBlk, false); + DPRINTF(RubySlicc, "Atomic after %s\n", tbe.dataBlk); + tbe.dataDirty := true; + } else if (tbe.dataValid && tbe.dataAMOValid && (tbe.reqType == CHIRequestType:AtomicLoad)) { + DPRINTF(RubySlicc, "Atomic before %s\n", tbe.oldDataBlk); + sequencer.atomicCallback(tbe.addr, tbe.oldDataBlk, false); } } @@ -3039,6 +3518,18 @@ action(Unset_Timeout_Cache, desc="") { wakeup_port(snpRdyPort, address); } +action(Callback_AtomicNoReturn, desc="") { + assert(is_valid(tbe)); + assert((tbe.is_local_pf || tbe.is_remote_pf) == false); + assert((tbe.reqType == CHIRequestType:AtomicNoReturn) || + (tbe.reqType == CHIRequestType:AtomicStore)); + + if(tbe.reqType == CHIRequestType:AtomicStore){ + sequencer.atomicCallback(tbe.addr, tbe.dataBlk); + DPRINTF(RubySlicc, "AtomicNoReturn %s\n", tbe.dataBlk); + } +} + action(Callback_WriteUnique, desc="") { assert(is_valid(tbe)); assert((tbe.is_local_pf || tbe.is_remote_pf) == false); @@ -3073,7 +3564,7 @@ action(Profile_Miss, desc="") { // FIXME: this dataBlk is likely to have stale data. This should be fixed // if our prefetcher uses cached data to make prefetch decisions. - notifyPfMiss(tbe.seqReq, is_read, tbe.dataBlk); + pfProxy.notifyPfMiss(tbe.seqReq, is_read, tbe.dataBlk); } } @@ -3097,7 +3588,7 @@ action(Profile_Hit, desc="") { } else { assert(isWriteReqType(tbe.reqType)); } - notifyPfHit(tbe.seqReq, is_read, tbe.dataBlk); + pfProxy.notifyPfHit(tbe.seqReq, is_read, tbe.dataBlk); cache_entry.HWPrefetched := false; } @@ -3111,10 +3602,11 @@ action(Profile_Fill, desc="") { cache_entry.HWPrefetched := tbe.is_local_pf || (tbe.is_remote_pf && (upstream_prefetch_trains_prefetcher == false)); + cache_entry.requestor := getRequestorID(tbe.seqReq); // Prefetchers that use this info require notifications from both // demand and pf fills (unlike notifyPfHit/notifyPfMiss) - notifyPfFill(tbe.seqReq, tbe.dataBlk, tbe.is_local_pf); + pfProxy.notifyPfFill(tbe.seqReq, tbe.dataBlk, tbe.is_local_pf); } } @@ -3128,7 +3620,7 @@ action(Profile_Eviction, desc="") { sequencer.evictionCallback(address); } if (use_prefetcher && is_valid(cache_entry)) { - notifyPfEvict(address, cache_entry.HWPrefetched); + pfProxy.notifyPfEvict(address, cache_entry.HWPrefetched, cache_entry.requestor); } } @@ -3152,9 +3644,7 @@ action(Profile_OutgoingEnd_DatalessResp, desc="") { action(TagArrayRead, desc="") { assert(is_valid(tbe)); tbe.delayNextAction := curTick() + cyclesToTicks( - tagLatency((tbe.reqType == CHIRequestType:Load) || - (tbe.reqType == CHIRequestType:Store) || - (tbe.reqType == CHIRequestType:StoreLine))); + tagLatency(fromSequencer(tbe.reqType))); } action(TagArrayWrite, desc="") { @@ -3206,6 +3696,11 @@ action(FillPipe, desc="") { tbe.delayNextAction := curTick() + cyclesToTicks(fill_latency); } +action(DelayAtomic, desc="") { + assert(is_valid(tbe)); + tbe.delayNextAction := curTick() + cyclesToTicks(atomic_op_latency); +} + action(SnpSharedPipe, desc="") { assert(is_valid(tbe)); tbe.delayNextAction := curTick() + cyclesToTicks(snp_latency); diff --git a/src/mem/ruby/protocol/chi/CHI-cache-funcs.sm b/src/mem/ruby/protocol/chi/CHI-cache-funcs.sm index f990c0b3b5..113f4c4871 100644 --- a/src/mem/ruby/protocol/chi/CHI-cache-funcs.sm +++ b/src/mem/ruby/protocol/chi/CHI-cache-funcs.sm @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 ARM Limited + * Copyright (c) 2021-2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -68,12 +68,6 @@ void outgoingTransactionEnd(Addr, bool, bool); Event curTransitionEvent(); State curTransitionNextState(); -// Placeholders for future prefetch support -void notifyPfHit(RequestPtr req, bool is_read, DataBlock blk) { } -void notifyPfMiss(RequestPtr req, bool is_read, DataBlock blk) { } -void notifyPfFill(RequestPtr req, DataBlock blk, bool from_pf) { } -void notifyPfEvict(Addr blkAddr, bool hwPrefetched) { } -void notifyPfComplete(Addr addr) { } //////////////////////////////////////////////////////////////////////////// // Interface functions required by SLICC @@ -299,7 +293,19 @@ Cycles dataLatency() { return cache.getDataLatency(); } -bool inCache(Addr addr) { +bool fromSequencer(CHIRequestType reqType) { + return reqType == CHIRequestType:Load || + reqType == CHIRequestType:Store || + reqType == CHIRequestType:StoreLine || + reqType == CHIRequestType:AtomicLoad || + reqType == CHIRequestType:AtomicStore; +} + +void regProbePoints() { + pfProxy.regProbePoints(); +} + +bool inCache(Addr addr, bool is_secure) { CacheEntry entry := getCacheEntry(makeLineAddress(addr)); // NOTE: we consider data for the addr to be in cache if it exists in local, // upstream, or both caches. @@ -310,7 +316,16 @@ bool inCache(Addr addr) { } } -bool hasBeenPrefetched(Addr addr) { +bool hasBeenPrefetched(Addr addr, bool is_secure, RequestorID requestor) { + CacheEntry entry := getCacheEntry(makeLineAddress(addr)); + if (is_valid(entry)) { + return entry.HWPrefetched && (entry.requestor == requestor); + } else { + return false; + } +} + +bool hasBeenPrefetched(Addr addr, bool is_secure) { CacheEntry entry := getCacheEntry(makeLineAddress(addr)); if (is_valid(entry)) { return entry.HWPrefetched; @@ -319,12 +334,16 @@ bool hasBeenPrefetched(Addr addr) { } } -bool inMissQueue(Addr addr) { +bool inMissQueue(Addr addr, bool is_secure) { Addr line_addr := makeLineAddress(addr); TBE tbe := getCurrentActiveTBE(line_addr); return is_valid(tbe); } +bool coalesce() { + return false; +} + void notifyCoalesced(Addr addr, RubyRequestType type, RequestPtr req, DataBlock data_blk, bool was_miss) { DPRINTF(RubySlicc, "notifyCoalesced(addr=%#x, type=%s, was_miss=%d)\n", @@ -339,9 +358,9 @@ void notifyCoalesced(Addr addr, RubyRequestType type, RequestPtr req, (type == RubyRequestType:Load_Linked) || (type == RubyRequestType:IFETCH); if (was_miss) { - notifyPfMiss(req, is_read, data_blk); + pfProxy.notifyPfMiss(req, is_read, data_blk); } else { - notifyPfHit(req, is_read, data_blk); + pfProxy.notifyPfHit(req, is_read, data_blk); } } } @@ -405,6 +424,12 @@ TBE allocateRequestTBE(Addr addr, CHIRequestMsg in_msg), return_by_pointer="yes" TBE tbe := TBEs[addr]; initializeTBE(tbe, addr, storTBEs.addEntryToNewSlot()); + if (fromSequencer(in_msg.type)) { + assert(in_msg.txnId == max_outstanding_transactions); + tbe.txnId := static_cast(Addr, "value", tbe.storSlot); + } else { + tbe.txnId := in_msg.txnId; + } assert(tbe.is_snp_tbe == false); assert(tbe.is_repl_tbe == false); @@ -422,6 +447,9 @@ TBE allocateRequestTBE(Addr addr, CHIRequestMsg in_msg), return_by_pointer="yes" tbe.is_local_pf := in_msg.is_local_pf; tbe.is_remote_pf := in_msg.is_remote_pf; + tbe.atomic_op.clear(); + tbe.atomic_op.orMask(in_msg.atomic_op); + tbe.use_DMT := false; tbe.use_DCT := false; @@ -486,6 +514,7 @@ TBE allocateSnoopTBE(Addr addr, CHIRequestMsg in_msg), return_by_pointer="yes" { tbe.requestor := in_msg.requestor; tbe.fwdRequestor := in_msg.fwdRequestor; tbe.reqType := in_msg.type; + tbe.txnId := in_msg.txnId; tbe.snpNeedsData := in_msg.retToSrc; @@ -538,6 +567,8 @@ TBE _allocateReplacementTBE(Addr addr, int storSlot), return_by_pointer="yes" { tbe.accSize := blockSize; tbe.requestor := machineID; tbe.reqType := CHIRequestType:null; + // This is an internal event and should generate a new TxnId + tbe.txnId := static_cast(Addr, "value", storSlot); tbe.use_DMT := false; tbe.use_DCT := false; @@ -607,6 +638,13 @@ void setupPendingPartialSend(TBE tbe) { scheduleSendData(tbe, 0); } +void setupPendingAtomicSend(TBE tbe) { + assert(blockSize >= data_channel_size); + assert((blockSize % data_channel_size) == 0); + tbe.snd_pendBytes.setMask(0,tbe.accSize,true); + scheduleSendData(tbe, 0); +} + // common code for downstream requests void prepareRequest(TBE tbe, CHIRequestType type, CHIRequestMsg & out_msg) { out_msg.addr := tbe.addr; @@ -624,6 +662,20 @@ void prepareRequest(TBE tbe, CHIRequestType type, CHIRequestMsg & out_msg) { out_msg.seqReq := tbe.seqReq; out_msg.is_local_pf := false; out_msg.is_remote_pf := tbe.is_local_pf || tbe.is_remote_pf; + out_msg.txnId := tbe.txnId; + + assert(tbe.txnId != static_cast(Addr, "value", -1)); +} + +void prepareRequestAtomic(TBE tbe, CHIRequestType type, + CHIRequestMsg & out_msg) { + assert((type == CHIRequestType:AtomicReturn) || + (type == CHIRequestType:AtomicNoReturn)); + prepareRequest(tbe, type, out_msg); + out_msg.accAddr := tbe.accAddr; + out_msg.accSize := tbe.accSize; + out_msg.atomic_op.clear(); + out_msg.atomic_op.orMask(tbe.atomic_op); } void allowRequestRetry(TBE tbe, CHIRequestMsg & out_msg) { @@ -654,6 +706,8 @@ void prepareRequestRetry(TBE tbe, CHIRequestMsg & out_msg) { out_msg.seqReq := tbe.seqReq; out_msg.is_local_pf := false; out_msg.is_remote_pf := tbe.is_local_pf || tbe.is_remote_pf; + out_msg.atomic_op.clear(); + out_msg.atomic_op.orMask(tbe.atomic_op); } void prepareRequestRetryDVM(TBE tbe, CHIRequestMsg & out_msg) { @@ -755,8 +809,12 @@ bool needCacheEntry(CHIRequestType req_type, (req_type == CHIRequestType:WriteEvictFull) || (is_HN && (req_type == CHIRequestType:WriteUniqueFull)))) || (alloc_on_seq_acc && ((req_type == CHIRequestType:Load) || - (req_type == CHIRequestType:Store))) || - (alloc_on_seq_line_write && (req_type == CHIRequestType:StoreLine)); + (req_type == CHIRequestType:Store) || + (req_type == CHIRequestType:AtomicLoad) || + (req_type == CHIRequestType:AtomicStore))) || + (alloc_on_seq_line_write && (req_type == CHIRequestType:StoreLine)) || + (alloc_on_atomic && ((req_type == CHIRequestType:AtomicReturn) || + (req_type == CHIRequestType:AtomicNoReturn))); } } @@ -780,12 +838,12 @@ bool upstreamHasShared(State state) { } void printTBEState(TBE tbe) { - DPRINTF(RubySlicc, "STATE: addr: %#x data present=%d valid=%d unique=%d dirty=%d mu_dirty=%d dir ownerV=%d ownerE=%d sharers=%d tobe_I=%d tobe_SC=%d doFill=%d pendAction=%s\n", + DPRINTF(RubySlicc, "STATE: addr: %#x data present=%d valid=%d unique=%d dirty=%d mu_dirty=%d dir ownerV=%d ownerE=%d sharers=%d tobe_I=%d tobe_SC=%d doFill=%d pendAction=%s txnId=%d\n", tbe.addr, tbe.dataBlkValid.isFull(), tbe.dataValid, tbe.dataUnique, tbe.dataDirty, tbe.dataMaybeDirtyUpstream, tbe.dir_ownerExists, tbe.dir_ownerIsExcl,tbe.dir_sharers.count(), tbe.dataToBeInvalid, tbe.dataToBeSharedClean, - tbe.doCacheFill, tbe.pendAction); + tbe.doCacheFill, tbe.pendAction, tbe.txnId); DPRINTF(RubySlicc, "dataBlkValid = %s\n", tbe.dataBlkValid); } @@ -1156,6 +1214,10 @@ Event reqToEvent(CHIRequestType type, bool is_prefetch) { return Event:Store; } else if (type == CHIRequestType:StoreLine) { return Event:Store; + } else if (type == CHIRequestType:AtomicLoad) { + return Event:AtomicLoad; + } else if (type == CHIRequestType:AtomicStore){ + return Event:AtomicStore; } else if (type == CHIRequestType:ReadShared) { return Event:ReadShared; } else if (type == CHIRequestType:ReadNotSharedDirty) { @@ -1196,6 +1258,18 @@ Event reqToEvent(CHIRequestType type, bool is_prefetch) { return Event:DvmTlbi_Initiate; } else if (type == CHIRequestType:DvmSync_Initiate) { return Event:DvmSync_Initiate; + } else if (type == CHIRequestType:AtomicReturn){ + if (is_HN) { + return Event:AtomicReturn_PoC; + } else { + return Event:AtomicReturn; + } + } else if (type == CHIRequestType:AtomicNoReturn){ + if (is_HN) { + return Event:AtomicNoReturn_PoC; + } else { + return Event:AtomicNoReturn; + } } else { error("Invalid CHIRequestType"); } diff --git a/src/mem/ruby/protocol/chi/CHI-cache-transitions.sm b/src/mem/ruby/protocol/chi/CHI-cache-transitions.sm index 4c93988afd..1654b9bf02 100644 --- a/src/mem/ruby/protocol/chi/CHI-cache-transitions.sm +++ b/src/mem/ruby/protocol/chi/CHI-cache-transitions.sm @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 ARM Limited + * Copyright (c) 2021-2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -155,6 +155,12 @@ transition({BUSY_INTR,BUSY_BLKD}, FillPipe) { ProcessNextState_ClearPending; } +transition({BUSY_INTR,BUSY_BLKD}, DelayAtomic) { + Pop_TriggerQueue; + DelayAtomic; + ProcessNextState_ClearPending; +} + transition({BUSY_INTR,BUSY_BLKD}, SnpSharedPipe) { Pop_TriggerQueue; SnpSharedPipe; @@ -418,8 +424,82 @@ transition({RSC,RSD,RUSD,RUSC,RU,I}, WriteUnique, BUSY_BLKD) { ProcessNextState; } +// AtomicReturn and AtomicNoReturn -// Load / Store from sequencer & Prefetch from prefetcher +transition({I,SC,SC_RSC,SD,SD_RSD,SD_RSC,RSD,RUSD, + UD,UD_RSC,UD_RSD,UD_RU,UC,UC_RSC,UC_RU,RSC,RU}, AtomicReturn, BUSY_BLKD) { + Initiate_Request; + Initiate_AtomicReturn_Forward; + Profile_Miss; + Pop_ReqRdyQueue; + ProcessNextState; +} + +transition({I,SC,SC_RSC,SD,SD_RSD,SD_RSC,RSD,RUSD, + UD,UD_RSC,UD_RSD,UD_RU,UC,UC_RSC,UC_RU,RSC,RU}, AtomicNoReturn, BUSY_BLKD) { + Initiate_Request; + Initiate_AtomicNoReturn_Forward; + Profile_Miss; + Pop_ReqRdyQueue; + ProcessNextState; +} + +transition({UD,UD_RU,UD_RSD,UD_RSC,UC,UC_RU,UC_RSC}, + AtomicReturn_PoC, BUSY_BLKD) { + Initiate_Request; + Initiate_AtomicReturn_LocalWrite; + Profile_Hit; + Pop_ReqRdyQueue; + ProcessNextState; +} + +transition({UD,UD_RU,UD_RSD,UD_RSC,UC,UC_RU,UC_RSC}, + AtomicNoReturn_PoC, BUSY_BLKD) { + Initiate_Request; + Initiate_AtomicNoReturn_LocalWrite; + Profile_Hit; + Pop_ReqRdyQueue; + ProcessNextState; +} + +transition({SD, SD_RSD, SD_RSC, SC, SC_RSC, RSC, RSD, RUSC, RUSD, RU}, + AtomicReturn_PoC, BUSY_BLKD) { + Initiate_Request; + Initiate_AtomicReturn_LocalWrite; + Profile_Miss; + Pop_ReqRdyQueue; + ProcessNextState; +} + +transition({SD, SD_RSD, SD_RSC, SC, SC_RSC, RSC, RSD, RUSC, RUSD, RU}, + AtomicNoReturn_PoC, BUSY_BLKD) { + Initiate_Request; + Initiate_AtomicNoReturn_LocalWrite; + Profile_Miss; + Pop_ReqRdyQueue; + ProcessNextState; +} + +transition(I, AtomicReturn_PoC, BUSY_BLKD) { + Initiate_Request; + Initiate_AtomicReturn_Miss; + Allocate_DirEntry; + Profile_Miss; + Pop_ReqRdyQueue; + ProcessNextState; +} + +transition(I, AtomicNoReturn_PoC, BUSY_BLKD) { + Initiate_Request; + Initiate_AtomicNoReturn_Miss; + Allocate_DirEntry; + Profile_Miss; + Pop_ReqRdyQueue; + ProcessNextState; +} + + +// Load / Store / Atomic from sequencer & Prefetch from prefetcher transition({UD,UD_T,SD,UC,SC}, Load, BUSY_BLKD) { Initiate_Request; @@ -435,7 +515,7 @@ transition({UD,UD_T,SD,UC,SC}, Load, BUSY_BLKD) { // the local cache entry at the end since our data is stale. If the cache is // inclusive for unique data we need to keep the block, so just bypass the // normal path. -transition({UD,UD_T,SD,UC,SC,RU,RSC,RSD,RUSD,SC_RSC,SD_RSC,SD_RSD,UC_RSC,UC_RU,UD_RU,UD_RSD,UD_RSC}, Prefetch) { +transition({UD,UD_T,SD,UC,SC,RU,RSC,RSD,RUSC,RUSD,SC_RSC,SD_RSC,SD_RSD,UC_RSC,UC_RU,UD_RU,UD_RSD,UD_RSC}, Prefetch) { Callback_ExpressPrefetchHit; Pop_ReqRdyQueue; } @@ -460,6 +540,28 @@ transition(BUSY_BLKD, StoreHit) { ProcessNextState_ClearPending; } +transition(UC, {AtomicLoad,AtomicStore}, BUSY_BLKD) { + Initiate_Request; + Initiate_Atomic_UC; + Profile_Hit; + Pop_ReqRdyQueue; + ProcessNextState; +} + +transition({UD,UD_T}, {AtomicLoad,AtomicStore}, BUSY_BLKD) { + Initiate_Request; + Initiate_Atomic_UD; + Profile_Hit; + Pop_ReqRdyQueue; + ProcessNextState; +} + +transition(BUSY_BLKD, AtomicHit) { + Pop_TriggerQueue; + Callback_AtomicHit; + ProcessNextState_ClearPending; +} + transition(I, {Load,Prefetch}, BUSY_BLKD) { Initiate_Request; Initiate_LoadMiss; @@ -494,6 +596,55 @@ transition({BUSY_BLKD,BUSY_INTR}, UseTimeout) { Unset_Timeout_TBE; } +transition(I, AtomicLoad, BUSY_BLKD){ + Initiate_Request; + Initiate_AtomicReturn_I; + Profile_Miss; + Pop_ReqRdyQueue; + ProcessNextState; +} + +transition(I, AtomicStore, BUSY_BLKD){ + Initiate_Request; + Initiate_AtomicNoReturn_I; + Profile_Miss; + Pop_ReqRdyQueue; + ProcessNextState; +} + +transition(SD, AtomicLoad, BUSY_BLKD) { + Initiate_Request; + Initiate_AtomicReturn_SD; + Profile_Miss; + Pop_ReqRdyQueue; + ProcessNextState; +} + +transition(SC, AtomicLoad, BUSY_BLKD) { + Initiate_Request; + Initiate_AtomicReturn_SC; + Profile_Miss; + Pop_ReqRdyQueue; + ProcessNextState; +} + +transition(SD, AtomicStore, BUSY_BLKD) { + Initiate_Request; + Initiate_AtomicNoReturn_SD; + Profile_Miss; + Pop_ReqRdyQueue; + ProcessNextState; +} + +transition(SC, AtomicStore, BUSY_BLKD) { + Initiate_Request; + Initiate_AtomicNoReturn_SC; + Profile_Miss; + Pop_ReqRdyQueue; + ProcessNextState; +} + + // Evict from Upstream transition({UD_RSC,SD_RSC,UC_RSC,SC_RSC,RSC,RSD,RUSD,RUSC,UD_RSD,SD_RSD}, Evict, BUSY_BLKD) { @@ -691,13 +842,15 @@ transition(BUSY_INTR, {SnpOnce,SnpOnceFwd}, BUSY_BLKD) { transition({BUSY_BLKD,BUSY_INTR}, {ReadShared, ReadNotSharedDirty, ReadUnique, ReadUnique_PoC, ReadOnce, CleanUnique, CleanUnique_Stale, - Load, Store, Prefetch, + Load, Store, AtomicLoad, AtomicStore, Prefetch, WriteBackFull, WriteBackFull_Stale, WriteEvictFull, WriteEvictFull_Stale, WriteCleanFull, WriteCleanFull_Stale, Evict, Evict_Stale, WriteUnique,WriteUniquePtl_PoC, - WriteUniqueFull_PoC,WriteUniqueFull_PoC_Alloc}) { + WriteUniqueFull_PoC,WriteUniqueFull_PoC_Alloc + AtomicReturn,AtomicReturn_PoC, + AtomicNoReturn,AtomicNoReturn_PoC}) { StallRequest; } @@ -754,6 +907,30 @@ transition(BUSY_BLKD, SendWriteUnique, BUSY_INTR) {DestinationAvailable} { ProcessNextState_ClearPending; } +transition(BUSY_BLKD, SendAtomicReturn, BUSY_INTR) {DestinationAvailable} { + Pop_TriggerQueue; + Send_AtomicReturn; + CheckARComp; + Profile_OutgoingStart; + ProcessNextState_ClearPending; +} + +transition(BUSY_BLKD, SendAtomicReturn_NoWait, BUSY_INTR) { + Pop_TriggerQueue; + Send_AtomicReturn_NoWait; + CheckARComp; + Profile_OutgoingStart; + ProcessNextState_ClearPending; +} + +transition(BUSY_BLKD, SendAtomicNoReturn, BUSY_INTR) {DestinationAvailable} { + Pop_TriggerQueue; + Send_AtomicNoReturn; + Profile_OutgoingStart; + ProcessNextState_ClearPending; +} + + transition(BUSY_BLKD, SendWriteNoSnp, BUSY_INTR) {DestinationAvailable} { Pop_TriggerQueue; Send_WriteNoSnp; @@ -804,6 +981,20 @@ transition(BUSY_BLKD, SendWUDataCB) { ProcessNextState_ClearPending; } +transition({BUSY_BLKD,BUSY_INTR}, SendARData) { + Pop_TriggerQueue; + Send_ARData; + ProcessNextState_ClearPending; +} + +transition({BUSY_BLKD,BUSY_INTR}, SendANRData) { + Pop_TriggerQueue; + Callback_AtomicNoReturn; + Send_ANRData; + CheckANRComp; + ProcessNextState_ClearPending; +} + transition(BUSY_BLKD, SendInvSnpResp) { Pop_TriggerQueue; Send_InvSnpResp; @@ -1025,6 +1216,26 @@ transition({BUSY_BLKD,BUSY_INTR}, SendComp_WU) { ProcessNextState_ClearPending; } +transition(BUSY_BLKD, SendCompDBIDResp_ANR) { + Pop_TriggerQueue; + ExpectNCBWrData_A; + Send_CompDBIDResp; + ProcessNextState_ClearPending; +} + +transition(BUSY_BLKD, SendDBIDResp_AR) { + Pop_TriggerQueue; + ExpectNCBWrData_A; + Send_DBIDResp; + ProcessNextState_ClearPending; +} + +transition({BUSY_BLKD,BUSY_INTR}, SendCompData_AR) { + Pop_TriggerQueue; + Send_CompData_AR; + ProcessNextState_ClearPending; +} + transition(BUSY_BLKD, SendCompDBIDRespStale) { Pop_TriggerQueue; Send_CompDBIDResp_Stale; @@ -1085,6 +1296,7 @@ transition(BUSY_BLKD, transition({BUSY_BLKD,BUSY_INTR}, NCBWrData) { Receive_ReqDataResp; UpdateDataState_FromWUDataResp; + UpdateDataState_FromADataResp; Pop_DataInQueue; ProcessNextState; } @@ -1212,8 +1424,7 @@ transition(BUSY_BLKD, } // waiting for WB or evict ack -transition(BUSY_INTR, - {CompDBIDResp,Comp_I}, BUSY_BLKD) { +transition(BUSY_INTR, Comp_I, BUSY_BLKD) { Receive_ReqResp; Profile_OutgoingEnd_DatalessResp; Pop_RespInQueue; @@ -1229,10 +1440,21 @@ transition(BUSY_INTR, Comp_UC, BUSY_BLKD) { ProcessNextState; } -// alternative flow for WU with separate Comp -transition(BUSY_INTR, DBIDResp, BUSY_BLKD) { +// waiting for WB or evict ack +transition(BUSY_INTR, CompDBIDResp, BUSY_BLKD) { Receive_ReqResp; + Receive_ReqResp_CopyDBID; + Profile_OutgoingEnd_DatalessResp; + Pop_RespInQueue; + ProcessNextState; +} + +// alternative flow for WU with separate Comp +transition({BUSY_INTR,BUSY_BLKD}, DBIDResp, BUSY_BLKD) { + Receive_ReqResp; + Receive_ReqResp_CopyDBID; Receive_ReqResp_WUNeedComp; + Receive_ReqResp_AR; Pop_RespInQueue; ProcessNextState; } diff --git a/src/mem/ruby/protocol/chi/CHI-cache.sm b/src/mem/ruby/protocol/chi/CHI-cache.sm index 3bd8d3f3c3..62fb2231ff 100644 --- a/src/mem/ruby/protocol/chi/CHI-cache.sm +++ b/src/mem/ruby/protocol/chi/CHI-cache.sm @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 ARM Limited + * Copyright (c) 2021-2023 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -46,11 +46,16 @@ machine(MachineType:Cache, "Cache coherency protocol") : // happen in parallel. The cache tag latency is used for both cases. CacheMemory * cache; + // Prefetcher to insert prefetch requests + // null if the cache does not support prefetching + prefetch::Base * prefetcher; + // Additional pipeline latency modeling for the different request types // When defined, these are applied after the initial tag array read and // sending necessary snoops. Cycles read_hit_latency := 0; Cycles read_miss_latency := 0; + Cycles atomic_op_latency := 0; Cycles write_fe_latency := 0; // Front-end: Rcv req -> Snd req Cycles write_be_latency := 0; // Back-end: Rcv ack -> Snd data Cycles fill_latency := 0; // Fill latency @@ -81,6 +86,9 @@ machine(MachineType:Cache, "Cache coherency protocol") : int sc_lock_multiplier_max := 256; bool sc_lock_enabled; + // Maximum number of outstanding transactions from a single requester + Addr max_outstanding_transactions := 1024; + // Recycle latency on resource stalls Cycles stall_recycle_lat := 1; @@ -123,11 +131,24 @@ machine(MachineType:Cache, "Cache coherency protocol") : // possible. bool enable_DCT; + // Atomic Operation Policy + // All Near executes all Atomics at L1 (variable set to 0; default) + // Unique Near executes Atomics at HNF for states I, SC, SD (set to 1) + // Present Near execites all Atomics at L1 except when state is I (set to 2) + int policy_type := 1; + + // Use separate Comp/DBIDResp responses for WriteUnique bool comp_wu := "False"; // additional latency for the WU Comp response Cycles comp_wu_latency := 0; + + // Use separate Comp/DBIDResp responses for AtomicNoResponse + bool comp_anr := "False"; + // additional latency for the ANR Comp response + Cycles comp_anr_latency := 0; + // Controls cache clusivity for different request types. // set all alloc_on* to false to completelly disable caching bool alloc_on_readshared; @@ -136,6 +157,7 @@ machine(MachineType:Cache, "Cache coherency protocol") : bool alloc_on_writeback; bool alloc_on_seq_acc; bool alloc_on_seq_line_write; + bool alloc_on_atomic; // Controls if the clusivity is strict. bool dealloc_on_unique; bool dealloc_on_shared; @@ -280,37 +302,43 @@ machine(MachineType:Cache, "Cache coherency protocol") : // Events triggered by sequencer requests or snoops in the rdy queue // See CHIRequestType in CHi-msg.sm for descriptions - Load, desc=""; - Store, desc=""; - Prefetch, desc=""; - ReadShared, desc=""; - ReadNotSharedDirty, desc=""; - ReadUnique, desc=""; - ReadUnique_PoC, desc=""; - ReadOnce, desc=""; - CleanUnique, desc=""; - Evict, desc=""; - WriteBackFull, desc=""; - WriteEvictFull, desc=""; - WriteCleanFull, desc=""; - WriteUnique, desc=""; - WriteUniquePtl_PoC, desc=""; - WriteUniqueFull_PoC, desc=""; - WriteUniqueFull_PoC_Alloc, desc=""; - SnpCleanInvalid, desc=""; - SnpShared, desc=""; - SnpSharedFwd, desc=""; - SnpNotSharedDirtyFwd, desc=""; - SnpUnique, desc=""; - SnpUniqueFwd, desc=""; - SnpOnce, desc=""; - SnpOnceFwd, desc=""; - SnpStalled, desc=""; // A snoop stall triggered from the inport + Load, desc="", in_trans="yes"; + Store, desc="", in_trans="yes"; + AtomicLoad, desc="", in_trans="yes"; + AtomicStore, desc="", in_trans="yes"; + Prefetch, desc="", in_trans="yes"; + ReadShared, desc="", in_trans="yes"; + ReadNotSharedDirty, desc="", in_trans="yes"; + ReadUnique, desc="", in_trans="yes"; + ReadUnique_PoC, desc="", in_trans="yes"; + ReadOnce, desc="", in_trans="yes"; + CleanUnique, desc="", in_trans="yes"; + Evict, desc="", in_trans="yes"; + WriteBackFull, desc="", in_trans="yes"; + WriteEvictFull, desc="", in_trans="yes"; + WriteCleanFull, desc="", in_trans="yes"; + WriteUnique, desc="", in_trans="yes"; + WriteUniquePtl_PoC, desc="", in_trans="yes"; + WriteUniqueFull_PoC, desc="", in_trans="yes"; + WriteUniqueFull_PoC_Alloc, desc="", in_trans="yes"; + AtomicReturn, desc="", in_trans="yes"; + AtomicNoReturn, desc="", in_trans="yes"; + AtomicReturn_PoC, desc="", in_trans="yes"; + AtomicNoReturn_PoC, desc="", in_trans="yes"; + SnpCleanInvalid, desc="", in_trans="yes"; + SnpShared, desc="", in_trans="yes"; + SnpSharedFwd, desc="", in_trans="yes"; + SnpNotSharedDirtyFwd, desc="", in_trans="yes"; + SnpUnique, desc="", in_trans="yes"; + SnpUniqueFwd, desc="", in_trans="yes"; + SnpOnce, desc="", in_trans="yes"; + SnpOnceFwd, desc="", in_trans="yes"; + SnpStalled, desc="", in_trans="yes"; // A snoop stall triggered from the inport // DVM sequencer requests - DvmTlbi_Initiate, desc=""; // triggered when a CPU core wants to send a TLBI + DvmTlbi_Initiate, desc="", out_trans="yes", in_trans="yes"; // triggered when a CPU core wants to send a TLBI // TLBIs are handled entirely within Ruby, so there's no ExternCompleted message - DvmSync_Initiate, desc=""; // triggered when a CPU core wants to send a sync + DvmSync_Initiate, desc="", out_trans="yes", in_trans="yes"; // triggered when a CPU core wants to send a sync DvmSync_ExternCompleted, desc=""; // triggered when an externally requested Sync is completed // Events triggered by incoming response messages @@ -344,10 +372,10 @@ machine(MachineType:Cache, "Cache coherency protocol") : PCrdGrant_PoC_Hazard, desc=""; // Events triggered by incoming DVM messages - SnpDvmOpSync_P1, desc=""; - SnpDvmOpSync_P2, desc=""; - SnpDvmOpNonSync_P1, desc=""; - SnpDvmOpNonSync_P2, desc=""; + SnpDvmOpSync_P1, desc="", in_trans="yes"; + SnpDvmOpSync_P2, desc="", in_trans="yes"; + SnpDvmOpNonSync_P1, desc="", in_trans="yes"; + SnpDvmOpNonSync_P2, desc="", in_trans="yes"; // Events triggered by incoming data response messages // See CHIDataType in CHi-msg.sm for descriptions @@ -383,20 +411,20 @@ machine(MachineType:Cache, "Cache coherency protocol") : // A Write or Evict becomes stale when the requester receives a snoop that // changes the state of the data while the request was pending. // Actual CHI implementations don't have this check. - Evict_Stale, desc=""; - WriteBackFull_Stale, desc=""; - WriteEvictFull_Stale, desc=""; - WriteCleanFull_Stale, desc=""; - CleanUnique_Stale, desc=""; + Evict_Stale, desc="", in_trans="yes"; + WriteBackFull_Stale, desc="", in_trans="yes"; + WriteEvictFull_Stale, desc="", in_trans="yes"; + WriteCleanFull_Stale, desc="", in_trans="yes"; + CleanUnique_Stale, desc="", in_trans="yes"; // Cache fill handling CheckCacheFill, desc="Check if need to write or update the cache and trigger any necessary allocation and evictions"; // Internal requests generated to evict or writeback a local copy // to free-up cache space - Local_Eviction, desc="Evicts/WB the local copy of the line"; - LocalHN_Eviction, desc="Local_Eviction triggered when is HN"; - Global_Eviction, desc="Local_Eviction + back-invalidate line in all upstream requesters"; + Local_Eviction, in_trans="yes", desc="Evicts/WB the local copy of the line"; + LocalHN_Eviction, in_trans="yes", desc="Local_Eviction triggered when is HN"; + Global_Eviction, in_trans="yes", desc="Local_Eviction + back-invalidate line in all upstream requesters"; // Events triggered from tbe.actions // In general, for each event we define a single transition from @@ -415,47 +443,62 @@ machine(MachineType:Cache, "Cache coherency protocol") : DataArrayWriteOnFill, desc="Write the cache data array (cache fill)"; // Events for modeling the pipeline latency - ReadHitPipe, desc="Latency of reads served from local cache"; - ReadMissPipe, desc="Latency of reads not served from local cache"; - WriteFEPipe, desc="Front-end latency of write requests"; - WriteBEPipe, desc="Back-end latency of write requests"; - FillPipe, desc="Cache fill latency"; + ReadHitPipe, desc="Latency of reads served from local cache"; + ReadMissPipe, desc="Latency of reads not served from local cache"; + WriteFEPipe, desc="Front-end latency of write requests"; + WriteBEPipe, desc="Back-end latency of write requests"; + FillPipe, desc="Cache fill latency"; + DelayAtomic, desc="Atomic operation latency"; SnpSharedPipe, desc="Latency for SnpShared requests"; SnpInvPipe, desc="Latency for SnpUnique and SnpCleanInv requests"; SnpOncePipe, desc="Latency for SnpOnce requests"; // Send a read request downstream. - SendReadShared, desc="Send a ReadShared or ReadNotSharedDirty is allow_SD is false"; - SendReadOnce, desc="Send a ReadOnce"; - SendReadNoSnp, desc="Send a SendReadNoSnp"; - SendReadNoSnpDMT, desc="Send a SendReadNoSnp using DMT"; - SendReadUnique, desc="Send a ReadUnique"; + SendReadShared, out_trans="yes", desc="Send a ReadShared or ReadNotSharedDirty is allow_SD is false"; + SendReadOnce, out_trans="yes", desc="Send a ReadOnce"; + SendReadNoSnp, out_trans="yes", desc="Send a SendReadNoSnp"; + SendReadNoSnpDMT, out_trans="yes", desc="Send a SendReadNoSnp using DMT"; + SendReadUnique, out_trans="yes", desc="Send a ReadUnique"; SendCompAck, desc="Send CompAck"; // Read handling at the completer - SendCompData, desc="Send CompData"; - WaitCompAck, desc="Expect to receive CompAck"; - SendRespSepData, desc="Send RespSepData for a DMT request"; + SendCompData, desc="Send CompData"; + WaitCompAck, desc="Expect to receive CompAck"; + SendRespSepData, desc="Send RespSepData for a DMT request"; // Send a write request downstream. - SendWriteBackOrWriteEvict, desc="Send a WriteBackFull (if line is UD or SD) or WriteEvictFull (if UC)"; - SendWriteClean, desc="Send a WriteCleanFull"; - SendWriteNoSnp, desc="Send a WriteNoSnp for a full line"; - SendWriteNoSnpPartial, desc="Send a WriteNoSnpPtl"; - SendWriteUnique, desc="Send a WriteUniquePtl"; + SendWriteBackOrWriteEvict, out_trans="yes", desc="Send a WriteBackFull (if line is UD or SD) or WriteEvictFull (if UC)"; + SendWriteClean, out_trans="yes", desc="Send a WriteCleanFull"; + SendWriteNoSnp, out_trans="yes", desc="Send a WriteNoSnp for a full line"; + SendWriteNoSnpPartial, out_trans="yes", desc="Send a WriteNoSnpPtl"; + SendWriteUnique, out_trans="yes", desc="Send a WriteUniquePtl"; SendWBData, desc="Send writeback data"; SendWUData, desc="Send write unique data"; SendWUDataCB, desc="Send write unique data from a sequencer callback"; // Write handling at the completer - SendCompDBIDResp, desc="Ack WB with CompDBIDResp"; - SendCompDBIDRespStale, desc="Ack stale WB with CompDBIDResp"; - SendCompDBIDResp_WU, desc="Ack WU with CompDBIDResp and set expected data"; - SendDBIDResp_WU, desc="Ack WU with DBIDResp and set expected data"; - SendComp_WU, desc="Ack WU completion"; + SendCompDBIDResp, desc="Ack WB with CompDBIDResp"; + SendCompDBIDRespStale, desc="Ack stale WB with CompDBIDResp"; + SendCompDBIDResp_WU, desc="Ack WU with CompDBIDResp and set expected data"; + SendDBIDResp_WU, desc="Ack WU with DBIDResp and set expected data"; + SendComp_WU, desc="Ack WU completion"; + + // Send an atomic request downstream. + SendAtomicReturn, out_trans="yes", desc="Send atomic request with return"; + SendAtomicReturn_NoWait, out_trans="yes", desc="Send atomic request with return, but no DBID"; + SendAtomicNoReturn, out_trans="yes", desc="Send atomic request without return"; + SendARData, desc="Send atomic return request data"; + SendANRData, desc="Send atomic no return request data"; + // Atomic handling at the completer + SendDBIDResp_AR, desc="Ack AR with DBIDResp and set expected data"; + SendCompData_AR, desc="Ack AR completion"; + SendCompDBIDResp_ANR, desc="Ack ANR with CompDBIDResp and set expected data"; + SendDBIDResp_ANR, desc="Ack ANR with DBIDResp and set expected data"; + SendComp_ANR, desc="Ack ANR completion"; + // Dataless requests - SendEvict, desc="Send a Evict"; + SendEvict, out_trans="yes", desc="Send a Evict"; SendCompIResp, desc="Ack Evict with Comp_I"; - SendCleanUnique,desc="Send a CleanUnique"; + SendCleanUnique,out_trans="yes", desc="Send a CleanUnique"; SendCompUCResp, desc="Ack CleanUnique with Comp_UC"; SendCompUCRespStale, desc="Ack stale CleanUnique with Comp_UC"; @@ -499,6 +542,7 @@ machine(MachineType:Cache, "Cache coherency protocol") : // Misc triggers LoadHit, desc="Complete a load hit"; StoreHit, desc="Complete a store hit"; + AtomicHit, desc="Complete an atomic hit"; UseTimeout, desc="Transition from UD_T -> UD"; RestoreFromHazard, desc="Restore from a snoop hazard"; TX_Data, desc="Transmit pending data messages"; @@ -528,6 +572,7 @@ machine(MachineType:Cache, "Cache coherency protocol") : State state, desc="SLICC line state"; DataBlock DataBlk, desc="data for the block"; bool HWPrefetched, default="false", desc="Set if this cache entry was prefetched"; + RequestorID requestor, desc="First requestor to fill this block"; } // Directory entry @@ -599,6 +644,7 @@ machine(MachineType:Cache, "Cache coherency protocol") : Addr accAddr, desc="Access address for Load/Store/WriteUniquePtl; otherwisse == addr"; int accSize, desc="Access size for Load/Store/WriteUniquePtl; otherwisse == blockSize"; CHIRequestType reqType, desc="Request type that initiated this transaction"; + Addr txnId, desc="Transaction ID. We default to -1 for debug purposes", default="-1"; MachineID requestor, desc="Requestor ID"; MachineID fwdRequestor, desc="Requestor to receive data on fwding snoops"; bool use_DMT, desc="Use DMT for this transaction"; @@ -609,6 +655,10 @@ machine(MachineType:Cache, "Cache coherency protocol") : bool is_local_pf, desc="Request generated by a local prefetcher"; bool is_remote_pf, desc="Request generated a prefetcher in another cache"; + // Atomic info associated with the transaction + WriteMask atomic_op, desc="Atomic Operation Wrapper"; + bool atomic_to_be_done, desc="We have yet to perform the atomic"; + // NOTE: seqReq is a smart pointer pointing to original CPU request object // that triggers transactions associated with this TBE. seqReq carries some // information (e.g., PC of requesting instruction, virtual address of this @@ -626,8 +676,10 @@ machine(MachineType:Cache, "Cache coherency protocol") : // stable state. bool hasUseTimeout, desc="Line is locked under store/use timeout"; DataBlock dataBlk, desc="Local copy of the line"; + DataBlock oldDataBlk, desc="Local copy of the line before executing atomic"; WriteMask dataBlkValid, desc="Marks which bytes in the DataBlock are valid"; bool dataValid, desc="Local copy is valid"; + bool dataAMOValid, desc="Local copy is valid for AMO"; bool dataDirty, desc="Local copy is dirtry"; bool dataMaybeDirtyUpstream, desc="Line maybe dirty upstream"; bool dataUnique, desc="Line is unique either locally or upsatream"; @@ -765,6 +817,10 @@ machine(MachineType:Cache, "Cache coherency protocol") : TBETable dvmSnpTBEs, template="", constructor="m_number_of_DVM_snoop_TBEs"; TBEStorage storDvmSnpTBEs, constructor="this, m_number_of_DVM_snoop_TBEs"; + // Interface to prefetchers + RubyPrefetcherProxy pfProxy, constructor="this, m_prefetcher_ptr, m_prefetchQueue_ptr"; + + // DVM data // Queue of non-sync operations that haven't been Comp-d yet. // Before a Sync operation can start, this queue must be emptied @@ -785,6 +841,7 @@ machine(MachineType:Cache, "Cache coherency protocol") : Event event; MachineID retryDest; bool usesTxnId; + Addr txnId; bool functionalRead(Packet *pkt) { return false; } bool functionalRead(Packet *pkt, WriteMask &mask) { return false; } diff --git a/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-funcs.sm b/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-funcs.sm index ce87d02cf9..5d07862d25 100644 --- a/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-funcs.sm +++ b/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-funcs.sm @@ -137,15 +137,19 @@ Cycles dataLatency() { return intToCycles(0); } -bool inCache(Addr txnId) { +bool inCache(Addr txnId, bool is_secure) { return false; } -bool hasBeenPrefetched(Addr txnId) { +bool hasBeenPrefetched(Addr txnId, bool is_secure, RequestorID requestor) { return false; } -bool inMissQueue(Addr txnId) { +bool hasBeenPrefetched(Addr txnId, bool is_secure) { + return false; +} + +bool inMissQueue(Addr txnId, bool is_secure) { return false; } diff --git a/src/mem/ruby/protocol/chi/CHI-dvm-misc-node.sm b/src/mem/ruby/protocol/chi/CHI-dvm-misc-node.sm index 92a04ed3d2..aa27c40964 100644 --- a/src/mem/ruby/protocol/chi/CHI-dvm-misc-node.sm +++ b/src/mem/ruby/protocol/chi/CHI-dvm-misc-node.sm @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 ARM Limited + * Copyright (c) 2021-2023 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -170,8 +170,8 @@ machine(MachineType:MiscNode, "CHI Misc Node for handling and distrbuting DVM op SendPCrdGrant, desc="Send PCrdGrant"; DoRetry, desc="Resend the current pending request"; - DvmTlbi_Initiate, desc="Initiate a DVM TLBI on the provided TBE"; - DvmSync_Initiate, desc="Initiate a DVM Sync on the provided TBE"; + DvmTlbi_Initiate, out_trans="yes", in_trans="yes", desc="Initiate a DVM TLBI on the provided TBE"; + DvmSync_Initiate, out_trans="yes", in_trans="yes", desc="Initiate a DVM Sync on the provided TBE"; DvmSendNextMessage_P1, desc="Trigger a SnpDvmOp_P1 message based on the TBE type"; DvmSendNextMessage_P2, desc="Trigger a SnpDvmOp_P2 message based on the TBE type"; DvmFinishDistributing, desc="Move the TBE out of the Distributing state into Waiting"; diff --git a/src/mem/ruby/protocol/chi/CHI-mem.sm b/src/mem/ruby/protocol/chi/CHI-mem.sm index 820f2dfcf4..46f57456a5 100644 --- a/src/mem/ruby/protocol/chi/CHI-mem.sm +++ b/src/mem/ruby/protocol/chi/CHI-mem.sm @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021,2022 ARM Limited + * Copyright (c) 2021-2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -164,6 +164,7 @@ machine(MachineType:Memory, "Memory controller interface") : int storSlot, desc="Slot in the storage tracker occupied by this entry"; Addr addr, desc="Line address for this TBE"; Addr accAddr, desc="Original access address. Set only for Write*Ptl"; + Addr txnId, desc="Transaction ID"; int accSize, desc="Access size. Set only for Write*Ptl"; State state, desc="Current line state"; DataBlock dataBlk, desc="Transaction data"; @@ -503,6 +504,7 @@ machine(MachineType:Memory, "Memory controller interface") : } tbe.accAddr := in_msg.accAddr; tbe.accSize := in_msg.accSize; + tbe.txnId := in_msg.txnId; } } @@ -608,6 +610,7 @@ machine(MachineType:Memory, "Memory controller interface") : assert(tbe.rxtxBytes < blockSize); enqueue(datOutPort, CHIDataMsg, data_latency) { out_msg.addr := tbe.addr; + out_msg.txnId := tbe.txnId; if (tbe.useDataSepResp) { out_msg.type := CHIDataType:DataSepResp_UC; } else { @@ -663,7 +666,7 @@ machine(MachineType:Memory, "Memory controller interface") : } action(popMemoryQueue, "pmem", desc="Pop memory queue.") { - memQueue_in.dequeue(clockEdge()); + dequeueMemRespQueue(); } // Stall/wake-up only used for requests that arrive when we are on the diff --git a/src/mem/ruby/protocol/chi/CHI-msg.sm b/src/mem/ruby/protocol/chi/CHI-msg.sm index 63648a5920..b9e11d9dd9 100644 --- a/src/mem/ruby/protocol/chi/CHI-msg.sm +++ b/src/mem/ruby/protocol/chi/CHI-msg.sm @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 ARM Limited + * Copyright (c) 2021, 2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -46,6 +46,8 @@ enumeration(CHIRequestType, desc="") { Load; Store; StoreLine; + AtomicLoad; + AtomicStore; // Incoming DVM-related requests generated by the sequencer DvmTlbi_Initiate; DvmSync_Initiate; @@ -66,6 +68,9 @@ enumeration(CHIRequestType, desc="") { WriteUniquePtl; WriteUniqueFull; + AtomicReturn; + AtomicNoReturn; + SnpSharedFwd; SnpNotSharedDirtyFwd; SnpUniqueFwd; @@ -108,6 +113,8 @@ structure(CHIRequestMsg, desc="", interface="Message") { bool is_local_pf, desc="Request generated by a local prefetcher"; bool is_remote_pf, desc="Request generated a prefetcher in another cache"; + WriteMask atomic_op, desc="Atomic Operation Wrapper"; + bool usesTxnId, desc="True if using a Transaction ID", default="false"; Addr txnId, desc="Transaction ID", default="0"; @@ -156,6 +163,7 @@ structure(CHIResponseMsg, desc="", interface="Message") { bool stale, desc="Response to a stale request"; bool usesTxnId, desc="True if using a Transaction ID", default="false"; Addr txnId, desc="Transaction ID", default="0"; + Addr dbid, desc="Data Buffer ID", default="0"; //NOTE: not in CHI and for debuging only MessageSizeType MessageSize, default="MessageSizeType_Control"; diff --git a/src/mem/ruby/protocol/chi/Kconfig b/src/mem/ruby/protocol/chi/Kconfig new file mode 100644 index 0000000000..708f162fd7 --- /dev/null +++ b/src/mem/ruby/protocol/chi/Kconfig @@ -0,0 +1,35 @@ +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +config PROTOCOL + default "CHI" if RUBY_PROTOCOL_CHI + +config NEED_PARTIAL_FUNC_READS + default y if RUBY_PROTOCOL_CHI + +cont_choice "Ruby protocol" + config RUBY_PROTOCOL_CHI + bool "CHI" +endchoice diff --git a/src/mem/ruby/protocol/chi/SConsopts b/src/mem/ruby/protocol/chi/SConsopts index 6f686878ef..8551d7325f 100644 --- a/src/mem/ruby/protocol/chi/SConsopts +++ b/src/mem/ruby/protocol/chi/SConsopts @@ -37,11 +37,4 @@ Import('*') -# Register this protocol with gem5/SCons - -main.Append(ALL_PROTOCOLS=['CHI']) - -# CHI requires Ruby's inerface to support partial functional reads -main.Append(NEED_PARTIAL_FUNC_READS=['CHI']) - main.Append(PROTOCOL_DIRS=[Dir('.')]) diff --git a/src/mem/ruby/slicc_interface/AbstractController.cc b/src/mem/ruby/slicc_interface/AbstractController.cc index 2d10422487..36092387ac 100644 --- a/src/mem/ruby/slicc_interface/AbstractController.cc +++ b/src/mem/ruby/slicc_interface/AbstractController.cc @@ -62,8 +62,10 @@ AbstractController::AbstractController(const Params &p) m_buffer_size(p.buffer_size), m_recycle_latency(p.recycle_latency), m_mandatory_queue_latency(p.mandatory_queue_latency), m_waiting_mem_retry(false), + m_mem_ctrl_waiting_retry(false), memoryPort(csprintf("%s.memory", name()), this), addrRanges(p.addr_ranges.begin(), p.addr_ranges.end()), + mRetryRespEvent{*this, false}, stats(this) { if (m_version == 0) { @@ -123,6 +125,7 @@ AbstractController::resetStats() for (uint32_t i = 0; i < size; i++) { stats.delayVCHistogram[i]->reset(); } + ClockedObject::resetStats(); } void @@ -367,11 +370,17 @@ AbstractController::functionalMemoryWrite(PacketPtr pkt) return num_functional_writes + 1; } -void +bool AbstractController::recvTimingResp(PacketPtr pkt) { - assert(getMemRespQueue()); - assert(pkt->isResponse()); + auto* memRspQueue = getMemRespQueue(); + gem5_assert(memRspQueue); + gem5_assert(pkt->isResponse()); + + if (!memRspQueue->areNSlotsAvailable(1, curTick())) { + m_mem_ctrl_waiting_retry = true; + return false; + } std::shared_ptr msg = std::make_shared(clockEdge()); (*msg).m_addr = pkt->getAddr(); @@ -395,8 +404,9 @@ AbstractController::recvTimingResp(PacketPtr pkt) panic("Incorrect packet type received from memory controller!"); } - getMemRespQueue()->enqueue(msg, clockEdge(), cyclesToTicks(Cycles(1))); + memRspQueue->enqueue(msg, clockEdge(), cyclesToTicks(Cycles(1))); delete pkt; + return true; } Tick @@ -438,11 +448,33 @@ const } +void +AbstractController::memRespQueueDequeued() { + if (m_mem_ctrl_waiting_retry && !mRetryRespEvent.scheduled()) { + schedule(mRetryRespEvent, clockEdge(Cycles{1})); + } +} + +void +AbstractController::dequeueMemRespQueue() { + auto* q = getMemRespQueue(); + gem5_assert(q); + q->dequeue(clockEdge()); + memRespQueueDequeued(); +} + +void +AbstractController::sendRetryRespToMem() { + if (m_mem_ctrl_waiting_retry) { + m_mem_ctrl_waiting_retry = false; + memoryPort.sendRetryResp(); + } +} + bool AbstractController::MemoryPort::recvTimingResp(PacketPtr pkt) { - controller->recvTimingResp(pkt); - return true; + return controller->recvTimingResp(pkt); } void diff --git a/src/mem/ruby/slicc_interface/AbstractController.hh b/src/mem/ruby/slicc_interface/AbstractController.hh index a5ab5c2c44..ce6a6972af 100644 --- a/src/mem/ruby/slicc_interface/AbstractController.hh +++ b/src/mem/ruby/slicc_interface/AbstractController.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017,2019-2022 ARM Limited + * Copyright (c) 2017,2019-2023 ARM Limited * All rights reserved. * * The license below extends only to copyright in the software and shall @@ -61,6 +61,7 @@ #include "mem/ruby/system/CacheRecorder.hh" #include "params/RubyController.hh" #include "sim/clocked_object.hh" +#include "sim/eventq.hh" namespace gem5 { @@ -100,6 +101,14 @@ class AbstractController : public ClockedObject, public Consumer virtual MessageBuffer* getMandatoryQueue() const = 0; virtual MessageBuffer* getMemReqQueue() const = 0; virtual MessageBuffer* getMemRespQueue() const = 0; + + // That function must be called by controller when dequeuing mem resp queue + // for memory controller to receive the retry request in time + void memRespQueueDequeued(); + // Or that function can be called to perform both dequeue and notification + // at once. + void dequeueMemRespQueue(); + virtual AccessPermission getAccessPermission(const Addr &addr) = 0; virtual void print(std::ostream & out) const = 0; @@ -165,7 +174,7 @@ class AbstractController : public ClockedObject, public Consumer Port &getPort(const std::string &if_name, PortID idx=InvalidPortID); - void recvTimingResp(PacketPtr pkt); + bool recvTimingResp(PacketPtr pkt); Tick recvAtomic(PacketPtr pkt); const AddrRangeList &getAddrRanges() const { return addrRanges; } @@ -258,7 +267,7 @@ class AbstractController : public ClockedObject, public Consumer assert(m_inTrans.find(addr) == m_inTrans.end()); m_inTrans[addr] = {type, initialState, curTick()}; if (retried) - ++(*stats.inTransLatRetries[type]); + ++(*stats.inTransRetryCnt[type]); } /** @@ -279,11 +288,23 @@ class AbstractController : public ClockedObject, public Consumer isAddressed ? m_inTransAddressed : m_inTransUnaddressed; auto iter = m_inTrans.find(addr); assert(iter != m_inTrans.end()); - stats.inTransLatHist[iter->second.transaction] - [iter->second.state] - [(unsigned)finalState]->sample( - ticksToCycles(curTick() - iter->second.time)); - ++(*stats.inTransLatTotal[iter->second.transaction]); + auto &trans = iter->second; + + auto stat_iter_ev = stats.inTransStateChanges.find(trans.transaction); + gem5_assert(stat_iter_ev != stats.inTransStateChanges.end(), + "%s: event type=%d not marked as in_trans in SLICC", + name(), trans.transaction); + + auto stat_iter_state = stat_iter_ev->second.find(trans.state); + gem5_assert(stat_iter_state != stat_iter_ev->second.end(), + "%s: event type=%d has no transition from state=%d", + name(), trans.transaction, trans.state); + + ++(*stat_iter_state->second[(unsigned)finalState]); + + stats.inTransLatHist[iter->second.transaction]->sample( + ticksToCycles(curTick() - trans.time)); + m_inTrans.erase(iter); } @@ -325,10 +346,17 @@ class AbstractController : public ClockedObject, public Consumer isAddressed ? m_outTransAddressed : m_outTransUnaddressed; auto iter = m_outTrans.find(addr); assert(iter != m_outTrans.end()); - stats.outTransLatHist[iter->second.transaction]->sample( - ticksToCycles(curTick() - iter->second.time)); + auto &trans = iter->second; + + auto stat_iter = stats.outTransLatHist.find(trans.transaction); + gem5_assert(stat_iter != stats.outTransLatHist.end(), + "%s: event type=%d not marked as out_trans in SLICC", + name(), trans.transaction); + + stat_iter->second->sample( + ticksToCycles(curTick() - trans.time)); if (retried) - ++(*stats.outTransLatHistRetries[iter->second.transaction]); + ++(*stats.outTransRetryCnt[trans.transaction]); m_outTrans.erase(iter); } @@ -339,6 +367,28 @@ class AbstractController : public ClockedObject, public Consumer void wakeUpAllBuffers(); bool serviceMemoryQueue(); + /** + * Functions needed by CacheAccessor. These are implemented in SLICC, + * thus the const& for all args to match the generated code. + */ + virtual bool inCache(const Addr &addr, const bool &is_secure) + { fatal("inCache: prefetching not supported"); return false; } + + virtual bool hasBeenPrefetched(const Addr &addr, const bool &is_secure) + { fatal("hasBeenPrefetched: prefetching not supported"); return false; } + + virtual bool hasBeenPrefetched(const Addr &addr, const bool &is_secure, + const RequestorID &requestor) + { fatal("hasBeenPrefetched: prefetching not supported"); return false; } + + virtual bool inMissQueue(const Addr &addr, const bool &is_secure) + { fatal("inMissQueue: prefetching not supported"); return false; } + + virtual bool coalesce() + { fatal("coalesce: prefetching not supported"); return false; } + + friend class RubyPrefetcherProxy; + protected: const NodeID m_version; MachineID m_machineID; @@ -364,6 +414,7 @@ class AbstractController : public ClockedObject, public Consumer Cycles m_recycle_latency; const Cycles m_mandatory_queue_latency; bool m_waiting_mem_retry; + bool m_mem_ctrl_waiting_retry; /** * Port that forwards requests and receives responses from the @@ -411,22 +462,33 @@ class AbstractController : public ClockedObject, public Consumer NetDest downstreamDestinations; NetDest upstreamDestinations; + void sendRetryRespToMem(); + MemberEventWrapper<&AbstractController::sendRetryRespToMem> mRetryRespEvent; + public: struct ControllerStats : public statistics::Group { ControllerStats(statistics::Group *parent); - // Initialized by the SLICC compiler for all combinations of event and - // states. Only histograms with samples will appear in the stats - std::vector>> - inTransLatHist; - std::vector inTransLatRetries; - std::vector inTransLatTotal; + // Initialized by the SLICC compiler for all events with the + // "in_trans" property. + // Only histograms with samples will appear in the stats + std::unordered_map inTransLatHist; + std::unordered_map inTransRetryCnt; + // Initialized by the SLICC compiler for all combinations of events + // with the "in_trans" property, potential initial states, and + // potential final states. Potential initial states are states that + // appear in transitions triggered by that event. Currently all states + // are considered as potential final states. + std::unordered_map>> inTransStateChanges; - // Initialized by the SLICC compiler for all events. + // Initialized by the SLICC compiler for all events with the + // "out_trans" property. // Only histograms with samples will appear in the stats. - std::vector outTransLatHist; - std::vector outTransLatHistRetries; + std::unordered_map outTransLatHist; + std::unordered_map + outTransRetryCnt; //! Counter for the number of cycles when the transitions carried out //! were equal to the maximum allowed diff --git a/src/mem/ruby/slicc_interface/Controller.py b/src/mem/ruby/slicc_interface/Controller.py index 42447f1cca..ef8a0afbf1 100644 --- a/src/mem/ruby/slicc_interface/Controller.py +++ b/src/mem/ruby/slicc_interface/Controller.py @@ -36,9 +36,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * -from m5.objects.ClockedObject import ClockedObject class RubyController(ClockedObject): diff --git a/src/mem/ruby/slicc_interface/RubyRequest.cc b/src/mem/ruby/slicc_interface/RubyRequest.cc index 643c1dec6f..fbd211d2a8 100644 --- a/src/mem/ruby/slicc_interface/RubyRequest.cc +++ b/src/mem/ruby/slicc_interface/RubyRequest.cc @@ -61,7 +61,9 @@ RubyRequest::print(std::ostream& out) const out << " " << "AccessMode = " << m_AccessMode << " "; out << "Size = " << m_Size << " "; out << "Prefetch = " << m_Prefetch << " "; -// out << "Time = " << getTime() << " "; + out << "isGLCSet = " << m_isGLCSet << ""; + out << "isSLCSet = " << m_isSLCSet << ""; + // out << "Time = " << getTime() << " "; out << "]"; } @@ -123,5 +125,14 @@ RubyRequest::functionalWrite(Packet *pkt) return cBase < cTail; } +void +RubyRequest::setWriteMask(uint32_t offset, uint32_t len, + std::vector< std::pair> atomicOps) +{ + m_writeMask.setMask(offset, len); + m_writeMask.setAtomicOps(atomicOps); +} + + } // namespace ruby } // namespace gem5 diff --git a/src/mem/ruby/slicc_interface/RubyRequest.hh b/src/mem/ruby/slicc_interface/RubyRequest.hh index 89ce83451e..1e9674b9f5 100644 --- a/src/mem/ruby/slicc_interface/RubyRequest.hh +++ b/src/mem/ruby/slicc_interface/RubyRequest.hh @@ -226,6 +226,8 @@ class RubyRequest : public Message const PrefetchBit& getPrefetch() const { return m_Prefetch; } RequestPtr getRequestPtr() const { return m_pkt->req; } + void setWriteMask(uint32_t offset, uint32_t len, + std::vector< std::pair> atomicOps); void print(std::ostream& out) const; bool functionalRead(Packet *pkt); bool functionalRead(Packet *pkt, WriteMask &mask); diff --git a/src/mem/ruby/slicc_interface/RubySlicc_Util.hh b/src/mem/ruby/slicc_interface/RubySlicc_Util.hh index edfbe4eea5..8df56c7013 100644 --- a/src/mem/ruby/slicc_interface/RubySlicc_Util.hh +++ b/src/mem/ruby/slicc_interface/RubySlicc_Util.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 ARM Limited + * Copyright (c) 2020-2021,2023 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -316,6 +316,12 @@ countBoolVec(BoolVec bVec) return count; } +inline RequestorID +getRequestorID(RequestPtr req) +{ + return req->requestorId(); +} + } // namespace ruby } // namespace gem5 diff --git a/src/mem/ruby/slicc_interface/SConscript b/src/mem/ruby/slicc_interface/SConscript index 47dd49d42e..7fb84b348c 100644 --- a/src/mem/ruby/slicc_interface/SConscript +++ b/src/mem/ruby/slicc_interface/SConscript @@ -28,7 +28,7 @@ Import('*') -if env['CONF']['PROTOCOL'] == 'None': +if not env['CONF']['RUBY']: Return() SimObject('Controller.py', sim_objects=['RubyController']) diff --git a/src/mem/ruby/structures/ALUFreeListArray.cc b/src/mem/ruby/structures/ALUFreeListArray.cc new file mode 100644 index 0000000000..87b5cbfbd2 --- /dev/null +++ b/src/mem/ruby/structures/ALUFreeListArray.cc @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2023 The University of Wisconsin + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "mem/ruby/structures/ALUFreeListArray.hh" + +#include "base/intmath.hh" +#include "mem/ruby/system/RubySystem.hh" +#include "sim/cur_tick.hh" + +namespace gem5 +{ + +namespace ruby +{ + +/* +* +* Models num_ALUs pipelined atomic ALUs with a depth of access_latency ticks. +* Rather than reserving ALUs, this class assumes multiple requests can go +* through an ALU at the same time. As such, up to numALU new requests can +* go through at once, with the caveat that a line already being processed +* in an ALU can't start processing again until the previous request has exited +* the pipeline. +* +* ALUs aren't mapped directly to cache lines. Rather, ALUs are treated as +* a free list. +* +* Behavior: +* Requests will go through unless one/both of the following are met: +* - There have been more than [numALUs] requests in the current cycle +* - The same line has been accessed in the past accessLatency ticks +*/ + +ALUFreeListArray::ALUFreeListArray(unsigned int num_ALUs, Tick access_latency) +{ + this->numALUs = num_ALUs; + this->accessLatency = access_latency; +} + +bool ALUFreeListArray::tryAccess(Addr addr) +{ + uint32_t accesses_this_tick = 0; + + // Remove requests from the tail of the queue that occured more than + // accessLatency ticks ago + Tick oldestValidRecordStart = curTick() - this->accessLatency; + + while (accessQueue.size() > 0 && + (accessQueue.back().startTick < oldestValidRecordStart)) { + accessQueue.pop_back(); + } + + for (AccessRecord& record : accessQueue) { + // Block access if we would be using more ALUs than we have in a + // single tick + if (record.startTick == curTick() && + (++accesses_this_tick > numALUs)) { + return false; + } + + // Block access if the line is already being used + if (record.lineAddr == makeLineAddress(addr)) { + return false; + } + } + + return true; +} + +void ALUFreeListArray::reserve(Addr addr) +{ + // Only called after tryAccess, so we know queue is up to date and that + // the access is valid + + // Add record to queue + accessQueue.push_front(AccessRecord(makeLineAddress(addr), curTick())); +} + +} // namespace ruby +} // namespace gem5 diff --git a/src/mem/ruby/structures/ALUFreeListArray.hh b/src/mem/ruby/structures/ALUFreeListArray.hh new file mode 100644 index 0000000000..bed1b00b5c --- /dev/null +++ b/src/mem/ruby/structures/ALUFreeListArray.hh @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2023 The University of Wisconsin + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MEM_RUBY_STRUCTURES_ALUFREELISTARRAY_HH__ +#define __MEM_RUBY_STRUCTURES_ALUFREELISTARRAY_HH__ + +#include + +#include "mem/ruby/common/TypeDefines.hh" +#include "sim/cur_tick.hh" + +namespace gem5 +{ + +namespace ruby +{ + +class ALUFreeListArray +{ + private: + unsigned int numALUs; + Tick accessLatency; + + class AccessRecord + { + public: + AccessRecord(Addr line_addr, Tick start_tick) { + this->lineAddr = line_addr; + this->startTick = start_tick; + } + + Addr lineAddr; + Tick startTick; + }; + + // Queue of accesses from past accessLatency cycles + std::deque accessQueue; + + public: + ALUFreeListArray(unsigned int num_ALUs, Tick access_latency); + + bool tryAccess(Addr addr); + + void reserve(Addr addr); + + Tick getLatency() const { return accessLatency; } +}; + +} // namespace ruby +} // namespace gem5 + +#endif diff --git a/src/mem/ruby/structures/CacheMemory.cc b/src/mem/ruby/structures/CacheMemory.cc index 5a5eaffa02..90d67fb29b 100644 --- a/src/mem/ruby/structures/CacheMemory.cc +++ b/src/mem/ruby/structures/CacheMemory.cc @@ -73,6 +73,8 @@ CacheMemory::CacheMemory(const Params &p) p.start_index_bit, p.ruby_system), tagArray(p.tagArrayBanks, p.tagAccessLatency, p.start_index_bit, p.ruby_system), + atomicALUArray(p.atomicALUs, p.atomicLatency * + p.ruby_system->clockPeriod()), cacheMemoryStats(this) { m_cache_size = p.size; @@ -186,7 +188,7 @@ bool CacheMemory::tryCacheAccess(Addr address, RubyRequestType type, DataBlock*& data_ptr) { - DPRINTF(RubyCache, "address: %#x\n", address); + DPRINTF(RubyCache, "trying to access address: %#x\n", address); AbstractCacheEntry* entry = lookup(address); if (entry != nullptr) { // Do we even have a tag match? @@ -195,14 +197,20 @@ CacheMemory::tryCacheAccess(Addr address, RubyRequestType type, data_ptr = &(entry->getDataBlk()); if (entry->m_Permission == AccessPermission_Read_Write) { + DPRINTF(RubyCache, "Have permission to access address: %#x\n", + address); return true; } if ((entry->m_Permission == AccessPermission_Read_Only) && (type == RubyRequestType_LD || type == RubyRequestType_IFETCH)) { + DPRINTF(RubyCache, "Have permission to access address: %#x\n", + address); return true; } // The line must not be accessible } + DPRINTF(RubyCache, "Do not have permission to access address: %#x\n", + address); data_ptr = NULL; return false; } @@ -211,7 +219,7 @@ bool CacheMemory::testCacheAccess(Addr address, RubyRequestType type, DataBlock*& data_ptr) { - DPRINTF(RubyCache, "address: %#x\n", address); + DPRINTF(RubyCache, "testing address: %#x\n", address); AbstractCacheEntry* entry = lookup(address); if (entry != nullptr) { // Do we even have a tag match? @@ -219,9 +227,14 @@ CacheMemory::testCacheAccess(Addr address, RubyRequestType type, entry->setLastAccess(curTick()); data_ptr = &(entry->getDataBlk()); + DPRINTF(RubyCache, "have permission for address %#x?: %d\n", + address, + entry->m_Permission != AccessPermission_NotPresent); return entry->m_Permission != AccessPermission_NotPresent; } + DPRINTF(RubyCache, "do not have permission for address %#x\n", + address); data_ptr = NULL; return false; } @@ -271,7 +284,7 @@ CacheMemory::allocate(Addr address, AbstractCacheEntry *entry) assert(address == makeLineAddress(address)); assert(!isTagPresent(address)); assert(cacheAvail(address)); - DPRINTF(RubyCache, "address: %#x\n", address); + DPRINTF(RubyCache, "allocating address: %#x\n", address); // Find the first open slot int64_t cacheSet = addressToCacheSet(address); @@ -288,7 +301,7 @@ CacheMemory::allocate(Addr address, AbstractCacheEntry *entry) set[i] = entry; // Init entry set[i]->m_Address = address; set[i]->m_Permission = AccessPermission_Invalid; - DPRINTF(RubyCache, "Allocate clearing lock for addr: %x\n", + DPRINTF(RubyCache, "Allocate clearing lock for addr: 0x%x\n", address); set[i]->m_locked = -1; m_tag_index[address] = i; @@ -309,7 +322,7 @@ CacheMemory::allocate(Addr address, AbstractCacheEntry *entry) void CacheMemory::deallocate(Addr address) { - DPRINTF(RubyCache, "address: %#x\n", address); + DPRINTF(RubyCache, "deallocating address: %#x\n", address); AbstractCacheEntry* entry = lookup(address); assert(entry != nullptr); m_replacementPolicy_ptr->invalidate(entry->replacementData); @@ -529,6 +542,8 @@ CacheMemoryStats::CacheMemoryStats(statistics::Group *parent) ADD_STAT(numTagArrayWrites, "Number of tag array writes"), ADD_STAT(numTagArrayStalls, "Number of stalls caused by tag array"), ADD_STAT(numDataArrayStalls, "Number of stalls caused by data array"), + ADD_STAT(numAtomicALUOperations, "Number of atomic ALU operations"), + ADD_STAT(numAtomicALUArrayStalls, "Number of stalls caused by atomic ALU array"), ADD_STAT(htmTransCommitReadSet, "Read set size of a committed " "transaction"), ADD_STAT(htmTransCommitWriteSet, "Write set size of a committed " @@ -564,6 +579,12 @@ CacheMemoryStats::CacheMemoryStats(statistics::Group *parent) numDataArrayStalls .flags(statistics::nozero); + numAtomicALUOperations + .flags(statistics::nozero); + + numAtomicALUArrayStalls + .flags(statistics::nozero); + htmTransCommitReadSet .init(8) .flags(statistics::pdf | statistics::dist | statistics::nozero | @@ -633,6 +654,11 @@ CacheMemory::recordRequestType(CacheRequestType requestType, Addr addr) tagArray.reserve(addressToCacheSet(addr)); cacheMemoryStats.numTagArrayWrites++; return; + case CacheRequestType_AtomicALUOperation: + if (m_resource_stalls) + atomicALUArray.reserve(addr); + cacheMemoryStats.numAtomicALUOperations++; + return; default: warn("CacheMemory access_type not found: %s", CacheRequestType_to_string(requestType)); @@ -664,6 +690,15 @@ CacheMemory::checkResourceAvailable(CacheResourceType res, Addr addr) cacheMemoryStats.numDataArrayStalls++; return false; } + } else if (res == CacheResourceType_AtomicALUArray) { + if (atomicALUArray.tryAccess(addr)) return true; + else { + DPRINTF(RubyResourceStalls, + "Atomic ALU array stall on addr %#x in line address %#x\n", + addr, makeLineAddress(addr)); + cacheMemoryStats.numAtomicALUArrayStalls++; + return false; + } } else { panic("Unrecognized cache resource type."); } diff --git a/src/mem/ruby/structures/CacheMemory.hh b/src/mem/ruby/structures/CacheMemory.hh index a63bb02748..de7c327f63 100644 --- a/src/mem/ruby/structures/CacheMemory.hh +++ b/src/mem/ruby/structures/CacheMemory.hh @@ -56,6 +56,7 @@ #include "mem/ruby/slicc_interface/AbstractCacheEntry.hh" #include "mem/ruby/slicc_interface/RubySlicc_ComponentMapping.hh" #include "mem/ruby/structures/BankedArray.hh" +#include "mem/ruby/structures/ALUFreeListArray.hh" #include "mem/ruby/system/CacheRecorder.hh" #include "params/RubyCache.hh" #include "sim/sim_object.hh" @@ -186,6 +187,7 @@ class CacheMemory : public SimObject BankedArray dataArray; BankedArray tagArray; + ALUFreeListArray atomicALUArray; int m_cache_size; int m_cache_num_sets; @@ -224,6 +226,9 @@ class CacheMemory : public SimObject statistics::Scalar numTagArrayStalls; statistics::Scalar numDataArrayStalls; + statistics::Scalar numAtomicALUOperations; + statistics::Scalar numAtomicALUArrayStalls; + // hardware transactional memory statistics::Histogram htmTransCommitReadSet; statistics::Histogram htmTransCommitWriteSet; diff --git a/src/mem/ruby/structures/RubyCache.py b/src/mem/ruby/structures/RubyCache.py index f2c1b7230c..2f457f5c4a 100644 --- a/src/mem/ruby/structures/RubyCache.py +++ b/src/mem/ruby/structures/RubyCache.py @@ -24,9 +24,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.ReplacementPolicies import * from m5.params import * from m5.proxy import * -from m5.objects.ReplacementPolicies import * from m5.SimObject import SimObject @@ -44,6 +44,11 @@ class RubyCache(SimObject): "0B", "block size in bytes. 0 means default RubyBlockSize" ) + # Atomic parameters only applicable to GPU atomics + # Zero atomic latency corresponds to instantanous atomic ALU operations + atomicLatency = Param.Cycles(0, "Cycles for an atomic ALU operation") + atomicALUs = Param.Int(64, "Number of atomic ALUs") + dataArrayBanks = Param.Int(1, "Number of banks for the data array") tagArrayBanks = Param.Int(1, "Number of banks for the tag array") dataAccessLatency = Param.Cycles(1, "cycles for a data array access") diff --git a/src/mem/ruby/structures/RubyPrefetcher.py b/src/mem/ruby/structures/RubyPrefetcher.py index cea6ec9604..d4189ae7d5 100644 --- a/src/mem/ruby/structures/RubyPrefetcher.py +++ b/src/mem/ruby/structures/RubyPrefetcher.py @@ -36,11 +36,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject +from m5.objects.System import System from m5.params import * from m5.proxy import * - -from m5.objects.System import System +from m5.SimObject import SimObject class RubyPrefetcher(SimObject): diff --git a/src/mem/ruby/structures/RubyPrefetcherProxy.cc b/src/mem/ruby/structures/RubyPrefetcherProxy.cc new file mode 100644 index 0000000000..2a29fbc88e --- /dev/null +++ b/src/mem/ruby/structures/RubyPrefetcherProxy.cc @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2023 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "mem/ruby/structures/RubyPrefetcherProxy.hh" + +#include "debug/HWPrefetch.hh" +#include "mem/ruby/system/RubySystem.hh" + +namespace gem5 +{ + +namespace ruby +{ + +RubyPrefetcherProxy::RubyPrefetcherProxy(AbstractController* _parent, + prefetch::Base* _prefetcher, + MessageBuffer *_pf_queue) + :Named(_parent->name()), + prefetcher(_prefetcher), + cacheCntrl(_parent), + pfQueue(_pf_queue), + pfEvent([this]{ issuePrefetch(); }, name()), + ppHit(nullptr), ppMiss(nullptr), + ppFill(nullptr), ppDataUpdate(nullptr) +{ + fatal_if(!cacheCntrl, + "initializing a RubyPrefetcherProxy without a parent"); + if (prefetcher) { + fatal_if(!pfQueue, + "%s initializing a RubyPrefetcherProxy without a prefetch queue", + name()); + prefetcher->setParentInfo( + cacheCntrl->params().system, + cacheCntrl->getProbeManager(), + RubySystem::getBlockSizeBytes()); + } +} + +void +RubyPrefetcherProxy::scheduleNextPrefetch() +{ + if (pfEvent.scheduled()) + return; + + Tick next_pf_time = std::max(prefetcher->nextPrefetchReadyTime(), + cacheCntrl->clockEdge(Cycles(1))); + if (next_pf_time != MaxTick) { + DPRINTF(HWPrefetch, "Next prefetch ready at %d\n", next_pf_time); + cacheCntrl->schedule(&pfEvent, next_pf_time); + } +} + +void +RubyPrefetcherProxy::deschedulePrefetch() +{ + if (pfEvent.scheduled()) + cacheCntrl->deschedule(&pfEvent); +} + +void +RubyPrefetcherProxy::completePrefetch(Addr addr) +{ + assert(makeLineAddress(addr) == addr); + assert(issuedPfPkts.count(addr) == 1); + DPRINTF(HWPrefetch, "Prefetch request for addr %#x completed\n", addr); + delete issuedPfPkts[addr]; + issuedPfPkts.erase(addr); +} + +void +RubyPrefetcherProxy::issuePrefetch() +{ + assert(prefetcher); + assert(pfQueue); + + if (pfQueue->areNSlotsAvailable(1, curTick())) { + PacketPtr pkt = prefetcher->getPacket(); + + if (pkt) { + DPRINTF(HWPrefetch, "Next prefetch ready %s\n", pkt->print()); + unsigned blk_size = RubySystem::getBlockSizeBytes(); + Addr line_addr = pkt->getBlockAddr(blk_size); + + if (issuedPfPkts.count(line_addr) == 0) { + DPRINTF(HWPrefetch, "Issued PF request for paddr=%#x, " + "line_addr=%#x, is_write=%d\n", + pkt->getAddr(), line_addr, + pkt->needsWritable()); + + RubyRequestType req_type = pkt->needsWritable() ? + RubyRequestType_ST : RubyRequestType_LD; + + std::shared_ptr msg = + std::make_shared(cacheCntrl->clockEdge(), + pkt->getAddr(), + blk_size, + 0, // pc + req_type, + RubyAccessMode_Supervisor, + pkt, + PrefetchBit_Yes); + + // enqueue request into prefetch queue to the cache + pfQueue->enqueue(msg, cacheCntrl->clockEdge(), + cacheCntrl->cyclesToTicks(Cycles(1))); + + // track all pending PF requests + issuedPfPkts[line_addr] = pkt; + } else { + DPRINTF(HWPrefetch, "Aborted PF request for address being " + "prefetched\n"); + delete pkt; + } + } + } else { + DPRINTF(HWPrefetch, "No prefetch slots are available\n"); + } + + scheduleNextPrefetch(); +} + +void +RubyPrefetcherProxy::notifyPfHit(const RequestPtr& req, bool is_read, + const DataBlock& data_blk) +{ + assert(ppHit); + assert(req); + Packet pkt(req, is_read ? Packet::makeReadCmd(req) : + Packet::makeWriteCmd(req)); + // NOTE: for now we only communicate physical address with prefetchers + pkt.dataStaticConst(data_blk.getData(getOffset(req->getPaddr()), + pkt.getSize())); + DPRINTF(HWPrefetch, "notify hit: %s\n", pkt.print()); + ppHit->notify(CacheAccessProbeArg(&pkt, *this)); + scheduleNextPrefetch(); +} + +void +RubyPrefetcherProxy::notifyPfMiss(const RequestPtr& req, bool is_read, + const DataBlock& data_blk) +{ + assert(ppMiss); + assert(req); + Packet pkt(req, is_read ? Packet::makeReadCmd(req) : + Packet::makeWriteCmd(req)); + // NOTE: for now we only communicate physical address with prefetchers + pkt.dataStaticConst(data_blk.getData(getOffset(req->getPaddr()), + pkt.getSize())); + DPRINTF(HWPrefetch, "notify miss: %s\n", pkt.print()); + ppMiss->notify(CacheAccessProbeArg(&pkt, *this)); + scheduleNextPrefetch(); +} + +void +RubyPrefetcherProxy::notifyPfFill(const RequestPtr& req, + const DataBlock& data_blk, + bool from_pf) +{ + assert(ppFill); + assert(req); + Packet pkt(req, Packet::makeReadCmd(req)); + if (from_pf) + pkt.cmd = Packet::Command::HardPFReq; + // NOTE: for now we only communicate physical address with prefetchers + pkt.dataStaticConst(data_blk.getData(getOffset(req->getPaddr()), + pkt.getSize())); + DPRINTF(HWPrefetch, "notify fill: %s\n", pkt.print()); + ppFill->notify(CacheAccessProbeArg(&pkt, *this)); + scheduleNextPrefetch(); +} + +void +RubyPrefetcherProxy::notifyPfEvict(Addr blkAddr, bool hwPrefetched, + RequestorID requestorID) +{ + DPRINTF(HWPrefetch, "notify evict: %#x hw_pf=%d\n", blkAddr, hwPrefetched); + CacheDataUpdateProbeArg data_update( + blkAddr, false, requestorID, *this); + data_update.hwPrefetched = hwPrefetched; + ppDataUpdate->notify(data_update); + scheduleNextPrefetch(); +} + +void +RubyPrefetcherProxy::regProbePoints() +{ + assert(cacheCntrl); + ppHit = new ProbePointArg( + cacheCntrl->getProbeManager(), "Hit"); + ppMiss = new ProbePointArg( + cacheCntrl->getProbeManager(), "Miss"); + ppFill = new ProbePointArg( + cacheCntrl->getProbeManager(), "Fill"); + ppDataUpdate = + new ProbePointArg( + cacheCntrl->getProbeManager(), "Data Update"); +} + +} // namespace ruby +} // namespace gem5 diff --git a/src/mem/ruby/structures/RubyPrefetcherProxy.hh b/src/mem/ruby/structures/RubyPrefetcherProxy.hh new file mode 100644 index 0000000000..34c40154b6 --- /dev/null +++ b/src/mem/ruby/structures/RubyPrefetcherProxy.hh @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2023 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MEM_RUBY_STRUCTURES_RUBY_PREFETCHER_WRAPPER_HH__ +#define __MEM_RUBY_STRUCTURES_RUBY_PREFETCHER_WRAPPER_HH__ + +#include + +#include "mem/cache/cache_probe_arg.hh" +#include "mem/cache/prefetch/base.hh" +#include "mem/ruby/slicc_interface/AbstractController.hh" +#include "mem/ruby/slicc_interface/RubyRequest.hh" + +namespace gem5 +{ + +namespace ruby +{ + +/** + * This is a proxy for prefetcher class in classic memory. This wrapper + * enables a SLICC machine to interact with classic prefetchers. + * + * The expected use case for this class is to instantiate it in the SLICC + * state machine definition and provide a pointer to the prefetcher object + * (typically defined in SLICC as one of the SM's configuration parameters) + * and the prefetch queue where prefetch requests will be inserted. + * + * The SLICC SM can them use the notifyPF* functions to notify the prefetcher. + * + * Notes: + * + * This object's regProbePoints() must be called explicitly. The SLICC SM may + * defined it's own regProbePoints() to call it. + * + * completePrefetch(Addr) must be called when a request injected into the + * prefetch queue is completed. + * + * A nullptr prefetcher can be provided, in which case the notifyPf* are + * no-ops. + * + */ +class RubyPrefetcherProxy : public CacheAccessor, public Named +{ + public: + + RubyPrefetcherProxy(AbstractController* parent, + prefetch::Base* prefetcher, + MessageBuffer *pf_queue); + + /** Deschedled the ready prefetch event */ + void deschedulePrefetch(); + + /** Notifies a completed prefetch request */ + void completePrefetch(Addr addr); + + /** + * Notify PF probes hit/miss/fill + */ + void notifyPfHit(const RequestPtr& req, bool is_read, + const DataBlock& data_blk); + void notifyPfMiss(const RequestPtr& req, bool is_read, + const DataBlock& data_blk); + void notifyPfFill(const RequestPtr& req, const DataBlock& data_blk, + bool from_pf); + void notifyPfEvict(Addr blkAddr, bool hwPrefetched, + RequestorID requestorID); + + /** Registers probes. */ + void regProbePoints(); + + private: + + /** Schedule the next ready prefetch */ + void scheduleNextPrefetch(); + + /** Issue prefetch to the contoller prefetch queue */ + void issuePrefetch(); + + /** Prefetcher from classic memory */ + prefetch::Base* prefetcher; + + /** Ruby cache controller */ + AbstractController* cacheCntrl; + + /** Prefetch queue to the cache controller */ + MessageBuffer* pfQueue; + + /** List of issued prefetch request packets */ + std::unordered_map issuedPfPkts; + + /** Prefetch event */ + EventFunctionWrapper pfEvent; + + /** To probe when a cache hit occurs */ + ProbePointArg *ppHit; + + /** To probe when a cache miss occurs */ + ProbePointArg *ppMiss; + + /** To probe when a cache fill occurs */ + ProbePointArg *ppFill; + + /** + * To probe when the contents of a block are updated. Content updates + * include data fills, overwrites, and invalidations, which means that + * this probe partially overlaps with other probes. + */ + ProbePointArg *ppDataUpdate; + + public: + + /** Accessor functions */ + + bool inCache(Addr addr, bool is_secure) const override + { + return cacheCntrl->inCache(addr, is_secure); + } + + bool hasBeenPrefetched(Addr addr, bool is_secure) const override + { + return cacheCntrl->hasBeenPrefetched(addr, is_secure); + } + + bool hasBeenPrefetched(Addr addr, bool is_secure, + RequestorID requestor) const override + { + return cacheCntrl->hasBeenPrefetched(addr, is_secure, requestor); + } + + bool inMissQueue(Addr addr, bool is_secure) const override + { + return cacheCntrl->inMissQueue(addr, is_secure); + } + + bool coalesce() const override + { return cacheCntrl->coalesce(); } + +}; + +} // namespace ruby +} // namespace gem5 + +#endif // __MEM_RUBY_STRUCTURES_RUBY_PREFETCHER_WRAPPER_HH__ diff --git a/src/mem/ruby/structures/SConscript b/src/mem/ruby/structures/SConscript index cae03909c7..fb7b99f265 100644 --- a/src/mem/ruby/structures/SConscript +++ b/src/mem/ruby/structures/SConscript @@ -1,6 +1,6 @@ # -*- mode:python -*- -# Copyright (c) 2021 ARM Limited +# Copyright (c) 2021,2023 ARM Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -40,7 +40,7 @@ Import('*') -if env['CONF']['PROTOCOL'] == 'None': +if not env['CONF']['RUBY']: Return() SimObject('RubyCache.py', sim_objects=['RubyCache']) @@ -53,8 +53,10 @@ Source('CacheMemory.cc') Source('WireBuffer.cc') Source('PersistentTable.cc') Source('RubyPrefetcher.cc') +Source('RubyPrefetcherProxy.cc') Source('TimerTable.cc') Source('BankedArray.cc') +Source('ALUFreeListArray.cc') Source('TBEStorage.cc') -if env['PROTOCOL'] == 'CHI': +if env['CONF']['PROTOCOL'] == 'CHI': Source('MN_TBETable.cc') diff --git a/src/mem/ruby/system/CacheRecorder.cc b/src/mem/ruby/system/CacheRecorder.cc index 20a8a30ebc..3326856849 100644 --- a/src/mem/ruby/system/CacheRecorder.cc +++ b/src/mem/ruby/system/CacheRecorder.cc @@ -30,8 +30,10 @@ #include "mem/ruby/system/CacheRecorder.hh" #include "debug/RubyCacheTrace.hh" +#include "mem/packet.hh" #include "mem/ruby/system/RubySystem.hh" #include "mem/ruby/system/Sequencer.hh" +#include "sim/sim_exit.hh" namespace gem5 { @@ -56,12 +58,14 @@ CacheRecorder::CacheRecorder() CacheRecorder::CacheRecorder(uint8_t* uncompressed_trace, uint64_t uncompressed_trace_size, - std::vector& seq_map, + std::vector& ruby_port_map, uint64_t block_size_bytes) : m_uncompressed_trace(uncompressed_trace), m_uncompressed_trace_size(uncompressed_trace_size), - m_seq_map(seq_map), m_bytes_read(0), m_records_read(0), - m_records_flushed(0), m_block_size_bytes(block_size_bytes) + m_ruby_port_map(ruby_port_map), m_bytes_read(0), + m_records_read(0), m_records_flushed(0), + m_block_size_bytes(block_size_bytes) + { if (m_uncompressed_trace != NULL) { if (m_block_size_bytes < RubySystem::getBlockSizeBytes()) { @@ -80,7 +84,7 @@ CacheRecorder::~CacheRecorder() delete [] m_uncompressed_trace; m_uncompressed_trace = NULL; } - m_seq_map.clear(); + m_ruby_port_map.clear(); } void @@ -94,13 +98,19 @@ CacheRecorder::enqueueNextFlushRequest() Request::funcRequestorId); MemCmd::Command requestType = MemCmd::FlushReq; Packet *pkt = new Packet(req, requestType); + pkt->req->setReqInstSeqNum(m_records_flushed); - Sequencer* m_sequencer_ptr = m_seq_map[rec->m_cntrl_id]; - assert(m_sequencer_ptr != NULL); - m_sequencer_ptr->makeRequest(pkt); + + RubyPort* m_ruby_port_ptr = m_ruby_port_map[rec->m_cntrl_id]; + assert(m_ruby_port_ptr != NULL); + m_ruby_port_ptr->makeRequest(pkt); DPRINTF(RubyCacheTrace, "Flushing %s\n", *rec); + } else { + if (m_records_flushed > 0) { + exitSimLoop("Finished Drain", 0); + } DPRINTF(RubyCacheTrace, "Flushed all %d records\n", m_records_flushed); } } @@ -141,15 +151,19 @@ CacheRecorder::enqueueNextFetchRequest() Packet *pkt = new Packet(req, requestType); pkt->dataStatic(traceRecord->m_data + rec_bytes_read); + pkt->req->setReqInstSeqNum(m_records_read); - Sequencer* m_sequencer_ptr = m_seq_map[traceRecord->m_cntrl_id]; - assert(m_sequencer_ptr != NULL); - m_sequencer_ptr->makeRequest(pkt); + + RubyPort* m_ruby_port_ptr = + m_ruby_port_map[traceRecord->m_cntrl_id]; + assert(m_ruby_port_ptr != NULL); + m_ruby_port_ptr->makeRequest(pkt); } m_bytes_read += (sizeof(TraceRecord) + m_block_size_bytes); m_records_read++; } else { + exitSimLoop("Finished Warmup", 0); DPRINTF(RubyCacheTrace, "Fetched all %d records\n", m_records_read); } } @@ -168,6 +182,8 @@ CacheRecorder::addRecord(int cntrl, Addr data_addr, Addr pc_addr, memcpy(rec->m_data, data.getData(0, m_block_size_bytes), m_block_size_bytes); + DPRINTF(RubyCacheTrace, "Inside addRecord with cntrl id %d and type %d\n", + cntrl, type); m_records.push_back(rec); } diff --git a/src/mem/ruby/system/CacheRecorder.hh b/src/mem/ruby/system/CacheRecorder.hh index be95590313..021da6a4da 100644 --- a/src/mem/ruby/system/CacheRecorder.hh +++ b/src/mem/ruby/system/CacheRecorder.hh @@ -50,7 +50,7 @@ namespace ruby { class Sequencer; - +class RubyPort; /*! * Class for recording cache contents. Note that the last element of the * class is an array of length zero. It is used for creating variable @@ -78,7 +78,7 @@ class CacheRecorder CacheRecorder(uint8_t* uncompressed_trace, uint64_t uncompressed_trace_size, - std::vector& SequencerMap, + std::vector& ruby_port_map, uint64_t block_size_bytes); void addRecord(int cntrl, Addr data_addr, Addr pc_addr, RubyRequestType type, Tick time, DataBlock& data); @@ -114,7 +114,7 @@ class CacheRecorder std::vector m_records; uint8_t* m_uncompressed_trace; uint64_t m_uncompressed_trace_size; - std::vector m_seq_map; + std::vector m_ruby_port_map; uint64_t m_bytes_read; uint64_t m_records_read; uint64_t m_records_flushed; diff --git a/src/mem/ruby/system/GPUCoalescer.cc b/src/mem/ruby/system/GPUCoalescer.cc index 8bde3f7bc8..90d6031c6e 100644 --- a/src/mem/ruby/system/GPUCoalescer.cc +++ b/src/mem/ruby/system/GPUCoalescer.cc @@ -73,6 +73,14 @@ UncoalescedTable::insertPacket(PacketPtr pkt) pkt->getAddr(), seqNum, instMap.size(), instMap[seqNum].size()); } +void +UncoalescedTable::insertReqType(PacketPtr pkt, RubyRequestType type) +{ + uint64_t seqNum = pkt->req->getReqInstSeqNum(); + + reqTypeMap[seqNum] = type; +} + bool UncoalescedTable::packetAvailable() { @@ -128,9 +136,21 @@ UncoalescedTable::updateResources() instMap.erase(iter++); instPktsRemaining.erase(seq_num); - // Release the token - DPRINTF(GPUCoalescer, "Returning token seqNum %d\n", seq_num); - coalescer->getGMTokenPort().sendTokens(1); + // Release the token if the Ruby system is not in cooldown + // or warmup phases. When in these phases, the RubyPorts + // are accessed directly using the makeRequest() command + // instead of accessing through the port. This makes + // sending tokens through the port unnecessary + if (!RubySystem::getWarmupEnabled() + && !RubySystem::getCooldownEnabled()) { + if (reqTypeMap[seq_num] != RubyRequestType_FLUSH) { + DPRINTF(GPUCoalescer, + "Returning token seqNum %d\n", seq_num); + coalescer->getGMTokenPort().sendTokens(1); + } + } + + reqTypeMap.erase(seq_num); } else { ++iter; } @@ -324,7 +344,8 @@ GPUCoalescer::printRequestTable(std::stringstream& ss) << "\t\tIssue time: " << request->getIssueTime() * clockPeriod() << "\n" << "\t\tDifference from current tick: " - << (curCycle() - request->getIssueTime()) * clockPeriod(); + << (curCycle() - request->getIssueTime()) * clockPeriod() + << "\n"; } } @@ -505,26 +526,16 @@ GPUCoalescer::readCallback(Addr address, fatal_if(crequest->getRubyType() != RubyRequestType_LD, "readCallback received non-read type response\n"); - // Iterate over the coalesced requests to respond to as many loads as - // possible until another request type is seen. Models MSHR for TCP. - while (crequest->getRubyType() == RubyRequestType_LD) { - hitCallback(crequest, mach, data, true, crequest->getIssueTime(), - forwardRequestTime, firstResponseTime, isRegion); - - delete crequest; - coalescedTable.at(address).pop_front(); - if (coalescedTable.at(address).empty()) { - break; - } - - crequest = coalescedTable.at(address).front(); - } + hitCallback(crequest, mach, data, true, crequest->getIssueTime(), + forwardRequestTime, firstResponseTime, isRegion); + delete crequest; + coalescedTable.at(address).pop_front(); if (coalescedTable.at(address).empty()) { - coalescedTable.erase(address); + coalescedTable.erase(address); } else { - auto nextRequest = coalescedTable.at(address).front(); - issueRequest(nextRequest); + auto nextRequest = coalescedTable.at(address).front(); + issueRequest(nextRequest); } } @@ -554,25 +565,56 @@ GPUCoalescer::hitCallback(CoalescedRequest* crequest, success, isRegion); // update the data // - // MUST AD DOING THIS FOR EACH REQUEST IN COALESCER + // MUST ADD DOING THIS FOR EACH REQUEST IN COALESCER std::vector pktList = crequest->getPackets(); + + uint8_t* log = nullptr; DPRINTF(GPUCoalescer, "Responding to %d packets for addr 0x%X\n", pktList.size(), request_line_address); + uint32_t offset; + int pkt_size; for (auto& pkt : pktList) { + offset = getOffset(pkt->getAddr()); + pkt_size = pkt->getSize(); request_address = pkt->getAddr(); + + // When the Ruby system is cooldown phase, the requests come from + // the cache recorder. These requests do not get coalesced and + // do not return valid data. + if (RubySystem::getCooldownEnabled()) + continue; + if (pkt->getPtr()) { - if ((type == RubyRequestType_LD) || - (type == RubyRequestType_ATOMIC) || - (type == RubyRequestType_ATOMIC_RETURN) || - (type == RubyRequestType_IFETCH) || - (type == RubyRequestType_RMW_Read) || - (type == RubyRequestType_Locked_RMW_Read) || - (type == RubyRequestType_Load_Linked)) { - pkt->setData( - data.getData(getOffset(request_address), pkt->getSize())); - } else { - data.setData(pkt->getPtr(), - getOffset(request_address), pkt->getSize()); + switch(type) { + // Store and AtomicNoReturns follow the same path, as the + // data response is not needed. + case RubyRequestType_ATOMIC_NO_RETURN: + assert(pkt->isAtomicOp()); + break; + case RubyRequestType_ST: + break; + case RubyRequestType_LD: + pkt->setData(data.getData(offset, pkt_size)); + break; + case RubyRequestType_ATOMIC_RETURN: + assert(pkt->isAtomicOp()); + // Atomic operations are performed by the WriteMask + // in packet order, set by the crequest. Thus, when + // unpacking the changes from the log, we read from + // the front of the log to correctly map response + // data into the packets. + + // Log entry contains the old value before the current + // atomic operation occurred. + log = data.popAtomicLogEntryFront(); + pkt->setData(&log[offset]); + delete [] log; + log = nullptr; + break; + default: + panic("Unsupported ruby packet type:%s\n", + RubyRequestType_to_string(type)); + break; } } else { DPRINTF(MemoryAccess, @@ -581,6 +623,7 @@ GPUCoalescer::hitCallback(CoalescedRequest* crequest, RubyRequestType_to_string(type)); } } + assert(data.numAtomicLogEntries() == 0); m_outstanding_count--; assert(m_outstanding_count >= 0); @@ -603,7 +646,6 @@ GPUCoalescer::getRequestType(PacketPtr pkt) assert(!pkt->req->isLLSC()); assert(!pkt->req->isLockedRMW()); assert(!pkt->req->isInstFetch()); - assert(!pkt->isFlush()); if (pkt->req->isAtomicReturn()) { req_type = RubyRequestType_ATOMIC_RETURN; @@ -613,6 +655,8 @@ GPUCoalescer::getRequestType(PacketPtr pkt) req_type = RubyRequestType_LD; } else if (pkt->isWrite()) { req_type = RubyRequestType_ST; + } else if (pkt->isFlush()) { + req_type = RubyRequestType_FLUSH; } else { panic("Unsupported ruby packet type\n"); } @@ -634,7 +678,7 @@ GPUCoalescer::makeRequest(PacketPtr pkt) issueMemSyncRequest(pkt); } else { // otherwise, this must be either read or write command - assert(pkt->isRead() || pkt->isWrite()); + assert(pkt->isRead() || pkt->isWrite() || pkt->isFlush()); InstSeqNum seq_num = pkt->req->getReqInstSeqNum(); @@ -643,10 +687,17 @@ GPUCoalescer::makeRequest(PacketPtr pkt) // number of lanes actives for that vmem request (i.e., the popcnt // of the exec_mask. int num_packets = 1; - if (!m_usingRubyTester) { - num_packets = 0; - for (int i = 0; i < TheGpuISA::NumVecElemPerVecReg; i++) { - num_packets += getDynInst(pkt)->getLaneStatus(i); + + // When Ruby is in warmup or cooldown phase, the requests come from + // the cache recorder. There is no dynamic instruction associated + // with these requests either + if (!RubySystem::getWarmupEnabled() + && !RubySystem::getCooldownEnabled()) { + if (!m_usingRubyTester) { + num_packets = 0; + for (int i = 0; i < TheGpuISA::NumVecElemPerVecReg; i++) { + num_packets += getDynInst(pkt)->getLaneStatus(i); + } } } @@ -655,6 +706,7 @@ GPUCoalescer::makeRequest(PacketPtr pkt) // future cycle. Packets remaining is set to the number of excepted // requests from the instruction based on its exec_mask. uncoalescedTable.insertPacket(pkt); + uncoalescedTable.insertReqType(pkt, getRequestType(pkt)); uncoalescedTable.initPacketsRemaining(seq_num, num_packets); DPRINTF(GPUCoalescer, "Put pkt with addr 0x%X to uncoalescedTable\n", pkt->getAddr()); @@ -921,21 +973,27 @@ void GPUCoalescer::completeHitCallback(std::vector & mylist) { for (auto& pkt : mylist) { - RubyPort::SenderState *ss = - safe_cast(pkt->senderState); - MemResponsePort *port = ss->port; - assert(port != NULL); + // When Ruby is in warmup or cooldown phase, the requests come + // from the cache recorder. They do not track which port to use + // and do not need to send the response back + if (!RubySystem::getWarmupEnabled() + && !RubySystem::getCooldownEnabled()) { + RubyPort::SenderState *ss = + safe_cast(pkt->senderState); + MemResponsePort *port = ss->port; + assert(port != NULL); - pkt->senderState = ss->predecessor; + pkt->senderState = ss->predecessor; - if (pkt->cmd != MemCmd::WriteReq) { - // for WriteReq, we keep the original senderState until - // writeCompleteCallback - delete ss; + if (pkt->cmd != MemCmd::WriteReq) { + // for WriteReq, we keep the original senderState until + // writeCompleteCallback + delete ss; + } + + port->hitCallback(pkt); + trySendRetries(); } - - port->hitCallback(pkt); - trySendRetries(); } // We schedule an event in the same tick as hitCallback (similar to @@ -947,7 +1005,14 @@ GPUCoalescer::completeHitCallback(std::vector & mylist) schedule(issueEvent, curTick()); } - testDrainComplete(); + RubySystem *rs = m_ruby_system; + if (RubySystem::getWarmupEnabled()) { + rs->m_cache_recorder->enqueueNextFetchRequest(); + } else if (RubySystem::getCooldownEnabled()) { + rs->m_cache_recorder->enqueueNextFlushRequest(); + } else { + testDrainComplete(); + } } void diff --git a/src/mem/ruby/system/GPUCoalescer.hh b/src/mem/ruby/system/GPUCoalescer.hh index dd28855547..d6db5c00ba 100644 --- a/src/mem/ruby/system/GPUCoalescer.hh +++ b/src/mem/ruby/system/GPUCoalescer.hh @@ -71,6 +71,7 @@ class UncoalescedTable ~UncoalescedTable() {} void insertPacket(PacketPtr pkt); + void insertReqType(PacketPtr pkt, RubyRequestType type); bool packetAvailable(); void printRequestTable(std::stringstream& ss); @@ -101,6 +102,8 @@ class UncoalescedTable std::map instMap; std::map instPktsRemaining; + + std::map reqTypeMap; }; class CoalescedRequest diff --git a/src/mem/ruby/system/GPUCoalescer.py b/src/mem/ruby/system/GPUCoalescer.py index fcf49e38b7..d69314eb54 100644 --- a/src/mem/ruby/system/GPUCoalescer.py +++ b/src/mem/ruby/system/GPUCoalescer.py @@ -27,11 +27,10 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +from m5.objects.Sequencer import * from m5.params import * from m5.proxy import * -from m5.objects.Sequencer import * - class RubyGPUCoalescer(RubyPort): type = "RubyGPUCoalescer" diff --git a/src/mem/ruby/system/RubySystem.cc b/src/mem/ruby/system/RubySystem.cc index b38c903b09..109fd43051 100644 --- a/src/mem/ruby/system/RubySystem.cc +++ b/src/mem/ruby/system/RubySystem.cc @@ -177,22 +177,32 @@ RubySystem::makeCacheRecorder(uint8_t *uncompressed_trace, uint64_t cache_trace_size, uint64_t block_size_bytes) { - std::vector sequencer_map; - Sequencer* sequencer_ptr = NULL; + std::vector ruby_port_map; + RubyPort* ruby_port_ptr = NULL; for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) { - sequencer_map.push_back(m_abs_cntrl_vec[cntrl]->getCPUSequencer()); - if (sequencer_ptr == NULL) { - sequencer_ptr = sequencer_map[cntrl]; + if (m_abs_cntrl_vec[cntrl]->getGPUCoalescer() != NULL) { + ruby_port_map.push_back( + (RubyPort*)m_abs_cntrl_vec[cntrl]->getGPUCoalescer()); + } else { + ruby_port_map.push_back( + (RubyPort*)m_abs_cntrl_vec[cntrl]->getCPUSequencer()); + } + + if (ruby_port_ptr == NULL) { + ruby_port_ptr = ruby_port_map[cntrl]; } } - assert(sequencer_ptr != NULL); + assert(ruby_port_ptr != NULL); for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) { - if (sequencer_map[cntrl] == NULL) { - sequencer_map[cntrl] = sequencer_ptr; + if (ruby_port_map[cntrl] == NULL) { + ruby_port_map[cntrl] = ruby_port_ptr; + } else { + ruby_port_ptr = ruby_port_map[cntrl]; } + } // Remove the old CacheRecorder if it's still hanging about. @@ -202,7 +212,8 @@ RubySystem::makeCacheRecorder(uint8_t *uncompressed_trace, // Create the CacheRecorder and record the cache trace m_cache_recorder = new CacheRecorder(uncompressed_trace, cache_trace_size, - sequencer_map, block_size_bytes); + ruby_port_map, + block_size_bytes); } void diff --git a/src/mem/ruby/system/RubySystem.py b/src/mem/ruby/system/RubySystem.py index 64e39bda4c..5dbcde90ec 100644 --- a/src/mem/ruby/system/RubySystem.py +++ b/src/mem/ruby/system/RubySystem.py @@ -24,10 +24,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.proxy import * from m5.objects.ClockedObject import ClockedObject from m5.objects.SimpleMemory import * +from m5.params import * +from m5.proxy import * class RubySystem(ClockedObject): diff --git a/src/mem/ruby/system/SConscript b/src/mem/ruby/system/SConscript index c0b85bb350..77bce7f851 100644 --- a/src/mem/ruby/system/SConscript +++ b/src/mem/ruby/system/SConscript @@ -40,12 +40,12 @@ Import('*') -if env['CONF']['PROTOCOL'] == 'None': +if not env['CONF']['RUBY']: Return() env.Append(CPPDEFINES=['PROTOCOL_' + env['CONF']['PROTOCOL']]) -if env['CONF']['PROTOCOL'] in env['NEED_PARTIAL_FUNC_READS']: +if env['CONF']['NEED_PARTIAL_FUNC_READS']: env.Append(CPPDEFINES=['PARTIAL_FUNC_READS']) if env['CONF']['BUILD_GPU']: diff --git a/src/mem/ruby/system/Sequencer.cc b/src/mem/ruby/system/Sequencer.cc index 82fc19b57c..48054febef 100644 --- a/src/mem/ruby/system/Sequencer.cc +++ b/src/mem/ruby/system/Sequencer.cc @@ -466,8 +466,12 @@ Sequencer::writeCallback(Addr address, DataBlock& data, bool ruby_request = true; while (!seq_req_list.empty()) { SequencerRequest &seq_req = seq_req_list.front(); + // Atomic Request may be executed remotly in the cache hierarchy + bool atomic_req = + ((seq_req.m_type == RubyRequestType_ATOMIC_RETURN) || + (seq_req.m_type == RubyRequestType_ATOMIC_NO_RETURN)); - if (noCoales && !ruby_request) { + if ((noCoales || atomic_req) && !ruby_request) { // Do not process follow-up requests // (e.g. if full line no present) // Reissue to the cache hierarchy @@ -479,6 +483,8 @@ Sequencer::writeCallback(Addr address, DataBlock& data, assert(seq_req.m_type != RubyRequestType_LD); assert(seq_req.m_type != RubyRequestType_Load_Linked); assert(seq_req.m_type != RubyRequestType_IFETCH); + assert(seq_req.m_type != RubyRequestType_ATOMIC_RETURN); + assert(seq_req.m_type != RubyRequestType_ATOMIC_NO_RETURN); } // handle write request @@ -594,6 +600,62 @@ Sequencer::readCallback(Addr address, DataBlock& data, } } +void +Sequencer::atomicCallback(Addr address, DataBlock& data, + const bool externalHit, const MachineType mach, + const Cycles initialRequestTime, + const Cycles forwardRequestTime, + const Cycles firstResponseTime) +{ + // + // Free the first request (an atomic operation) from the list. + // Then issue the next request to ruby system as we cannot + // assume the cache line is present in the cache + // (the opperation could be performed remotly) + // + assert(address == makeLineAddress(address)); + assert(m_RequestTable.find(address) != m_RequestTable.end()); + auto &seq_req_list = m_RequestTable[address]; + + // Perform hitCallback only on the first cpu request that + // issued the ruby request + bool ruby_request = true; + while (!seq_req_list.empty()) { + SequencerRequest &seq_req = seq_req_list.front(); + + if (ruby_request) { + // Check that the request was an atomic memory operation + // and record the latency + assert((seq_req.m_type == RubyRequestType_ATOMIC_RETURN) || + (seq_req.m_type == RubyRequestType_ATOMIC_NO_RETURN)); + recordMissLatency(&seq_req, true, mach, externalHit, + initialRequestTime, forwardRequestTime, + firstResponseTime); + } else { + // Read, Write or Atomic request: + // reissue request to the cache hierarchy + // (we don't know if op was performed remotly) + issueRequest(seq_req.pkt, seq_req.m_second_type); + break; + } + + // Atomics clean the monitor entry + llscClearMonitor(address); + + markRemoved(); + ruby_request = false; + hitCallback(&seq_req, data, true, mach, externalHit, + initialRequestTime, forwardRequestTime, + firstResponseTime, false); + seq_req_list.pop_front(); + } + + // free all outstanding requests corresponding to this address + if (seq_req_list.empty()) { + m_RequestTable.erase(address); + } +} + void Sequencer::hitCallback(SequencerRequest* srequest, DataBlock& data, bool llscSuccess, @@ -637,10 +699,16 @@ Sequencer::hitCallback(SequencerRequest* srequest, DataBlock& data, (type == RubyRequestType_IFETCH) || (type == RubyRequestType_RMW_Read) || (type == RubyRequestType_Locked_RMW_Read) || - (type == RubyRequestType_Load_Linked)) { + (type == RubyRequestType_Load_Linked) || + (type == RubyRequestType_ATOMIC_RETURN)) { pkt->setData( data.getData(getOffset(request_address), pkt->getSize())); - DPRINTF(RubySequencer, "read data %s\n", data); + + if (type == RubyRequestType_ATOMIC_RETURN) { + DPRINTF(RubySequencer, "ATOMIC RETURN data %s\n", data); + } else { + DPRINTF(RubySequencer, "read data %s\n", data); + } } else if (pkt->req->isSwap()) { assert(!pkt->isMaskedWrite()); std::vector overwrite_val(pkt->getSize()); @@ -807,6 +875,19 @@ Sequencer::makeRequest(PacketPtr pkt) } else if (pkt->req->isTlbiCmd()) { primary_type = secondary_type = tlbiCmdToRubyRequestType(pkt); DPRINTF(RubySequencer, "Issuing TLBI\n"); +#if defined (PROTOCOL_CHI) + } else if (pkt->isAtomicOp()) { + if (pkt->req->isAtomicReturn()){ + DPRINTF(RubySequencer, "Issuing ATOMIC RETURN \n"); + primary_type = secondary_type = + RubyRequestType_ATOMIC_RETURN; + } else { + DPRINTF(RubySequencer, "Issuing ATOMIC NO RETURN\n"); + primary_type = secondary_type = + RubyRequestType_ATOMIC_NO_RETURN; + + } +#endif } else { // // To support SwapReq, we need to check isWrite() first: a SwapReq @@ -914,6 +995,18 @@ Sequencer::issueRequest(PacketPtr pkt, RubyRequestType secondary_type) RubyAccessMode_Supervisor, pkt, PrefetchBit_No, proc_id, core_id); + if (pkt->isAtomicOp() && + ((secondary_type == RubyRequestType_ATOMIC_RETURN) || + (secondary_type == RubyRequestType_ATOMIC_NO_RETURN))){ + // Create the blocksize, access mask and atomicops + uint32_t offset = getOffset(pkt->getAddr()); + std::vector> atomicOps; + atomicOps.push_back(std::make_pair + (offset, pkt->getAtomicOp())); + + msg->setWriteMask(offset, pkt->getSize(), atomicOps); + } + DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %#x %s\n", curTick(), m_version, "Seq", "Begin", "", "", printAddress(msg->getPhysicalAddress()), diff --git a/src/mem/ruby/system/Sequencer.hh b/src/mem/ruby/system/Sequencer.hh index 020a7d8c20..8f736da6d5 100644 --- a/src/mem/ruby/system/Sequencer.hh +++ b/src/mem/ruby/system/Sequencer.hh @@ -126,6 +126,14 @@ class Sequencer : public RubyPort const Cycles forwardRequestTime = Cycles(0), const Cycles firstResponseTime = Cycles(0)); + void atomicCallback(Addr address, + DataBlock& data, + const bool externalHit = false, + const MachineType mach = MachineType_NUM, + const Cycles initialRequestTime = Cycles(0), + const Cycles forwardRequestTime = Cycles(0), + const Cycles firstResponseTime = Cycles(0)); + void unaddressedCallback(Addr unaddressedReqId, RubyRequestType requestType, const MachineType mach = MachineType_NUM, diff --git a/src/mem/ruby/system/Sequencer.py b/src/mem/ruby/system/Sequencer.py index eb1003692f..3f570fb952 100644 --- a/src/mem/ruby/system/Sequencer.py +++ b/src/mem/ruby/system/Sequencer.py @@ -37,9 +37,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.ClockedObject import ClockedObject from m5.params import * from m5.proxy import * -from m5.objects.ClockedObject import ClockedObject class RubyPort(ClockedObject): diff --git a/src/mem/ruby/system/VIPERCoalescer.cc b/src/mem/ruby/system/VIPERCoalescer.cc index ea95129841..a5198cce63 100644 --- a/src/mem/ruby/system/VIPERCoalescer.cc +++ b/src/mem/ruby/system/VIPERCoalescer.cc @@ -75,12 +75,14 @@ VIPERCoalescer::makeRequest(PacketPtr pkt) // ReadReq : cache read // WriteReq : cache write // AtomicOp : cache atomic + // Flush : flush and invalidate cache // // VIPER does not expect MemSyncReq & Release since in GCN3, compute unit // does not specify an equivalent type of memory request. assert((pkt->cmd == MemCmd::MemSyncReq && pkt->req->isInvL1()) || pkt->cmd == MemCmd::ReadReq || pkt->cmd == MemCmd::WriteReq || + pkt->cmd == MemCmd::FlushReq || pkt->isAtomicOp()); if (pkt->req->isInvL1() && m_cache_inv_pkt) { diff --git a/src/mem/ruby/system/VIPERCoalescer.py b/src/mem/ruby/system/VIPERCoalescer.py index af43cddb2c..d2411f858c 100644 --- a/src/mem/ruby/system/VIPERCoalescer.py +++ b/src/mem/ruby/system/VIPERCoalescer.py @@ -27,9 +27,9 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +from m5.objects.GPUCoalescer import * from m5.params import * from m5.proxy import * -from m5.objects.GPUCoalescer import * class VIPERCoalescer(RubyGPUCoalescer): diff --git a/src/mem/slicc/ast/AST.py b/src/mem/slicc/ast/AST.py index ff8e3326ad..52700f9dc9 100644 --- a/src/mem/slicc/ast/AST.py +++ b/src/mem/slicc/ast/AST.py @@ -25,7 +25,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from slicc.util import PairContainer, Location +from slicc.util import ( + Location, + PairContainer, +) class AST(PairContainer): diff --git a/src/mem/slicc/ast/ActionDeclAST.py b/src/mem/slicc/ast/ActionDeclAST.py index ff6a4ff9d5..375f4109c1 100644 --- a/src/mem/slicc/ast/ActionDeclAST.py +++ b/src/mem/slicc/ast/ActionDeclAST.py @@ -26,7 +26,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from slicc.ast.DeclAST import DeclAST -from slicc.symbols import Action, Type, Var +from slicc.symbols import ( + Action, + Type, + Var, +) class ActionDeclAST(DeclAST): diff --git a/src/mem/slicc/ast/DeferEnqueueingStatementAST.py b/src/mem/slicc/ast/DeferEnqueueingStatementAST.py index 0c34113902..14b2e48cd3 100644 --- a/src/mem/slicc/ast/DeferEnqueueingStatementAST.py +++ b/src/mem/slicc/ast/DeferEnqueueingStatementAST.py @@ -41,7 +41,7 @@ class DeferEnqueueingStatementAST(StatementAST): self.statements = statements def __repr__(self): - return "[DeferEnqueueingStatementAst: %s %s %s]" % ( + return "[DeferEnqueueingStatementAst: {} {} {}]".format( self.queue_name, self.type_ast.ident, self.statements, diff --git a/src/mem/slicc/ast/EnqueueStatementAST.py b/src/mem/slicc/ast/EnqueueStatementAST.py index 148cc3a223..c2d47af9ce 100644 --- a/src/mem/slicc/ast/EnqueueStatementAST.py +++ b/src/mem/slicc/ast/EnqueueStatementAST.py @@ -31,16 +31,25 @@ from slicc.symbols import Var class EnqueueStatementAST(StatementAST): - def __init__(self, slicc, queue_name, type_ast, lexpr, statements): + def __init__( + self, + slicc, + queue_name, + type_ast, + lexpr, + bypass_strict_fifo, + statements, + ): super().__init__(slicc) self.queue_name = queue_name self.type_ast = type_ast self.latexpr = lexpr + self.bypass_strict_fifo = bypass_strict_fifo self.statements = statements def __repr__(self): - return "[EnqueueStatementAst: %s %s %s]" % ( + return "[EnqueueStatementAst: {} {} {}]".format( self.queue_name, self.type_ast.ident, self.statements, @@ -76,10 +85,17 @@ class EnqueueStatementAST(StatementAST): if self.latexpr != None: ret_type, rcode = self.latexpr.inline(True) - code( - "(${{self.queue_name.var.code}}).enqueue(" - "out_msg, clockEdge(), cyclesToTicks(Cycles($rcode)));" - ) + if self.bypass_strict_fifo != None: + bypass_strict_fifo_code = self.bypass_strict_fifo.inline(False) + code( + "(${{self.queue_name.var.code}}).enqueue(" + "out_msg, clockEdge(), cyclesToTicks(Cycles($rcode)), $bypass_strict_fifo_code);" + ) + else: + code( + "(${{self.queue_name.var.code}}).enqueue(" + "out_msg, clockEdge(), cyclesToTicks(Cycles($rcode)));" + ) else: code( "(${{self.queue_name.var.code}}).enqueue(out_msg, " diff --git a/src/mem/slicc/ast/EnumDeclAST.py b/src/mem/slicc/ast/EnumDeclAST.py index 9b4a6be77a..f7ff5c3cfd 100644 --- a/src/mem/slicc/ast/EnumDeclAST.py +++ b/src/mem/slicc/ast/EnumDeclAST.py @@ -26,7 +26,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from slicc.ast.DeclAST import DeclAST -from slicc.symbols import Func, Type +from slicc.symbols import ( + Func, + Type, +) class EnumDeclAST(DeclAST): @@ -47,7 +50,7 @@ class EnumDeclAST(DeclAST): ident = f"{parent}_{self.type_ast.ident}" else: ident = self.type_ast.ident - s = set((f"{ident}.hh", f"{ident}.cc")) + s = {f"{ident}.hh", f"{ident}.cc"} return s def generate(self): diff --git a/src/mem/slicc/ast/ExprStatementAST.py b/src/mem/slicc/ast/ExprStatementAST.py index 9545ef3e41..2aa7456816 100644 --- a/src/mem/slicc/ast/ExprStatementAST.py +++ b/src/mem/slicc/ast/ExprStatementAST.py @@ -26,8 +26,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from slicc.ast.StatementAST import StatementAST from slicc.ast.LocalVariableAST import LocalVariableAST +from slicc.ast.StatementAST import StatementAST from slicc.symbols import Type diff --git a/src/mem/slicc/ast/FuncCallExprAST.py b/src/mem/slicc/ast/FuncCallExprAST.py index 6ccca6650a..b807a5f933 100644 --- a/src/mem/slicc/ast/FuncCallExprAST.py +++ b/src/mem/slicc/ast/FuncCallExprAST.py @@ -39,7 +39,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from slicc.ast.ExprAST import ExprAST -from slicc.symbols import Func, Type +from slicc.symbols import ( + Func, + Type, +) class FuncCallExprAST(ExprAST): @@ -282,7 +285,7 @@ if (!(${{cvec[0]}})) { params = "" first_param = True - for (param_code, type) in zip(cvec, type_vec): + for param_code, type in zip(cvec, type_vec): if first_param: params = str(param_code) first_param = False diff --git a/src/mem/slicc/ast/FuncDeclAST.py b/src/mem/slicc/ast/FuncDeclAST.py index 38898ff9e5..2e28dfd75c 100644 --- a/src/mem/slicc/ast/FuncDeclAST.py +++ b/src/mem/slicc/ast/FuncDeclAST.py @@ -26,7 +26,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from slicc.ast.DeclAST import DeclAST -from slicc.symbols import Func, Type +from slicc.symbols import ( + Func, + Type, +) class FuncDeclAST(DeclAST): diff --git a/src/mem/slicc/ast/InPortDeclAST.py b/src/mem/slicc/ast/InPortDeclAST.py index 2cbf3bb617..6f1c121075 100644 --- a/src/mem/slicc/ast/InPortDeclAST.py +++ b/src/mem/slicc/ast/InPortDeclAST.py @@ -39,7 +39,11 @@ from slicc.ast.DeclAST import DeclAST from slicc.ast.TypeAST import TypeAST -from slicc.symbols import Func, Type, Var +from slicc.symbols import ( + Func, + Type, + Var, +) class InPortDeclAST(DeclAST): diff --git a/src/mem/slicc/ast/MachineAST.py b/src/mem/slicc/ast/MachineAST.py index 5c76aa8173..a6cb8a93ee 100644 --- a/src/mem/slicc/ast/MachineAST.py +++ b/src/mem/slicc/ast/MachineAST.py @@ -26,7 +26,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from slicc.ast.DeclAST import DeclAST -from slicc.symbols import StateMachine, Type +from slicc.symbols import ( + StateMachine, + Type, +) class MachineAST(DeclAST): @@ -42,15 +45,13 @@ class MachineAST(DeclAST): return f"[Machine: {self.ident!r}]" def files(self, parent=None): - s = set( - ( - f"{self.ident}_Controller.cc", - f"{self.ident}_Controller.hh", - f"{self.ident}_Controller.py", - f"{self.ident}_Transitions.cc", - f"{self.ident}_Wakeup.cc", - ) - ) + s = { + f"{self.ident}_Controller.cc", + f"{self.ident}_Controller.hh", + f"{self.ident}_Controller.py", + f"{self.ident}_Transitions.cc", + f"{self.ident}_Wakeup.cc", + } s |= self.decls.files(self.ident) return s diff --git a/src/mem/slicc/ast/MethodCallExprAST.py b/src/mem/slicc/ast/MethodCallExprAST.py index 7bdf0c7dd9..b1649a644e 100644 --- a/src/mem/slicc/ast/MethodCallExprAST.py +++ b/src/mem/slicc/ast/MethodCallExprAST.py @@ -77,7 +77,7 @@ class MemberMethodCallExprAST(MethodCallExprAST): self.obj_expr_ast = obj_expr_ast def __repr__(self): - return "[MethodCallExpr: %r%r %r]" % ( + return "[MethodCallExpr: {!r}{!r} {!r}]".format( self.proc_name, self.obj_expr_ast, self.expr_ast_vec, @@ -189,7 +189,6 @@ class ClassMethodCallExprAST(MethodCallExprAST): return f"[MethodCallExpr: {self.proc_name!r} {self.expr_ast_vec!r}]" def generate_prefix(self, paramTypes): - # class method call prefix = f"({self.type_ast}::" obj_type = self.type_ast.type diff --git a/src/mem/slicc/ast/OperatorExprAST.py b/src/mem/slicc/ast/OperatorExprAST.py index 714b553101..87417b50ea 100644 --- a/src/mem/slicc/ast/OperatorExprAST.py +++ b/src/mem/slicc/ast/OperatorExprAST.py @@ -1,3 +1,15 @@ +# Copyright (c) 2023 Arm Limited +# All rights reserved. +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# # Copyright (c) 1999-2008 Mark D. Hill and David A. Wood # Copyright (c) 2009 The Hewlett-Packard Development Company # All rights reserved. @@ -76,11 +88,14 @@ class InfixOperatorExprAST(ExprAST): ("int", "int", "int"), ("Cycles", "Cycles", "Cycles"), ("Tick", "Tick", "Tick"), + ("Addr", "Addr", "Addr"), ("Cycles", "int", "Cycles"), ("Scalar", "int", "Scalar"), ("int", "bool", "int"), ("bool", "int", "int"), ("int", "Cycles", "Cycles"), + ("Addr", "int", "Addr"), + ("int", "Addr", "Addr"), ] else: self.error(f"No operator matched with {self.op}!") @@ -94,8 +109,8 @@ class InfixOperatorExprAST(ExprAST): if output == None: self.error( - "Type mismatch: operands ({0}, {1}) for operator " - "'{2}' failed to match with the expected types".format( + "Type mismatch: operands ({}, {}) for operator " + "'{}' failed to match with the expected types".format( ltype, rtype, self.op ) ) diff --git a/src/mem/slicc/ast/OutPortDeclAST.py b/src/mem/slicc/ast/OutPortDeclAST.py index e21a4a6fa7..e6899c8700 100644 --- a/src/mem/slicc/ast/OutPortDeclAST.py +++ b/src/mem/slicc/ast/OutPortDeclAST.py @@ -27,8 +27,10 @@ from slicc.ast.DeclAST import DeclAST from slicc.ast.TypeAST import TypeAST -from slicc.symbols import Var -from slicc.symbols import Type +from slicc.symbols import ( + Type, + Var, +) class OutPortDeclAST(DeclAST): diff --git a/src/mem/slicc/ast/PeekStatementAST.py b/src/mem/slicc/ast/PeekStatementAST.py index fd07d4e13a..00edff4e7b 100644 --- a/src/mem/slicc/ast/PeekStatementAST.py +++ b/src/mem/slicc/ast/PeekStatementAST.py @@ -40,11 +40,13 @@ class PeekStatementAST(StatementAST): self.method = method def __repr__(self): - return "[PeekStatementAST: %r queue_name: %r type: %r %r]" % ( - self.method, - self.queue_name, - self.type_ast, - self.statements, + return ( + "[PeekStatementAST: {!r} queue_name: {!r} type: {!r} {!r}]".format( + self.method, + self.queue_name, + self.type_ast, + self.statements, + ) ) def generate(self, code, return_type, **kwargs): diff --git a/src/mem/slicc/ast/StateDeclAST.py b/src/mem/slicc/ast/StateDeclAST.py index d190326484..1455c50599 100644 --- a/src/mem/slicc/ast/StateDeclAST.py +++ b/src/mem/slicc/ast/StateDeclAST.py @@ -25,7 +25,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from slicc.ast.DeclAST import DeclAST -from slicc.symbols import Func, Type +from slicc.symbols import ( + Func, + Type, +) class StateDeclAST(DeclAST): @@ -46,7 +49,7 @@ class StateDeclAST(DeclAST): ident = f"{parent}_{self.type_ast.ident}" else: ident = self.type_ast.ident - s = set((f"{ident}.hh", f"{ident}.cc")) + s = {f"{ident}.hh", f"{ident}.cc"} return s def generate(self): diff --git a/src/mem/slicc/ast/StaticCastAST.py b/src/mem/slicc/ast/StaticCastAST.py index 178285202b..b6b70efcb5 100644 --- a/src/mem/slicc/ast/StaticCastAST.py +++ b/src/mem/slicc/ast/StaticCastAST.py @@ -1,3 +1,15 @@ +# Copyright (c) 2023 Arm Limited +# All rights reserved. +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# # Copyright (c) 2009 Advanced Micro Devices, Inc. # All rights reserved. # @@ -42,22 +54,9 @@ class StaticCastAST(ExprAST): actual_type, ecode = self.expr_ast.inline(True) if self.type_modifier == "pointer": code("static_cast<${{self.type_ast.type.c_ident}} *>($ecode)") + elif self.type_modifier == "value": + code("static_cast<${{self.type_ast.type.c_ident}} >($ecode)") else: code("static_cast<${{self.type_ast.type.c_ident}} &>($ecode)") - if not "interface" in self.type_ast.type: - self.expr_ast.error( - "static cast only premitted for those types " - "that implement inherit an interface" - ) - - # The interface type should match - if str(actual_type) != str(self.type_ast.type["interface"]): - self.expr_ast.error( - "static cast miss-match, type is '%s'," - "but inherited type is '%s'", - actual_type, - self.type_ast.type["interface"], - ) - return self.type_ast.type diff --git a/src/mem/slicc/ast/TypeAST.py b/src/mem/slicc/ast/TypeAST.py index 92c3190fe2..7da9a21593 100644 --- a/src/mem/slicc/ast/TypeAST.py +++ b/src/mem/slicc/ast/TypeAST.py @@ -26,7 +26,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from slicc.ast.AST import AST - from slicc.symbols import Type diff --git a/src/mem/slicc/ast/TypeDeclAST.py b/src/mem/slicc/ast/TypeDeclAST.py index d39e678477..bef49b484c 100644 --- a/src/mem/slicc/ast/TypeDeclAST.py +++ b/src/mem/slicc/ast/TypeDeclAST.py @@ -47,7 +47,7 @@ class TypeDeclAST(DeclAST): ident = f"{parent}_{self.type_ast.ident}" else: ident = self.type_ast.ident - return set((f"{ident}.hh", f"{ident}.cc")) + return {f"{ident}.hh", f"{ident}.cc"} def generate(self): ident = str(self.type_ast) diff --git a/src/mem/slicc/ast/TypeFieldEnumAST.py b/src/mem/slicc/ast/TypeFieldEnumAST.py index 68dd0cd0fa..6a8fe2f191 100644 --- a/src/mem/slicc/ast/TypeFieldEnumAST.py +++ b/src/mem/slicc/ast/TypeFieldEnumAST.py @@ -26,7 +26,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from slicc.ast.TypeFieldAST import TypeFieldAST -from slicc.symbols import Event, State, RequestType +from slicc.symbols import ( + Event, + RequestType, + State, +) class TypeFieldEnumAST(TypeFieldAST): diff --git a/src/mem/slicc/ast/TypeFieldStateAST.py b/src/mem/slicc/ast/TypeFieldStateAST.py index b04a708f21..4bef5136d4 100644 --- a/src/mem/slicc/ast/TypeFieldStateAST.py +++ b/src/mem/slicc/ast/TypeFieldStateAST.py @@ -25,7 +25,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from slicc.ast.TypeFieldAST import TypeFieldAST -from slicc.symbols import Event, State +from slicc.symbols import ( + Event, + State, +) class TypeFieldStateAST(TypeFieldAST): diff --git a/src/mem/slicc/ast/VarExprAST.py b/src/mem/slicc/ast/VarExprAST.py index 3c4023e8fe..8431dd132c 100644 --- a/src/mem/slicc/ast/VarExprAST.py +++ b/src/mem/slicc/ast/VarExprAST.py @@ -27,7 +27,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from slicc.ast.ExprAST import ExprAST -from slicc.symbols import Type, Var +from slicc.symbols import ( + Type, + Var, +) class VarExprAST(ExprAST): diff --git a/src/mem/slicc/ast/__init__.py b/src/mem/slicc/ast/__init__.py index 247546fb8f..86f9b09479 100644 --- a/src/mem/slicc/ast/__init__.py +++ b/src/mem/slicc/ast/__init__.py @@ -36,13 +36,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from slicc.ast.AST import * - # actual ASTs from slicc.ast.ActionDeclAST import * from slicc.ast.AssignStatementAST import * +from slicc.ast.AST import * from slicc.ast.CheckAllocateStatementAST import * from slicc.ast.CheckNextCycleAST import * +from slicc.ast.CheckProbeStatementAST import * from slicc.ast.DeclAST import * from slicc.ast.DeclListAST import * from slicc.ast.DeferEnqueueingStatementAST import * @@ -63,8 +63,8 @@ from slicc.ast.MachineAST import * from slicc.ast.MemberExprAST import * from slicc.ast.MethodCallExprAST import * from slicc.ast.NewExprAST import * -from slicc.ast.OodAST import * from slicc.ast.ObjDeclAST import * +from slicc.ast.OodAST import * from slicc.ast.OperatorExprAST import * from slicc.ast.OutPortDeclAST import * from slicc.ast.PairAST import * @@ -72,7 +72,6 @@ from slicc.ast.PairListAST import * from slicc.ast.PeekStatementAST import * from slicc.ast.ReturnStatementAST import * from slicc.ast.StallAndWaitStatementAST import * -from slicc.ast.WakeupPortStatementAST import * from slicc.ast.StateDeclAST import * from slicc.ast.StatementAST import * from slicc.ast.StatementListAST import * @@ -84,4 +83,4 @@ from slicc.ast.TypeFieldAST import * from slicc.ast.TypeFieldEnumAST import * from slicc.ast.TypeFieldStateAST import * from slicc.ast.VarExprAST import * -from slicc.ast.CheckProbeStatementAST import * +from slicc.ast.WakeupPortStatementAST import * diff --git a/src/mem/slicc/parser.py b/src/mem/slicc/parser.py index 155eb07f7a..dba82d8480 100644 --- a/src/mem/slicc/parser.py +++ b/src/mem/slicc/parser.py @@ -42,7 +42,10 @@ import re import sys from code_formatter import code_formatter -from grammar import Grammar, ParseError +from grammar import ( + Grammar, + ParseError, +) import slicc.ast as ast import slicc.util as util @@ -86,7 +89,7 @@ class SLICC(Grammar): self.symtab.writeHTMLFiles(html_path) def files(self): - f = set(["Types.hh"]) + f = {"Types.hh"} f |= self.decl_list.files() @@ -284,7 +287,7 @@ class SLICC(Grammar): def p_decl__protocol(self, p): "decl : PROTOCOL STRING SEMI" if self.protocol: - msg = "Protocol can only be set once! Error at %s:%s\n" % ( + msg = "Protocol can only be set once! Error at {}:{}\n".format( self.current_source, self.current_line, ) @@ -633,11 +636,15 @@ class SLICC(Grammar): def p_statement__enqueue(self, p): "statement : ENQUEUE '(' var ',' type ')' statements" - p[0] = ast.EnqueueStatementAST(self, p[3], p[5], None, p[7]) + p[0] = ast.EnqueueStatementAST(self, p[3], p[5], None, None, p[7]) def p_statement__enqueue_latency(self, p): "statement : ENQUEUE '(' var ',' type ',' expr ')' statements" - p[0] = ast.EnqueueStatementAST(self, p[3], p[5], p[7], p[9]) + p[0] = ast.EnqueueStatementAST(self, p[3], p[5], p[7], None, p[9]) + + def p_statement__enqueue_latency_bypass_strict_fifo(self, p): + "statement : ENQUEUE '(' var ',' type ',' expr ',' expr ')' statements" + p[0] = ast.EnqueueStatementAST(self, p[3], p[5], p[7], p[9], p[11]) def p_statement__defer_enqueueing(self, p): "statement : DEFER_ENQUEUEING '(' var ',' type ')' statements" diff --git a/src/mem/slicc/symbols/StateMachine.py b/src/mem/slicc/symbols/StateMachine.py index 4712064089..b523522501 100644 --- a/src/mem/slicc/symbols/StateMachine.py +++ b/src/mem/slicc/symbols/StateMachine.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019-2021 ARM Limited +# Copyright (c) 2019-2021,2023 ARM Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -38,12 +38,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import re from collections import OrderedDict +import slicc.generate.html as html from slicc.symbols.Symbol import Symbol from slicc.symbols.Var import Var -import slicc.generate.html as html -import re python_class_map = { "int": "Int", @@ -63,7 +63,9 @@ python_class_map = { "MessageBuffer": "MessageBuffer", "DMASequencer": "DMASequencer", "RubyPrefetcher": "RubyPrefetcher", + "prefetch::Base": "BasePrefetcher", "Cycles": "Cycles", + "Addr": "Addr", } @@ -111,8 +113,11 @@ class StateMachine(Symbol): self.actions = OrderedDict() self.request_types = OrderedDict() self.transitions = [] + self.transitions_per_ev = {} self.in_ports = [] self.functions = [] + self.event_stats_in_trans = [] + self.event_stats_out_trans = [] # Data members in the State Machine that have been declared inside # the {} machine. Note that these along with the config params @@ -136,6 +141,10 @@ class StateMachine(Symbol): def addEvent(self, event): assert self.table is None self.events[event.ident] = event + if "in_trans" in event.pairs: + self.event_stats_in_trans.append(event) + if "out_trans" in event.pairs: + self.event_stats_out_trans.append(event) def addAction(self, action): assert self.table is None @@ -163,6 +172,9 @@ class StateMachine(Symbol): def addTransition(self, trans): assert self.table is None self.transitions.append(trans) + if trans.event not in self.transitions_per_ev: + self.transitions_per_ev[trans.event] = [] + self.transitions_per_ev[trans.event].append(trans) def addInPort(self, var): self.in_ports.append(var) @@ -957,53 +969,91 @@ $c_ident::regStats() } } - for (${ident}_Event event = ${ident}_Event_FIRST; - event < ${ident}_Event_NUM; ++event) { +""" + ) + # check if Events/States have profiling qualifiers flags for + # inTransLatHist and outTransLatHist stats. + ev_ident_list = [ + f"{ident}_Event_{ev.ident}" for ev in self.event_stats_out_trans + ] + ev_ident_str = "{" + ",".join(ev_ident_list) + "}" + code( + """ + const std::vector<${ident}_Event> out_trans_evs = ${ev_ident_str}; +""" + ) + ev_ident_list = [ + f"{ident}_Event_{ev.ident}" for ev in self.event_stats_in_trans + ] + ev_ident_str = "{" + ",".join(ev_ident_list) + "}" + code( + """ + const std::vector<${ident}_Event> in_trans_evs = ${ev_ident_str}; +""" + ) + kv_ident_list = [] + for ev in self.event_stats_in_trans: + key_ident = f"{ident}_Event_{ev.ident}" + val_ident_lst = [ + f"{ident}_State_{trans.state.ident}" + for trans in self.transitions_per_ev[ev] + ] + val_ident_str = "{" + ",".join(val_ident_lst) + "}" + kv_ident_list.append(f"{{{key_ident}, {val_ident_str}}}") + key_ident_str = "{" + ",".join(kv_ident_list) + "}" + code( + """ + const std::unordered_map<${ident}_Event, std::vector<${ident}_State>> + in_trans_evs_states = ${key_ident_str}; +""" + ) + code( + """ + + for (const auto event : out_trans_evs) { std::string stat_name = "outTransLatHist." + ${ident}_Event_to_string(event); statistics::Histogram* t = new statistics::Histogram(&stats, stat_name.c_str()); - stats.outTransLatHist.push_back(t); + stats.outTransLatHist[event] = t; t->init(5); t->flags(statistics::pdf | statistics::total | statistics::oneline | statistics::nozero); statistics::Scalar* r = new statistics::Scalar(&stats, (stat_name + ".retries").c_str()); - stats.outTransLatHistRetries.push_back(r); + stats.outTransRetryCnt[event] = r; r->flags(statistics::nozero); } - for (${ident}_Event event = ${ident}_Event_FIRST; - event < ${ident}_Event_NUM; ++event) { - std::string stat_name = "inTransLatHist." + - ${ident}_Event_to_string(event); + for (const auto event : in_trans_evs) { + std::string stat_name = + "inTransLatHist." + ${ident}_Event_to_string(event); + statistics::Histogram* t = + new statistics::Histogram(&stats, stat_name.c_str()); + stats.inTransLatHist[event] = t; + t->init(5); + t->flags(statistics::pdf | statistics::total | + statistics::oneline | statistics::nozero); + statistics::Scalar* r = new statistics::Scalar(&stats, - (stat_name + ".total").c_str()); - stats.inTransLatTotal.push_back(r); + (stat_name + ".retries").c_str()); + stats.inTransRetryCnt[event] = r; r->flags(statistics::nozero); - r = new statistics::Scalar(&stats, - (stat_name + ".retries").c_str()); - stats.inTransLatRetries.push_back(r); - r->flags(statistics::nozero); - - stats.inTransLatHist.emplace_back(); - for (${ident}_State initial_state = ${ident}_State_FIRST; - initial_state < ${ident}_State_NUM; ++initial_state) { - stats.inTransLatHist.back().emplace_back(); + auto &src_states = stats.inTransStateChanges[event]; + for (const auto initial_state : in_trans_evs_states.at(event)) { + auto &dst_vector = src_states[initial_state]; for (${ident}_State final_state = ${ident}_State_FIRST; final_state < ${ident}_State_NUM; ++final_state) { std::string stat_name = "inTransLatHist." + ${ident}_Event_to_string(event) + "." + ${ident}_State_to_string(initial_state) + "." + - ${ident}_State_to_string(final_state); - statistics::Histogram* t = - new statistics::Histogram(&stats, stat_name.c_str()); - stats.inTransLatHist.back().back().push_back(t); - t->init(5); - t->flags(statistics::pdf | statistics::total | - statistics::oneline | statistics::nozero); + ${ident}_State_to_string(final_state) + ".total"; + statistics::Scalar* t = + new statistics::Scalar(&stats, stat_name.c_str()); + t->flags(statistics::nozero); + dst_vector.push_back(t); } } } @@ -1683,7 +1733,7 @@ ${ident}_Controller::doTransitionWorker(${ident}_Event event, cases = OrderedDict() for trans in self.transitions: - case_string = "%s_State_%s, %s_Event_%s" % ( + case_string = "{}_State_{}, {}_Event_{}".format( self.ident, trans.state.ident, self.ident, @@ -1727,10 +1777,10 @@ if (!{key.code}.areNSlotsAvailable({val}, clockEdge())) # Check all of the request_types for resource constraints for request_type in request_types: val = """ -if (!checkResourceAvailable(%s_RequestType_%s, addr)) { +if (!checkResourceAvailable({}_RequestType_{}, addr)) {{ return TransitionResult_ResourceStall; -} -""" % ( +}} +""".format( self.ident, request_type.ident, ) diff --git a/src/mem/slicc/symbols/Symbol.py b/src/mem/slicc/symbols/Symbol.py index 74863724d2..261aac5720 100644 --- a/src/mem/slicc/symbols/Symbol.py +++ b/src/mem/slicc/symbols/Symbol.py @@ -44,8 +44,8 @@ class Symbol(PairContainer): def __init__(self, symtab, ident, location, pairs=None): super().__init__() - from slicc.util import Location from slicc.symbols import SymbolTable + from slicc.util import Location if not isinstance(symtab, SymbolTable): raise AttributeError diff --git a/src/mem/slicc/symbols/SymbolTable.py b/src/mem/slicc/symbols/SymbolTable.py index d2fbf8f7a9..5ea18c6d62 100644 --- a/src/mem/slicc/symbols/SymbolTable.py +++ b/src/mem/slicc/symbols/SymbolTable.py @@ -43,7 +43,7 @@ def makeDir(path): os.makedirs(path, exist_ok=True) -class SymbolTable(object): +class SymbolTable: def __init__(self, slicc): self.slicc = slicc diff --git a/src/mem/slicc/symbols/Transition.py b/src/mem/slicc/symbols/Transition.py index b517cf4d44..67e05c6997 100644 --- a/src/mem/slicc/symbols/Transition.py +++ b/src/mem/slicc/symbols/Transition.py @@ -25,8 +25,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from slicc.symbols.Symbol import Symbol from slicc.symbols.State import WildcardState +from slicc.symbols.Symbol import Symbol class Transition(Symbol): @@ -72,7 +72,7 @@ class Transition(Symbol): self.resources[var] = str(num) def __repr__(self): - return "[Transition: (%r, %r) -> %r, %r]" % ( + return "[Transition: ({!r}, {!r}) -> {!r}, {!r}]".format( self.state, self.event, self.nextState, diff --git a/src/mem/slicc/symbols/Type.py b/src/mem/slicc/symbols/Type.py index 7010461e0c..535a4165b3 100644 --- a/src/mem/slicc/symbols/Type.py +++ b/src/mem/slicc/symbols/Type.py @@ -39,9 +39,9 @@ from collections import OrderedDict -from slicc.util import PairContainer from slicc.symbols.Symbol import Symbol from slicc.symbols.Var import Var +from slicc.util import PairContainer class DataMember(Var): diff --git a/src/mem/slicc/symbols/__init__.py b/src/mem/slicc/symbols/__init__.py index e4ce4826fa..58e039f988 100644 --- a/src/mem/slicc/symbols/__init__.py +++ b/src/mem/slicc/symbols/__init__.py @@ -27,8 +27,8 @@ from slicc.symbols.Action import Action from slicc.symbols.Event import Event from slicc.symbols.Func import Func -from slicc.symbols.State import State from slicc.symbols.RequestType import RequestType +from slicc.symbols.State import State from slicc.symbols.StateMachine import StateMachine from slicc.symbols.Symbol import Symbol from slicc.symbols.SymbolTable import SymbolTable diff --git a/src/mem/slicc/util.py b/src/mem/slicc/util.py index 3bb4131a01..bcbf057170 100644 --- a/src/mem/slicc/util.py +++ b/src/mem/slicc/util.py @@ -28,7 +28,7 @@ import os import sys -class PairContainer(object): +class PairContainer: def __init__(self, pairs=None): self.pairs = {} if pairs: @@ -47,7 +47,7 @@ class PairContainer(object): return self.pairs.get(item, failobj) -class Location(object): +class Location: def __init__(self, filename, lineno, no_warning=False): if not isinstance(filename, str): raise AttributeError( diff --git a/src/mem/snoop_filter.hh b/src/mem/snoop_filter.hh index 7d4a222874..23cf77fcdd 100644 --- a/src/mem/snoop_filter.hh +++ b/src/mem/snoop_filter.hh @@ -302,7 +302,7 @@ class SnoopFilter : public SimObject /** Track the mapping from port ids to the local mask ids. */ std::vector localResponsePortIds; /** Cache line size. */ - const unsigned linesize; + const Addr linesize; /** Latency for doing a lookup in the filter */ const Cycles lookupLatency; /** Max capacity in terms of cache blocks tracked, for sanity checking */ diff --git a/src/mem/translating_port_proxy.cc b/src/mem/translating_port_proxy.cc index 8ab859f40d..8daa390d80 100644 --- a/src/mem/translating_port_proxy.cc +++ b/src/mem/translating_port_proxy.cc @@ -86,7 +86,7 @@ TranslatingPortProxy::tryOnBlob(BaseMMU::Mode mode, TranslationGenPtr gen, } bool -TranslatingPortProxy::tryReadBlob(Addr addr, void *p, int size) const +TranslatingPortProxy::tryReadBlob(Addr addr, void *p, uint64_t size) const { constexpr auto mode = BaseMMU::Read; return tryOnBlob(mode, _tc->getMMUPtr()->translateFunctional( @@ -99,7 +99,7 @@ TranslatingPortProxy::tryReadBlob(Addr addr, void *p, int size) const bool TranslatingPortProxy::tryWriteBlob( - Addr addr, const void *p, int size) const + Addr addr, const void *p, uint64_t size) const { constexpr auto mode = BaseMMU::Write; return tryOnBlob(mode, _tc->getMMUPtr()->translateFunctional( @@ -111,7 +111,7 @@ TranslatingPortProxy::tryWriteBlob( } bool -TranslatingPortProxy::tryMemsetBlob(Addr addr, uint8_t v, int size) const +TranslatingPortProxy::tryMemsetBlob(Addr addr, uint8_t v, uint64_t size) const { constexpr auto mode = BaseMMU::Write; return tryOnBlob(mode, _tc->getMMUPtr()->translateFunctional( diff --git a/src/mem/translating_port_proxy.hh b/src/mem/translating_port_proxy.hh index bedb57a3ce..9e60a858b9 100644 --- a/src/mem/translating_port_proxy.hh +++ b/src/mem/translating_port_proxy.hh @@ -77,16 +77,16 @@ class TranslatingPortProxy : public PortProxy /** Version of tryReadblob that translates virt->phys and deals * with page boundries. */ - bool tryReadBlob(Addr addr, void *p, int size) const override; + bool tryReadBlob(Addr addr, void *p, uint64_t size) const override; /** Version of tryWriteBlob that translates virt->phys and deals * with page boundries. */ - bool tryWriteBlob(Addr addr, const void *p, int size) const override; + bool tryWriteBlob(Addr addr, const void *p, uint64_t size) const override; /** * Fill size bytes starting at addr with byte value val. */ - bool tryMemsetBlob(Addr address, uint8_t v, int size) const override; + bool tryMemsetBlob(Addr address, uint8_t v, uint64_t size) const override; }; } // namespace gem5 diff --git a/src/mem/xbar.cc b/src/mem/xbar.cc index e1b2a8b521..0d4b2fca97 100644 --- a/src/mem/xbar.cc +++ b/src/mem/xbar.cc @@ -45,6 +45,9 @@ #include "mem/xbar.hh" +#include +#include + #include "base/logging.hh" #include "base/trace.hh" #include "debug/AddrRanges.hh" @@ -328,7 +331,7 @@ BaseXBar::Layer::recvRetry() } PortID -BaseXBar::findPort(AddrRange addr_range) +BaseXBar::findPort(AddrRange addr_range, PacketPtr pkt) { // we should never see any address lookups before we've got the // ranges of all connected CPU-side-port modules @@ -353,10 +356,17 @@ BaseXBar::findPort(AddrRange addr_range) return defaultPortID; } - // we should use the range for the default port and it did not - // match, or the default port is not set - fatal("Unable to find destination for %s on %s\n", addr_range.to_string(), - name()); + // We should use the range for the default port and it did not match, + // or the default port is not set. Dump out the port trace if possible. + std::string port_trace = ""; + if (pkt) { + std::shared_ptr ext = + pkt->getExtension(); + port_trace = ext ? ext->getTraceInString() : + "Use --debug-flags=PortTrace to see the port trace of the packet."; + } + fatal("Unable to find destination for %s on %s\n%s\n", + addr_range.to_string(), name(), port_trace); } /** Function called by the port when the crossbar is receiving a range change.*/ diff --git a/src/mem/xbar.hh b/src/mem/xbar.hh index 1df8b7ea13..b7f763a15a 100644 --- a/src/mem/xbar.hh +++ b/src/mem/xbar.hh @@ -344,9 +344,16 @@ class BaseXBar : public ClockedObject * given a packet with this address range. * * @param addr_range Address range to find port for. - * @return id of port that the packet should be sent out of. + * @param pkt Packet that containing the address range. + * @return id of port that the packet should be sent ou of. */ - PortID findPort(AddrRange addr_range); + PortID findPort(AddrRange addr_range, PacketPtr pkt=nullptr); + + PortID + findPort(PacketPtr pkt) + { + return findPort(pkt->getAddrRange(), pkt); + } /** * Return the address ranges the crossbar is responsible for. diff --git a/src/proto/Kconfig b/src/proto/Kconfig new file mode 100644 index 0000000000..a497d6e2de --- /dev/null +++ b/src/proto/Kconfig @@ -0,0 +1,27 @@ +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +config HAVE_PROTOBUF + def_bool $(HAVE_PROTOBUF) diff --git a/src/proto/SConsopts b/src/proto/SConsopts index 6b5b25d0ce..6cc4a48642 100644 --- a/src/proto/SConsopts +++ b/src/proto/SConsopts @@ -50,14 +50,6 @@ with gem5_scons.Configure(main) as conf: warning('protoc version', min_protoc_version, 'or newer required.\n' 'Installed version:', protoc_version[1]) else: - # Attempt to determine the appropriate include path and - # library path using pkg-config, that means we also need to - # check for pkg-config. Note that it is possible to use - # protobuf without the involvement of pkg-config. Later on we - # check go a library config check and at that point the test - # will fail if libprotobuf cannot be found. - if conf.env['HAVE_PKG_CONFIG']: - conf.CheckPkgConfig('protobuf', '--cflags', '--libs-only-L') conf.env['HAVE_PROTOC'] = True # If we have the protobuf compiler, also make sure we have the @@ -65,9 +57,13 @@ with gem5_scons.Configure(main) as conf: # automatically added to the LIBS environment variable. After # this, we can use the HAVE_PROTOBUF flag to determine if we have # got both protoc and libprotobuf available. - conf.env['CONF']['HAVE_PROTOBUF'] = conf.env['HAVE_PROTOC'] and \ - conf.CheckLibWithHeader('protobuf', 'google/protobuf/message.h', - 'C++', 'GOOGLE_PROTOBUF_VERIFY_VERSION;') + conf.env['CONF']['HAVE_PROTOBUF'] = bool( + conf.env['HAVE_PROTOC'] and ( + (conf.env['HAVE_PKG_CONFIG'] and + conf.CheckPkgConfig('protobuf', '--cflags', '--libs')) or + conf.CheckLibWithHeader('protobuf', 'google/protobuf/message.h', + 'C++', 'GOOGLE_PROTOBUF_VERIFY_VERSION;')) + ) # If we have the compiler but not the library, print another warning. if main['HAVE_PROTOC'] and not main['CONF']['HAVE_PROTOBUF']: diff --git a/src/python/SConscript b/src/python/SConscript index ea9d3d4021..eaaea203f0 100644 --- a/src/python/SConscript +++ b/src/python/SConscript @@ -311,6 +311,7 @@ PySource('gem5.utils.multiprocessing', PySource('', 'importer.py') PySource('m5', 'm5/__init__.py') PySource('m5', 'm5/SimObject.py') +PySource('m5', 'm5/citations.py') PySource('m5', 'm5/core.py') PySource('m5', 'm5/debug.py') PySource('m5', 'm5/event.py') diff --git a/src/python/gem5/components/boards/abstract_board.py b/src/python/gem5/components/boards/abstract_board.py index 4ea8866009..d3c01e21f8 100644 --- a/src/python/gem5/components/boards/abstract_board.py +++ b/src/python/gem5/components/boards/abstract_board.py @@ -24,23 +24,33 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from abc import ABCMeta, abstractmethod import inspect - -from .mem_mode import MemMode, mem_mode_to_string -from ...resources.workload import AbstractWorkload +from abc import ( + ABCMeta, + abstractmethod, +) +from typing import ( + List, + Optional, + Sequence, + Tuple, +) from m5.objects import ( AddrRange, - System, - Port, - IOXBar, ClockDomain, + IOXBar, + Port, SrcClockDomain, + System, VoltageDomain, ) -from typing import List, Optional, Sequence, Tuple +from ...resources.resource import WorkloadResource +from .mem_mode import ( + MemMode, + mem_mode_to_string, +) class AbstractBoard: @@ -49,7 +59,7 @@ class AbstractBoard: Boards are used as the object which can connect together all other components. This abstract class defines the external interface that other boards must provide. Boards can be specialized for different ISAs or system - designs (e.g., core counts, cache types, memory channels, I/O devices, etc) + designs (e.g., core counts, cache types, memory channels, I/O devices, etc). In addition to providing the place that system components are connected, the board also exposes an interface for the caches, processor, and memory @@ -58,7 +68,7 @@ class AbstractBoard: The board also exposes an interface to set up I/O devices which needs to be specialized for each ISA and/or platform. - Board inherits from System and can therefore be used as a System simobject + Board inherits from System and can therefore be used as a System SimObject when required. """ @@ -77,7 +87,7 @@ class AbstractBoard: :param memory: The memory for this board. :param cache_hierarchy: The Cache Hierarchy for this board. In some boards caches can be optional. If so, - that board must override `_connect_things`. + that board must override ``_connect_things``. """ if not isinstance(self, System): @@ -133,8 +143,10 @@ class AbstractBoard: def get_mem_ports(self) -> Sequence[Tuple[AddrRange, Port]]: """Get the memory ports exposed on this board - Note: The ports should be returned such that the address ranges are - in ascending order. + .. note:: + + The ports should be returned such that the address ranges are + in ascending order. """ return self.get_memory().get_mem_ports() @@ -165,6 +177,7 @@ class AbstractBoard: def get_clock_domain(self) -> ClockDomain: """Get the clock domain. + :returns: The clock domain. """ return self.clk_domain @@ -182,7 +195,7 @@ class AbstractBoard: def is_fullsystem(self) -> bool: """ - Returns True if the board is to be run in FS mode. Otherwise the board + Returns ``True`` if the board is to be run in FS mode. Otherwise the board is to be run in Se mode. An exception will be thrown if this has not been set. @@ -198,15 +211,15 @@ class AbstractBoard: ) return self._is_fs - def set_workload(self, workload: AbstractWorkload) -> None: + def set_workload(self, workload: WorkloadResource) -> None: """ Set the workload for this board to run. This function will take the workload specified and run the correct - workload function (e.g., `set_kernel_disk_workload`) with the correct + workload function (e.g., ``set_kernel_disk_workload``) with the correct parameters - :params workload: The workload to be set to this board. + :param workload: The workload to be set to this board. """ try: @@ -234,7 +247,7 @@ class AbstractBoard: """ This function is called in the AbstractBoard constructor, before the memory, processor, and cache hierarchy components are incorporated via - `_connect_thing()`, but after the `_setup_memory_ranges()` function. + ``_connect_thing()``, but after the ``_setup_memory_ranges()`` function. This function should be overridden by boards to specify components, connections unique to that board. """ @@ -247,7 +260,7 @@ class AbstractBoard: def has_dma_ports(self) -> bool: """Determine whether the board has DMA ports or not. - :returns: True if the board has DMA ports, otherwise False. + :returns: ``True`` if the board has DMA ports, otherwise ``False``. """ raise NotImplementedError @@ -266,13 +279,14 @@ class AbstractBoard: def has_io_bus(self) -> bool: """Determine whether the board has an IO bus or not. - :returns: True if the board has an IO bus, otherwise False. + :returns: ``True`` if the board has an IO bus, otherwise ``False``. """ raise NotImplementedError @abstractmethod def get_io_bus(self) -> IOXBar: """Get the board's IO Bus. + This abstract method must be implemented within the subclasses if they support DMA and/or full system simulation. @@ -289,14 +303,15 @@ class AbstractBoard: def has_coherent_io(self) -> bool: """Determine whether the board needs coherent I/O - :returns: True if the board needs coherent I/O, false otherwise + :returns: ``True`` if the board needs coherent I/O, ``False`` otherwise. """ raise NotImplementedError @abstractmethod def get_mem_side_coherent_io_port(self): """Get the memory-side coherent I/O port. - This abstract method must be implemented if has_coherent_io is true. + + This abstract method must be implemented if ``has_coherent_io`` is ``True``. This returns a *port* (not a bus) that should be connected to a CPU-side port for which coherent I/O (DMA) is issued. @@ -308,8 +323,8 @@ class AbstractBoard: """ Set the memory ranges for this board and memory system. - This is called in the constructor, prior to `_setup_board` and - `_connect_things`. It should query the board's memory to determine the + This is called in the constructor, prior to ``_setup_board`` and + ``_connect_things``. It should query the board's memory to determine the size and the set the memory ranges on the memory system and on the board. @@ -317,11 +332,11 @@ class AbstractBoard: of memory and memory system's range to be the same as the board. Full system implementations will likely need something more complicated. - Notes - ----- - * This *must* be called prior to the incorporation of the cache - hierarchy (via `_connect_things`) as cache hierarchies depend upon - knowing the memory system's ranges. + .. note:: + + This *must* be called prior to the incorporation of the cache + hierarchy (via ``_connect_things``) as cache hierarchies depend upon + knowing the memory system's ranges. """ raise NotImplementedError @@ -336,14 +351,13 @@ class AbstractBoard: Developers may build upon this assumption when creating components. - Notes - ----- + .. note:: - * The processor is incorporated after the cache hierarchy due to a bug - noted here: https://gem5.atlassian.net/browse/GEM5-1113. Until this - bug is fixed, this ordering must be maintained. - * Once this function is called `_connect_things_called` *must* be set - to `True`. + * The processor is incorporated after the cache hierarchy due to a bug + noted here: https://gem5.atlassian.net/browse/GEM5-1113. Until this + bug is fixed, this ordering must be maintained. + * Once this function is called ``_connect_things_called`` *must* be set + to ``True``. """ if self._connect_things_called: @@ -364,15 +378,15 @@ class AbstractBoard: self._connect_things_called = True def _post_instantiate(self): - """Called to set up anything needed after m5.instantiate""" + """Called to set up anything needed after ``m5.instantiate``.""" self.get_processor()._post_instantiate() if self.get_cache_hierarchy(): self.get_cache_hierarchy()._post_instantiate() self.get_memory()._post_instantiate() def _pre_instantiate(self): - """To be called immediately before m5.instantiate. This is where - `_connect_things` is executed by default.""" + """To be called immediately before ``m5.instantiate``. This is where + ``_connect_things`` is executed by default.""" # Connect the memory, processor, and cache hierarchy. self._connect_things() @@ -382,28 +396,28 @@ class AbstractBoard: Here we check that connect things has been called and throw an Exception if it has not. - Since v22.1 `_connect_things` function has + Since v22.1 ``_connect_things`` function has been moved from the AbstractBoard constructor to the - `_pre_instantation` function. Users who have used the gem5 stdlib + ``_pre_instantation`` function. Users who have used the gem5 stdlib components (i.e., boards which inherit from AbstractBoard) and the Simulator module should notice no change. Those who do not use the - Simulator module and instead called `m5.instantiate` directly must - call `AbstractBoard._pre_instantation` prior so `_connect_things` is + Simulator module and instead called ``m5.instantiate`` directly must + call ``AbstractBoard._pre_instantation`` prior to ``_connect_things`` is called. In order to avoid confusion, this check has been incorporated and the Exception thrown explains the fix needed to convert old scripts - to function with v22.1. + to function with `v22.1`. - This function is called in `AbstractSystemBoard.createCCObject` and - ArmBoard.createCCObject`. Both these functions override - `SimObject.createCCObject`. We can not do that here as AbstractBoard + This function is called in ``AbstractSystemBoard.createCCObject`` and + ``ArmBoard.createCCObject``. Both these functions override + ``SimObject.createCCObject``. We can not do that here as AbstractBoard does not inherit form System. """ if not self._connect_things_called: raise Exception( """ -AbstractBoard's `_connect_things` function has not been called. This is likely +AbstractBoard's ``_connect_things`` function has not been called. This is likely due to not running a board outside of the gem5 Standard Library Simulator module. If this is the case, this can be resolved by calling -`._pre_instantiate()` prior to `m5.instantiate()`. +``._pre_instantiate()`` prior to ``m5.instantiate()``. """ ) diff --git a/src/python/gem5/components/boards/abstract_system_board.py b/src/python/gem5/components/boards/abstract_system_board.py index 2812b37260..8fe48920b5 100644 --- a/src/python/gem5/components/boards/abstract_system_board.py +++ b/src/python/gem5/components/boards/abstract_system_board.py @@ -26,10 +26,13 @@ from abc import ABCMeta -from .abstract_board import AbstractBoard -from ...utils.override import overrides +from m5.objects import ( + SimObject, + System, +) -from m5.objects import System, SimObject +from ...utils.override import overrides +from .abstract_board import AbstractBoard class AbstractSystemBoard(System, AbstractBoard): @@ -58,8 +61,8 @@ class AbstractSystemBoard(System, AbstractBoard): @overrides(SimObject) def createCCObject(self): - """We override this function as it is called in `m5.instantiate`. This - means we can insert a check to ensure the `_connect_things` function + """We override this function as it is called in ``m5.instantiate``. This + means we can insert a check to ensure the ``_connect_things`` function has been run. """ super()._connect_things_check() diff --git a/src/python/gem5/components/boards/arm_board.py b/src/python/gem5/components/boards/arm_board.py index b439edf970..c60761c16d 100644 --- a/src/python/gem5/components/boards/arm_board.py +++ b/src/python/gem5/components/boards/arm_board.py @@ -24,43 +24,48 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import ( - Port, - IOXBar, - Bridge, - BadAddr, - Terminal, - PciVirtIO, - VncServer, - AddrRange, - ArmSystem, - ArmRelease, - ArmFsLinux, - VirtIOBlock, - CowDiskImage, - RawDiskImage, - VoltageDomain, - SrcClockDomain, - ArmDefaultRelease, - VExpress_GEM5_Base, - VExpress_GEM5_Foundation, - SimObject, +import os +from abc import ABCMeta +from typing import ( + List, + Sequence, + Tuple, ) -import os import m5 -from abc import ABCMeta +from m5.objects import ( + AddrRange, + ArmDefaultRelease, + ArmFsLinux, + ArmRelease, + ArmSystem, + BadAddr, + Bridge, + CowDiskImage, + IOXBar, + PciVirtIO, + Port, + RawDiskImage, + SimObject, + SrcClockDomain, + Terminal, + VExpress_GEM5_Base, + VExpress_GEM5_Foundation, + VirtIOBlock, + VncServer, + VoltageDomain, +) + from ...isas import ISA -from ...utils.requires import requires -from ...utils.override import overrides -from typing import List, Sequence, Tuple -from .abstract_board import AbstractBoard from ...resources.resource import AbstractResource -from .kernel_disk_workload import KernelDiskWorkload -from ..cachehierarchies.classic.no_cache import NoCache -from ..processors.abstract_processor import AbstractProcessor -from ..memory.abstract_memory_system import AbstractMemorySystem +from ...utils.override import overrides +from ...utils.requires import requires from ..cachehierarchies.abstract_cache_hierarchy import AbstractCacheHierarchy +from ..cachehierarchies.classic.no_cache import NoCache +from ..memory.abstract_memory_system import AbstractMemorySystem +from ..processors.abstract_processor import AbstractProcessor +from .abstract_board import AbstractBoard +from .kernel_disk_workload import KernelDiskWorkload class ArmBoard(ArmSystem, AbstractBoard, KernelDiskWorkload): @@ -88,7 +93,6 @@ class ArmBoard(ArmSystem, AbstractBoard, KernelDiskWorkload): platform: VExpress_GEM5_Base = VExpress_GEM5_Foundation(), release: ArmRelease = ArmDefaultRelease(), ) -> None: - # The platform and the clk has to be set before calling the super class self._platform = platform self._clk_freq = clk_freq @@ -117,7 +121,6 @@ class ArmBoard(ArmSystem, AbstractBoard, KernelDiskWorkload): @overrides(AbstractBoard) def _setup_board(self) -> None: - # This board is expected to run full-system simulation. # Loading ArmFsLinux() from `src/arch/arm/ArmFsWorkload.py` self.workload = ArmFsLinux() @@ -191,7 +194,7 @@ class ArmBoard(ArmSystem, AbstractBoard, KernelDiskWorkload): def _setup_io_devices(self) -> None: """ - This method first sets up the platform. ARM uses `realview` platform. + This method first sets up the platform. ARM uses ``realview`` platform. Most of the on-chip and off-chip devices are setup by the realview platform. Once realview is setup, we connect the I/O devices to the I/O bus. @@ -213,7 +216,6 @@ class ArmBoard(ArmSystem, AbstractBoard, KernelDiskWorkload): # created. The IO device has to be attached first. This is done in the # realview class. if self.get_cache_hierarchy().is_ruby(): - # All the on-chip devices are attached in this method. self.realview.attachOnChipIO( self.iobus, @@ -332,16 +334,19 @@ class ArmBoard(ArmSystem, AbstractBoard, KernelDiskWorkload): self.generateDtb(self._get_dtb_filename()) def _get_dtb_filename(self) -> str: - """Returns the dtb file location. + """Returns the ``dtb`` file location. - **Note**: This may be the _expected_ file location when generated. A - file may not exist at this location when this function is called.""" + .. note:: + + This may be the ``_expected_`` file location when generated. A + file may not exist at this location when this function is called. + """ return os.path.join(m5.options.outdir, "device.dtb") def _add_pci_device(self, pci_device: PciVirtIO) -> None: """Attaches the PCI Device to the board. All devices will be added to - `self.pci_device` as a pre-instantiation setup. + ``self.pci_device`` as a pre-instantiation setup. :param pci_device: The PCI Device to add. """ @@ -359,7 +364,6 @@ class ArmBoard(ArmSystem, AbstractBoard, KernelDiskWorkload): @overrides(KernelDiskWorkload) def _add_disk_to_board(self, disk_image: AbstractResource): - self._image = CowDiskImage( child=RawDiskImage( read_only=True, image_file=disk_image.get_local_path() @@ -373,27 +377,27 @@ class ArmBoard(ArmSystem, AbstractBoard, KernelDiskWorkload): def _setup_memory_ranges(self) -> None: """ The ArmBoard's memory can only be setup after realview is setup. We set - this up in the `_setup_board` function. + this up in the ``_setup_board`` function. """ pass @overrides(KernelDiskWorkload) def get_default_kernel_args(self) -> List[str]: - # The default kernel string is taken from the devices.py file. return [ "console=ttyAMA0", "lpj=19988480", "norandmaps", "root={root_value}", + "disk_device={disk_device}", "rw", f"mem={self.get_memory().get_size()}", ] @overrides(SimObject) def createCCObject(self): - """We override this function as it is called in `m5.instantiate`. This - means we can insert a check to ensure the `_connect_things` function + """We override this function as it is called in ``m5.instantiate``. This + means we can insert a check to ensure the ``_connect_things`` function has been run. """ super()._connect_things_check() diff --git a/src/python/gem5/components/boards/experimental/lupv_board.py b/src/python/gem5/components/boards/experimental/lupv_board.py index ad130b7273..2448ede45e 100644 --- a/src/python/gem5/components/boards/experimental/lupv_board.py +++ b/src/python/gem5/components/boards/experimental/lupv_board.py @@ -27,42 +27,31 @@ import os from typing import List -from ....utils.override import overrides -from ..abstract_system_board import AbstractSystemBoard -from ...processors.abstract_processor import AbstractProcessor -from ...memory.abstract_memory_system import AbstractMemorySystem -from ...cachehierarchies.abstract_cache_hierarchy import AbstractCacheHierarchy -from ..kernel_disk_workload import KernelDiskWorkload -from ....resources.resource import AbstractResource -from ....isas import ISA - import m5 from m5.objects import ( - Bridge, - PMAChecker, - RiscvLinux, - RiscvRTC, AddrRange, - IOXBar, + Bridge, Clint, - Plic, - Terminal, + CowDiskImage, + Frequency, + IOXBar, LupioBLK, LupioIPI, LupioPIC, LupioRNG, LupioRTC, + LupioSYS, LupioTMR, LupioTTY, - LupioSYS, LupV, - AddrRange, - CowDiskImage, - RawDiskImage, - Frequency, + Plic, + PMAChecker, Port, + RawDiskImage, + RiscvLinux, + RiscvRTC, + Terminal, ) - from m5.util.fdthelper import ( Fdt, FdtNode, @@ -72,6 +61,15 @@ from m5.util.fdthelper import ( FdtState, ) +from ....isas import ISA +from ....resources.resource import AbstractResource +from ....utils.override import overrides +from ...cachehierarchies.abstract_cache_hierarchy import AbstractCacheHierarchy +from ...memory.abstract_memory_system import AbstractMemorySystem +from ...processors.abstract_processor import AbstractProcessor +from ..abstract_system_board import AbstractSystemBoard +from ..kernel_disk_workload import KernelDiskWorkload + class LupvBoard(AbstractSystemBoard, KernelDiskWorkload): """ @@ -90,9 +88,8 @@ class LupvBoard(AbstractSystemBoard, KernelDiskWorkload): memory: AbstractMemorySystem, cache_hierarchy: AbstractCacheHierarchy, ) -> None: - if cache_hierarchy.is_ruby(): - raise EnvironmentError("RiscvBoard is not compatible with Ruby") + raise OSError("RiscvBoard is not compatible with Ruby") if processor.get_isa() != ISA.RISCV: raise Exception( @@ -105,7 +102,6 @@ class LupvBoard(AbstractSystemBoard, KernelDiskWorkload): @overrides(AbstractSystemBoard) def _setup_board(self) -> None: - self.workload = RiscvLinux() # Initialize all the devices that we want to use on this board @@ -220,7 +216,7 @@ class LupvBoard(AbstractSystemBoard, KernelDiskWorkload): ] def _setup_io_devices(self) -> None: - """Connect the I/O devices to the I/O bus""" + """Connect the I/O devices to the I/O bus.""" for device in self._off_chip_devices: device.pio = self.iobus.mem_side_ports self.lupio_blk.dma = self.iobus.cpu_side_ports @@ -238,7 +234,7 @@ class LupvBoard(AbstractSystemBoard, KernelDiskWorkload): ] def _setup_pma(self) -> None: - """Set the PMA devices on each core""" + """Set the PMA devices on each core.""" uncacheable_range = [ AddrRange(dev.pio_addr, size=dev.pio_size) for dev in self._on_chip_devices + self._off_chip_devices @@ -282,9 +278,11 @@ class LupvBoard(AbstractSystemBoard, KernelDiskWorkload): memory.set_memory_range(self.mem_ranges) def _generate_device_tree(self, outdir: str) -> None: - """Creates the dtb and dts files. - Creates two files in the outdir: 'device.dtb' and 'device.dts' - :param outdir: Directory to output the files + """Creates the ``dtb`` and ``dts`` files. + + Creates two files in the outdir: ``device.dtb`` and ``device.dts``. + + :param outdir: Directory to output the files. """ state = FdtState(addr_cells=2, size_cells=2, cpu_cells=1) root = FdtNode("/") @@ -318,7 +316,7 @@ class LupvBoard(AbstractSystemBoard, KernelDiskWorkload): node.append(FdtPropertyWords("reg", state.CPUAddrCells(i))) node.append(FdtPropertyStrings("mmu-type", "riscv,sv48")) node.append(FdtPropertyStrings("status", "okay")) - node.append(FdtPropertyStrings("riscv,isa", "rv64imafdcsu")) + node.append(FdtPropertyStrings("riscv,isa", "rv64imafdc")) # TODO: Should probably get this from the core. freq = self.clk_domain.clock[0].frequency node.appendCompatible(["riscv"]) @@ -534,14 +532,19 @@ class LupvBoard(AbstractSystemBoard, KernelDiskWorkload): fdt.writeDtsFile(os.path.join(outdir, "device.dts")) fdt.writeDtbFile(os.path.join(outdir, "device.dtb")) - @overrides(KernelDiskWorkload) - def get_default_kernel_args(self) -> List[str]: - return ["console=ttyLIO0", "root={root_value}", "rw"] - @overrides(KernelDiskWorkload) def get_disk_device(self) -> str: return "/dev/lda" + @overrides(KernelDiskWorkload) + def get_default_kernel_args(self) -> List[str]: + return [ + "console=ttyLIO0", + "root={root_value}", + "disk_device={disk_device}", + "rw", + ] + @overrides(KernelDiskWorkload) def _add_disk_to_board(self, disk_image: AbstractResource) -> None: # Note: This must be called after set_workload because it looks for an diff --git a/src/python/gem5/components/boards/kernel_disk_workload.py b/src/python/gem5/components/boards/kernel_disk_workload.py index 15e0cdf303..dcf20081fa 100644 --- a/src/python/gem5/components/boards/kernel_disk_workload.py +++ b/src/python/gem5/components/boards/kernel_disk_workload.py @@ -24,22 +24,25 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import os from abc import abstractmethod - -from .abstract_board import AbstractBoard -from ...resources.resource import ( - DiskImageResource, - BootloaderResource, - CheckpointResource, - KernelResource, +from pathlib import Path +from typing import ( + List, + Optional, + Union, ) -from typing import List, Optional, Union -import os -from pathlib import Path - import m5 +from ...resources.resource import ( + BootloaderResource, + CheckpointResource, + DiskImageResource, + KernelResource, +) +from .abstract_board import AbstractBoard + class KernelDiskWorkload: """ @@ -50,25 +53,25 @@ class KernelDiskWorkload: added as a superclass to a board and the abstract methods implemented. E.g.: - ``` - class X86Board(AbstractBoard, KernelDiskWorkload): - ... - @overrides(KernelDiskWorkload) - def get_default_kernel_args(self) -> List[str]: - return [ - "earlyprintk=ttyS0", - "console=ttyS0", - "lpj=7999923", - "root={root_value}", - ] - ... - ``` + .. code-block:: python - Notes - ----- + class X86Board(AbstractBoard, KernelDiskWorkload): + ... + @overrides(KernelDiskWorkload) + def get_default_kernel_args(self) -> List[str]: + return [ + "earlyprintk=ttyS0", + "console=ttyS0", + "lpj=7999923", + "root={root_value}", + ] + ... - * This assumes only one disk is set. - * This assumes the Linux kernel is used. + + .. note:: + + * This assumes only one disk is set. + * This assumes the Linux kernel is used. """ @abstractmethod @@ -76,9 +79,9 @@ class KernelDiskWorkload: """ Returns a default list of arguments for the workload kernel. We assume the following strings may be used as placeholders, to be replaced when - `set_kernel_disk_workload` is executed: + ``set_kernel_disk_workload`` is executed: - * `{root_value}` : set to `get_default_kernel_root_val()`. + * `{root_value}` : set to ``get_default_kernel_root_val()``. :returns: A default list of arguments for the workload kernel. """ @@ -87,7 +90,7 @@ class KernelDiskWorkload: @abstractmethod def get_disk_device(self) -> str: """ - Get the disk device, e.g., "/dev/sda", where the disk image is placed. + Set a default disk device, in case user does not specify a disk device. :returns: The disk device. """ @@ -98,8 +101,10 @@ class KernelDiskWorkload: """ Sets the configuration needed to add the disk image to the board. - **Note:** This will be executed at the end of the - `set_kernel_disk_workload` function. + .. note:: + + This will be executed at the end of the + ``set_kernel_disk_workload`` function. :param disk_image: The disk image to add to the system. """ @@ -121,14 +126,14 @@ class KernelDiskWorkload: ) -> str: """ Get the default kernel root value to be passed to the kernel. This is - determined by the value implemented in the `get_disk_device()` + determined by the value implemented in the ``get_disk_device()`` function, and the disk image partition, obtained from - `get_disk_root_partition()` - + ``get_disk_root_partition()`` :param disk_image: The disk image to be added to the system. - :returns: The default value for the 'root' argument to be passed to the - kernel. + + :returns: The default value for the ``root`` argument to be passed to the + kernel. """ return self.get_disk_device() + ( self.get_disk_root_partition(disk_image) or "" @@ -139,6 +144,7 @@ class KernelDiskWorkload: kernel: KernelResource, disk_image: DiskImageResource, bootloader: Optional[BootloaderResource] = None, + disk_device: Optional[str] = None, readfile: Optional[str] = None, readfile_contents: Optional[str] = None, kernel_args: Optional[List[str]] = None, @@ -152,25 +158,32 @@ class KernelDiskWorkload: :param kernel: The kernel to boot. :param disk_image: The disk image to mount. :param bootloader: The current implementation of the ARM board requires - three resources to operate -- kernel, disk image, and, a bootloader. + three resources to operate -- kernel, disk image, + and, a bootloader. :param readfile: An optional parameter stating the file to be read by - by `m5 readfile`. + by ``m5 readfile``. :param readfile_contents: An optional parameter stating the contents of - the readfile file. If set with `readfile`, the contents of `readfile` - will be overwritten with `readfile_contents`, otherwise a new file will - be created with the value of `readfile_contents`. + the readfile file. If set with ``readfile``, + the contents of `readfile` will be overwritten + with ``readfile_contents``, otherwise a new file + will be created with the value of + ``readfile_contents``. :param kernel_args: An optional parameter for setting arguments to be - passed to the kernel. By default set to `get_default_kernel_args()`. + passed to the kernel. By default set to + ``get_default_kernel_args()``. :param exit_on_work_items: Whether the simulation should exit on work - items. True by default. + items. ``True`` by default. :param checkpoint: The checkpoint directory. Used to restore the - simulation to that checkpoint. + simulation to that checkpoint. """ # We assume this this is in a multiple-inheritance setup with an # Abstract board. This function will not work otherwise. assert isinstance(self, AbstractBoard) + # Set the disk device + self._disk_device = disk_device + # If we are setting a workload of this type, we need to run as a # full-system simulation. self._set_fullsystem(True) @@ -182,15 +195,21 @@ class KernelDiskWorkload: self.workload.command_line = ( " ".join(kernel_args or self.get_default_kernel_args()) ).format( - root_value=self.get_default_kernel_root_val(disk_image=disk_image) + root_value=self.get_default_kernel_root_val(disk_image=disk_image), + disk_device=( + self._disk_device + if self._disk_device + else self.get_disk_device() + ), ) # Setting the bootloader information for ARM board. The current # implementation of the ArmBoard class expects a boot loader file to be # provided along with the kernel and the disk image. + self._bootloader = [] if bootloader is not None: - self._bootloader = [bootloader.get_local_path()] + self._bootloader.append(bootloader.get_local_path()) # Set the readfile. if readfile: diff --git a/src/python/gem5/components/boards/mem_mode.py b/src/python/gem5/components/boards/mem_mode.py index c1e1baca8a..c794b01673 100644 --- a/src/python/gem5/components/boards/mem_mode.py +++ b/src/python/gem5/components/boards/mem_mode.py @@ -38,10 +38,10 @@ class MemMode(Enum): def mem_mode_to_string(mem_mode: MemMode) -> str: """ - Returns the string form of the mem_mode, compatible with the gem5 + Returns the string form of the ``mem_mode``, compatible with the gem5 simulator. - :returns: The string form of the mem_mode + :returns: The string form of the ``mem_mode``. """ if mem_mode == MemMode.TIMING: return "timing" diff --git a/src/python/gem5/components/boards/riscv_board.py b/src/python/gem5/components/boards/riscv_board.py index 25f1fac562..dcb6fab7c5 100644 --- a/src/python/gem5/components/boards/riscv_board.py +++ b/src/python/gem5/components/boards/riscv_board.py @@ -26,41 +26,28 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import os - from typing import List -from ...utils.override import overrides -from .abstract_system_board import AbstractSystemBoard -from .kernel_disk_workload import KernelDiskWorkload -from ..processors.abstract_processor import AbstractProcessor -from ..memory.abstract_memory_system import AbstractMemorySystem -from ..cachehierarchies.abstract_cache_hierarchy import AbstractCacheHierarchy -from ...resources.resource import AbstractResource - -from ...isas import ISA - import m5 - from m5.objects import ( + AddrRange, BadAddr, Bridge, - PMAChecker, - RiscvLinux, - AddrRange, - IOXBar, - RiscvRTC, - HiFive, - GenericRiscvPciHost, - IGbE_e1000, CowDiskImage, + Frequency, + GenericRiscvPciHost, + HiFive, + IGbE_e1000, + IOXBar, + PMAChecker, + Port, RawDiskImage, + RiscvBootloaderKernelWorkload, RiscvMmioVirtIO, + RiscvRTC, VirtIOBlock, VirtIORng, - Frequency, - Port, ) - from m5.util.fdthelper import ( Fdt, FdtNode, @@ -70,10 +57,19 @@ from m5.util.fdthelper import ( FdtState, ) +from ...isas import ISA +from ...resources.resource import AbstractResource +from ...utils.override import overrides +from ..cachehierarchies.abstract_cache_hierarchy import AbstractCacheHierarchy +from ..memory.abstract_memory_system import AbstractMemorySystem +from ..processors.abstract_processor import AbstractProcessor +from .abstract_system_board import AbstractSystemBoard +from .kernel_disk_workload import KernelDiskWorkload + class RiscvBoard(AbstractSystemBoard, KernelDiskWorkload): """ - A board capable of full system simulation for RISC-V + A board capable of full system simulation for RISC-V. At a high-level, this is based on the HiFive Unmatched board from SiFive. @@ -101,7 +97,7 @@ class RiscvBoard(AbstractSystemBoard, KernelDiskWorkload): @overrides(AbstractSystemBoard) def _setup_board(self) -> None: - self.workload = RiscvLinux() + self.workload = RiscvBootloaderKernelWorkload() # Contains a CLINT, PLIC, UART, and some functions for the dtb, etc. self.platform = HiFive() @@ -142,7 +138,7 @@ class RiscvBoard(AbstractSystemBoard, KernelDiskWorkload): self._off_chip_devices = [self.platform.uart, self.disk, self.rng] def _setup_io_devices(self) -> None: - """Connect the I/O devices to the I/O bus""" + """Connect the I/O devices to the I/O bus.""" # Add PCI self.platform.pci_host.pio = self.iobus.mem_side_ports @@ -181,7 +177,7 @@ class RiscvBoard(AbstractSystemBoard, KernelDiskWorkload): self.bridge.ranges.append(AddrRange(0x40000000, size="512MB")) def _setup_pma(self) -> None: - """Set the PMA devices on each core""" + """Set the PMA devices on each core.""" uncacheable_range = [ AddrRange(dev.pio_addr, size=dev.pio_size) @@ -234,11 +230,11 @@ class RiscvBoard(AbstractSystemBoard, KernelDiskWorkload): memory.set_memory_range(self.mem_ranges) def generate_device_tree(self, outdir: str) -> None: - """Creates the dtb and dts files. + """Creates the ``dtb`` and ``dts`` files. - Creates two files in the outdir: 'device.dtb' and 'device.dts' + Creates two files in the outdir: ``device.dtb`` and ``device.dts``. - :param outdir: Directory to output the files + :param outdir: Directory to output the files. """ state = FdtState(addr_cells=2, size_cells=2, cpu_cells=1) @@ -259,6 +255,12 @@ class RiscvBoard(AbstractSystemBoard, KernelDiskWorkload): ) root.append(node) + node = FdtNode(f"chosen") + bootargs = self.workload.command_line + node.append(FdtPropertyStrings("bootargs", [bootargs])) + node.append(FdtPropertyStrings("stdout-path", ["/uart@10000000"])) + root.append(node) + # See Documentation/devicetree/bindings/riscv/cpus.txt for details. cpus_node = FdtNode("cpus") cpus_state = FdtState(addr_cells=1, size_cells=0) @@ -273,8 +275,24 @@ class RiscvBoard(AbstractSystemBoard, KernelDiskWorkload): node.append(FdtPropertyStrings("device_type", "cpu")) node.append(FdtPropertyWords("reg", state.CPUAddrCells(i))) node.append(FdtPropertyStrings("mmu-type", "riscv,sv48")) + if core.core.isa[0].enable_Zicbom_fs.value: + node.append( + FdtPropertyWords( + "riscv,cbom-block-size", self.get_cache_line_size() + ) + ) + if core.core.isa[0].enable_Zicboz_fs.value: + node.append( + FdtPropertyWords( + "riscv,cboz-block-size", self.get_cache_line_size() + ) + ) node.append(FdtPropertyStrings("status", "okay")) - node.append(FdtPropertyStrings("riscv,isa", "rv64imafdc")) + node.append( + FdtPropertyStrings( + "riscv,isa", core.core.isa[0].get_isa_string() + ) + ) # TODO: Should probably get this from the core. freq = self.clk_domain.clock[0].frequency node.append(FdtPropertyWords("clock-frequency", freq)) @@ -432,7 +450,7 @@ class RiscvBoard(AbstractSystemBoard, KernelDiskWorkload): uart_node.append( FdtPropertyWords("interrupt-parent", soc_state.phandle(plic)) ) - uart_node.appendCompatible(["ns8250"]) + uart_node.appendCompatible(["ns8250", "ns16550a"]) soc_node.append(uart_node) # VirtIO MMIO disk node @@ -470,6 +488,18 @@ class RiscvBoard(AbstractSystemBoard, KernelDiskWorkload): def get_disk_device(self): return "/dev/vda" + @overrides(AbstractSystemBoard) + def _pre_instantiate(self): + if len(self._bootloader) > 0: + self.workload.bootloader_addr = 0x0 + self.workload.bootloader_filename = self._bootloader[0] + self.workload.kernel_addr = 0x80200000 + self.workload.entry_point = 0x80000000 # Bootloader starting point + else: + self.workload.kernel_addr = 0x0 + self.workload.entry_point = 0x80000000 + self._connect_things() + @overrides(KernelDiskWorkload) def _add_disk_to_board(self, disk_image: AbstractResource): image = CowDiskImage( @@ -494,4 +524,9 @@ class RiscvBoard(AbstractSystemBoard, KernelDiskWorkload): @overrides(KernelDiskWorkload) def get_default_kernel_args(self) -> List[str]: - return ["console=ttyS0", "root={root_value}", "rw"] + return [ + "console=ttyS0", + "root={root_value}", + "disk_device={disk_device}", + "rw", + ] diff --git a/src/python/gem5/components/boards/se_binary_workload.py b/src/python/gem5/components/boards/se_binary_workload.py index c62a1b67ea..4db4e008ba 100644 --- a/src/python/gem5/components/boards/se_binary_workload.py +++ b/src/python/gem5/components/boards/se_binary_workload.py @@ -24,27 +24,32 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .abstract_board import AbstractBoard - -from ...resources.resource import ( - FileResource, - AbstractResource, - BinaryResource, - CheckpointResource, - SimpointResource, - SimpointDirectoryResource, +from pathlib import Path +from typing import ( + List, + Optional, + Union, ) -from ..processors.switchable_processor import SwitchableProcessor +from m5.objects import ( + Process, + SEWorkload, +) +from m5.util import warn from gem5.resources.elfie import ELFieInfo from gem5.resources.looppoint import Looppoint -from m5.objects import SEWorkload, Process - -from typing import Optional, List, Union -from m5.util import warn -from pathlib import Path +from ...resources.resource import ( + AbstractResource, + BinaryResource, + CheckpointResource, + FileResource, + SimpointDirectoryResource, + SimpointResource, +) +from ..processors.switchable_processor import SwitchableProcessor +from .abstract_board import AbstractBoard class SEBinaryWorkload: @@ -52,13 +57,15 @@ class SEBinaryWorkload: This class is used to enable simple Syscall-Execution (SE) mode execution of a binary. - For this to function correctly the SEBinaryWorkload class should be added + For this to function correctly the `SEBinaryWorkload` class should be added as a superclass to a board (i.e., something that inherits from - AbstractBoard). + `AbstractBoard`). - **Important Notes:** At present this implementation is limited. A single - process is added to all cores as the workload. Therefore, despite allowing - for multi-core setups, multi-program workloads are not presently supported. + .. note:: + + At present this implementation is limited. A single + process is added to all cores as the workload. Therefore, despite allowing + for multi-core setups, multi-program workloads are not presently supported. """ def set_se_binary_workload( @@ -75,20 +82,19 @@ class SEBinaryWorkload: """Set up the system to run a specific binary. **Limitations** - * Only supports single threaded applications. * Dynamically linked executables are partially supported when the host ISA and the simulated ISA are the same. :param binary: The resource encapsulating the binary to be run. :param exit_on_work_items: Whether the simulation should exit on work - items. True by default. + items. ``True`` by default. :param stdin_file: The input file for the binary :param stdout_file: The output file for the binary :param stderr_file: The error output file for the binary :param env_list: The environment variables defined for the binary :param arguments: The input arguments for the binary :param checkpoint: The checkpoint directory. Used to restore the - simulation to that checkpoint. + simulation to that checkpoint. """ # We assume this this is in a multiple-inheritance setup with an @@ -162,15 +168,17 @@ class SEBinaryWorkload: * Dynamically linked executables are partially supported when the host ISA and the simulated ISA are the same. - **Warning:** Simpoints only works with one core + .. warning:: + + SimPoints only works with one core :param binary: The resource encapsulating the binary to be run. - :param arguments: The input arguments for the binary + :param arguments: The input arguments for the binary. :param simpoint: The SimpointResource that contains the list of - SimPoints starting instructions, the list of weights, and the SimPoints - interval + SimPoints starting instructions, the list of + weights, and the SimPoints interval. :param checkpoint: The checkpoint directory. Used to restore the - simulation to that checkpoint. + simulation to that checkpoint. """ self._simpoint_resource = simpoint @@ -214,11 +222,11 @@ class SEBinaryWorkload: :param binary: The resource encapsulating the binary to be run. :param looppoint: The LoopPoint object that contain all the information - gather from the LoopPoint files and a LoopPointManager that will raise - exit events for LoopPoints - :param arguments: The input arguments for the binary + gather from the LoopPoint files and a LoopPointManager + that will raise exit events for LoopPoints. + :param arguments: The input arguments for the binary. :param region_id: If set, will only load the Looppoint region - corresponding to that ID. + corresponding to that ID. """ assert isinstance(looppoint, Looppoint) @@ -247,10 +255,10 @@ class SEBinaryWorkload: * Dynamically linked executables are partially supported when the host ISA and the simulated ISA are the same. - :param elfie: The resource encapsulating the binary elfie to be run. + :param elfie: The resource encapsulating the binary ELFie to be run. :param elfie_info: The ELFieInfo object that contain all the - information for the ELFie - :param arguments: The input arguments for the binary + information for the ELFie. + :param arguments: The input arguments for the binary. """ assert isinstance(elfie_info, ELFieInfo) diff --git a/src/python/gem5/components/boards/simple_board.py b/src/python/gem5/components/boards/simple_board.py index 2e4122e061..85f36b94b0 100644 --- a/src/python/gem5/components/boards/simple_board.py +++ b/src/python/gem5/components/boards/simple_board.py @@ -24,16 +24,20 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import AddrRange, IOXBar, Port +from typing import List +from m5.objects import ( + AddrRange, + IOXBar, + Port, +) + +from ...utils.override import overrides +from ..cachehierarchies.abstract_cache_hierarchy import AbstractCacheHierarchy +from ..memory.abstract_memory_system import AbstractMemorySystem +from ..processors.abstract_processor import AbstractProcessor from .abstract_system_board import AbstractSystemBoard from .se_binary_workload import SEBinaryWorkload -from ..processors.abstract_processor import AbstractProcessor -from ..memory.abstract_memory_system import AbstractMemorySystem -from ..cachehierarchies.abstract_cache_hierarchy import AbstractCacheHierarchy -from ...utils.override import overrides - -from typing import List class SimpleBoard(AbstractSystemBoard, SEBinaryWorkload): @@ -44,7 +48,7 @@ class SimpleBoard(AbstractSystemBoard, SEBinaryWorkload): **Limitations** * Only supports SE mode - You can run a binary executable via the `set_se_binary_workload` function. + You can run a binary executable via the ``set_se_binary_workload`` function. """ def __init__( @@ -94,7 +98,7 @@ class SimpleBoard(AbstractSystemBoard, SEBinaryWorkload): @overrides(AbstractSystemBoard) def get_mem_side_coherent_io_port(self) -> Port: raise NotImplementedError( - "SimpleBoard does not have any I/O ports. Use has_coherent_io to " + "SimpleBoard does not have any I/O ports. Use `has_coherent_io` to " "check this." ) diff --git a/src/python/gem5/components/boards/test_board.py b/src/python/gem5/components/boards/test_board.py index dea5adab56..2599c6853d 100644 --- a/src/python/gem5/components/boards/test_board.py +++ b/src/python/gem5/components/boards/test_board.py @@ -24,17 +24,23 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import Port, IOXBar, AddrRange +from typing import ( + List, + Optional, +) + +from m5.objects import ( + AddrRange, + IOXBar, + Port, +) from ...utils.override import overrides +from ..cachehierarchies.abstract_cache_hierarchy import AbstractCacheHierarchy +from ..memory.abstract_memory_system import AbstractMemorySystem +from ..processors.abstract_generator import AbstractGenerator from .abstract_board import AbstractBoard from .abstract_system_board import AbstractSystemBoard -from ..processors.abstract_generator import AbstractGenerator -from ..memory.abstract_memory_system import AbstractMemorySystem -from ..cachehierarchies.abstract_cache_hierarchy import AbstractCacheHierarchy - - -from typing import List, Optional class TestBoard(AbstractSystemBoard): @@ -44,7 +50,7 @@ class TestBoard(AbstractSystemBoard): To work as a traffic generator board, pass a generator as a processor. - This board does not require a cache hierarchy (it can be none) in which + This board does not require a cache hierarchy (it can be ``none``) in which case the processor (generator) will be directly connected to the memory. The clock frequency is only used if there is a cache hierarchy or when using the GUPS generators. diff --git a/src/python/gem5/components/boards/x86_board.py b/src/python/gem5/components/boards/x86_board.py index 04fec617c1..a0ba325475 100644 --- a/src/python/gem5/components/boards/x86_board.py +++ b/src/python/gem5/components/boards/x86_board.py @@ -25,40 +25,41 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .kernel_disk_workload import KernelDiskWorkload -from ...resources.resource import AbstractResource -from ...utils.override import overrides -from .abstract_system_board import AbstractSystemBoard -from ...isas import ISA - -from m5.objects import ( - Pc, - AddrRange, - X86FsLinux, - Addr, - X86SMBiosBiosInformation, - X86IntelMPProcessor, - X86IntelMPIOAPIC, - X86IntelMPBus, - X86IntelMPBusHierarchy, - X86IntelMPIOIntAssignment, - X86E820Entry, - Bridge, - IOXBar, - IdeDisk, - CowDiskImage, - RawDiskImage, - BaseXBar, - Port, +from typing import ( + List, + Sequence, ) +from m5.objects import ( + Addr, + AddrRange, + BaseXBar, + Bridge, + CowDiskImage, + IdeDisk, + IOXBar, + Pc, + Port, + RawDiskImage, + X86E820Entry, + X86FsLinux, + X86IntelMPBus, + X86IntelMPBusHierarchy, + X86IntelMPIOAPIC, + X86IntelMPIOIntAssignment, + X86IntelMPProcessor, + X86SMBiosBiosInformation, +) from m5.util.convert import toMemorySize -from ..processors.abstract_processor import AbstractProcessor -from ..memory.abstract_memory_system import AbstractMemorySystem +from ...isas import ISA +from ...resources.resource import AbstractResource +from ...utils.override import overrides from ..cachehierarchies.abstract_cache_hierarchy import AbstractCacheHierarchy - -from typing import List, Sequence +from ..memory.abstract_memory_system import AbstractMemorySystem +from ..processors.abstract_processor import AbstractProcessor +from .abstract_system_board import AbstractSystemBoard +from .kernel_disk_workload import KernelDiskWorkload class X86Board(AbstractSystemBoard, KernelDiskWorkload): @@ -66,8 +67,8 @@ class X86Board(AbstractSystemBoard, KernelDiskWorkload): A board capable of full system simulation for X86. **Limitations** - * Currently, this board's memory is hardcoded to 3GB - * Much of the I/O subsystem is hard coded + * Currently, this board's memory is hardcoded to 3GB. + * Much of the I/O subsystem is hard coded. """ def __init__( @@ -107,8 +108,10 @@ class X86Board(AbstractSystemBoard, KernelDiskWorkload): def _setup_io_devices(self): """Sets up the x86 IO devices. - Note: This is mostly copy-paste from prior X86 FS setups. Some of it - may not be documented and there may be bugs. + .. note:: + + This is mostly copy-paste from prior X86 FS setups. Some of it + may not be documented and there may be bugs. """ # Constants similar to x86_traits.hh @@ -202,7 +205,6 @@ class X86Board(AbstractSystemBoard, KernelDiskWorkload): base_entries.append(pci_dev4_inta) def assignISAInt(irq, apicPin): - assign_8259_to_apic = X86IntelMPIOIntAssignment( interrupt_type="ExtInt", polarity="ConformPolarity", @@ -318,4 +320,5 @@ class X86Board(AbstractSystemBoard, KernelDiskWorkload): "console=ttyS0", "lpj=7999923", "root={root_value}", + "disk_device={disk_device}", ] diff --git a/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py index 8d59a383f1..930c6e7cff 100644 --- a/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py +++ b/src/python/gem5/components/cachehierarchies/abstract_cache_hierarchy.py @@ -24,12 +24,15 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from abc import ABCMeta, abstractmethod - -from ..boards.abstract_board import AbstractBoard +from abc import ( + ABCMeta, + abstractmethod, +) from m5.objects import SubSystem +from ..boards.abstract_board import AbstractBoard + class AbstractCacheHierarchy(SubSystem): __metaclass__ = ABCMeta @@ -54,9 +57,7 @@ class AbstractCacheHierarchy(SubSystem): unique for each setup. :param board: The board in which the cache heirarchy is to be - incorporated. - - :type board: AbstractBoard + incorporated. """ raise NotImplementedError @@ -67,10 +68,10 @@ class AbstractCacheHierarchy(SubSystem): Specifies whether this cache hierarchy is using the Ruby memory system or not. - :returns: True if the cache hierarchy is ruby. Otherwise False. + :returns: ``True`` if the cache hierarchy is ruby. Otherwise ``False``. """ raise NotImplementedError def _post_instantiate(self): - """Called to set up anything needed after m5.instantiate""" + """Called to set up anything needed after ``m5.instantiate``.""" pass diff --git a/src/python/gem5/components/cachehierarchies/abstract_two_level_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/abstract_two_level_cache_hierarchy.py index d6a035f2cb..593f9c7c1e 100644 --- a/src/python/gem5/components/cachehierarchies/abstract_two_level_cache_hierarchy.py +++ b/src/python/gem5/components/cachehierarchies/abstract_two_level_cache_hierarchy.py @@ -43,27 +43,15 @@ class AbstractTwoLevelCacheHierarchy: """ :param l1i_size: The size of the L1 Instruction cache (e.g. "32kB"). - :type l1i_size: str - :param l1i_assoc: - :type l1i_assoc: int - :param l1d_size: The size of the L1 Data cache (e.g. "32kB"). - :type l1d_size: str - :param l1d_assoc: - :type l1d_assoc: int - :param l2_size: The size of the L2 cache (e.g., "256kB"). - :type l2_size: str - :param l2_assoc: - - :type l2_assoc: int """ self._l1i_size = l1i_size self._l1i_assoc = l1i_assoc diff --git a/src/python/gem5/components/cachehierarchies/chi/nodes/abstract_node.py b/src/python/gem5/components/cachehierarchies/chi/nodes/abstract_node.py index 9853174464..beb0d467d1 100644 --- a/src/python/gem5/components/cachehierarchies/chi/nodes/abstract_node.py +++ b/src/python/gem5/components/cachehierarchies/chi/nodes/abstract_node.py @@ -24,14 +24,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from abc import abstractmethod -from gem5.isas import ISA -from gem5.components.processors.cpu_types import CPUTypes -from gem5.components.processors.abstract_core import AbstractCore - -from m5.objects import Cache_Controller, MessageBuffer, RubyNetwork - import math +from abc import abstractmethod + +from m5.objects import ( + Cache_Controller, + MessageBuffer, + RubyNetwork, +) + +from gem5.components.processors.abstract_core import AbstractCore +from gem5.components.processors.cpu_types import CPUTypes +from gem5.isas import ISA class TriggerMessageBuffer(MessageBuffer): @@ -66,7 +70,7 @@ class AbstractNode(Cache_Controller): # TODO: I don't love that we have to pass in the cache line size. # However, we need some way to set the index bits def __init__(self, network: RubyNetwork, cache_line_size: int): - super(AbstractNode, self).__init__() + super().__init__() # Note: Need to call versionCount method on *this* class, not the # potentially derived class @@ -85,6 +89,9 @@ class AbstractNode(Cache_Controller): # Use 32-byte channels (two flits per message) self.data_channel_size = 32 + # Use near atomics (see: https://github.com/gem5/gem5/issues/449) + self.policy_type = 0 + self.connectQueues(network) def getBlockSizeBits(self): diff --git a/src/python/gem5/components/cachehierarchies/chi/nodes/directory.py b/src/python/gem5/components/cachehierarchies/chi/nodes/directory.py index 3488435d56..b2515d3c84 100644 --- a/src/python/gem5/components/cachehierarchies/chi/nodes/directory.py +++ b/src/python/gem5/components/cachehierarchies/chi/nodes/directory.py @@ -24,9 +24,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .abstract_node import AbstractNode +from m5.objects import ( + NULL, + ClockDomain, + RubyCache, + RubyNetwork, +) -from m5.objects import ClockDomain, NULL, RubyCache, RubyNetwork +from .abstract_node import AbstractNode class SimpleDirectory(AbstractNode): @@ -56,6 +61,7 @@ class SimpleDirectory(AbstractNode): self.sequencer = NULL self.use_prefetcher = False + self.prefetcher = NULL # Set up home node that allows three hop protocols self.is_HN = True @@ -72,6 +78,7 @@ class SimpleDirectory(AbstractNode): self.alloc_on_readunique = False self.alloc_on_readonce = False self.alloc_on_writeback = False + self.alloc_on_atomic = False self.dealloc_on_unique = False self.dealloc_on_shared = False self.dealloc_backinv_unique = False diff --git a/src/python/gem5/components/cachehierarchies/chi/nodes/dma_requestor.py b/src/python/gem5/components/cachehierarchies/chi/nodes/dma_requestor.py index ccac6cae91..850c7e9002 100644 --- a/src/python/gem5/components/cachehierarchies/chi/nodes/dma_requestor.py +++ b/src/python/gem5/components/cachehierarchies/chi/nodes/dma_requestor.py @@ -24,13 +24,17 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects import ( + NULL, + ClockDomain, + RubyCache, +) + from gem5.components.processors.abstract_core import AbstractCore from gem5.isas import ISA from .abstract_node import AbstractNode -from m5.objects import ClockDomain, RubyCache - class DMARequestor(AbstractNode): def __init__(self, network, cache_line_size, clk_domain: ClockDomain): @@ -56,6 +60,7 @@ class DMARequestor(AbstractNode): self.alloc_on_readunique = False self.alloc_on_readonce = False self.alloc_on_writeback = False + self.alloc_on_atomic = False self.dealloc_on_unique = False self.dealloc_on_shared = False self.dealloc_backinv_unique = True @@ -63,6 +68,7 @@ class DMARequestor(AbstractNode): self.send_evictions = False self.use_prefetcher = False + self.prefetcher = NULL # Some reasonable default TBE params self.number_of_TBEs = 16 self.number_of_repl_TBEs = 1 diff --git a/src/python/gem5/components/cachehierarchies/chi/nodes/memory_controller.py b/src/python/gem5/components/cachehierarchies/chi/nodes/memory_controller.py index e7cbafefb2..54bab22a64 100644 --- a/src/python/gem5/components/cachehierarchies/chi/nodes/memory_controller.py +++ b/src/python/gem5/components/cachehierarchies/chi/nodes/memory_controller.py @@ -40,6 +40,7 @@ from .abstract_node import TriggerMessageBuffer class MemCtrlMessageBuffer(MessageBuffer): """ MessageBuffer exchanging messages with the memory + These buffers should also not be affected by the Ruby tester randomization. """ @@ -48,7 +49,7 @@ class MemCtrlMessageBuffer(MessageBuffer): class MemoryController(Memory_Controller): - """A controller that connects to memory""" + """A controller that connects to memory.""" _version = 0 diff --git a/src/python/gem5/components/cachehierarchies/chi/nodes/private_l1_moesi_cache.py b/src/python/gem5/components/cachehierarchies/chi/nodes/private_l1_moesi_cache.py index 3e38c9038f..8b056b3967 100644 --- a/src/python/gem5/components/cachehierarchies/chi/nodes/private_l1_moesi_cache.py +++ b/src/python/gem5/components/cachehierarchies/chi/nodes/private_l1_moesi_cache.py @@ -24,13 +24,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects import ( + NULL, + ClockDomain, + RubyCache, + RubyNetwork, +) + from gem5.components.processors.abstract_core import AbstractCore from gem5.isas import ISA from .abstract_node import AbstractNode -from m5.objects import ClockDomain, RubyCache, RubyNetwork - class PrivateL1MOESICache(AbstractNode): def __init__( @@ -52,6 +57,7 @@ class PrivateL1MOESICache(AbstractNode): self.clk_domain = clk_domain self.send_evictions = core.requires_send_evicts() self.use_prefetcher = False + self.prefetcher = NULL # Only applies to home nodes self.is_HN = False @@ -66,6 +72,7 @@ class PrivateL1MOESICache(AbstractNode): self.alloc_on_readunique = True self.alloc_on_readonce = True self.alloc_on_writeback = False # Should never happen in an L1 + self.alloc_on_atomic = False self.dealloc_on_unique = False self.dealloc_on_shared = False self.dealloc_backinv_unique = True diff --git a/src/python/gem5/components/cachehierarchies/chi/private_l1_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/chi/private_l1_cache_hierarchy.py index 9c91e05ac1..f3b7036e66 100644 --- a/src/python/gem5/components/cachehierarchies/chi/private_l1_cache_hierarchy.py +++ b/src/python/gem5/components/cachehierarchies/chi/private_l1_cache_hierarchy.py @@ -27,30 +27,34 @@ from itertools import chain from typing import List -from m5.objects.SubSystem import SubSystem -from gem5.components.cachehierarchies.ruby.abstract_ruby_cache_hierarchy import ( - AbstractRubyCacheHierarchy, +from m5.objects import ( + NULL, + RubyPortProxy, + RubySequencer, + RubySystem, ) +from m5.objects.SubSystem import SubSystem + +from gem5.coherence_protocol import CoherenceProtocol +from gem5.components.boards.abstract_board import AbstractBoard from gem5.components.cachehierarchies.abstract_cache_hierarchy import ( AbstractCacheHierarchy, ) -from gem5.coherence_protocol import CoherenceProtocol -from gem5.isas import ISA -from gem5.utils.requires import requires -from gem5.utils.override import overrides -from gem5.components.boards.abstract_board import AbstractBoard -from gem5.components.processors.abstract_core import AbstractCore - +from gem5.components.cachehierarchies.ruby.abstract_ruby_cache_hierarchy import ( + AbstractRubyCacheHierarchy, +) from gem5.components.cachehierarchies.ruby.topologies.simple_pt2pt import ( SimplePt2Pt, ) +from gem5.components.processors.abstract_core import AbstractCore +from gem5.isas import ISA +from gem5.utils.override import overrides +from gem5.utils.requires import requires -from .nodes.private_l1_moesi_cache import PrivateL1MOESICache -from .nodes.dma_requestor import DMARequestor from .nodes.directory import SimpleDirectory +from .nodes.dma_requestor import DMARequestor from .nodes.memory_controller import MemoryController - -from m5.objects import NULL, RubySystem, RubySequencer, RubyPortProxy +from .nodes.private_l1_moesi_cache import PrivateL1MOESICache class PrivateL1CacheHierarchy(AbstractRubyCacheHierarchy): @@ -75,7 +79,6 @@ class PrivateL1CacheHierarchy(AbstractRubyCacheHierarchy): @overrides(AbstractCacheHierarchy) def incorporate_cache(self, board: AbstractBoard) -> None: - requires(coherence_protocol_required=CoherenceProtocol.CHI) self.ruby_system = RubySystem() @@ -140,7 +143,7 @@ class PrivateL1CacheHierarchy(AbstractRubyCacheHierarchy): self, core: AbstractCore, core_num: int, board: AbstractBoard ) -> SubSystem: """Given the core and the core number this function creates a cluster - for the core with a split I/D cache + for the core with a split I/D cache. """ cluster = SubSystem() cluster.dcache = PrivateL1MOESICache( diff --git a/src/python/gem5/components/cachehierarchies/classic/abstract_classic_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/classic/abstract_classic_cache_hierarchy.py index 3423c9ca9a..7531b2a3f1 100644 --- a/src/python/gem5/components/cachehierarchies/classic/abstract_classic_cache_hierarchy.py +++ b/src/python/gem5/components/cachehierarchies/classic/abstract_classic_cache_hierarchy.py @@ -25,11 +25,12 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from abc import abstractmethod -from ....utils.override import overrides -from ..abstract_cache_hierarchy import AbstractCacheHierarchy from m5.objects import Port +from ....utils.override import overrides +from ..abstract_cache_hierarchy import AbstractCacheHierarchy + class AbstractClassicCacheHierarchy(AbstractCacheHierarchy): """ diff --git a/src/python/gem5/components/cachehierarchies/classic/caches/l1dcache.py b/src/python/gem5/components/cachehierarchies/classic/caches/l1dcache.py index da4a4ead9b..5fc9a79a1b 100644 --- a/src/python/gem5/components/cachehierarchies/classic/caches/l1dcache.py +++ b/src/python/gem5/components/cachehierarchies/classic/caches/l1dcache.py @@ -24,19 +24,23 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .....utils.override import * - -from m5.objects import Cache, BasePrefetcher, StridePrefetcher - from typing import Type +from m5.objects import ( + BasePrefetcher, + Cache, + StridePrefetcher, +) + +from .....utils.override import * + class L1DCache(Cache): """ A simple L1 data cache with default values. - If the cache has a mostly exclusive downstream cache, writeback_clean - should be set to True. + If the cache has a mostly exclusive downstream cache, ``writeback_clean`` + should be set to ``True``. """ def __init__( diff --git a/src/python/gem5/components/cachehierarchies/classic/caches/l1icache.py b/src/python/gem5/components/cachehierarchies/classic/caches/l1icache.py index f1ac89cf1d..2a2d6d41d5 100644 --- a/src/python/gem5/components/cachehierarchies/classic/caches/l1icache.py +++ b/src/python/gem5/components/cachehierarchies/classic/caches/l1icache.py @@ -26,7 +26,11 @@ from typing import Type -from m5.objects import Cache, BasePrefetcher, StridePrefetcher +from m5.objects import ( + BasePrefetcher, + Cache, + StridePrefetcher, +) from .....utils.override import * @@ -36,7 +40,7 @@ class L1ICache(Cache): A simple L1 instruction cache with default values. If the cache does not have a downstream cache or the downstream cache - is mostly inclusive as usual, writeback_clean should be set to False. + is mostly inclusive as usual, ``writeback_clean`` should be set to ``False``. """ def __init__( diff --git a/src/python/gem5/components/cachehierarchies/classic/caches/l2cache.py b/src/python/gem5/components/cachehierarchies/classic/caches/l2cache.py index 86b69855b6..aa796f4ceb 100644 --- a/src/python/gem5/components/cachehierarchies/classic/caches/l2cache.py +++ b/src/python/gem5/components/cachehierarchies/classic/caches/l2cache.py @@ -24,12 +24,17 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .....utils.override import * - -from m5.objects import Cache, Clusivity, BasePrefetcher, StridePrefetcher - from typing import Type +from m5.objects import ( + BasePrefetcher, + Cache, + Clusivity, + StridePrefetcher, +) + +from .....utils.override import * + class L2Cache(Cache): """ diff --git a/src/python/gem5/components/cachehierarchies/classic/caches/mmu_cache.py b/src/python/gem5/components/cachehierarchies/classic/caches/mmu_cache.py index a6eb43cfb4..badb242ccf 100644 --- a/src/python/gem5/components/cachehierarchies/classic/caches/mmu_cache.py +++ b/src/python/gem5/components/cachehierarchies/classic/caches/mmu_cache.py @@ -24,9 +24,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .....utils.override import * +from m5.objects import ( + BasePrefetcher, + Cache, + StridePrefetcher, +) -from m5.objects import Cache, BasePrefetcher, StridePrefetcher +from .....utils.override import * class MMUCache(Cache): @@ -34,7 +38,7 @@ class MMUCache(Cache): A simple Memory Management Unit (MMU) cache with default values. If the cache does not have a downstream cache or the downstream cache - is mostly inclusive as usual, writeback_clean should be set to False. + is mostly inclusive as usual, ``writeback_clean`` should be set to ``False``. """ def __init__( diff --git a/src/python/gem5/components/cachehierarchies/classic/no_cache.py b/src/python/gem5/components/cachehierarchies/classic/no_cache.py index 51b5d30eb4..3fb37bb43c 100644 --- a/src/python/gem5/components/cachehierarchies/classic/no_cache.py +++ b/src/python/gem5/components/cachehierarchies/classic/no_cache.py @@ -24,14 +24,19 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .abstract_classic_cache_hierarchy import AbstractClassicCacheHierarchy -from ..abstract_cache_hierarchy import AbstractCacheHierarchy -from ...boards.abstract_board import AbstractBoard +from m5.objects import ( + BadAddr, + BaseXBar, + Bridge, + Port, + SystemXBar, +) + from ....isas import ISA - -from m5.objects import Bridge, BaseXBar, SystemXBar, BadAddr, Port - from ....utils.override import * +from ...boards.abstract_board import AbstractBoard +from ..abstract_cache_hierarchy import AbstractCacheHierarchy +from .abstract_classic_cache_hierarchy import AbstractClassicCacheHierarchy class NoCache(AbstractClassicCacheHierarchy): @@ -41,20 +46,22 @@ class NoCache(AbstractClassicCacheHierarchy): By default a SystemXBar of width 64bit is used, though this can be configured via the constructor. - NOTE: At present this does not work with FS. The following error is - received: + .. note:: + + At present this does not work with FS. The following error is + received: + + .. code-block:: + ... + build/X86/mem/snoop_filter.cc:277: panic: panic condition + (sf_item.requested & req_mask).none() occurred: SF value + 0000000000000000000000000000000000000000000000000000000000000000 ... + missing the original request + Memory Usage: 3554472 KBytes + Program aborted at tick 1668400099164 + --- BEGIN LIBC BACKTRACE --- + ... - ``` - ... - build/X86/mem/snoop_filter.cc:277: panic: panic condition - (sf_item.requested & req_mask).none() occurred: SF value - 0000000000000000000000000000000000000000000000000000000000000000 ... - missing the original request - Memory Usage: 3554472 KBytes - Program aborted at tick 1668400099164 - --- BEGIN LIBC BACKTRACE --- - ... - ``` """ @staticmethod @@ -80,7 +87,8 @@ class NoCache(AbstractClassicCacheHierarchy): ) -> None: """ :param membus: The memory bus for this setup. This parameter is - optional and will default toa 64 bit width SystemXBar is not specified. + optional and will default toa 64 bit width SystemXBar + is not specified. :type membus: BaseXBar """ @@ -97,12 +105,10 @@ class NoCache(AbstractClassicCacheHierarchy): @overrides(AbstractCacheHierarchy) def incorporate_cache(self, board: AbstractBoard) -> None: - if board.has_coherent_io(): self._setup_coherent_io_bridge(board) for core in board.get_processor().get_cores(): - core.connect_icache(self.membus.cpu_side_ports) core.connect_dcache(self.membus.cpu_side_ports) core.connect_walker_ports( diff --git a/src/python/gem5/components/cachehierarchies/classic/private_l1_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/classic/private_l1_cache_hierarchy.py index 42ff183a1d..97348fdbe5 100644 --- a/src/python/gem5/components/cachehierarchies/classic/private_l1_cache_hierarchy.py +++ b/src/python/gem5/components/cachehierarchies/classic/private_l1_cache_hierarchy.py @@ -24,17 +24,22 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects import ( + BadAddr, + BaseXBar, + Cache, + Port, + SystemXBar, +) + +from ....isas import ISA +from ....utils.override import * +from ...boards.abstract_board import AbstractBoard from ..abstract_cache_hierarchy import AbstractCacheHierarchy from .abstract_classic_cache_hierarchy import AbstractClassicCacheHierarchy from .caches.l1dcache import L1DCache from .caches.l1icache import L1ICache from .caches.mmu_cache import MMUCache -from ...boards.abstract_board import AbstractBoard -from ....isas import ISA - -from m5.objects import Cache, BaseXBar, SystemXBar, BadAddr, Port - -from ....utils.override import * class PrivateL1CacheHierarchy(AbstractClassicCacheHierarchy): @@ -49,7 +54,7 @@ class PrivateL1CacheHierarchy(AbstractClassicCacheHierarchy): the PrivateL1CacheHierarchy. :returns: The default memory bus for the PrivateL1PrivateL2 - CacheHierarchy. + CacheHierarchy. """ membus = SystemXBar(width=64) membus.badaddr_responder = BadAddr() @@ -68,7 +73,7 @@ class PrivateL1CacheHierarchy(AbstractClassicCacheHierarchy): :param l1i_size: The size of the L1 Instruction Cache (e.g., "32kB"). :param membus: The memory bus. This parameter is optional parameter and - will default to a 64 bit width SystemXBar is not specified. + will default to a 64 bit width SystemXBar is not specified. """ AbstractClassicCacheHierarchy.__init__(self=self) @@ -86,7 +91,6 @@ class PrivateL1CacheHierarchy(AbstractClassicCacheHierarchy): @overrides(AbstractCacheHierarchy) def incorporate_cache(self, board: AbstractBoard) -> None: - # Set up the system port for functional access from the simulator. board.connect_system_port(self.membus.cpu_side_ports) @@ -117,7 +121,6 @@ class PrivateL1CacheHierarchy(AbstractClassicCacheHierarchy): self._setup_io_cache(board) for i, cpu in enumerate(board.get_processor().get_cores()): - cpu.connect_icache(self.l1icaches[i].cpu_side) cpu.connect_dcache(self.l1dcaches[i].cpu_side) diff --git a/src/python/gem5/components/cachehierarchies/classic/private_l1_private_l2_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/classic/private_l1_private_l2_cache_hierarchy.py index 8b60aef7f6..d1d43ec0c8 100644 --- a/src/python/gem5/components/cachehierarchies/classic/private_l1_private_l2_cache_hierarchy.py +++ b/src/python/gem5/components/cachehierarchies/classic/private_l1_private_l2_cache_hierarchy.py @@ -24,18 +24,25 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects import ( + BadAddr, + BaseXBar, + Cache, + L2XBar, + Port, + SystemXBar, +) + +from ....isas import ISA +from ....utils.override import * +from ...boards.abstract_board import AbstractBoard from ..abstract_cache_hierarchy import AbstractCacheHierarchy -from .abstract_classic_cache_hierarchy import AbstractClassicCacheHierarchy from ..abstract_two_level_cache_hierarchy import AbstractTwoLevelCacheHierarchy +from .abstract_classic_cache_hierarchy import AbstractClassicCacheHierarchy from .caches.l1dcache import L1DCache from .caches.l1icache import L1ICache from .caches.l2cache import L2Cache from .caches.mmu_cache import MMUCache -from ...boards.abstract_board import AbstractBoard -from ....isas import ISA -from m5.objects import Cache, L2XBar, BaseXBar, SystemXBar, BadAddr, Port - -from ....utils.override import * class PrivateL1PrivateL2CacheHierarchy( @@ -53,9 +60,8 @@ class PrivateL1PrivateL2CacheHierarchy( the PrivateL1PrivateL2 CacheHierarchy. :returns: The default memory bus for the PrivateL1PrivateL2 - CacheHierarchy. + CacheHierarchy. - :rtype: SystemXBar """ membus = SystemXBar(width=64) membus.badaddr_responder = BadAddr() @@ -72,20 +78,12 @@ class PrivateL1PrivateL2CacheHierarchy( """ :param l1d_size: The size of the L1 Data Cache (e.g., "32kB"). - :type l1d_size: str - :param l1i_size: The size of the L1 Instruction Cache (e.g., "32kB"). - :type l1i_size: str - :param l2_size: The size of the L2 Cache (e.g., "256kB"). - :type l2_size: str - :param membus: The memory bus. This parameter is optional parameter and - will default to a 64 bit width SystemXBar is not specified. - - :type membus: BaseXBar + will default to a 64 bit width SystemXBar is not specified. """ AbstractClassicCacheHierarchy.__init__(self=self) @@ -111,7 +109,6 @@ class PrivateL1PrivateL2CacheHierarchy( @overrides(AbstractCacheHierarchy) def incorporate_cache(self, board: AbstractBoard) -> None: - # Set up the system port for functional access from the simulator. board.connect_system_port(self.membus.cpu_side_ports) @@ -148,7 +145,6 @@ class PrivateL1PrivateL2CacheHierarchy( self._setup_io_cache(board) for i, cpu in enumerate(board.get_processor().get_cores()): - cpu.connect_icache(self.l1icaches[i].cpu_side) cpu.connect_dcache(self.l1dcaches[i].cpu_side) diff --git a/src/python/gem5/components/cachehierarchies/classic/private_l1_shared_l2_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/classic/private_l1_shared_l2_cache_hierarchy.py index 72df1a53de..4e2c622d62 100644 --- a/src/python/gem5/components/cachehierarchies/classic/private_l1_shared_l2_cache_hierarchy.py +++ b/src/python/gem5/components/cachehierarchies/classic/private_l1_shared_l2_cache_hierarchy.py @@ -24,18 +24,25 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects import ( + BadAddr, + BaseXBar, + Cache, + L2XBar, + Port, + SystemXBar, +) + +from ....isas import ISA +from ....utils.override import * +from ...boards.abstract_board import AbstractBoard from ..abstract_cache_hierarchy import AbstractCacheHierarchy -from .abstract_classic_cache_hierarchy import AbstractClassicCacheHierarchy from ..abstract_two_level_cache_hierarchy import AbstractTwoLevelCacheHierarchy +from .abstract_classic_cache_hierarchy import AbstractClassicCacheHierarchy from .caches.l1dcache import L1DCache from .caches.l1icache import L1ICache from .caches.l2cache import L2Cache from .caches.mmu_cache import MMUCache -from ...boards.abstract_board import AbstractBoard -from ....isas import ISA -from m5.objects import Cache, L2XBar, BaseXBar, SystemXBar, BadAddr, Port - -from ....utils.override import * class PrivateL1SharedL2CacheHierarchy( @@ -54,7 +61,7 @@ class PrivateL1SharedL2CacheHierarchy( the PrivateL1SharedL2 CacheHierarchy. :returns: The default memory bus for the PrivateL1SharedL2 - CacheHierarchy. + CacheHierarchy. :rtype: SystemXBar """ @@ -81,7 +88,7 @@ class PrivateL1SharedL2CacheHierarchy( :param l1i_assoc: The associativity of the L1 Instruction Cache. :param l2_assoc: The associativity of the L2 Cache. :param membus: The memory bus. This parameter is optional parameter and - will default to a 64 bit width SystemXBar is not specified. + will default to a 64 bit width SystemXBar is not specified. """ AbstractClassicCacheHierarchy.__init__(self=self) @@ -107,7 +114,6 @@ class PrivateL1SharedL2CacheHierarchy( @overrides(AbstractCacheHierarchy) def incorporate_cache(self, board: AbstractBoard) -> None: - # Set up the system port for functional access from the simulator. board.connect_system_port(self.membus.cpu_side_ports) @@ -143,7 +149,6 @@ class PrivateL1SharedL2CacheHierarchy( self._setup_io_cache(board) for i, cpu in enumerate(board.get_processor().get_cores()): - cpu.connect_icache(self.l1icaches[i].cpu_side) cpu.connect_dcache(self.l1dcaches[i].cpu_side) diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_directory.py b/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_directory.py index e39a38ccc9..8552b975e1 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_directory.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_directory.py @@ -30,7 +30,6 @@ from m5.objects import Directory_Controller class AbstractDirectory(Directory_Controller): - _version = 0 @classmethod diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_dma_controller.py b/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_dma_controller.py index 8d36736017..dad1f7cd72 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_dma_controller.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_dma_controller.py @@ -30,7 +30,6 @@ from m5.objects import DMA_Controller class AbstractDMAController(DMA_Controller): - _version = 0 @classmethod diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_l1_cache.py b/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_l1_cache.py index 683d69584c..a9944c4d03 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_l1_cache.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_l1_cache.py @@ -24,18 +24,17 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import math from abc import abstractmethod -from .....isas import ISA -from ....processors.cpu_types import CPUTypes -from ....processors.abstract_core import AbstractCore from m5.objects import L1Cache_Controller -import math +from .....isas import ISA +from ....processors.abstract_core import AbstractCore +from ....processors.cpu_types import CPUTypes class AbstractL1Cache(L1Cache_Controller): - _version = 0 @classmethod diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_l2_cache.py b/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_l2_cache.py index 88b41228c4..41929f4e42 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_l2_cache.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/abstract_l2_cache.py @@ -30,7 +30,6 @@ from m5.objects import L2Cache_Controller class AbstractL2Cache(L2Cache_Controller): - _version = 0 @classmethod diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/directory.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/directory.py index cd4f166fed..4840e3b264 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/directory.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/directory.py @@ -24,15 +24,17 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects import ( + MessageBuffer, + RubyDirectoryMemory, +) + from ......utils.override import overrides from ..abstract_directory import AbstractDirectory -from m5.objects import MessageBuffer, RubyDirectoryMemory - class Directory(AbstractDirectory): def __init__(self, network, cache_line_size, mem_range, port): - super().__init__(network, cache_line_size) self.addr_ranges = [mem_range] self.directory = RubyDirectoryMemory() diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/dma_controller.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/dma_controller.py index f731869f54..145757c15a 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/dma_controller.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/dma_controller.py @@ -24,9 +24,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from ......utils.override import overrides +from m5.objects import ( + DMA_Controller, + MessageBuffer, +) -from m5.objects import MessageBuffer, DMA_Controller +from ......utils.override import overrides class DMAController(DMA_Controller): diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l1_cache.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l1_cache.py index e746579834..6d203f978a 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l1_cache.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l1_cache.py @@ -24,25 +24,25 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .....processors.abstract_core import AbstractCore -from ......isas import ISA -from ......utils.override import * +import math from m5.objects import ( - MessageBuffer, - RubyPrefetcher, - RubyCache, - ClockDomain, LRURP, + ClockDomain, L0Cache_Controller, + MessageBuffer, + RubyCache, + RubyPrefetcher, ) -import math +from ......isas import ISA +from ......utils.override import * +from .....processors.abstract_core import AbstractCore + # L0Cache_Controller is the ruby backend's terminology corresponding to # L1 cache in stdlib terms. class L1Cache(L0Cache_Controller): - _version = 0 @classmethod diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l2_cache.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l2_cache.py index dfc1304a87..ff2b8e3dd9 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l2_cache.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l2_cache.py @@ -24,24 +24,24 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .....processors.abstract_core import AbstractCore -from ......isas import ISA -from ......utils.override import * +import math from m5.objects import ( - MessageBuffer, - RubyPrefetcher, - RubyCache, ClockDomain, L1Cache_Controller, + MessageBuffer, + RubyCache, + RubyPrefetcher, ) -import math +from ......isas import ISA +from ......utils.override import * +from .....processors.abstract_core import AbstractCore + # L1Cache_Controller is ruby backend's terminology corresponding to # L2Cache in stdlib's terms class L2Cache(L1Cache_Controller): - _version = 0 @classmethod diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l3_cache.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l3_cache.py index 0a93d9b0c8..8509a35cfc 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l3_cache.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_three_level/l3_cache.py @@ -24,14 +24,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import MessageBuffer, RubyCache, L2Cache_Controller - import math +from m5.objects import ( + L2Cache_Controller, + MessageBuffer, + RubyCache, +) + + # L2Cache_Controller is ruby backend's terminology corresponding to # L3 cache in stdlib. class L3Cache(L2Cache_Controller): - _version = 0 @classmethod diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/directory.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/directory.py index cd4f166fed..4840e3b264 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/directory.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/directory.py @@ -24,15 +24,17 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects import ( + MessageBuffer, + RubyDirectoryMemory, +) + from ......utils.override import overrides from ..abstract_directory import AbstractDirectory -from m5.objects import MessageBuffer, RubyDirectoryMemory - class Directory(AbstractDirectory): def __init__(self, network, cache_line_size, mem_range, port): - super().__init__(network, cache_line_size) self.addr_ranges = [mem_range] self.directory = RubyDirectoryMemory() diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/dma_controller.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/dma_controller.py index ab76d4cb5e..aa7a5ad778 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/dma_controller.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/dma_controller.py @@ -24,11 +24,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects import MessageBuffer + from ......utils.override import overrides from ..abstract_dma_controller import AbstractDMAController -from m5.objects import MessageBuffer - class DMAController(AbstractDMAController): def __init__(self, network, cache_line_size): diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/l1_cache.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/l1_cache.py index 0e0e333da9..7787644c9b 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/l1_cache.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/l1_cache.py @@ -24,15 +24,20 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .....processors.abstract_core import AbstractCore -from ......isas import ISA -from ..abstract_l1_cache import AbstractL1Cache -from ......utils.override import * - -from m5.objects import MessageBuffer, RubyPrefetcher, RubyCache, ClockDomain - import math +from m5.objects import ( + ClockDomain, + MessageBuffer, + RubyCache, + RubyPrefetcher, +) + +from ......isas import ISA +from ......utils.override import * +from .....processors.abstract_core import AbstractCore +from ..abstract_l1_cache import AbstractL1Cache + class L1Cache(AbstractL1Cache): def __init__( diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/l2_cache.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/l2_cache.py index 81ef4dbe90..4f7c923c54 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/l2_cache.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/l2_cache.py @@ -24,13 +24,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from ..abstract_l2_cache import AbstractL2Cache -from ......utils.override import * - -from m5.objects import MessageBuffer, RubyCache - import math +from m5.objects import ( + MessageBuffer, + RubyCache, +) + +from ......utils.override import * +from ..abstract_l2_cache import AbstractL2Cache + class L2Cache(AbstractL2Cache): def __init__( diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mi_example/directory.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mi_example/directory.py index e74772cc18..3d1ae54104 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/mi_example/directory.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mi_example/directory.py @@ -24,11 +24,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from ..abstract_directory import AbstractDirectory +from m5.objects import ( + MessageBuffer, + RubyDirectoryMemory, +) + from ......utils.override import overrides - - -from m5.objects import MessageBuffer, RubyDirectoryMemory +from ..abstract_directory import AbstractDirectory class Directory(AbstractDirectory): @@ -37,7 +39,6 @@ class Directory(AbstractDirectory): """ def __init__(self, network, cache_line_size, mem_range, port): - super().__init__(network, cache_line_size) self.addr_ranges = [mem_range] self.directory = RubyDirectoryMemory() diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mi_example/dma_controller.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mi_example/dma_controller.py index fb6895f4f8..b9c1f4a60f 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/mi_example/dma_controller.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mi_example/dma_controller.py @@ -24,11 +24,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from ..abstract_dma_controller import AbstractDMAController -from ......utils.override import overrides - from m5.objects import MessageBuffer +from ......utils.override import overrides +from ..abstract_dma_controller import AbstractDMAController + class DMAController(AbstractDMAController): """ diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/mi_example/l1_cache.py b/src/python/gem5/components/cachehierarchies/ruby/caches/mi_example/l1_cache.py index 1368b92bfc..2feae222d8 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/mi_example/l1_cache.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/mi_example/l1_cache.py @@ -24,13 +24,17 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects import ( + ClockDomain, + MessageBuffer, + RubyCache, +) + +from ......isas import ISA from ......utils.override import overrides from .....processors.abstract_core import AbstractCore -from ......isas import ISA from ..abstract_l1_cache import AbstractL1Cache -from m5.objects import MessageBuffer, RubyCache, ClockDomain - class L1Cache(AbstractL1Cache): def __init__( diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/core_complex.py b/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/core_complex.py index f056d76f98..9aa0dc4a36 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/core_complex.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/core_complex.py @@ -24,11 +24,17 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from typing import List, Tuple +from typing import ( + List, + Tuple, +) + +from m5.objects import ( + RubySequencer, + SubSystem, +) -from gem5.isas import ISA from gem5.components.boards.abstract_board import AbstractBoard -from gem5.components.processors.abstract_core import AbstractCore from gem5.components.cachehierarchies.ruby.caches.mesi_three_level.l1_cache import ( L1Cache, ) @@ -38,14 +44,14 @@ from gem5.components.cachehierarchies.ruby.caches.mesi_three_level.l2_cache impo from gem5.components.cachehierarchies.ruby.caches.mesi_three_level.l3_cache import ( L3Cache, ) - -from m5.objects import SubSystem, RubySequencer +from gem5.components.processors.abstract_core import AbstractCore +from gem5.isas import ISA from .ruby_network_components import ( - RubyRouter, RubyExtLink, RubyIntLink, RubyNetworkComponent, + RubyRouter, ) diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/octopi.py b/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/octopi.py index 9c8b93d812..f7d4d63de1 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/octopi.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/octopi.py @@ -24,26 +24,33 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from ...abstract_ruby_cache_hierarchy import AbstractRubyCacheHierarchy -from ....abstract_three_level_cache_hierarchy import ( - AbstractThreeLevelCacheHierarchy, +from m5.objects import ( + DMASequencer, + RubyPortProxy, + RubySystem, ) + from ......coherence_protocol import CoherenceProtocol from ......components.boards.abstract_board import AbstractBoard -from ......utils.requires import requires - from ......components.cachehierarchies.ruby.caches.mesi_three_level.directory import ( Directory, ) from ......components.cachehierarchies.ruby.caches.mesi_three_level.dma_controller import ( DMAController, ) - -from m5.objects import RubySystem, DMASequencer, RubyPortProxy - +from ......utils.requires import requires +from ....abstract_three_level_cache_hierarchy import ( + AbstractThreeLevelCacheHierarchy, +) +from ...abstract_ruby_cache_hierarchy import AbstractRubyCacheHierarchy from .core_complex import CoreComplex from .octopi_network import OctopiNetwork -from .ruby_network_components import RubyRouter, RubyExtLink, RubyIntLink +from .ruby_network_components import ( + RubyExtLink, + RubyIntLink, + RubyRouter, +) + # CoreComplex sub-systems own the L1, L2, L3 controllers # OctopiCache owns the directory controllers @@ -85,7 +92,6 @@ class OctopiCache( self._is_fullsystem = is_fullsystem def incorporate_cache(self, board: AbstractBoard) -> None: - requires( coherence_protocol_required=CoherenceProtocol.MESI_THREE_LEVEL ) diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/octopi_network.py b/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/octopi_network.py index 745ef826c7..c175a24778 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/octopi_network.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/octopi_network.py @@ -27,11 +27,12 @@ from m5.objects import SimpleNetwork from .ruby_network_components import ( + RubyIntLink, RubyNetworkComponent, RubyRouter, - RubyIntLink, ) + # . The Network owns all routers, all int links and all ext links that are not in CCD's. # . The CCD subsystems are not of type RubyNetwork, so we need to copy the references of # routers and links to OctopiNetwork._routers, ._int_links, and ._ext_links; which will diff --git a/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/ruby_network_components.py b/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/ruby_network_components.py index 8a413ea59d..70cfe4242d 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/ruby_network_components.py +++ b/src/python/gem5/components/cachehierarchies/ruby/caches/prebuilt/octopi_cache/ruby_network_components.py @@ -24,7 +24,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import Switch, SimpleIntLink, SimpleExtLink +from m5.objects import ( + SimpleExtLink, + SimpleIntLink, + Switch, +) class RubyNetworkComponent: diff --git a/src/python/gem5/components/cachehierarchies/ruby/mesi_three_level_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/ruby/mesi_three_level_cache_hierarchy.py index 89b6b21177..039ba8fb7a 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/mesi_three_level_cache_hierarchy.py +++ b/src/python/gem5/components/cachehierarchies/ruby/mesi_three_level_cache_hierarchy.py @@ -25,23 +25,27 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .abstract_ruby_cache_hierarchy import AbstractRubyCacheHierarchy +from m5.objects import ( + DMASequencer, + RubyPortProxy, + RubySequencer, + RubySystem, +) + +from ....coherence_protocol import CoherenceProtocol +from ....isas import ISA +from ....utils.requires import requires +from ...boards.abstract_board import AbstractBoard from ..abstract_three_level_cache_hierarchy import ( AbstractThreeLevelCacheHierarchy, ) -from ....coherence_protocol import CoherenceProtocol -from ....isas import ISA -from ...boards.abstract_board import AbstractBoard -from ....utils.requires import requires - -from .topologies.simple_pt2pt import SimplePt2Pt +from .abstract_ruby_cache_hierarchy import AbstractRubyCacheHierarchy +from .caches.mesi_three_level.directory import Directory +from .caches.mesi_three_level.dma_controller import DMAController from .caches.mesi_three_level.l1_cache import L1Cache from .caches.mesi_three_level.l2_cache import L2Cache from .caches.mesi_three_level.l3_cache import L3Cache -from .caches.mesi_three_level.directory import Directory -from .caches.mesi_three_level.dma_controller import DMAController - -from m5.objects import RubySystem, RubySequencer, DMASequencer, RubyPortProxy +from .topologies.simple_pt2pt import SimplePt2Pt class MESIThreeLevelCacheHierarchy( @@ -80,7 +84,6 @@ class MESIThreeLevelCacheHierarchy( self._num_l3_banks = num_l3_banks def incorporate_cache(self, board: AbstractBoard) -> None: - requires( coherence_protocol_required=CoherenceProtocol.MESI_THREE_LEVEL ) @@ -193,10 +196,10 @@ class MESIThreeLevelCacheHierarchy( if board.has_dma_ports(): dma_ports = board.get_dma_ports() for i, port in enumerate(dma_ports): - ctrl = DMAController(self.ruby_system.network, cache_line_size) - ctrl.dma_sequencer = DMASequencer(version=i, in_ports=port) + ctrl = DMAController( + DMASequencer(version=i, in_ports=port), self.ruby_system + ) self._dma_controllers.append(ctrl) - ctrl.ruby_system = self.ruby_system self.ruby_system.num_of_sequencers = len(self._l1_controllers) + len( self._dma_controllers diff --git a/src/python/gem5/components/cachehierarchies/ruby/mesi_two_level_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/ruby/mesi_two_level_cache_hierarchy.py index 79c8b0ada3..083e4d43fe 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/mesi_two_level_cache_hierarchy.py +++ b/src/python/gem5/components/cachehierarchies/ruby/mesi_two_level_cache_hierarchy.py @@ -25,20 +25,24 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .abstract_ruby_cache_hierarchy import AbstractRubyCacheHierarchy -from ..abstract_two_level_cache_hierarchy import AbstractTwoLevelCacheHierarchy +from m5.objects import ( + DMASequencer, + RubyPortProxy, + RubySequencer, + RubySystem, +) + from ....coherence_protocol import CoherenceProtocol from ....isas import ISA -from ...boards.abstract_board import AbstractBoard from ....utils.requires import requires - -from .topologies.simple_pt2pt import SimplePt2Pt -from .caches.mesi_two_level.l1_cache import L1Cache -from .caches.mesi_two_level.l2_cache import L2Cache +from ...boards.abstract_board import AbstractBoard +from ..abstract_two_level_cache_hierarchy import AbstractTwoLevelCacheHierarchy +from .abstract_ruby_cache_hierarchy import AbstractRubyCacheHierarchy from .caches.mesi_two_level.directory import Directory from .caches.mesi_two_level.dma_controller import DMAController - -from m5.objects import RubySystem, RubySequencer, DMASequencer, RubyPortProxy +from .caches.mesi_two_level.l1_cache import L1Cache +from .caches.mesi_two_level.l2_cache import L2Cache +from .topologies.simple_pt2pt import SimplePt2Pt class MESITwoLevelCacheHierarchy( @@ -76,7 +80,6 @@ class MESITwoLevelCacheHierarchy( self._num_l2_banks = num_l2_banks def incorporate_cache(self, board: AbstractBoard) -> None: - requires(coherence_protocol_required=CoherenceProtocol.MESI_TWO_LEVEL) cache_line_size = board.get_cache_line_size() diff --git a/src/python/gem5/components/cachehierarchies/ruby/mi_example_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/ruby/mi_example_cache_hierarchy.py index 5955ad3b20..328fb0f998 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/mi_example_cache_hierarchy.py +++ b/src/python/gem5/components/cachehierarchies/ruby/mi_example_cache_hierarchy.py @@ -24,20 +24,24 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .caches.mi_example.l1_cache import L1Cache -from .caches.mi_example.dma_controller import DMAController -from .caches.mi_example.directory import Directory -from .topologies.simple_pt2pt import SimplePt2Pt -from .abstract_ruby_cache_hierarchy import AbstractRubyCacheHierarchy -from ..abstract_cache_hierarchy import AbstractCacheHierarchy -from ...boards.abstract_board import AbstractBoard +from m5.objects import ( + DMASequencer, + RubyPortProxy, + RubySequencer, + RubySystem, +) + from ....coherence_protocol import CoherenceProtocol from ....isas import ISA from ....utils.override import overrides from ....utils.requires import requires - - -from m5.objects import RubySystem, RubySequencer, DMASequencer, RubyPortProxy +from ...boards.abstract_board import AbstractBoard +from ..abstract_cache_hierarchy import AbstractCacheHierarchy +from .abstract_ruby_cache_hierarchy import AbstractRubyCacheHierarchy +from .caches.mi_example.directory import Directory +from .caches.mi_example.dma_controller import DMAController +from .caches.mi_example.l1_cache import L1Cache +from .topologies.simple_pt2pt import SimplePt2Pt class MIExampleCacheHierarchy(AbstractRubyCacheHierarchy): @@ -58,7 +62,6 @@ class MIExampleCacheHierarchy(AbstractRubyCacheHierarchy): @overrides(AbstractCacheHierarchy) def incorporate_cache(self, board: AbstractBoard) -> None: - requires(coherence_protocol_required=CoherenceProtocol.MI_EXAMPLE) self.ruby_system = RubySystem() diff --git a/src/python/gem5/components/cachehierarchies/ruby/topologies/simple_pt2pt.py b/src/python/gem5/components/cachehierarchies/ruby/topologies/simple_pt2pt.py index 649f027d72..65c3ad51c2 100644 --- a/src/python/gem5/components/cachehierarchies/ruby/topologies/simple_pt2pt.py +++ b/src/python/gem5/components/cachehierarchies/ruby/topologies/simple_pt2pt.py @@ -24,7 +24,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import SimpleNetwork, Switch, SimpleExtLink, SimpleIntLink +from m5.objects import ( + SimpleExtLink, + SimpleIntLink, + SimpleNetwork, + Switch, +) class SimplePt2Pt(SimpleNetwork): diff --git a/src/python/gem5/components/memory/__init__.py b/src/python/gem5/components/memory/__init__.py index 546d5d98ed..49a8cb8173 100644 --- a/src/python/gem5/components/memory/__init__.py +++ b/src/python/gem5/components/memory/__init__.py @@ -24,26 +24,32 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .single_channel import SingleChannelDDR3_1600 -from .single_channel import SingleChannelDDR3_2133 -from .single_channel import SingleChannelDDR4_2400 -from .single_channel import SingleChannelHBM -from .single_channel import SingleChannelLPDDR3_1600 -from .single_channel import DIMM_DDR5_4400 -from .single_channel import DIMM_DDR5_6400 -from .single_channel import DIMM_DDR5_8400 -from .multi_channel import DualChannelDDR3_1600 -from .multi_channel import DualChannelDDR3_2133 -from .multi_channel import DualChannelDDR4_2400 -from .multi_channel import DualChannelLPDDR3_1600 from .hbm import HBM2Stack +from .multi_channel import ( + DualChannelDDR3_1600, + DualChannelDDR3_2133, + DualChannelDDR4_2400, + DualChannelLPDDR3_1600, +) +from .single_channel import ( + DIMM_DDR5_4400, + DIMM_DDR5_6400, + DIMM_DDR5_8400, + SingleChannelDDR3_1600, + SingleChannelDDR3_2133, + SingleChannelDDR4_2400, + SingleChannelHBM, + SingleChannelLPDDR3_1600, +) try: - from .dramsys import DRAMSysMem - from .dramsys import DRAMSysDDR4_1866 - from .dramsys import DRAMSysDDR3_1600 - from .dramsys import DRAMSysLPDDR4_3200 - from .dramsys import DRAMSysHBM2 + from .dramsys import ( + DRAMSysDDR3_1600, + DRAMSysDDR4_1866, + DRAMSysHBM2, + DRAMSysLPDDR4_3200, + DRAMSysMem, + ) except: # In the case that DRAMSys is not compiled into the gem5 binary, importing # DRAMSys components will fail. This try-exception statement is needed to diff --git a/src/python/gem5/components/memory/abstract_memory_system.py b/src/python/gem5/components/memory/abstract_memory_system.py index cfbf6ac01c..e214602732 100644 --- a/src/python/gem5/components/memory/abstract_memory_system.py +++ b/src/python/gem5/components/memory/abstract_memory_system.py @@ -24,14 +24,25 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from abc import ABCMeta, abstractmethod -from typing import Tuple, Sequence, List +from abc import ( + ABCMeta, + abstractmethod, +) +from typing import ( + List, + Sequence, + Tuple, +) +from m5.objects import ( + AddrRange, + MemCtrl, + Port, + SubSystem, +) from ..boards.abstract_board import AbstractBoard -from m5.objects import AddrRange, Port, SubSystem, MemCtrl - class AbstractMemorySystem(SubSystem): __metaclass__ = ABCMeta @@ -47,22 +58,22 @@ class AbstractMemorySystem(SubSystem): @abstractmethod def get_mem_ports(self) -> Sequence[Tuple[AddrRange, Port]]: - """Get the ports to connect this memory system to the cache""" + """Get the ports to connect this memory system to the cache.""" raise NotImplementedError @abstractmethod def get_memory_controllers(self) -> List[MemCtrl]: - """Get all of the memory controllers in this memory system""" + """Get all of the memory controllers in this memory system.""" raise NotImplementedError @abstractmethod def get_size(self) -> int: - """Returns the total size of the memory system""" + """Returns the total size of the memory system.""" raise NotImplementedError @abstractmethod def set_memory_range(self, ranges: List[AddrRange]) -> None: - """Set the total range for this memory system + """Set the total range for this memory system. May pass multiple non-overlapping ranges. The total size of the ranges should match the size of the memory. @@ -73,5 +84,5 @@ class AbstractMemorySystem(SubSystem): raise NotImplementedError def _post_instantiate(self) -> None: - """Called to set up anything needed after m5.instantiate""" + """Called to set up anything needed after ``m5.instantiate``.""" pass diff --git a/src/python/gem5/components/memory/dram_interfaces/ddr3.py b/src/python/gem5/components/memory/dram_interfaces/ddr3.py index ec8f0392cc..7b75354de0 100644 --- a/src/python/gem5/components/memory/dram_interfaces/ddr3.py +++ b/src/python/gem5/components/memory/dram_interfaces/ddr3.py @@ -43,7 +43,7 @@ These memory "interfaces" contain the timing, energy, etc. parameters for each memory type and are usually based on datasheets for the memory devices. -You can use these interfaces in the MemCtrl object as the `dram` timing +You can use these interfaces in the MemCtrl object as the ``dram`` timing interface. """ diff --git a/src/python/gem5/components/memory/dram_interfaces/ddr4.py b/src/python/gem5/components/memory/dram_interfaces/ddr4.py index 62017e4493..b35c7ac399 100644 --- a/src/python/gem5/components/memory/dram_interfaces/ddr4.py +++ b/src/python/gem5/components/memory/dram_interfaces/ddr4.py @@ -40,10 +40,10 @@ """Interfaces for DDR4 memories -These memory "interfaces" contain the timing,energy,etc parameters for each +These memory "interfaces" contain the timing, energy, etc parameters for each memory type and are usually based on datasheets for the memory devices. -You can use these interfaces in the MemCtrl object as the `dram` timing +You can use these interfaces in the MemCtrl object as the ``dram`` timing interface. """ @@ -55,7 +55,9 @@ class DDR4_2400_16x4(DRAMInterface): A single DDR4-2400 x64 channel (one command and address bus), with timings based on a DDR4-2400 8 Gbit datasheet (Micron MT40A2G4) in an 16x4 configuration. - Total channel capacity is 32GiB + + Total channel capacity is 32GiB. + 16 devices/rank * 2 ranks/channel * 1GiB/device = 32GiB/channel """ @@ -170,7 +172,9 @@ class DDR4_2400_8x8(DDR4_2400_16x4): A single DDR4-2400 x64 channel (one command and address bus), with timings based on a DDR4-2400 8 Gbit datasheet (Micron MT40A1G8) in an 8x8 configuration. - Total channel capacity is 16GiB + + Total channel capacity is 16GiB. + 8 devices/rank * 2 ranks/channel * 1GiB/device = 16GiB/channel """ @@ -201,7 +205,9 @@ class DDR4_2400_4x16(DDR4_2400_16x4): A single DDR4-2400 x64 channel (one command and address bus), with timings based on a DDR4-2400 8 Gbit datasheet (Micron MT40A512M16) in an 4x16 configuration. - Total channel capacity is 4GiB + + Total channel capacity is 4GiB. + 4 devices/rank * 1 ranks/channel * 1GiB/device = 4GiB/channel """ diff --git a/src/python/gem5/components/memory/dram_interfaces/gddr.py b/src/python/gem5/components/memory/dram_interfaces/gddr.py index 4c5e3c5bf4..fc28316e29 100644 --- a/src/python/gem5/components/memory/dram_interfaces/gddr.py +++ b/src/python/gem5/components/memory/dram_interfaces/gddr.py @@ -40,10 +40,10 @@ """Interfaces for GDDR memory devices -These memory "interfaces" contain the timing,energy,etc parameters for each +These memory "interfaces" contain the timing, energy, etc parameters for each memory type and are usually based on datasheets for the memory devices. -You can use these interfaces in the MemCtrl object as the `dram` timing +You can use these interfaces in the MemCtrl object as the ``dram`` timing interface. """ diff --git a/src/python/gem5/components/memory/dram_interfaces/hbm.py b/src/python/gem5/components/memory/dram_interfaces/hbm.py index 5063c4d9e1..50af4a9325 100644 --- a/src/python/gem5/components/memory/dram_interfaces/hbm.py +++ b/src/python/gem5/components/memory/dram_interfaces/hbm.py @@ -40,10 +40,10 @@ """Interfaces for HBM memory devices -These memory "interfaces" contain the timing,energy,etc parameters for each +These memory "interfaces" contain the timing, energy, etc parameters for each memory type and are usually based on datasheets for the memory devices. -You can use these interfaces in the MemCtrl object as the `dram` timing +You can use these interfaces in the MemCtrl object as the ``dram`` timing interface. """ @@ -56,15 +56,18 @@ class HBM_1000_4H_1x128(DRAMInterface): default timings based on data publically released ("HBM: Memory Solution for High Performance Processors", MemCon, 2014), IDD measurement values, and by extrapolating data from other classes. - Architecture values based on published HBM spec + + Architecture values based on published HBM spec. + A 4H stack is defined, 2Gb per die for a total of 1GiB of memory. **IMPORTANT** HBM gen1 supports up to 8 128-bit physical channels Configuration defines a single channel, with the capacity - set to (full_ stack_capacity / 8) based on 2Gb dies - To use all 8 channels, set 'channels' parameter to 8 in - system configuration + set to (full_ stack_capacity / 8) based on 2Gb dies. + + To use all 8 channels, set ``channels`` parameter to 8 in + system configuration. """ # 128-bit interface legacy mode @@ -149,16 +152,17 @@ class HBM_1000_4H_1x64(HBM_1000_4H_1x128): default timings based on HBM gen1 and data publically released A 4H stack is defined, 8Gb per die for a total of 4GiB of memory. Note: This defines a pseudo-channel with a unique controller - instantiated per pseudo-channel + instantiated per pseudo-channel. + Stay at same IO rate (1Gbps) to maintain timing relationship with - HBM gen1 class (HBM_1000_4H_x128) where possible + HBM gen1 class (HBM_1000_4H_x128) where possible. **IMPORTANT** For HBM gen2 with pseudo-channel mode, configure 2X channels. Configuration defines a single pseudo channel, with the capacity - set to (full_ stack_capacity / 16) based on 8Gb dies - To use all 16 pseudo channels, set 'channels' parameter to 16 in - system configuration + set to (full_ stack_capacity / 16) based on 8Gb dies. + To use all 16 pseudo channels, set ``channels`` parameter to 16 in + system configuration. """ # 64-bit pseudo-channel interface @@ -201,7 +205,6 @@ class HBM_1000_4H_1x64(HBM_1000_4H_1x128): # on HBM gen2 specifications. 4H stack, 8Gb per die and total capacity # of 4GiB. class HBM_2000_4H_1x64(DRAMInterface): - # 64-bit interface for a single pseudo channel device_bus_width = 64 diff --git a/src/python/gem5/components/memory/dram_interfaces/hmc.py b/src/python/gem5/components/memory/dram_interfaces/hmc.py index 2c445ef57f..1371b78c18 100644 --- a/src/python/gem5/components/memory/dram_interfaces/hmc.py +++ b/src/python/gem5/components/memory/dram_interfaces/hmc.py @@ -40,10 +40,10 @@ """Interfaces for HMC memory devices -These memory "interfaces" contain the timing,energy,etc parameters for each +These memory "interfaces" contain the timing, energy, etc parameters for each memory type and are usually based on datasheets for the memory devices. -You can use these interfaces in the MemCtrl object as the `dram` timing +You can use these interfaces in the MemCtrl object as the ``dram`` timing interface. Note that HMC is configured differently than some other DRAM interfaces. @@ -58,22 +58,29 @@ class HMC_2500_1x32(DDR3_1600_8x8): uses RC (resistance-capacitance) and CV (capacitance-voltage) models to estimate the DRAM bank latency and power numbers. [2] High performance AXI-4.0 based interconnect for extensible smart memory - cubes (E. Azarkhish et. al) + cubes (E. Azarkhish et. al). Assumed for the HMC model is a 30 nm technology node. The modelled HMC consists of 4 Gbit layers which sum up to 2GiB of memory (4 layers). Each layer has 16 vaults and each vault consists of 2 banks per layer. In order to be able to use the same controller used for 2D DRAM generations for HMC, the following analogy is done: + Channel (DDR) => Vault (HMC) + device_size (DDR) => size of a single layer in a vault + ranks per channel (DDR) => number of layers + banks per rank (DDR) => banks per layer + devices per rank (DDR) => devices per layer ( 1 for HMC). + The parameters for which no input is available are inherited from the DDR3 configuration. + This configuration includes the latencies from the DRAM to the logic layer - of the HMC + of the HMC. """ # size of device diff --git a/src/python/gem5/components/memory/dram_interfaces/lpddr2.py b/src/python/gem5/components/memory/dram_interfaces/lpddr2.py index 53725a2266..b5209e3a12 100644 --- a/src/python/gem5/components/memory/dram_interfaces/lpddr2.py +++ b/src/python/gem5/components/memory/dram_interfaces/lpddr2.py @@ -43,7 +43,7 @@ These memory "interfaces" contain the timing,energy,etc parameters for each memory type and are usually based on datasheets for the memory devices. -You can use these interfaces in the MemCtrl object as the `dram` timing +You can use these interfaces in the MemCtrl object as the ``dram`` timing interface. """ diff --git a/src/python/gem5/components/memory/dram_interfaces/lpddr3.py b/src/python/gem5/components/memory/dram_interfaces/lpddr3.py index d08e8fbdda..6b483e9d3f 100644 --- a/src/python/gem5/components/memory/dram_interfaces/lpddr3.py +++ b/src/python/gem5/components/memory/dram_interfaces/lpddr3.py @@ -43,7 +43,7 @@ These memory "interfaces" contain the timing,energy,etc parameters for each memory type and are usually based on datasheets for the memory devices. -You can use these interfaces in the MemCtrl object as the `dram` timing +You can use these interfaces in the MemCtrl object as the ``dram`` timing interface. """ diff --git a/src/python/gem5/components/memory/dram_interfaces/lpddr5.py b/src/python/gem5/components/memory/dram_interfaces/lpddr5.py index f35bef15ae..574f5c242b 100644 --- a/src/python/gem5/components/memory/dram_interfaces/lpddr5.py +++ b/src/python/gem5/components/memory/dram_interfaces/lpddr5.py @@ -43,7 +43,7 @@ These memory "interfaces" contain the timing,energy,etc parameters for each memory type and are usually based on datasheets for the memory devices. -You can use these interfaces in the MemCtrl object as the `dram` timing +You can use these interfaces in the MemCtrl object as the ``dram`` timing interface. """ @@ -54,10 +54,12 @@ class LPDDR5_5500_1x16_BG_BL32(DRAMInterface): """ A single LPDDR5 x16 interface (one command/address bus) for a single x16 channel with default timings based on - initial JEDEC specification - Starting with 5.5Gbps data rates and 8Gbit die + initial JEDEC specification. + + Starting with 5.5Gbps data rates and 8Gbit die. + Configuring for 16-bank mode with bank-group architecture - burst of 32, which means bursts can be interleaved + burst of 32, which means bursts can be interleaved. """ # Increase buffer size to account for more bank resources @@ -208,9 +210,11 @@ class LPDDR5_5500_1x16_8B_BL32(LPDDR5_5500_1x16_BG_BL32): """ A single LPDDR5 x16 interface (one command/address bus) for a single x16 channel with default timings based on - initial JEDEC specification - Starting with 5.5Gbps data rates and 8Gbit die - Configuring for 8-bank mode, burst of 32 + initial JEDEC specification. + + Starting with 5.5Gbps data rates and 8Gbit die. + + Configuring for 8-bank mode, burst of 32. """ # 4KiB page with 8B mode @@ -249,10 +253,12 @@ class LPDDR5_6400_1x16_BG_BL32(LPDDR5_5500_1x16_BG_BL32): """ A single LPDDR5 x16 interface (one command/address bus) for a single x16 channel with default timings based on - initial JEDEC specification - 6.4Gbps data rates and 8Gbit die + initial JEDEC specification. + + 6.4Gbps data rates and 8Gbit die. + Configuring for 16-bank mode with bank-group architecture - burst of 32, which means bursts can be interleaved + burst of 32, which means bursts can be interleaved. """ # 5.5Gb/s DDR with 4:1 WCK:CK ratio for 687.5 MHz CK @@ -297,9 +303,11 @@ class LPDDR5_6400_1x16_BG_BL16(LPDDR5_6400_1x16_BG_BL32): """ A single LPDDR5 x16 interface (one command/address bus) for a single x16 channel with default timings based on initial - JEDEC specifcation - 6.4Gbps data rates and 8Gbit die - Configuring for 16-bank mode with bank-group architecture, burst of 16 + JEDEC specifcation. + + 6.4Gbps data rates and 8Gbit die. + + Configuring for 16-bank mode with bank-group architecture, burst of 16. """ # LPDDR5 is a BL16 or BL32 device @@ -321,9 +329,11 @@ class LPDDR5_6400_1x16_8B_BL32(LPDDR5_6400_1x16_BG_BL32): """ A single LPDDR5 x16 interface (one command/address bus) for a single x16 channel with default timings based on - initial JEDEC specification - 6.4Gbps data rates and 8Gbit die - Configuring for 8-bank mode, burst of 32 + initial JEDEC specification. + + 6.4Gbps data rates and 8Gbit die. + + Configuring for 8-bank mode, burst of 32. """ # 4KiB page with 8B mode diff --git a/src/python/gem5/components/memory/dram_interfaces/wideio.py b/src/python/gem5/components/memory/dram_interfaces/wideio.py index 1e00865071..82a9f8b440 100644 --- a/src/python/gem5/components/memory/dram_interfaces/wideio.py +++ b/src/python/gem5/components/memory/dram_interfaces/wideio.py @@ -43,7 +43,7 @@ These memory "interfaces" contain the timing,energy,etc parameters for each memory type and are usually based on datasheets for the memory devices. -You can use these interfaces in the MemCtrl object as the `dram` timing +You can use these interfaces in the MemCtrl object as the ``dram`` timing interface. """ diff --git a/src/python/gem5/components/memory/dramsim_3.py b/src/python/gem5/components/memory/dramsim_3.py index f154ba354f..62167e0b49 100644 --- a/src/python/gem5/components/memory/dramsim_3.py +++ b/src/python/gem5/components/memory/dramsim_3.py @@ -1,8 +1,19 @@ -import m5 -import os import configparser +import os +from typing import ( + List, + Optional, + Sequence, + Tuple, +) -from m5.objects import DRAMsim3, AddrRange, Port, MemCtrl +import m5 +from m5.objects import ( + AddrRange, + DRAMsim3, + MemCtrl, + Port, +) from m5.util.convert import toMemorySize from ...utils.override import overrides @@ -10,16 +21,15 @@ from ..boards.abstract_board import AbstractBoard from .abstract_memory_system import AbstractMemorySystem -from typing import Optional, Tuple, Sequence, List - - def config_ds3(mem_type: str, num_chnls: int) -> Tuple[str, str]: """ This function creates a config file that will be used to create a memory - controller of type DRAMSim3. It stores the config file in /tmp/ directory. + controller of type DRAMSim3. It stores the config file in ``/tmp/`` directory. :param mem_type: The name for the type of the memory to be configured. + :param num_chnls: The number of channels to configure for the memory + :returns: A tuple containing the output file and the output directory. """ config = configparser.ConfigParser() diff --git a/src/python/gem5/components/memory/dramsys.py b/src/python/gem5/components/memory/dramsys.py index 28f3bd319f..25e1c84b5a 100644 --- a/src/python/gem5/components/memory/dramsys.py +++ b/src/python/gem5/components/memory/dramsys.py @@ -24,14 +24,20 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import m5 +from pathlib import Path +from typing import ( + List, + Optional, + Sequence, + Tuple, +) from m5.objects import ( - DRAMSys, AddrRange, - Port, - MemCtrl, + DRAMSys, Gem5ToTlmBridge32, + MemCtrl, + Port, SystemC_Kernel, ) from m5.util.convert import toMemorySize @@ -40,27 +46,45 @@ from ...utils.override import overrides from ..boards.abstract_board import AbstractBoard from .abstract_memory_system import AbstractMemorySystem -from typing import Tuple, Sequence, List +DEFAULT_DRAMSYS_DIRECTORY = Path("ext/dramsys/DRAMSys") class DRAMSysMem(AbstractMemorySystem): + """ + A DRAMSys memory controller. + + This class requires gem5 to be built with DRAMSys (see ext/dramsys). + The specified memory size does not control the simulated memory size but it's sole purpose is + to notify gem5 of DRAMSys's memory size. + Therefore it has to match the DRAMSys configuration. + DRAMSys is configured using JSON files, whose base configuration has to be passed as a + parameter. Sub-configs are specified relative to the optional resource directory parameter. + """ + def __init__( self, configuration: str, size: str, - resource_directory: str, recordable: bool, + resource_directory: Optional[str] = None, ) -> None: """ :param configuration: Path to the base configuration JSON for DRAMSys. :param size: Memory size of DRAMSys. Must match the size specified in JSON configuration. - :param resource_directory: Path to the base resource directory for DRAMSys. :param recordable: Whether the database recording feature of DRAMSys is enabled. + :param resource_directory: Path to the base resource directory for DRAMSys. """ super().__init__() + + resource_directory_path = ( + DEFAULT_DRAMSYS_DIRECTORY / "configs" + if resource_directory is None + else Path(resource_directory) + ) + self.dramsys = DRAMSys( configuration=configuration, - resource_directory=resource_directory, + resource_directory=resource_directory_path.as_posix(), recordable=recordable, ) @@ -97,56 +121,72 @@ class DRAMSysMem(AbstractMemorySystem): class DRAMSysDDR4_1866(DRAMSysMem): + """ + An example DDR4 1866 DRAMSys configuration. + """ + def __init__(self, recordable: bool): """ :param recordable: Whether the database recording feature of DRAMSys is enabled. """ super().__init__( - configuration="ext/dramsys/DRAMSys/DRAMSys/" - "library/resources/simulations/ddr4-example.json", + configuration=( + DEFAULT_DRAMSYS_DIRECTORY / "configs/ddr4-example.json" + ).as_posix(), size="4GB", - resource_directory="ext/dramsys/DRAMSys/DRAMSys/library/resources", recordable=recordable, ) class DRAMSysDDR3_1600(DRAMSysMem): + """ + An example DDR3 1600 DRAMSys configuration. + """ + def __init__(self, recordable: bool): """ :param recordable: Whether the database recording feature of DRAMSys is enabled. """ super().__init__( - configuration="ext/dramsys/DRAMSys/DRAMSys/" - "library/resources/simulations/ddr3-gem5-se.json", - size="4GB", - resource_directory="ext/dramsys/DRAMSys/DRAMSys/library/resources", + configuration=( + DEFAULT_DRAMSYS_DIRECTORY / "configs/ddr3-gem5-se.json" + ).as_posix(), + size="1GB", recordable=recordable, ) class DRAMSysLPDDR4_3200(DRAMSysMem): + """ + An example LPDDR4 3200 DRAMSys configuration. + """ + def __init__(self, recordable: bool): """ :param recordable: Whether the database recording feature of DRAMSys is enabled. """ super().__init__( - configuration="ext/dramsys/DRAMSys/DRAMSys/" - "library/resources/simulations/lpddr4-example.json", - size="4GB", - resource_directory="ext/dramsys/DRAMSys/DRAMSys/library/resources", + configuration=( + DEFAULT_DRAMSYS_DIRECTORY / "configs/lpddr4-example.json" + ).as_posix(), + size="1GB", recordable=recordable, ) class DRAMSysHBM2(DRAMSysMem): + """ + An example HBM2 DRAMSys configuration. + """ + def __init__(self, recordable: bool): """ :param recordable: Whether the database recording feature of DRAMSys is enabled. """ super().__init__( - configuration="ext/dramsys/DRAMSys/DRAMSys/" - "library/resources/simulations/hbm2-example.json", - size="4GB", - resource_directory="ext/dramsys/DRAMSys/DRAMSys/library/resources", + configuration=( + DEFAULT_DRAMSYS_DIRECTORY / "configs/hbm2-example.json" + ).as_posix(), + size="1GB", recordable=recordable, ) diff --git a/src/python/gem5/components/memory/hbm.py b/src/python/gem5/components/memory/hbm.py index 75db1f9fde..1e1c83e2bd 100644 --- a/src/python/gem5/components/memory/hbm.py +++ b/src/python/gem5/components/memory/hbm.py @@ -27,14 +27,29 @@ """ HBM2 memory system using HBMCtrl """ -from .memory import ChanneledMemory -from .abstract_memory_system import AbstractMemorySystem from math import log +from typing import ( + Optional, + Sequence, + Tuple, + Type, + Union, +) + +from m5.objects import ( + AddrRange, + DRAMInterface, + HBMCtrl, + Port, +) + from ...utils.override import overrides -from m5.objects import AddrRange, DRAMInterface, HBMCtrl, Port -from typing import Type, Optional, Union, Sequence, Tuple -from .memory import _try_convert +from .abstract_memory_system import AbstractMemorySystem from .dram_interfaces.hbm import HBM_2000_4H_1x64 +from .memory import ( + ChanneledMemory, + _try_convert, +) class HighBandwidthMemory(ChanneledMemory): @@ -55,17 +70,18 @@ class HighBandwidthMemory(ChanneledMemory): ) -> None: """ :param dram_interface_class: The DRAM interface type to create with - this memory controller + this memory controller. :param num_channels: The number of channels that needs to be - simulated + simulated. :param size: Optionally specify the size of the DRAM controller's - address space. By default, it starts at 0 and ends at the size of - the DRAM device specified + address space. By default, it starts at 0 and ends at + the size of the DRAM device specified. :param addr_mapping: Defines the address mapping scheme to be used. - If None, it is defaulted to addr_mapping from dram_interface_class. + If ``None``, it is defaulted to ``addr_mapping`` + from ``dram_interface_class``. :param interleaving_size: Defines the interleaving size of the multi- - channel memory system. By default, it is equivalent to the atom - size, i.e., 64. + channel memory system. By default, it is + equivalent to the atom size, i.e., 64. """ super().__init__( dram_interface_class, @@ -137,7 +153,6 @@ class HighBandwidthMemory(ChanneledMemory): @overrides(ChanneledMemory) def get_mem_ports(self) -> Sequence[Tuple[AddrRange, Port]]: - intlv_bits = log(self._num_channels, 2) mask_list = [] diff --git a/src/python/gem5/components/memory/memory.py b/src/python/gem5/components/memory/memory.py index e7e6cf46e3..6f17a9f7b3 100644 --- a/src/python/gem5/components/memory/memory.py +++ b/src/python/gem5/components/memory/memory.py @@ -28,12 +28,26 @@ """ from math import log -from ...utils.override import overrides +from typing import ( + List, + Optional, + Sequence, + Tuple, + Type, + Union, +) + +from m5.objects import ( + AddrRange, + DRAMInterface, + MemCtrl, + Port, +) from m5.util.convert import toMemorySize + +from ...utils.override import overrides from ..boards.abstract_board import AbstractBoard from .abstract_memory_system import AbstractMemorySystem -from m5.objects import AddrRange, DRAMInterface, MemCtrl, Port -from typing import Type, Sequence, Tuple, List, Optional, Union def _try_convert(val, cls): @@ -68,17 +82,18 @@ class ChanneledMemory(AbstractMemorySystem): ) -> None: """ :param dram_interface_class: The DRAM interface type to create with - this memory controller + this memory controller. :param num_channels: The number of channels that needs to be - simulated + simulated. :param size: Optionally specify the size of the DRAM controller's - address space. By default, it starts at 0 and ends at the size of - the DRAM device specified + address space. By default, it starts at 0 and ends at + the size of the DRAM device specified. :param addr_mapping: Defines the address mapping scheme to be used. - If None, it is defaulted to addr_mapping from dram_interface_class. + If ``None``, it is defaulted to ``addr_mapping`` from + ``dram_interface_class``. :param interleaving_size: Defines the interleaving size of the multi- - channel memory system. By default, it is equivalent to the atom - size, i.e., 64. + channel memory system. By default, it is + equivalent to the atom size, i.e., 64. """ num_channels = _try_convert(num_channels, int) interleaving_size = _try_convert(interleaving_size, int) diff --git a/src/python/gem5/components/memory/multi_channel.py b/src/python/gem5/components/memory/multi_channel.py index 1f14190c97..0e26a1a795 100644 --- a/src/python/gem5/components/memory/multi_channel.py +++ b/src/python/gem5/components/memory/multi_channel.py @@ -24,21 +24,24 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .memory import ChanneledMemory -from .abstract_memory_system import AbstractMemorySystem - from typing import Optional -from .dram_interfaces.ddr3 import DDR3_1600_8x8, DDR3_2133_8x8 + +from .abstract_memory_system import AbstractMemorySystem +from .dram_interfaces.ddr3 import ( + DDR3_1600_8x8, + DDR3_2133_8x8, +) from .dram_interfaces.ddr4 import DDR4_2400_8x8 -from .dram_interfaces.lpddr3 import LPDDR3_1600_1x32 from .dram_interfaces.hbm import HBM_1000_4H_1x64 +from .dram_interfaces.lpddr3 import LPDDR3_1600_1x32 +from .memory import ChanneledMemory def DualChannelDDR3_1600( size: Optional[str] = None, ) -> AbstractMemorySystem: """ - A dual channel memory system using DDR3_1600_8x8 based DIMM + A dual channel memory system using DDR3_1600_8x8 based DIMM. """ return ChanneledMemory(DDR3_1600_8x8, 2, 64, size=size) @@ -47,7 +50,7 @@ def DualChannelDDR3_2133( size: Optional[str] = None, ) -> AbstractMemorySystem: """ - A dual channel memory system using DDR3_2133_8x8 based DIMM + A dual channel memory system using DDR3_2133_8x8 based DIMM. """ return ChanneledMemory(DDR3_2133_8x8, 2, 64, size=size) @@ -56,7 +59,7 @@ def DualChannelDDR4_2400( size: Optional[str] = None, ) -> AbstractMemorySystem: """ - A dual channel memory system using DDR4_2400_8x8 based DIMM + A dual channel memory system using DDR4_2400_8x8 based DIMM. """ return ChanneledMemory(DDR4_2400_8x8, 2, 64, size=size) diff --git a/src/python/gem5/components/memory/simple.py b/src/python/gem5/components/memory/simple.py index b650a68ba4..a849641135 100644 --- a/src/python/gem5/components/memory/simple.py +++ b/src/python/gem5/components/memory/simple.py @@ -27,12 +27,23 @@ """Simple memory controllers """ -from ...utils.override import overrides +from typing import ( + List, + Sequence, + Tuple, +) + +from m5.objects import ( + AddrRange, + MemCtrl, + Port, + SimpleMemory, +) from m5.util.convert import toMemorySize -from typing import List, Sequence, Tuple + +from ...utils.override import overrides from ..boards.abstract_board import AbstractBoard from .abstract_memory_system import AbstractMemorySystem -from m5.objects import AddrRange, MemCtrl, Port, SimpleMemory class SingleChannelSimpleMemory(AbstractMemorySystem): diff --git a/src/python/gem5/components/memory/single_channel.py b/src/python/gem5/components/memory/single_channel.py index 9235bbd52d..246dfa8b6e 100644 --- a/src/python/gem5/components/memory/single_channel.py +++ b/src/python/gem5/components/memory/single_channel.py @@ -24,23 +24,29 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .memory import ChanneledMemory -from .abstract_memory_system import AbstractMemorySystem - from typing import Optional -from .dram_interfaces.ddr5 import DDR5_4400_4x8, DDR5_6400_4x8, DDR5_8400_4x8 +from .abstract_memory_system import AbstractMemorySystem +from .dram_interfaces.ddr3 import ( + DDR3_1600_8x8, + DDR3_2133_8x8, +) from .dram_interfaces.ddr4 import DDR4_2400_8x8 +from .dram_interfaces.ddr5 import ( + DDR5_4400_4x8, + DDR5_6400_4x8, + DDR5_8400_4x8, +) from .dram_interfaces.hbm import HBM_1000_4H_1x128 from .dram_interfaces.lpddr3 import LPDDR3_1600_1x32 -from .dram_interfaces.ddr3 import DDR3_1600_8x8, DDR3_2133_8x8 +from .memory import ChanneledMemory def SingleChannelDDR3_1600( size: Optional[str] = None, ) -> AbstractMemorySystem: """ - A single channel memory system using DDR3_1600_8x8 based DIMM + A single channel memory system using DDR3_1600_8x8 based DIMM. """ return ChanneledMemory(DDR3_1600_8x8, 1, 64, size=size) @@ -49,7 +55,7 @@ def SingleChannelDDR3_2133( size: Optional[str] = None, ) -> AbstractMemorySystem: """ - A single channel memory system using DDR3_2133_8x8 based DIMM + A single channel memory system using DDR3_2133_8x8 based DIMM. """ return ChanneledMemory(DDR3_2133_8x8, 1, 64, size=size) @@ -58,7 +64,7 @@ def SingleChannelDDR4_2400( size: Optional[str] = None, ) -> AbstractMemorySystem: """ - A single channel memory system using DDR4_2400_8x8 based DIMM + A single channel memory system using DDR4_2400_8x8 based DIMM. """ return ChanneledMemory(DDR4_2400_8x8, 1, 64, size=size) @@ -81,7 +87,7 @@ def DIMM_DDR5_4400( size: Optional[str] = None, ) -> AbstractMemorySystem: """ - A single DIMM of DDR5 has two channels + A single DIMM of DDR5 has two channels. """ return ChanneledMemory(DDR5_4400_4x8, 2, 64, size=size) @@ -90,7 +96,7 @@ def DIMM_DDR5_6400( size: Optional[str] = None, ) -> AbstractMemorySystem: """ - A single DIMM of DDR5 has two channels + A single DIMM of DDR5 has two channels. """ return ChanneledMemory(DDR5_6400_4x8, 2, 64, size=size) @@ -99,6 +105,6 @@ def DIMM_DDR5_8400( size: Optional[str] = None, ) -> AbstractMemorySystem: """ - A single DIMM of DDR5 has two channels + A single DIMM of DDR5 has two channels. """ return ChanneledMemory(DDR5_8400_4x8, 2, 64, size=size) diff --git a/src/python/gem5/components/processors/abstract_core.py b/src/python/gem5/components/processors/abstract_core.py index 8259df8a8b..ce0181187b 100644 --- a/src/python/gem5/components/processors/abstract_core.py +++ b/src/python/gem5/components/processors/abstract_core.py @@ -24,14 +24,25 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from abc import ABCMeta, abstractmethod -from typing import Optional, List +from abc import ( + ABCMeta, + abstractmethod, +) +from typing import ( + List, + Optional, +) + +from m5.objects import ( + BaseMMU, + PcCountTrackerManager, + Port, + SubSystem, +) +from m5.params import PcCountPair from ...isas import ISA -from m5.objects import BaseMMU, Port, SubSystem, PcCountTrackerManager -from m5.params import PcCountPair - class AbstractCore(SubSystem): __metaclass__ = ABCMeta @@ -47,9 +58,9 @@ class AbstractCore(SubSystem): def requires_send_evicts(self) -> bool: """True if the CPU model or ISA requires sending evictions from caches to the CPU. Scenarios warrant forwarding evictions to the CPU: - 1. The O3 model must keep the LSQ coherent with the caches - 2. The x86 mwait instruction is built on top of coherence - 3. The local exclusive monitor in ARM systems + 1. The O3 model must keep the LSQ coherent with the caches. + 2. The x86 mwait instruction is built on top of coherence. + 3. The local exclusive monitor in ARM systems. """ return False @@ -85,11 +96,11 @@ class AbstractCore(SubSystem): @abstractmethod def connect_walker_ports(self, port1: Port, port2: Port) -> None: """ - Connect the response port from itb and dtb to their respective request + Connect the response port from ``itb`` and ``dtb`` to their respective request ports in the core. - :param port1: The response port from itb walker to connect to. - :param port2: The response port from dtb walker to connect to. + :param port1: The response port from ``itb`` walker to connect to. + :param port2: The response port from ``dtb`` walker to connect to. """ raise NotImplementedError @@ -128,15 +139,16 @@ class AbstractCore(SubSystem): ) -> None: """Schedule simpoint exit events for the core. - This is used to raise SIMPOINT_BEGIN exit events in the gem5 standard - library. This is called through the set_workload functions and should - not be called directly. Duplicate instruction counts in the inst_starts list will not - be scheduled. + This is used to raise ``SIMPOINT_BEGIN`` exit events in the gem5 standard + library. This is called through the ``set_workload`` functions and should + not be called directly. Duplicate instruction counts in the ``inst_starts`` + list will not be scheduled. - :param inst_starts: a list of SimPoints starting instructions - :param board_initialized: True if the board has already been - initialized, otherwise False. This parameter is necessary as simpoints - are setup differently dependent on this. + :param inst_starts: A list of SimPoints starting instructions. + :param board_initialized: ``True`` if the board has already been + initialized, otherwise ``False``. This parameter is + necessary as SimPoints are setup differently + dependent on this. """ raise NotImplementedError("This core type does not support simpoints") @@ -148,12 +160,13 @@ class AbstractCore(SubSystem): given number of instructions. This is called through the simulator module and should not be called directly. - This is used to raise MAX_INSTS exit event in the gem5 standard library + This is used to raise ``MAX_INSTS`` exit event in the gem5 standard library. :param inst: a number of instructions - :param board_initialized: True if the board has already been - initialized, otherwise False. This parameter is necessary as the - instruction stop is setup differently dependent on this. + :param board_initialized: ``True`` if the board has already been + initialized, otherwise ``False``. This parameter is + necessary as the instruction stop is setup + differently dependent on this. """ raise NotImplementedError("This core type does not support MAX_INSTS") diff --git a/src/python/gem5/components/processors/abstract_generator.py b/src/python/gem5/components/processors/abstract_generator.py index ff5387dd14..d1f4a51833 100644 --- a/src/python/gem5/components/processors/abstract_generator.py +++ b/src/python/gem5/components/processors/abstract_generator.py @@ -25,14 +25,29 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from abc import abstractmethod +from typing import List + from ...utils.override import overrides +from ..boards.abstract_board import AbstractBoard from ..boards.mem_mode import MemMode from .abstract_generator_core import AbstractGeneratorCore - from .abstract_processor import AbstractProcessor -from ..boards.abstract_board import AbstractBoard -from typing import List + +def partition_range( + min_addr: int, max_addr: int, num_partitions: int +) -> List[tuple]: + assert ( + isinstance(min_addr, int) + and isinstance(max_addr, int) + and isinstance(num_partitions, int) + ) + assert ((max_addr - min_addr) % num_partitions) == 0 + chunk_size = int((max_addr - min_addr) / num_partitions) + return [ + (min_addr + chunk_size * i, min_addr + chunk_size * (i + 1)) + for i in range(num_partitions) + ] class AbstractGenerator(AbstractProcessor): @@ -45,11 +60,12 @@ class AbstractGenerator(AbstractProcessor): Create a list of AbstractGeneratorCore (which is an AbstractCore), to pass to the constructor of the AbstractProcessor. Due to the different prototypes for the constructor of different generator types - inputs are noted as *args. This way the abstract method _create_cores + inputs are noted as *args. This way the abstract ``method _create_cores`` could be called without AbstractGenerator having to know what the prototype for the constructor of the inheriting class is. It also - limits the _create_cores function to only using positional arguments. - keyword (optional arguments) are still allowable in the constructor of + limits the ``_create_cores`` function to only using positional arguments. + + Keyword (optional arguments) are still allowable in the constructor of the inheriting classes. """ super().__init__(cores=cores) diff --git a/src/python/gem5/components/processors/abstract_generator_core.py b/src/python/gem5/components/processors/abstract_generator_core.py index b49e86ee19..8b411f3fb3 100644 --- a/src/python/gem5/components/processors/abstract_generator_core.py +++ b/src/python/gem5/components/processors/abstract_generator_core.py @@ -26,14 +26,17 @@ from abc import abstractmethod -from m5.objects import Port, PortTerminator -from ...utils.override import overrides - -from .abstract_core import AbstractCore -from ...isas import ISA - from typing import Optional +from m5.objects import ( + Port, + PortTerminator, +) + +from ...isas import ISA +from ...utils.override import overrides +from .abstract_core import AbstractCore + class AbstractGeneratorCore(AbstractCore): """The abstract generator core @@ -65,7 +68,7 @@ class AbstractGeneratorCore(AbstractCore): def connect_icache(self, port: Port) -> None: """ Generator cores only have one request port which we will connect to - the data cache not the icache. Just connect the icache to the + the data cache not the ``icache``. Just connect the ``icache`` to the PortTerminator here. """ self.port_end.req_ports = port @@ -106,6 +109,7 @@ class AbstractGeneratorCore(AbstractCore): def start_traffic(self): """ External interface to start generating the trace of addresses. + Depending on what SimObject is wrapped by this component this method might need be implemented. """ diff --git a/src/python/gem5/components/processors/abstract_processor.py b/src/python/gem5/components/processors/abstract_processor.py index a0f8b5cf44..79dba438a2 100644 --- a/src/python/gem5/components/processors/abstract_processor.py +++ b/src/python/gem5/components/processors/abstract_processor.py @@ -24,17 +24,21 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from abc import ABCMeta, abstractmethod - -from ...utils.requires import requires -from .abstract_core import AbstractCore +from abc import ( + ABCMeta, + abstractmethod, +) +from typing import ( + List, + Optional, +) from m5.objects import SubSystem -from ..boards.abstract_board import AbstractBoard from ...isas import ISA - -from typing import List, Optional +from ...utils.requires import requires +from ..boards.abstract_board import AbstractBoard +from .abstract_core import AbstractCore class AbstractProcessor(SubSystem): @@ -46,15 +50,16 @@ class AbstractProcessor(SubSystem): isa: ISA = ISA.NULL, ) -> None: """Set the cores on the processor + Cores are optional for some processor types. If a processor does not - set the cores here, it must override `get_num_cores` and `get_cores` + set the cores here, it must override ``get_num_cores`` and ``get_cores``. """ super().__init__() if cores: # In the stdlib we assume the system processor conforms to a single # ISA target. - assert len(set(core.get_isa() for core in cores)) == 1 + assert len({core.get_isa() for core in cores}) == 1 self.cores = cores self._isa = cores[0].get_isa() else: @@ -76,5 +81,5 @@ class AbstractProcessor(SubSystem): raise NotImplementedError def _post_instantiate(self) -> None: - """Called to set up anything needed after m5.instantiate""" + """Called to set up anything needed after ``m5.instantiate``.""" pass diff --git a/src/python/gem5/components/processors/base_cpu_core.py b/src/python/gem5/components/processors/base_cpu_core.py index c75c0029cf..19f5c85af2 100644 --- a/src/python/gem5/components/processors/base_cpu_core.py +++ b/src/python/gem5/components/processors/base_cpu_core.py @@ -24,42 +24,45 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from typing import Optional, List -from ...utils.requires import requires -from .abstract_core import AbstractCore - -from ...isas import ISA -from ...runtime import get_runtime_isa -from ...utils.override import overrides -from ...utils.requires import requires +from typing import ( + List, + Optional, +) from m5.objects import ( - BaseMMU, - Port, BaseCPU, - Process, + BaseMMU, PcCountTracker, PcCountTrackerManager, + Port, + Process, ) from m5.params import PcCountPair +from ...isas import ISA +from ...utils.override import overrides +from ...utils.requires import requires +from .abstract_core import AbstractCore + class BaseCPUCore(AbstractCore): """ An stdlib AbstractCore subclass which wraps a BaseCPU SimObject type. """ - def __init__(self, core: BaseCPU, isa: Optional[ISA] = None): + def __init__(self, core: BaseCPU, isa: ISA): super().__init__() # There is some annoying redundancy here. The BaseCPU type already # defines the ISA, so here we are defining it twice. However, there # currently isn't a good way to get the ISA from the BaseCPU Type. - if isa: - requires(isa_required=isa) - self._isa = isa - else: - self._isa = get_runtime_isa() + # + # TODO: Have some helper function to get the ISA from a BaseCPU type. + # This may just be a cause of using `instanceof`: + # e.g., `if instanceof(cpu, X86Cpu): return ISA.X86`. + # + requires(isa_required=isa) + self._isa = isa self.core = core self.core.createThreads() @@ -93,7 +96,6 @@ class BaseCPUCore(AbstractCore): @overrides(AbstractCore) def is_kvm_core(self) -> bool: - try: from m5.objects import BaseKvmCPU @@ -118,7 +120,6 @@ class BaseCPUCore(AbstractCore): @overrides(AbstractCore) def connect_walker_ports(self, port1: Port, port2: Port) -> None: if self.get_isa() == ISA.ARM: - # Unlike X86 and RISCV MMU, the ARM MMU has two L1 TLB walker ports # named `walker` and `stage2_walker` for both data and instruction. # The gem5 standard library currently supports one TLB walker port @@ -144,7 +145,6 @@ class BaseCPUCore(AbstractCore): interrupt_requestor: Optional[Port] = None, interrupt_responce: Optional[Port] = None, ) -> None: - # TODO: This model assumes that we will only create an interrupt # controller as we require it. Not sure how true this is in all cases. self.core.createInterruptController() diff --git a/src/python/gem5/components/processors/base_cpu_processor.py b/src/python/gem5/components/processors/base_cpu_processor.py index 9a7561587a..b1a63ea8ce 100644 --- a/src/python/gem5/components/processors/base_cpu_processor.py +++ b/src/python/gem5/components/processors/base_cpu_processor.py @@ -25,23 +25,22 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .base_cpu_core import BaseCPUCore -from ..boards.mem_mode import MemMode -from ...utils.override import overrides -from ..boards.mem_mode import MemMode -from .abstract_processor import AbstractProcessor -from ..boards.abstract_board import AbstractBoard - from typing import List -from m5.util import warn from m5.objects import ( - BaseO3CPU, - BaseMinorCPU, BaseAtomicSimpleCPU, + BaseMinorCPU, BaseNonCachingSimpleCPU, + BaseO3CPU, BaseTimingSimpleCPU, ) +from m5.util import warn + +from ...utils.override import overrides +from ..boards.abstract_board import AbstractBoard +from ..boards.mem_mode import MemMode +from .abstract_processor import AbstractProcessor +from .base_cpu_core import BaseCPUCore class BaseCPUProcessor(AbstractProcessor): @@ -56,7 +55,6 @@ class BaseCPUProcessor(AbstractProcessor): Disclaimer ---------- - Multiple cores comprising of different BaseCPU types has not been tested and is not officially supported. """ @@ -71,7 +69,6 @@ class BaseCPUProcessor(AbstractProcessor): @overrides(AbstractProcessor) def incorporate_processor(self, board: AbstractBoard) -> None: - if any(core.is_kvm_core() for core in self.get_cores()): board.kvm_vm = self.kvm_vm # To get the KVM CPUs to run on different host CPUs diff --git a/src/python/gem5/components/processors/complex_generator.py b/src/python/gem5/components/processors/complex_generator.py index b113640ae7..ca3422b83c 100644 --- a/src/python/gem5/components/processors/complex_generator.py +++ b/src/python/gem5/components/processors/complex_generator.py @@ -24,11 +24,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from ...utils.override import overrides -from .complex_generator_core import ComplexGeneratorCore -from .abstract_generator import AbstractGenerator +from typing import ( + Any, + Iterator, + List, +) -from typing import Iterator, List, Any +from ...utils.override import overrides +from .abstract_generator import ( + AbstractGenerator, + partition_range, +) +from .complex_generator_core import ComplexGeneratorCore class ComplexGenerator(AbstractGenerator): @@ -63,26 +70,28 @@ class ComplexGenerator(AbstractGenerator): generator with the params specified. :param duration: The number of ticks for the generator core to generate - traffic. + traffic. :param rate: The rate at which the synthetic data is read/written. :param block_size: The number of bytes to be read/written with each - request. + request. :param min_addr: The lower bound of the address range the generator - will read/write from/to. + will read/write from/to. :param max_addr: The upper bound of the address range the generator - will read/write from/to. + will read/write from/to. :param rd_perc: The percentage of read requests among all the generated - requests. The write percentage would be equal to 100 - rd_perc. + requests. The write percentage would be equal to + ``100 - rd_perc``. :param data_limit: The amount of data in bytes to read/write by the - generator before stopping generation. + generator before stopping generation. """ - for core in self.cores: + ranges = partition_range(min_addr, max_addr, len(self.cores)) + for i, core in enumerate(self.cores): core.add_linear( duration, rate, block_size, - min_addr, - max_addr, + ranges[i][0], + ranges[i][1], rd_perc, data_limit, ) @@ -102,18 +111,19 @@ class ComplexGenerator(AbstractGenerator): generator with the params specified. :param duration: The number of ticks for the generator core to generate - traffic. + traffic. :param rate: The rate at which the synthetic data is read/written. :param block_size: The number of bytes to be read/written with each - request. + request. :param min_addr: The lower bound of the address range the generator - will read/write from/to. + will read/write from/to. :param max_addr: The upper bound of the address range the generator - will read/write from/to. + will read/write from/to. :param rd_perc: The percentage of read requests among all the generated - requests. The write percentage would be equal to 100 - rd_perc. + requests. The write percentage would be equal to + ``100 - rd_perc``. :param data_limit: The amount of data in bytes to read/write by the - generator before stopping generation. + generator before stopping generation. """ for core in self.cores: core.add_random( @@ -133,7 +143,7 @@ class ComplexGenerator(AbstractGenerator): Sets the traffic pattern defined by generator argument. :param generator: A python generator object that creates traffic - patterns through calls to methods of PyTrafficGen. + patterns through calls to methods of PyTrafficGen. """ for core in self.cores: core.set_traffic_from_python_generator(generator) diff --git a/src/python/gem5/components/processors/complex_generator_core.py b/src/python/gem5/components/processors/complex_generator_core.py index 92f62ded09..076bdd9662 100644 --- a/src/python/gem5/components/processors/complex_generator_core.py +++ b/src/python/gem5/components/processors/complex_generator_core.py @@ -24,22 +24,31 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from typing import Iterator, Any -from m5.ticks import fromSeconds -from m5.util.convert import toLatency, toMemoryBandwidth -from m5.objects import PyTrafficGen, Port +from enum import Enum +from typing import ( + Any, + Iterator, +) -from .abstract_core import AbstractCore -from .abstract_generator_core import AbstractGeneratorCore +from m5.objects import ( + Port, + PyTrafficGen, +) +from m5.ticks import fromSeconds +from m5.util.convert import ( + toLatency, + toMemoryBandwidth, +) from ...utils.override import overrides - -from enum import Enum +from .abstract_core import AbstractCore +from .abstract_generator_core import AbstractGeneratorCore class TrafficModes(Enum): """The traffic mode class - This class is an enum to store traffic mode in a more meaningful way + + This class is an enum to store traffic mode in a more meaningful way. """ linear = 0 @@ -59,9 +68,10 @@ class ComplexTrafficParams: data_limit: int, ): """The complex traffic params class + This class is a container for parameters to create either a linear or random traffic. The complex generator core stores a list of complex - traffic params for resolution after m5.instantiate is called. + traffic params for resolution after ``m5.instantiate`` is called. """ self._mode = mode self._duration = duration @@ -78,7 +88,7 @@ class ComplexGeneratorCore(AbstractGeneratorCore): """The complex generator core interface. This class defines the interface for a generator core that will create - a series of different types of traffic. This core uses PyTrafficGen to + a series of different types of traffic. This core uses `PyTrafficGen` to create and inject the synthetic traffic. This generator could be used to create more complex traffics that consist of linear and random traffic in different phases. @@ -106,22 +116,23 @@ class ComplexGeneratorCore(AbstractGeneratorCore): """ This function will add the params for a linear traffic to the list of traffic params in this generator core. These params will be later - resolved by the start_traffic call. This core uses a PyTrafficGen to + resolved by the ``start_traffic`` call. This core uses a `PyTrafficGen` to create the traffic based on the specified params below. :param duration: The number of ticks for the generator core to generate - traffic. + traffic. :param rate: The rate at which the synthetic data is read/written. :param block_size: The number of bytes to be read/written with each - request. + request. :param min_addr: The lower bound of the address range the generator - will read/write from/to. + will read/write from/to. :param max_addr: The upper bound of the address range the generator - will read/write from/to. + will read/write from/to. :param rd_perc: The percentage of read requests among all the generated - requests. The write percentage would be equal to 100 - rd_perc. + requests. The write percentage would be equal to + ``100 - rd_perc``. :param data_limit: The amount of data in bytes to read/write by the - generator before stopping generation. + generator before stopping generation. """ param = ComplexTrafficParams( TrafficModes.linear, @@ -149,22 +160,23 @@ class ComplexGeneratorCore(AbstractGeneratorCore): """ This function will add the params for a random traffic to the list of traffic params in this generator core. These params will be later - resolved by the start_traffic call. This core uses a PyTrafficGen to + resolved by the ``start_traffic`` call. This core uses a PyTrafficGen to create the traffic based on the specified params below. :param duration: The number of ticks for the generator core to generate - traffic. + traffic. :param rate: The rate at which the synthetic data is read/written. :param block_size: The number of bytes to be read/written with each - request. + request. :param min_addr: The lower bound of the address range the generator - will read/write from/to. + will read/write from/to. :param max_addr: The upper bound of the address range the generator - will read/write from/to. + will read/write from/to. :param rd_perc: The percentage of read requests among all the generated - requests. The write percentage would be equal to 100 - rd_perc. + requests. The write percentage would be equal to + ``100 - rd_perc``. :param data_limit: The amount of data in bytes to read/write by the - generator before stopping generation. + generator before stopping generation. """ param = ComplexTrafficParams( TrafficModes.random, @@ -184,13 +196,13 @@ class ComplexGeneratorCore(AbstractGeneratorCore): """ This function first checks if there are any pending traffics that require creation, if so it will create the pending traffics based on - traffic_params list and adds them to the traffic list, then it starts + ``traffic_params`` list and adds them to the traffic list, then it starts generating the traffic at the top of the traffic list. It also pops the first element in the list so that every time this function is called a new traffic is generated, each instance of a call to this function - should happen before each instance of the call to m5.simulate(). All + should happen before each instance of the call to ``m5.simulate()``. All the instances of calls to this function should happen after - m5.instantiate() + ``m5.instantiate()``. """ if not self._traffic_set: self._set_traffic() @@ -246,15 +258,15 @@ class ComplexGeneratorCore(AbstractGeneratorCore): ) -> None: """ Function to set the traffic from a user defined python generator. - The generator should only only assume one input argument (positional) + The generator should only assume one input argument (positional) for the actual PyTrafficGen object to create the traffic. This is possible either through using a generator with hardcoded parameters in the function calls to PyTrafficGen methods or by compiling a flexible python generator into a generator object with only one - input argument (positional) using functools.partial. + input argument (positional) using ``functools.partial``. :param generator: A python generator object that creates traffic - patterns through calls to methods of PyTrafficGen. + patterns through calls to methods of PyTrafficGen. """ if not self._traffic_set: self._set_traffic() @@ -276,18 +288,19 @@ class ComplexGeneratorCore(AbstractGeneratorCore): used to exit the simulation). :param duration: The number of ticks for the generator core to generate - traffic. + traffic. :param rate: The rate at which the synthetic data is read/written. :param block_size: The number of bytes to be read/written with each - request. + request. :param min_addr: The lower bound of the address range the generator - will read/write from/to. + will read/write from/to. :param max_addr: The upper bound of the address range the generator - will read/write from/to. + will read/write from/to. :param rd_perc: The percentage of read requests among all the generated - requests. The write percentage would be equal to 100 - rd_perc. + requests. The write percentage would be equal to + ``100 - rd_perc``. :param data_limit: The amount of data in bytes to read/write by the - generator before stopping generation. + generator before stopping generation. """ duration = fromSeconds(toLatency(duration)) rate = toMemoryBandwidth(rate) @@ -322,18 +335,19 @@ class ComplexGeneratorCore(AbstractGeneratorCore): used to exit the simulation). :param duration: The number of ticks for the generator core to generate - traffic. + traffic. :param rate: The rate at which the synthetic data is read/written. :param block_size: The number of bytes to be read/written with each - request. + request. :param min_addr: The lower bound of the address range the generator - will read/write from/to. + will read/write from/to. :param max_addr: The upper bound of the address range the generator - will read/write from/to. + will read/write from/to. :param rd_perc: The percentage of read requests among all the generated - requests. The write percentage would be equal to 100 - rd_perc. + requests. The write percentage would be equal to + ``100 - rd_perc``. :param data_limit: The amount of data in bytes to read/write by the - generator before stopping generation. + generator before stopping generation. """ duration = fromSeconds(toLatency(duration)) rate = toMemoryBandwidth(rate) diff --git a/src/python/gem5/components/processors/cpu_types.py b/src/python/gem5/components/processors/cpu_types.py index e12eb99816..20f0356c3e 100644 --- a/src/python/gem5/components/processors/cpu_types.py +++ b/src/python/gem5/components/processors/cpu_types.py @@ -24,11 +24,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from ..boards.mem_mode import MemMode - +import os from enum import Enum from typing import Set -import os + +from ..boards.mem_mode import MemMode class CPUTypes(Enum): @@ -52,7 +52,7 @@ def get_cpu_type_from_str(input: str) -> CPUTypes: the enum's value. E.g., "kvm" will return ISA.KVM. Throws an exception if the input string is invalid. - `get_cpu_types_str_set()` can be used to determine the valid strings. + ``get_cpu_types_str_set()`` can be used to determine the valid strings. This is for parsing text inputs that specify CPU Type targets. @@ -62,7 +62,7 @@ def get_cpu_type_from_str(input: str) -> CPUTypes: if input.lower() == cpu_type.value: return cpu_type - valid_cpu_types_list_str = str() + valid_cpu_types_list_str = "" for cpu_type_str in get_cpu_types_str_set(): valid_cpu_types_list_str += f"{os.linesep}{cpu_type_str}" diff --git a/src/python/gem5/components/processors/gups_generator.py b/src/python/gem5/components/processors/gups_generator.py index 76ea9e64b4..b90be0ec1c 100644 --- a/src/python/gem5/components/processors/gups_generator.py +++ b/src/python/gem5/components/processors/gups_generator.py @@ -28,8 +28,8 @@ from typing import Optional from m5.objects import Addr -from ...utils.override import overrides +from ...utils.override import overrides from .abstract_generator import AbstractGenerator from .gups_generator_core import GUPSGeneratorCore @@ -43,6 +43,7 @@ class GUPSGenerator(AbstractGenerator): clk_freq: Optional[str] = None, ): """The GUPSGenerator class + This class defines the interface for a single core GUPSGenerator, this generator could be used in place of a processor. For multicore versions of this generator look at GUPSGeneraorEP (EP stands for embarrassingly @@ -51,10 +52,11 @@ class GUPSGenerator(AbstractGenerator): :param start_addr: The start address for allocating the update table. :param mem_size: The size of memory to allocate for the update table. - Should be a power of 2. + Should be a power of 2. :param update_limit: The number of updates to do before terminating - simulation. Pass zero to run the benchmark to completion (The amount of - time it takes to simulate depends on ) + simulation. Pass zero to run the benchmark to + completion (The amount of time it takes to simulate + depends on it). """ super().__init__( cores=[ @@ -70,7 +72,7 @@ class GUPSGenerator(AbstractGenerator): @overrides(AbstractGenerator) def start_traffic(self): """ - Since GUPSGeneratorCore does not need a call to start_traffic to + Since GUPSGeneratorCore does not need a call to ``start_traffic`` to start generation. This function is just pass. """ pass diff --git a/src/python/gem5/components/processors/gups_generator_core.py b/src/python/gem5/components/processors/gups_generator_core.py index 0090c72847..5bb4ec44d2 100644 --- a/src/python/gem5/components/processors/gups_generator_core.py +++ b/src/python/gem5/components/processors/gups_generator_core.py @@ -26,10 +26,18 @@ from typing import Optional + +from m5.objects import ( + Addr, + GUPSGen, + Port, + SrcClockDomain, + VoltageDomain, +) + from ...utils.override import overrides from .abstract_core import AbstractCore from .abstract_generator_core import AbstractGeneratorCore -from m5.objects import Port, GUPSGen, Addr, SrcClockDomain, VoltageDomain class GUPSGeneratorCore(AbstractGeneratorCore): diff --git a/src/python/gem5/components/processors/gups_generator_ep.py b/src/python/gem5/components/processors/gups_generator_ep.py index 68c9dcea04..8c62f01e02 100644 --- a/src/python/gem5/components/processors/gups_generator_ep.py +++ b/src/python/gem5/components/processors/gups_generator_ep.py @@ -26,9 +26,11 @@ from typing import Optional + from m5.objects import Addr -from ...utils.override import overrides from m5.util.convert import toMemorySize + +from ...utils.override import overrides from .abstract_generator import AbstractGenerator from .gups_generator_core import GUPSGeneratorCore @@ -43,6 +45,7 @@ class GUPSGeneratorEP(AbstractGenerator): clk_freq: Optional[str] = None, ): """The GUPSGeneratorEP class + This class defines the interface for multi core GUPSGenerator, this generator could be used in place of a processor. In terms of benchmarking this generator implements the embarrassigly parallel @@ -50,10 +53,11 @@ class GUPSGeneratorEP(AbstractGenerator): :param start_addr: The start address for allocating the update table. :param mem_size: The size of memory to allocate for the update table. - Should be a power of 2. + Should be a power of 2. :param update_limit: The number of updates to do before terminating - simulation. Pass zero to run the benchmark to completion (The amount of - time it takes to simulate depends on ) + simulation. Pass zero to run the benchmark to + completion (The amount of time it takes to simulate + depends on it). """ super().__init__( cores=self._create_cores( @@ -92,7 +96,7 @@ class GUPSGeneratorEP(AbstractGenerator): @overrides(AbstractGenerator) def start_traffic(self): """ - Since GUPSGeneratorCore does not need a call to start_traffic to + Since GUPSGeneratorCore does not need a call to ``start_traffic`` to start generation. This function is just pass. """ pass diff --git a/src/python/gem5/components/processors/gups_generator_par.py b/src/python/gem5/components/processors/gups_generator_par.py index 5f6485b9e2..c80a8ca62c 100644 --- a/src/python/gem5/components/processors/gups_generator_par.py +++ b/src/python/gem5/components/processors/gups_generator_par.py @@ -26,12 +26,13 @@ from typing import Optional -from m5.objects import Addr -from ...utils.override import overrides +from m5.objects import Addr + +from ...utils.override import overrides +from ..boards.abstract_board import AbstractBoard from ..boards.mem_mode import MemMode from .abstract_generator import AbstractGenerator -from ..boards.abstract_board import AbstractBoard from .gups_generator_core import GUPSGeneratorCore @@ -45,6 +46,7 @@ class GUPSGeneratorPAR(AbstractGenerator): clk_freq: Optional[str] = None, ): """The GUPSGeneratorPAR class + This class defines the interface for multi core GUPSGenerator, this generator could be used in place of a processor. In terms of benchmarking this generator implements the parallel (3rd) @@ -52,10 +54,11 @@ class GUPSGeneratorPAR(AbstractGenerator): :param start_addr: The start address for allocating the update table. :param mem_size: The size of memory to allocate for the update table. - Should be a power of 2. + Should be a power of 2. :param update_limit: The number of updates to do before terminating - simulation. Pass zero to run the benchmark to completion (The amount of - time it takes to simulate depends on ) + simulation. Pass zero to run the benchmark to + completion (The amount of time it takes to simulate + depends on it). """ super().__init__( cores=self._create_cores( @@ -88,7 +91,7 @@ class GUPSGeneratorPAR(AbstractGenerator): @overrides(AbstractGenerator) def start_traffic(self): """ - Since GUPSGeneratorCore does not need a call to start_traffic to + Since GUPSGeneratorCore does not need a call to ``start_traffic`` to start generation. This function is just pass. """ pass diff --git a/src/python/gem5/components/processors/linear_generator.py b/src/python/gem5/components/processors/linear_generator.py index 90fe62e7d6..0e847614c7 100644 --- a/src/python/gem5/components/processors/linear_generator.py +++ b/src/python/gem5/components/processors/linear_generator.py @@ -24,12 +24,15 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from ...utils.override import overrides -from .linear_generator_core import LinearGeneratorCore -from .abstract_generator import AbstractGenerator - from typing import List +from ...utils.override import overrides +from .abstract_generator import ( + AbstractGenerator, + partition_range, +) +from .linear_generator_core import LinearGeneratorCore + class LinearGenerator(AbstractGenerator): def __init__( @@ -62,18 +65,19 @@ class LinearGenerator(AbstractGenerator): :param num_cores: The number of linear generator cores to create. :param duration: The number of ticks for the generator to generate - traffic. + traffic. :param rate: The rate at which the synthetic data is read/written. :param block_size: The number of bytes to be read/written with each - request. + request. :param min_addr: The lower bound of the address range the generator - will read/write from/to. + will read/write from/to. :param max_addr: The upper bound of the address range the generator - will read/write from/to. + will read/write from/to. :param rd_perc: The percentage of read requests among all the generated - requests. The write percentage would be equal to 100 - rd_perc. + requests. The write percentage would be equal to + ``100 - rd_perc``. :param data_limit: The amount of data in bytes to read/write by the - generator before stopping generation. + generator before stopping generation. """ def _create_cores( @@ -91,17 +95,20 @@ class LinearGenerator(AbstractGenerator): The helper function to create the cores for the generator, it will use the same inputs as the constructor function. """ + + ranges = partition_range(min_addr, max_addr, num_cores) + return [ LinearGeneratorCore( duration=duration, rate=rate, block_size=block_size, - min_addr=min_addr, - max_addr=max_addr, + min_addr=ranges[i][0], + max_addr=ranges[i][1], rd_perc=rd_perc, data_limit=data_limit, ) - for _ in range(num_cores) + for i in range(num_cores) ] @overrides(AbstractGenerator) diff --git a/src/python/gem5/components/processors/linear_generator_core.py b/src/python/gem5/components/processors/linear_generator_core.py index b91b44d6aa..449d90a98a 100644 --- a/src/python/gem5/components/processors/linear_generator_core.py +++ b/src/python/gem5/components/processors/linear_generator_core.py @@ -24,16 +24,22 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.ticks import fromSeconds -from m5.util.convert import toLatency, toMemoryBandwidth -from m5.objects import PyTrafficGen, Port, BaseTrafficGen +from typing import Iterator -from .abstract_core import AbstractCore -from .abstract_generator_core import AbstractGeneratorCore +from m5.objects import ( + BaseTrafficGen, + Port, + PyTrafficGen, +) +from m5.ticks import fromSeconds +from m5.util.convert import ( + toLatency, + toMemoryBandwidth, +) from ...utils.override import overrides - -from typing import Iterator +from .abstract_core import AbstractCore +from .abstract_generator_core import AbstractGeneratorCore class LinearGeneratorCore(AbstractGeneratorCore): @@ -55,18 +61,19 @@ class LinearGeneratorCore(AbstractGeneratorCore): uses PyTrafficGen to create and inject the synthetic traffic. :param duration: The number of ticks for the generator core to generate - traffic. + traffic. :param rate: The rate at which the synthetic data is read/written. :param block_size: The number of bytes to be read/written with each - request. + request. :param min_addr: The lower bound of the address range the generator - will read/write from/to. + will read/write from/to. :param max_addr: The upper bound of the address range the generator - will read/write from/to. + will read/write from/to. :param rd_perc: The percentage of read requests among all the generated - requests. The write percentage would be equal to 100 - rd_perc. + requests. The write percentage would be equal to + ``100 - rd_perc``. :param data_limit: The amount of data in bytes to read/write by the - generator before stopping generation. + generator before stopping generation. """ self.generator = PyTrafficGen() self._duration = duration diff --git a/src/python/gem5/components/processors/random_generator.py b/src/python/gem5/components/processors/random_generator.py index ca7ed98f89..affe37cf80 100644 --- a/src/python/gem5/components/processors/random_generator.py +++ b/src/python/gem5/components/processors/random_generator.py @@ -24,15 +24,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from ...utils.override import overrides -from ..boards.mem_mode import MemMode -from .random_generator_core import RandomGeneratorCore - -from .abstract_generator import AbstractGenerator -from ..boards.abstract_board import AbstractBoard - from typing import List +from ...utils.override import overrides +from ..boards.abstract_board import AbstractBoard +from ..boards.mem_mode import MemMode +from .abstract_generator import AbstractGenerator +from .random_generator_core import RandomGeneratorCore + class RandomGenerator(AbstractGenerator): def __init__( @@ -65,18 +64,19 @@ class RandomGenerator(AbstractGenerator): :param num_cores: The number of linear generator cores to create. :param duration: The number of ticks for the generator to generate - traffic. + traffic. :param rate: The rate at which the synthetic data is read/written. :param block_size: The number of bytes to be read/written with each - request. + request. :param min_addr: The lower bound of the address range the generator - will read/write from/to. + will read/write from/to. :param max_addr: The upper bound of the address range the generator - will read/write from/to. + will read/write from/to. :param rd_perc: The percentage of read requests among all the generated - requests. The write percentage would be equal to 100 - rd_perc. + requests. The write percentage would be equal to + ``100 - rd_perc``. :param data_limit: The amount of data in bytes to read/write by the - generator before stopping generation. + generator before stopping generation. """ def _create_cores( diff --git a/src/python/gem5/components/processors/random_generator_core.py b/src/python/gem5/components/processors/random_generator_core.py index b5aced620d..67f3f5e4ac 100644 --- a/src/python/gem5/components/processors/random_generator_core.py +++ b/src/python/gem5/components/processors/random_generator_core.py @@ -24,16 +24,22 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.ticks import fromSeconds -from m5.util.convert import toLatency, toMemoryBandwidth -from m5.objects import PyTrafficGen, Port, BaseTrafficGen +from typing import Iterator -from .abstract_core import AbstractCore -from .abstract_generator_core import AbstractGeneratorCore +from m5.objects import ( + BaseTrafficGen, + Port, + PyTrafficGen, +) +from m5.ticks import fromSeconds +from m5.util.convert import ( + toLatency, + toMemoryBandwidth, +) from ...utils.override import overrides - -from typing import Iterator +from .abstract_core import AbstractCore +from .abstract_generator_core import AbstractGeneratorCore class RandomGeneratorCore(AbstractGeneratorCore): @@ -55,18 +61,19 @@ class RandomGeneratorCore(AbstractGeneratorCore): PyTrafficGen to create and inject the synthetic traffic. :param duration: The number of ticks for the generator core to generate - traffic. + traffic. :param rate: The rate at which the synthetic data is read/written. :param block_size: The number of bytes to be read/written with each - request. + request. :param min_addr: The lower bound of the address range the generator - will read/write from/to. + will read/write from/to. :param max_addr: The upper bound of the address range the generator - will read/write from/to. + will read/write from/to. :param rd_perc: The percentage of read requests among all the generated - requests. The write percentage would be equal to 100 - rd_perc. + requests. The write percentage would be equal to + ``100 - rd_perc``. :param data_limit: The amount of data in bytes to read/write by the - generator before stopping generation. + generator before stopping generation. """ self.generator = PyTrafficGen() self._duration = duration diff --git a/src/python/gem5/components/processors/simple_core.py b/src/python/gem5/components/processors/simple_core.py index 15e15dc0cf..0d05b34e2d 100644 --- a/src/python/gem5/components/processors/simple_core.py +++ b/src/python/gem5/components/processors/simple_core.py @@ -24,35 +24,24 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import importlib +import platform from typing import Optional + +from ...isas import ISA from ...utils.requires import requires from .base_cpu_core import BaseCPUCore from .cpu_types import CPUTypes -from ...isas import ISA -from ...utils.requires import requires -from ...runtime import get_runtime_isa -import importlib -import platform class SimpleCore(BaseCPUCore): """ - A SimpleCore instantiates a core based on the CPUType enum pass. The - SimpleCore creates a single SimObject of that type. + A `SimpleCore` instantiates a core based on the CPUType enum pass. The + `SimpleCore` creates a single `SimObject` of that type. """ - def __init__( - self, cpu_type: CPUTypes, core_id: int, isa: Optional[ISA] = None - ): - - # If the ISA is not specified, we infer it via the `get_runtime_isa` - # function. - if isa: - requires(isa_required=isa) - isa = isa - else: - isa = get_runtime_isa() - + def __init__(self, cpu_type: CPUTypes, core_id: int, isa: ISA): + requires(isa_required=isa) super().__init__( core=SimpleCore.cpu_simobject_factory( isa=isa, cpu_type=cpu_type, core_id=core_id @@ -66,15 +55,14 @@ class SimpleCore(BaseCPUCore): return self._cpu_type @classmethod - def cpu_simobject_factory(cls, cpu_type: CPUTypes, isa: ISA, core_id: int): + def cpu_class_factory(cls, cpu_type: CPUTypes, isa: ISA) -> type: """ - A factory used to return the SimObject core object given the cpu type, + A factory used to return the SimObject type given the cpu type, and ISA target. An exception will be thrown if there is an incompatibility. :param cpu_type: The target CPU type. :param isa: The target ISA. - :param core_id: The id of the core to be returned. """ assert isa is not None @@ -146,4 +134,22 @@ class SimpleCore(BaseCPUCore): "gem5." ) - return to_return_cls(cpu_id=core_id) + return to_return_cls + + @classmethod + def cpu_simobject_factory( + cls, cpu_type: CPUTypes, isa: ISA, core_id: int + ) -> BaseCPUCore: + """ + A factory used to return the SimObject core object given the cpu type, + and ISA target. An exception will be thrown if there is an + incompatibility. + + :param cpu_type: The target CPU type. + :param isa: The target ISA. + :param core_id: The id of the core to be returned. + """ + + return cls.cpu_class_factory(cpu_type=cpu_type, isa=isa)( + cpu_id=core_id + ) diff --git a/src/python/gem5/components/processors/simple_processor.py b/src/python/gem5/components/processors/simple_processor.py index 510e37df0e..d47e3794b2 100644 --- a/src/python/gem5/components/processors/simple_processor.py +++ b/src/python/gem5/components/processors/simple_processor.py @@ -25,15 +25,15 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.util import warn -from .base_cpu_processor import BaseCPUProcessor -from ..processors.simple_core import SimpleCore - -from .cpu_types import CPUTypes -from ...isas import ISA - from typing import Optional +from m5.util import warn + +from ...isas import ISA +from ..processors.simple_core import SimpleCore +from .base_cpu_processor import BaseCPUProcessor +from .cpu_types import CPUTypes + class SimpleProcessor(BaseCPUProcessor): """ @@ -41,27 +41,14 @@ class SimpleProcessor(BaseCPUProcessor): same CPUType. """ - def __init__( - self, cpu_type: CPUTypes, num_cores: int, isa: Optional[ISA] = None - ) -> None: + def __init__(self, cpu_type: CPUTypes, num_cores: int, isa: ISA) -> None: """ :param cpu_type: The CPU type for each type in the processor. + :param num_cores: The number of CPU cores in the processor. - :param isa: The ISA of the processor. This argument is optional. If not - set the `runtime.get_runtime_isa` is used to determine the ISA at - runtime. **WARNING**: This functionality is deprecated. It is - recommended you explicitly set your ISA via SimpleProcessor - construction. + :param isa: The ISA of the processor. """ - if not isa: - warn( - "An ISA for the SimpleProcessor was not set. This will " - "result in usage of `runtime.get_runtime_isa` to obtain the " - "ISA. This function is deprecated and will be removed in " - "future releases of gem5. Please explicitly state the ISA " - "via the processor constructor." - ) super().__init__( cores=[ SimpleCore(cpu_type=cpu_type, core_id=i, isa=isa) diff --git a/src/python/gem5/components/processors/simple_switchable_processor.py b/src/python/gem5/components/processors/simple_switchable_processor.py index e3978412c3..2be422afe6 100644 --- a/src/python/gem5/components/processors/simple_switchable_processor.py +++ b/src/python/gem5/components/processors/simple_switchable_processor.py @@ -24,17 +24,20 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from ..boards.mem_mode import MemMode -from ..boards.abstract_board import AbstractBoard -from ..processors.simple_core import SimpleCore -from ..processors.cpu_types import CPUTypes, get_mem_mode -from .switchable_processor import SwitchableProcessor -from ...isas import ISA +from typing import Optional + from m5.util import warn +from ...isas import ISA from ...utils.override import * - -from typing import Optional +from ..boards.abstract_board import AbstractBoard +from ..boards.mem_mode import MemMode +from ..processors.cpu_types import ( + CPUTypes, + get_mem_mode, +) +from ..processors.simple_core import SimpleCore +from .switchable_processor import SwitchableProcessor class SimpleSwitchableProcessor(SwitchableProcessor): @@ -50,31 +53,18 @@ class SimpleSwitchableProcessor(SwitchableProcessor): starting_core_type: CPUTypes, switch_core_type: CPUTypes, num_cores: int, - isa: Optional[ISA] = None, + isa: ISA = None, ) -> None: """ :param starting_core_type: The CPU type for each type in the processor - to start with (i.e., when the simulation has just started). - + to start with (i.e., when the simulation has + just started). :param switch_core_types: The CPU type for each core, to be switched - to.. + to. - :param isa: The ISA of the processor. This argument is optional. If not - set the `runtime.get_runtime_isa` is used to determine the ISA at - runtime. **WARNING**: This functionality is deprecated. It is - recommended you explicitly set your ISA via SimpleSwitchableProcessor - construction. + :param isa: The ISA of the processor. """ - if not isa: - warn( - "An ISA for the SimpleSwitchableProcessor was not set. This " - "will result in usage of `runtime.get_runtime_isa` to obtain " - "the ISA. This function is deprecated and will be removed in " - "future releases of gem5. Please explicitly state the ISA " - "via the processor constructor." - ) - if num_cores <= 0: raise AssertionError("Number of cores must be a positive integer!") diff --git a/src/python/gem5/components/processors/switchable_processor.py b/src/python/gem5/components/processors/switchable_processor.py index 20754fbf73..2436c9e81f 100644 --- a/src/python/gem5/components/processors/switchable_processor.py +++ b/src/python/gem5/components/processors/switchable_processor.py @@ -25,17 +25,19 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .simple_core import SimpleCore -from .abstract_core import AbstractCore -from .cpu_types import CPUTypes +from typing import ( + Dict, + List, +) import m5 -from typing import Dict, List - -from .abstract_processor import AbstractProcessor -from ..boards.abstract_board import AbstractBoard from ...utils.override import * +from ..boards.abstract_board import AbstractBoard +from .abstract_core import AbstractCore +from .abstract_processor import AbstractProcessor +from .cpu_types import CPUTypes +from .simple_core import SimpleCore class SwitchableProcessor(AbstractProcessor): @@ -44,7 +46,7 @@ class SwitchableProcessor(AbstractProcessor): system using SimpleCores. Though this class can be used directly, it is best inherited from. See - "SimpleSwitchableCPU" for an example of this. + SimpleSwitchableCPU for an example of this. """ def __init__( @@ -52,7 +54,6 @@ class SwitchableProcessor(AbstractProcessor): switchable_cores: Dict[str, List[SimpleCore]], starting_cores: str, ) -> None: - if starting_cores not in switchable_cores.keys(): raise AssertionError( f"Key {starting_cores} cannot be found in the " @@ -64,7 +65,7 @@ class SwitchableProcessor(AbstractProcessor): # In the stdlib we assume the system processor conforms to a single # ISA target. - assert len(set(core.get_isa() for core in self._current_cores)) == 1 + assert len({core.get_isa() for core in self._current_cores}) == 1 super().__init__(isa=self._current_cores[0].get_isa()) for name, core_list in self._switchable_cores.items(): @@ -85,7 +86,6 @@ class SwitchableProcessor(AbstractProcessor): @overrides(AbstractProcessor) def incorporate_processor(self, board: AbstractBoard) -> None: - # This is a bit of a hack. The `m5.switchCpus` function, used in the # "switch_to_processor" function, requires the System simobject as an # argument. We therefore need to store the board when incorporating the @@ -115,11 +115,9 @@ class SwitchableProcessor(AbstractProcessor): def _all_cores(self): for core_list in self._switchable_cores.values(): - for core in core_list: - yield core + yield from core_list def switch_to_processor(self, switchable_core_key: str): - # Run various checks. if not hasattr(self, "_board"): raise AssertionError("The processor has not been incorporated.") diff --git a/src/python/gem5/components/processors/traffic_generator.py b/src/python/gem5/components/processors/traffic_generator.py index b4c400a64a..6364e0b9b7 100644 --- a/src/python/gem5/components/processors/traffic_generator.py +++ b/src/python/gem5/components/processors/traffic_generator.py @@ -24,13 +24,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from ...utils.override import overrides -from .traffic_generator_core import TrafficGeneratorCore - -from .abstract_generator import AbstractGenerator - from typing import List +from ...utils.override import overrides +from .abstract_generator import AbstractGenerator +from .traffic_generator_core import TrafficGeneratorCore + class TrafficGenerator(AbstractGenerator): def __init__( @@ -46,8 +45,8 @@ class TrafficGenerator(AbstractGenerator): generator cores that could replace the processing cores in a board. :param config_file_list: A list containing the path to configuration - file each describing the traffic pattern that should be created by - each core of the generator. + file each describing the traffic pattern that + should be created by each core of the generator. """ def _create_cores( diff --git a/src/python/gem5/components/processors/traffic_generator_core.py b/src/python/gem5/components/processors/traffic_generator_core.py index d542352481..9e17fe8860 100644 --- a/src/python/gem5/components/processors/traffic_generator_core.py +++ b/src/python/gem5/components/processors/traffic_generator_core.py @@ -25,11 +25,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import Port, TrafficGen +from m5.objects import ( + Port, + TrafficGen, +) +from ...utils.override import overrides from .abstract_core import AbstractCore from .abstract_generator_core import AbstractGeneratorCore -from ...utils.override import overrides class TrafficGeneratorCore(AbstractGeneratorCore): @@ -39,8 +42,8 @@ class TrafficGeneratorCore(AbstractGeneratorCore): a compound traffic specified by the parameters below. It uses TrafficGen to create the traffic. - :param config_file: path to the configuration file specifying the - pattern of traffic. + :param config_file: Path to the configuration file specifying the + pattern of traffic. """ def __init__(self, config_file: str): diff --git a/src/python/gem5/isas.py b/src/python/gem5/isas.py index 84f02b87e7..2f54c33916 100644 --- a/src/python/gem5/isas.py +++ b/src/python/gem5/isas.py @@ -42,10 +42,11 @@ class ISA(Enum): E.g., to check if the X86 ISA is compiled: - ``` - if buildEnv[f"USE_{ISA.X86.value}_ISA"]: + .. code-block:: + + if buildEnv[f"USE_{ISA.X86.value}_ISA"]: ... - ``` + """ X86 = "x86" @@ -70,7 +71,7 @@ def get_isa_from_str(input: str) -> ISA: the enum's value. E.g., "x86" will return ISA.X86. Throws an exception if the input string is invalid. - `get_isas_str_set()` can be used to determine the valid strings. + ``get_isas_str_set()`` can be used to determine the valid strings. This is for parsing text inputs that specify ISA targets. @@ -80,7 +81,7 @@ def get_isa_from_str(input: str) -> ISA: if input.lower() == isa.value: return isa - valid_isas_str_list = str() + valid_isas_str_list = "" for isa_str in get_isas_str_set(): valid_isas_str_list += f"{os.linesep}{isa_str}" diff --git a/src/python/gem5/prebuilt/demo/x86_demo_board.py b/src/python/gem5/prebuilt/demo/x86_demo_board.py index eb38bb3e95..cb68a2dbb4 100644 --- a/src/python/gem5/prebuilt/demo/x86_demo_board.py +++ b/src/python/gem5/prebuilt/demo/x86_demo_board.py @@ -26,14 +26,14 @@ from m5.util import warn -from ...components.processors.cpu_types import CPUTypes +from ...coherence_protocol import CoherenceProtocol from ...components.boards.x86_board import X86Board -from ...components.memory.single_channel import SingleChannelDDR3_1600 -from ...components.processors.simple_processor import SimpleProcessor from ...components.cachehierarchies.ruby.mesi_two_level_cache_hierarchy import ( MESITwoLevelCacheHierarchy, ) -from ...coherence_protocol import CoherenceProtocol +from ...components.memory.single_channel import SingleChannelDDR3_1600 +from ...components.processors.cpu_types import CPUTypes +from ...components.processors.simple_processor import SimpleProcessor from ...isas import ISA from ...utils.requires import requires @@ -54,14 +54,14 @@ class X86DemoBoard(X86Board): ------- An example of using the X86DemoBoard can be found in - `configs/example/gem5_library/x86-ubuntu-run.py`. + ``configs/example/gem5_library/x86-ubuntu-run.py``. To run: - ``` - scons build/X86/gem5.opt -j`nproc` - ./build/X86/gem5.opt configs/example/gem5_library/x86-ubuntu-run.py - ``` + .. code-block:: + + scons build/X86/gem5.opt -j`nproc` + ./build/X86/gem5.opt configs/example/gem5_library/x86-ubuntu-run.py """ diff --git a/src/python/gem5/prebuilt/riscvmatched/riscvmatched_board.py b/src/python/gem5/prebuilt/riscvmatched/riscvmatched_board.py index 9ca95839f8..0de69a40f2 100644 --- a/src/python/gem5/prebuilt/riscvmatched/riscvmatched_board.py +++ b/src/python/gem5/prebuilt/riscvmatched/riscvmatched_board.py @@ -27,42 +27,30 @@ import os import re - -from typing import List, Optional - -from gem5.utils.override import overrides -from gem5.components.boards.abstract_system_board import AbstractSystemBoard -from gem5.components.boards.kernel_disk_workload import KernelDiskWorkload -from gem5.components.boards.se_binary_workload import SEBinaryWorkload -from gem5.resources.resource import AbstractResource -from gem5.components.memory import SingleChannelDDR4_2400 -from gem5.utils.requires import requires -from gem5.isas import ISA -from .riscvmatched_cache import RISCVMatchedCacheHierarchy -from .riscvmatched_processor import U74Processor -from gem5.isas import ISA - -import m5 - -from m5.objects import ( - BadAddr, - Bridge, - PMAChecker, - RiscvLinux, - AddrRange, - IOXBar, - RiscvRTC, - HiFive, - IGbE_e1000, - CowDiskImage, - RawDiskImage, - RiscvMmioVirtIO, - VirtIOBlock, - VirtIORng, - Frequency, - Port, +from typing import ( + List, + Optional, ) +import m5 +from m5.objects import ( + AddrRange, + BadAddr, + Bridge, + CowDiskImage, + Frequency, + HiFive, + IGbE_e1000, + IOXBar, + PMAChecker, + Port, + RawDiskImage, + RiscvBootloaderKernelWorkload, + RiscvMmioVirtIO, + RiscvRTC, + VirtIOBlock, + VirtIORng, +) from m5.util.fdthelper import ( Fdt, FdtNode, @@ -72,15 +60,30 @@ from m5.util.fdthelper import ( FdtState, ) +from gem5.components.boards.abstract_system_board import AbstractSystemBoard +from gem5.components.boards.kernel_disk_workload import KernelDiskWorkload +from gem5.components.boards.se_binary_workload import SEBinaryWorkload +from gem5.components.memory import SingleChannelDDR4_2400 +from gem5.isas import ISA +from gem5.resources.resource import AbstractResource +from gem5.utils.override import overrides +from gem5.utils.requires import requires + +from .riscvmatched_cache import RISCVMatchedCacheHierarchy +from .riscvmatched_processor import U74Processor + def U74Memory(): """ Memory for the U74 board. + DDR4 Subsystem with 16GB of memory. + Starts at 0x80000000. + Details at: Section 23, page 195 of the datasheet. - return: ChanneledMemory + :return: ChanneledMemory """ memory = SingleChannelDDR4_2400("16GB") memory.set_memory_range( @@ -96,14 +99,15 @@ class RISCVMatchedBoard( A board capable of full system simulation for RISC-V At a high-level, this is based on the HiFive Unmatched board from SiFive. - Based on : src/python/gem5/components/boards/riscv_board.py + Based on : ``src/python/gem5/components/boards/riscv_board.py`` This board assumes that you will be booting Linux for fullsystem emulation. The frequency of the RTC for the system is set to 1MHz. Details can be found on page 77, section 7.1 of the datasheet. - Datasheet for inbuilt params can be found here: https://sifive.cdn.prismic.io/sifive/1a82e600-1f93-4f41-b2d8-86ed8b16acba_fu740-c000-manual-v1p6.pdf + Datasheet for inbuilt params can be found here: + https://sifive.cdn.prismic.io/sifive/1a82e600-1f93-4f41-b2d8-86ed8b16acba_fu740-c000-manual-v1p6.pdf """ def __init__( @@ -140,7 +144,7 @@ class RISCVMatchedBoard( @overrides(AbstractSystemBoard) def _setup_board(self) -> None: if self._fs: - self.workload = RiscvLinux() + self.workload = RiscvBootloaderKernelWorkload() # Contains a CLINT, PLIC, UART, and some functions for the dtb, etc. self.platform = HiFive() @@ -306,10 +310,26 @@ class RISCVMatchedBoard( self.mem_ranges = [AddrRange(memory.get_size())] memory.set_memory_range(self.mem_ranges) - def generate_device_tree(self, outdir: str) -> None: - """Creates the dtb and dts files. + @overrides(AbstractSystemBoard) + def _pre_instantiate(self): + if self._fs: + if len(self._bootloader) > 0: + self.workload.bootloader_addr = 0x0 + self.workload.bootloader_filename = self._bootloader[0] + self.workload.kernel_addr = 0x80200000 + self.workload.entry_point = ( + 0x80000000 # Bootloader starting point + ) + else: + self.workload.kernel_addr = 0x0 + self.workload.entry_point = 0x80000000 - Creates two files in the outdir: 'device.dtb' and 'device.dts' + self._connect_things() + + def generate_device_tree(self, outdir: str) -> None: + """Creates the ``dtb`` and ``dts`` files. + + Creates two files in the outdir: ``device.dtb`` and ``device.dts`` :param outdir: Directory to output the files """ @@ -332,6 +352,12 @@ class RISCVMatchedBoard( ) root.append(node) + node = FdtNode(f"chosen") + bootargs = self.workload.command_line + node.append(FdtPropertyStrings("bootargs", [bootargs])) + node.append(FdtPropertyStrings("stdout-path", ["/uart@10000000"])) + root.append(node) + # See Documentation/devicetree/bindings/riscv/cpus.txt for details. cpus_node = FdtNode("cpus") cpus_state = FdtState(addr_cells=1, size_cells=0) @@ -504,7 +530,7 @@ class RISCVMatchedBoard( uart_node.append( FdtPropertyWords("interrupt-parent", soc_state.phandle(plic)) ) - uart_node.appendCompatible(["ns8250"]) + uart_node.appendCompatible(["ns8250", "ns16550a"]) soc_node.append(uart_node) # VirtIO MMIO disk node @@ -566,7 +592,12 @@ class RISCVMatchedBoard( @overrides(KernelDiskWorkload) def get_default_kernel_args(self) -> List[str]: - return ["console=ttyS0", "root={root_value}", "rw"] + return [ + "console=ttyS0", + "root={root_value}", + "disk_device={disk_device}", + "rw", + ] @overrides(KernelDiskWorkload) def set_kernel_disk_workload( @@ -579,7 +610,7 @@ class RISCVMatchedBoard( kernel_args: Optional[List[str]] = None, exit_on_work_items: bool = True, ) -> None: - self.workload = RiscvLinux() + self.workload = RiscvBootloaderKernelWorkload() KernelDiskWorkload.set_kernel_disk_workload( self=self, kernel=kernel, diff --git a/src/python/gem5/prebuilt/riscvmatched/riscvmatched_cache.py b/src/python/gem5/prebuilt/riscvmatched/riscvmatched_cache.py index 25e55ef310..abec836a37 100644 --- a/src/python/gem5/prebuilt/riscvmatched/riscvmatched_cache.py +++ b/src/python/gem5/prebuilt/riscvmatched/riscvmatched_cache.py @@ -24,25 +24,33 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from typing import Type + +from m5.objects import ( + BadAddr, + BaseXBar, + Cache, + L2XBar, + Port, + SystemXBar, +) + +from gem5.components.boards.abstract_board import AbstractBoard from gem5.components.cachehierarchies.abstract_cache_hierarchy import ( AbstractCacheHierarchy, ) -from gem5.components.cachehierarchies.classic.abstract_classic_cache_hierarchy import ( - AbstractClassicCacheHierarchy, -) from gem5.components.cachehierarchies.abstract_two_level_cache_hierarchy import ( AbstractTwoLevelCacheHierarchy, ) +from gem5.components.cachehierarchies.classic.abstract_classic_cache_hierarchy import ( + AbstractClassicCacheHierarchy, +) from gem5.components.cachehierarchies.classic.caches.l1dcache import L1DCache from gem5.components.cachehierarchies.classic.caches.l1icache import L1ICache from gem5.components.cachehierarchies.classic.caches.l2cache import L2Cache from gem5.components.cachehierarchies.classic.caches.mmu_cache import MMUCache -from gem5.components.boards.abstract_board import AbstractBoard from gem5.isas import ISA -from m5.objects import Cache, L2XBar, BaseXBar, SystemXBar, BadAddr, Port - from gem5.utils.override import * -from typing import Type class RISCVMatchedCacheHierarchy( @@ -52,7 +60,9 @@ class RISCVMatchedCacheHierarchy( A cache setup where each core has a private L1 Data and Instruction Cache, and a shared L2 cache. + The HiFive board has a partially inclusive cache hierarchy, hence this hierarchy is chosen. + The details of the cache hierarchy are in Table 7, page 36 of the datasheet. - L1 Instruction Cache: @@ -70,7 +80,6 @@ class RISCVMatchedCacheHierarchy( ) -> None: """ :param l2_size: The size of the L2 Cache (e.g., "256kB"). - :type l2_size: str """ AbstractClassicCacheHierarchy.__init__(self=self) AbstractTwoLevelCacheHierarchy.__init__( @@ -97,7 +106,6 @@ class RISCVMatchedCacheHierarchy( @overrides(AbstractCacheHierarchy) def incorporate_cache(self, board: AbstractBoard) -> None: - # Set up the system port for functional access from the simulator. board.connect_system_port(self.membus.cpu_side_ports) @@ -135,7 +143,6 @@ class RISCVMatchedCacheHierarchy( self._setup_io_cache(board) for i, cpu in enumerate(board.get_processor().get_cores()): - cpu.connect_icache(self.l1icaches[i].cpu_side) cpu.connect_dcache(self.l1dcaches[i].cpu_side) @@ -159,7 +166,7 @@ class RISCVMatchedCacheHierarchy( self.membus.cpu_side_ports = self.l2cache.mem_side def _setup_io_cache(self, board: AbstractBoard) -> None: - """Create a cache for coherent I/O connections""" + """Create a cache for coherent I/O connections.""" self.iocache = Cache( assoc=8, tag_latency=50, diff --git a/src/python/gem5/prebuilt/riscvmatched/riscvmatched_core.py b/src/python/gem5/prebuilt/riscvmatched/riscvmatched_core.py index 4b8d2c1d32..179e466e7a 100644 --- a/src/python/gem5/prebuilt/riscvmatched/riscvmatched_core.py +++ b/src/python/gem5/prebuilt/riscvmatched/riscvmatched_core.py @@ -25,20 +25,21 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from typing import Optional -from gem5.utils.requires import requires + +from m5.objects import ( + BaseCPU, + BaseMMU, + Port, + Process, +) +from m5.objects.BaseMinorCPU import * +from m5.objects.RiscvCPU import RiscvMinorCPU + from gem5.components.processors.base_cpu_core import BaseCPUCore from gem5.components.processors.cpu_types import CPUTypes from gem5.isas import ISA from gem5.utils.override import overrides -from m5.objects.RiscvCPU import RiscvMinorCPU -from m5.objects import ( - BaseMMU, - Port, - BaseCPU, - Process, -) -from m5.objects.BaseMinorCPU import * -from gem5.isas import ISA +from gem5.utils.requires import requires class U74IntFU(MinorDefaultIntFU): @@ -95,8 +96,8 @@ class U74FUPool(MinorFUPool): class U74BP(TournamentBP): - BTBEntries = 32 - RASSize = 12 + btb = SimpleBTB(numEntries=32) + ras = ReturnAddrStack(numEntries=12) localHistoryTableSize = 4096 # is 3.6 KiB but gem5 requires power of 2 localPredictorSize = 16384 globalPredictorSize = 16384 @@ -112,7 +113,7 @@ class U74CPU(RiscvMinorCPU): """ The fetch, decode, and execute stage parameters from the ARM HPI CPU This information about the CPU can be found on page 15 of - gem5_rsk_gem5-21.2.pdf at https://github.com/arm-university/arm-gem5-rsk + `gem5_rsk_gem5-21.2.pdf` at https://github.com/arm-university/arm-gem5-rsk The parameters that are changed are: - threadPolicy: @@ -205,8 +206,11 @@ class U74Core(BaseCPUCore): - globalCtrBits: 4 - choiceCtrBits: 4 - localHistoryTableSize: 4096 B - NOTE: The TournamentBP deviates from the actual BP. - This configuration performs the best in relation to the hardware. + + .. note:: + + The TournamentBP deviates from the actual BP. + This configuration performs the best in relation to the hardware. """ def __init__( @@ -214,3 +218,4 @@ class U74Core(BaseCPUCore): core_id, ): super().__init__(core=U74CPU(cpu_id=core_id), isa=ISA.RISCV) + self.core.isa[0].enable_rvv = False diff --git a/src/python/gem5/prebuilt/riscvmatched/riscvmatched_processor.py b/src/python/gem5/prebuilt/riscvmatched/riscvmatched_processor.py index 838f810073..a1c9d5b8c2 100644 --- a/src/python/gem5/prebuilt/riscvmatched/riscvmatched_processor.py +++ b/src/python/gem5/prebuilt/riscvmatched/riscvmatched_processor.py @@ -24,14 +24,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from gem5.utils.override import overrides -from gem5.components.boards.mem_mode import MemMode - from m5.util import warn +from gem5.components.boards.abstract_board import AbstractBoard +from gem5.components.boards.mem_mode import MemMode from gem5.components.processors.base_cpu_processor import BaseCPUProcessor from gem5.components.processors.cpu_types import CPUTypes -from gem5.components.boards.abstract_board import AbstractBoard +from gem5.utils.override import overrides + from .riscvmatched_core import U74Core diff --git a/src/python/gem5/resources/client.py b/src/python/gem5/resources/client.py index ab8262bf92..1fecbb9c08 100644 --- a/src/python/gem5/resources/client.py +++ b/src/python/gem5/resources/client.py @@ -25,23 +25,36 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import json -from pathlib import Path import os -from typing import Optional, Dict, List -from .client_api.client_wrapper import ClientWrapper -from gem5.gem5_default_config import config -from m5.util import inform +from pathlib import Path +from typing import ( + Dict, + List, + Optional, +) + +from m5.util import ( + inform, + warn, +) + from _m5 import core +from gem5.gem5_default_config import config + +from .client_api.client_wrapper import ClientWrapper + def getFileContent(file_path: Path) -> Dict: """ - Get the content of the file at the given path - :param file_path: The path of the file - :return: The content of the file + Get the content of the file at the given path. + + :param file_path: The path of the file. + + :return: The content of the file. """ if file_path.exists(): - with open(file_path, "r") as file: + with open(file_path) as file: return json.load(file) else: raise Exception(f"File not found at {file_path}") @@ -53,8 +66,38 @@ clientwrapper = None def _get_clientwrapper(): global clientwrapper if clientwrapper is None: + if ( + "GEM5_RESOURCE_JSON" in os.environ + and "GEM5_RESOURCE_JSON_APPEND" in os.environ + ): + raise Exception( + "Both GEM5_RESOURCE_JSON and GEM5_RESOURCE_JSON_APPEND are set. Please set only one of them." + ) + gem5_config = {} + # If the GEM5_RESOURCE_JSON is set, use it as the only source + if "GEM5_RESOURCE_JSON" in os.environ: + json_source = { + "url": os.environ["GEM5_RESOURCE_JSON"], + "isMongo": False, + } + gem5_config["sources"] = {"GEM5_RESOURCE_JSON": json_source} + if "GEM5_CONFIG" in os.environ: + warn( + f"Both GEM5_CONFIG and GEM5_RESOURCE_JSON are set.\n" + f"GEM5_CONFIG will be ignored in favor of the GEM5_RESOURCE_JSON environment variable." + ) + elif (Path().cwd().resolve() / "gem5-config.json").exists(): + warn( + f"Both gem5-config.json and GEM5_RESOURCE_JSON are set.\n" + f"gem5-config.json will be ignored in favor of the GEM5_RESOURCE_JSON environment variable." + ) + else: + warn( + f"GEM5_RESOURCE_JSON is set.\n" + f"gem5-default-config will be ignored in favor of the GEM5_RESOURCE_JSON environment variable." + ) # First check if the config file path is provided in the environment variable - if "GEM5_CONFIG" in os.environ: + elif "GEM5_CONFIG" in os.environ: config_file_path = Path(os.environ["GEM5_CONFIG"]) gem5_config = getFileContent(config_file_path) inform("Using config file specified by $GEM5_CONFIG") @@ -68,6 +111,20 @@ def _get_clientwrapper(): else: gem5_config = config inform("Using default config") + + # If the GEM5_RESOURCE_JSON_APPEND is set, append the resources to the gem5_config + if "GEM5_RESOURCE_JSON_APPEND" in os.environ: + json_source = { + "url": os.environ["GEM5_RESOURCE_JSON_APPEND"], + "isMongo": False, + } + gem5_config["sources"].update( + {"GEM5_RESOURCE_JSON_APPEND": json_source} + ) + inform( + f"Appending resources from {os.environ['GEM5_RESOURCE_JSON_APPEND']}" + ) + clientwrapper = ClientWrapper(gem5_config) return clientwrapper @@ -81,10 +138,10 @@ def list_resources( :param clients: The list of clients to query :param gem5_version: The gem5 version of the resource to get. By default, - it is the gem5 version of the current build. If set to none, it will return - all gem5 versions of the resource. + it is the gem5 version of the current build. If set to + ``None``, it will return all gem5 versions of the resource. :return: A Python Dict where the key is the resource id and the value is - a list of all the supported resource versions. + a list of all the supported resource versions. """ return _get_clientwrapper().list_resources(clients, gem5_version) @@ -96,13 +153,15 @@ def get_resource_json_obj( gem5_version: Optional[str] = core.gem5Version, ) -> Dict: """ - Get the resource json object from the clients wrapper - :param resource_id: The resource id - :param resource_version: The resource version - :param clients: The list of clients to query + Get the resource json object from the clients wrapper. + + :param resource_id: The resource id. + :param resource_version: The resource version. + :param clients: The list of clients to query. :param gem5_version: The gem5 versions to filter the resources based on - compatibility. By default, it is the gem5 version of the current build. - If None, filtering based on compatibility is not performed. + compatibility. By default, it is the gem5 version of the + current build. If ``None``, filtering based on compatibility + is not performed. """ return _get_clientwrapper().get_resource_json_obj_from_client( diff --git a/src/python/gem5/resources/client_api/abstract_client.py b/src/python/gem5/resources/client_api/abstract_client.py index 7f8ad6166e..2ec4a3cdb8 100644 --- a/src/python/gem5/resources/client_api/abstract_client.py +++ b/src/python/gem5/resources/client_api/abstract_client.py @@ -24,37 +24,27 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from abc import ABC, abstractmethod -from typing import Any, Dict, List, Optional import urllib.parse +from abc import ( + ABC, + abstractmethod, +) +from typing import ( + Any, + Dict, + List, + Optional, +) class AbstractClient(ABC): - def verify_status_code(self, status_code: int) -> None: - """ - Verifies that the status code is 200. - :param status_code: The status code to verify. - """ - if status_code == 200: - return - if status_code == 429: - raise Exception("Panic: Too many requests") - if status_code == 401: - raise Exception("Panic: Unauthorized") - if status_code == 404: - raise Exception("Panic: Not found") - if status_code == 400: - raise Exception("Panic: Bad request") - if status_code == 500: - raise Exception("Panic: Internal server error") - - raise Exception(f"Panic: Unknown status code {status_code}") - def _url_validator(self, url: str) -> bool: """ Validates the provided URL. + :param url: The URL to be validated. - :return: True if the URL is valid, False otherwise. + + :return: ``True`` if the URL is valid, ``False`` otherwise. """ try: result = urllib.parse.urlparse(url) @@ -71,12 +61,13 @@ class AbstractClient(ABC): ) -> List[Dict[str, Any]]: """ :param resource_id: The ID of the Resource. Optional, if not set, all - resources will be returned. - :param resource_version: The version of the Resource. Optional, if - not set, all resource versions will be returned. Note: If `resource_id` - is not set, this parameter will be ignored. + resources will be returned. + :param resource_version: The version of the `Resource`. Optional, if + not set, all resource versions will be returned. + Note: If ``resource_id`` is not set, this + parameter will be ignored. :param gem5_version: The version of gem5. Optional, if not set, all - versions will be returned. + versions will be returned. :return: A list of all the Resources with the given ID. """ raise NotImplementedError @@ -86,15 +77,19 @@ class AbstractClient(ABC): resources_to_filter: List[Dict[str, Any]], gem5_version: Optional[str] = None, ) -> List[Dict[str, Any]]: - """Returns a filtered list resources based on gem5 version + """ + Returns a filtered list resources based on gem5 version compatibility. - Note: This function assumes if the minor component of - a resource's gem5_version is not specified, the resource is compatible - with all minor versions of the same major version. - Likewise, if no hot-fix component is specified, it is assumed that - the resource is compatible with all hot-fix versions of the same - minor version. + .. note:: + + This function assumes if the minor component of a resource's + gem5_version is not specified, the resource is compatible with all + minor versions of the same major version. + + Likewise, if no hot-fix component is specified, it is assumed that + the resource is compatible with all hot-fix versions of the same + minor version. * '20.1' would be compatible with gem5 '20.1.1.0' and '20.1.2.0'. * '21.5.2' would be compatible with gem5 '21.5.2.0' and '21.5.2.0'. @@ -102,8 +97,9 @@ class AbstractClient(ABC): :param resources_to_filter: The list of resources to filter. :param gem5_version: The gem5 version in which the filtered resources - should be compatible. If None, no filtering will be done. - : + should be compatible. If ``None``, no filtering will + be done. + """ if not gem5_version: return resources_to_filter @@ -118,6 +114,7 @@ class AbstractClient(ABC): def get_resources_by_id(self, resource_id: str) -> List[Dict[str, Any]]: """ :param resource_id: The ID of the Resource. + :return: A list of all the Resources with the given ID. """ return self.get_resources(resource_id=resource_id) diff --git a/src/python/gem5/resources/client_api/atlasclient.py b/src/python/gem5/resources/client_api/atlasclient.py index 7d2a27c3f7..1a4f9737c9 100644 --- a/src/python/gem5/resources/client_api/atlasclient.py +++ b/src/python/gem5/resources/client_api/atlasclient.py @@ -24,17 +24,59 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from urllib import request, parse -from urllib.error import HTTPError, URLError -from typing import Optional, Dict, Union, Type, Tuple, List, Any +import itertools import json +import time +from typing import ( + Any, + Dict, + List, + Optional, + Tuple, + Type, + Union, +) +from urllib import ( + parse, + request, +) + +from m5.util import warn + from .abstract_client import AbstractClient +class AtlasClientHttpJsonRequestError(Exception): + def __init__( + self, + client: "AtlasClient", + data: Dict[str, Any], + purpose_of_request: Optional[str], + ): + """An exception raised when an HTTP request to Atlas MongoDB fails. + :param client: The AtlasClient instance that raised the exception. + :param purpose_of_request: A string describing the purpose of the + request. + """ + error_str = ( + f"Http Request to Atlas MongoDB failed.\n" + f"Atlas URL: {client.url}\n" + f"Auth URL: {client.authUrl}\n" + f"Database: {client.database}\n" + f"Collection: {client.collection}\n\n" + f"Data sent:\n\n{json.dumps(data,indent=4)}\n\n" + ) + + if purpose_of_request: + error_str += f"Purpose of Request: {purpose_of_request}\n\n" + super().__init__(error_str) + + class AtlasClient(AbstractClient): def __init__(self, config: Dict[str, str]): """ Initializes a connection to a MongoDB Atlas database. + :param uri: The URI for connecting to the MongoDB server. :param db: The name of the database to connect to. :param collection: The name of the collection within the database. @@ -47,22 +89,69 @@ class AtlasClient(AbstractClient): self.authUrl = config["authUrl"] def get_token(self): - data = {"key": self.apiKey} - data = json.dumps(data).encode("utf-8") + return self._atlas_http_json_req( + self.authUrl, + data_json={"key": self.apiKey}, + headers={"Content-Type": "application/json"}, + purpose_of_request="Get Access Token with API key", + )["access_token"] + + def _atlas_http_json_req( + self, + url: str, + data_json: Dict[str, Any], + headers: Dict[str, str], + purpose_of_request: Optional[str], + max_failed_attempts: int = 4, + reattempt_pause_base: int = 2, + ) -> Dict[str, Any]: + """Sends a JSON object over HTTP to a given Atlas MongoDB server and + returns the response. This function will attempt to reconnect to the + server if the connection fails a set number of times before raising an + exception. + + :param url: The URL to open the connection. + :param data_json: The JSON object to send. + :param headers: The headers to send with the request. + :param purpose_of_request: A string describing the purpose of the + request. This is optional. It's used to give context to the user if an + exception is raised. + :param max_failed_attempts: The maximum number of times to an attempt + at making a request should be done before throwing an exception. + :param reattempt_pause_base: The base of the exponential backoff -- the + time between each attempt. + + **Warning**: This function assumes a JSON response. + """ + data = json.dumps(data_json).encode("utf-8") req = request.Request( - self.authUrl, + url, data=data, - headers={"Content-Type": "application/json"}, + headers=headers, ) - try: - response = request.urlopen(req) - except HTTPError as e: - self.verify_status_code(e.status) - return None - result = json.loads(response.read().decode("utf-8")) - token = result["access_token"] - return token + + for attempt in itertools.count(start=1): + try: + response = request.urlopen(req) + break + except Exception as e: + if attempt >= max_failed_attempts: + raise AtlasClientHttpJsonRequestError( + client=self, + data=data_json, + purpose_of_request=purpose_of_request, + ) + pause = reattempt_pause_base**attempt + warn( + f"Attempt {attempt} of Atlas HTTP Request failed.\n" + f"Purpose of Request: {purpose_of_request}.\n\n" + f"Failed with Exception:\n{e}\n\n" + f"Retrying after {pause} seconds..." + ) + time.sleep(pause) + + return json.loads(response.read().decode("utf-8")) def get_resources( self, @@ -84,21 +173,18 @@ class AtlasClient(AbstractClient): if filter: data["filter"] = filter - data = json.dumps(data).encode("utf-8") headers = { "Authorization": f"Bearer {self.get_token()}", "Content-Type": "application/json", } - req = request.Request(url, data=data, headers=headers) - try: - response = request.urlopen(req) - except HTTPError as e: - self.verify_status_code(e.status) - return None - result = json.loads(response.read().decode("utf-8")) - resources = result["documents"] + resources = self._atlas_http_json_req( + url, + data_json=data, + headers=headers, + purpose_of_request="Get Resources", + )["documents"] # I do this as a lazy post-processing step because I can't figure out # how to do this via an Atlas query, which may be more efficient. diff --git a/src/python/gem5/resources/client_api/client_wrapper.py b/src/python/gem5/resources/client_api/client_wrapper.py index d2baabc52d..4e93fc46b1 100644 --- a/src/python/gem5/resources/client_api/client_wrapper.py +++ b/src/python/gem5/resources/client_api/client_wrapper.py @@ -24,13 +24,22 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .jsonclient import JSONClient -from .atlasclient import AtlasClient -from _m5 import core -from typing import Optional, Dict, List, Tuple import itertools +import sys +from typing import ( + Dict, + List, + Optional, + Tuple, +) + from m5.util import warn +from _m5 import core + +from .atlasclient import AtlasClient +from .jsonclient import JSONClient + class ClientWrapper: def __init__(self, config): @@ -43,8 +52,10 @@ class ClientWrapper: """ This function creates respective client object for each source in the config file according to the type of source. - Params: config: config file containing the source information - Returns: clients: dictionary of clients for each source + + :param config: config file containing the source information + + :returns: clients: dictionary of clients for each source """ clients = {} for client in config["sources"]: @@ -63,7 +74,6 @@ class ClientWrapper: clients: Optional[List[str]] = None, gem5_version: Optional[str] = core.gem5Version, ) -> Dict[str, List[str]]: - clients_to_search = ( list(self.clients.keys()) if clients is None else clients ) @@ -98,9 +108,10 @@ class ClientWrapper: """ This function returns all the resources with the given id from all the sources. + :param resource_id: The id of the resource to search for. - :param clients: A list of clients to search through. If None, all - clients are searched. + :param clients: A list of clients to search through. If ``None``, all + clients are searched. :return: A list of resources as Python dictionaries. """ resources = [] @@ -114,7 +125,12 @@ class ClientWrapper: self.clients[client].get_resources_by_id(resource_id) ) except Exception as e: - warn(f"Error getting resources from client {client}: {str(e)}") + print( + f"Exception thrown while getting resource '{resource_id}' " + f"from client '{client}'\n", + file=sys.stderr, + ) + raise e # check if no 2 resources have the same id and version for res1, res2 in itertools.combinations(resources, 2): if res1["resource_version"] == res2["resource_version"]: @@ -134,15 +150,16 @@ class ClientWrapper: """ This function returns the resource object from the client with the given id and version. + :param resource_id: The id of the resource to search for. :param resource_version: The version of the resource to search for. - :param clients: A list of clients to search through. If None, all - clients are searched. + :param clients: A list of clients to search through. If ``None``, all + clients are searched. :param gem5_version: The gem5 version to check compatibility with. If - None, no compatibility check is performed. By default, is the current - version of gem5. + ``None``, no compatibility check is performed. By + default, is the current version of gem5. :return: The resource object as a Python dictionary if found. - If not found, exception is thrown. + If not found, exception is thrown. """ # getting all the resources with the given id from the dictionary resources = self.get_all_resources_by_id(resource_id, clients) @@ -183,10 +200,12 @@ class ClientWrapper: """ Searches for the resource with the given version. If the resource is not found, an exception is thrown. + :param resources: A list of resources to search through. :param resource_version: The version of the resource to search for. + :return: The resource object as a Python dictionary if found. - If not found, None is returned. + If not found, ``None`` is returned. """ return_resource = next( iter( @@ -213,23 +232,29 @@ class ClientWrapper: """ Returns a list of compatible resources with the current gem5 version. - Note: This function assumes if the minor component of - a resource's gem5_version is not specified, it that the - resource is compatible all minor versions of the same major version. - Likewise, if no hot-fix component is specified, it is assumed that - the resource is compatible with all hot-fix versions of the same - minor version. + .. note:: + + This function assumes if the minor component of a resource's + gem5_version is not specified, it that the resource is compatible + all minor versions of the same major version. + + Likewise, if no hot-fix component is specified, it is assumed that + the resource is compatible with all hot-fix versions of the same + minor version. * '20.1' would be compatible with gem5 '20.1.1.0' and '20.1.2.0'. * '21.5.2' would be compatible with gem5 '21.5.2.0' and '21.5.2.0'. * '22.3.2.4' would only be compatible with gem5 '22.3.2.4'. :param resources: A list of resources to filter. + :return: A list of compatible resources as Python dictionaries. - **Note**: This is a big duplication of code. This functionality already - exists in the `AbstractClient` class. This code should be refactored - to avoid this duplication. + .. note:: + + This is a big duplication of code. This functionality already + exists in the `AbstractClient` class. This code should be refactored + to avoid this duplication. """ compatible_resources = [] @@ -242,8 +267,11 @@ class ClientWrapper: def _sort_resources(self, resources: List) -> List: """ Sorts the resources by ID. + If the IDs are the same, the resources are sorted by version. + :param resources: A list of resources to sort. + :return: A list of sorted resources. """ @@ -251,7 +279,7 @@ class ClientWrapper: """This is used for sorting resources by ID and version. First the ID is sorted, then the version. In cases where the version contains periods, it's assumed this is to separate a - "major.minor.hotfix" style versioning system. In which case, the + ``major.minor.hotfix`` style versioning system. In which case, the value separated in the most-significant position is sorted before those less significant. If the value is a digit it is cast as an int, otherwise, it is cast as a string, to lower-case. @@ -275,29 +303,33 @@ class ClientWrapper: ) -> bool: """ Checks if the resource is compatible with the gem5 version. + Prints a warning if the resource is not compatible. + :param resource: The resource to check. :optional param gem5_version: The gem5 version to check - compatibility with. - :return: True if the resource is compatible, False otherwise. + compatibility with. + :return: ``True`` if the resource is compatible, ``False`` otherwise. """ if not resource: return False if ( gem5_version + and not gem5_version.upper().startswith("DEVELOP") and not self._get_resources_compatible_with_gem5_version( [resource], gem5_version=gem5_version ) ): - warn( - f"Resource {resource['id']} with version " - f"{resource['resource_version']} is not known to be compatible" - f" with gem5 version {gem5_version}. " - "This may cause problems with your simulation. " - "This resource's compatibility " - "with different gem5 versions can be found here: " - "https://resources.gem5.org" - f"/resources/{resource['id']}/versions" - ) + if not gem5_version.upper().startswith("DEVELOP"): + warn( + f"Resource {resource['id']} with version " + f"{resource['resource_version']} is not known to be compatible" + f" with gem5 version {gem5_version}. " + "This may cause problems with your simulation. " + "This resource's compatibility " + "with different gem5 versions can be found here: " + "https://resources.gem5.org" + f"/resources/{resource['id']}/versions" + ) return False return True diff --git a/src/python/gem5/resources/client_api/jsonclient.py b/src/python/gem5/resources/client_api/jsonclient.py index 9e837131b0..f242a788a9 100644 --- a/src/python/gem5/resources/client_api/jsonclient.py +++ b/src/python/gem5/resources/client_api/jsonclient.py @@ -26,17 +26,28 @@ import json from pathlib import Path +from typing import ( + Any, + Dict, + List, + Optional, + Tuple, + Type, + Union, +) from urllib import request -from typing import Optional, Dict, Union, Type, Tuple, List, Any -from .abstract_client import AbstractClient from urllib.error import URLError + from m5.util import warn +from .abstract_client import AbstractClient + class JSONClient(AbstractClient): def __init__(self, path: str): """ Initializes a JSON client. + :param path: The path to the Resource, either URL or local. """ self.path = path diff --git a/src/python/gem5/resources/downloader.py b/src/python/gem5/resources/downloader.py index 1aeb487e61..1d495509ee 100644 --- a/src/python/gem5/resources/downloader.py +++ b/src/python/gem5/resources/downloader.py @@ -24,28 +24,36 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import urllib.request -import urllib.parse -import os -import shutil import gzip -import time +import os import random -from pathlib import Path +import shutil import tarfile +import time +import urllib.parse +import urllib.request +from pathlib import Path +from typing import ( + Dict, + List, + Optional, +) from urllib.error import HTTPError -from typing import List, Optional, Dict +from urllib.parse import urlparse from _m5 import core -from .client import ( - get_resource_json_obj, - list_resources as client_list_resources, -) -from .md5_utils import md5_file, md5_dir -from ..utils.progress_bar import tqdm, progress_hook - from ..utils.filelock import FileLock +from ..utils.progress_bar import ( + progress_hook, + tqdm, +) +from .client import get_resource_json_obj +from .client import list_resources as client_list_resources +from .md5_utils import ( + md5_dir, + md5_file, +) """ This Python module contains functions used to download, list, and obtain @@ -65,8 +73,8 @@ def _download(url: str, download_to: str, max_attempts: int = 6) -> None: :param download_to: The location the downloaded file is to be stored. :param max_attempts: The max number of download attempts before stopping. - The default is 6. This translates to roughly 1 minute of retrying before - stopping. + The default is 6. This translates to roughly 1 minute + of retrying before stopping. """ # TODO: This whole setup will only work for single files we can get via @@ -85,10 +93,11 @@ def _download(url: str, download_to: str, max_attempts: int = 6) -> None: # If the "use_proxy" variable is specified we setup a socks5 # connection. - import socks import socket import ssl + import socks + IP_ADDR, host_port = use_proxy.split(":") PORT = int(host_port) socks.set_default_proxy(socks.SOCKS5, IP_ADDR, PORT) @@ -179,16 +188,18 @@ def list_resources( Lists all available resources. Returns a dictionary where the key is the id of the resources and the value is a list of that resource's versions. - :param clients: A list of clients to use when listing resources. If None, - all clients will be used. None by default. + :param clients: A list of clients to use when listing resources. If ``None``, + all clients will be used. ``None`` by default. :param gem5_version: The gem5 version to which all resources should be - compatible with. If None, compatibility of resources is not considered and - all resources will be returned. + compatible with. If ``None``, compatibility of resources + is not considered and all resources will be returned. - **Note**: This function is here for legacy reasons. The `list_resources` - function was originally stored here. In order to remain backwards - compatible, this function will call the `client_list_resources` function + .. note:: + + This function is here for legacy reasons. The ``list_resources`` + function was originally stored here. In order to remain backwards + compatible, this function will call the ``client_list_resources`` function. """ return client_list_resources(clients=clients, gem5_version=gem5_version) @@ -203,6 +214,7 @@ def get_resource( resource_version: Optional[str] = None, clients: Optional[List] = None, gem5_version: Optional[str] = core.gem5Version, + quiet: bool = False, ) -> None: """ Obtains a gem5 resource and stored it to a specified location. If the @@ -211,33 +223,39 @@ def get_resource( :param resource_name: The resource to be obtained. :param to_path: The location in the file system the resource is to be - stored. The filename should be included. + stored. The filename should be included. - :param unzip: If true, gzipped resources will be unzipped prior to saving - to `to_path`. True by default. + :param unzip: If ``True``, gzipped resources will be unzipped prior to saving + to ``to_path``. ``True`` by default. - :param untar: If true, tar achieve resource will be unpacked prior to - saving to `to_path`. True by default. + :param untar: If ``True``, tar achieve resource will be unpacked prior to + saving to ``to_path``. ``True`` by default. :param download_md5_mismatch: If a resource is present with an incorrect - hash (e.g., an outdated version of the resource is present), `get_resource` - will delete this local resource and re-download it if this parameter is - True. True by default. + hash (e.g., an outdated version of the resource + is present), ``get_resource`` will delete this + local resource and re-download it if this parameter + is ``True``. ``True`` by default. :param resource_version: The version of the resource to be obtained. If - None, the latest version of the resource compatible with the working - directory's gem5 version will be obtained. None by default. + ``None``, the latest version of the resource compatible + with the working directory's gem5 version will be obtained. + ``None`` by default. :param clients: A list of clients to use when obtaining the resource. If - None, all clients will be used. None by default. + ``None``, all clients will be used. ``None`` by default. :param gem5_version: The gem5 version to use when obtaining the resource. - By default, the version of gem5 being used is used. This is used primarily - for testing purposes. + By default, the version of gem5 being used is used. This + is used primarily for testing purposes. + + :param quiet: If ``True``, no output will be printed to the console (baring + exceptions). ``False`` by default. :raises Exception: An exception is thrown if a file is already present at - `to_path` but it does not have the correct md5 sum. An exception will also - be thrown is a directory is present at `to_path` + ``to_path`` but it does not have the correct md5 sum. An + exception will also be thrown is a directory is present + at ``to_path``. """ # We apply a lock for a specific resource. This is to avoid circumstances @@ -280,17 +298,21 @@ def get_resource( # string-based way of doing things. It can be refactored away over # time: # https://gem5-review.googlesource.com/c/public/gem5-resources/+/51168 - if isinstance(resource_json["is_zipped"], str): - run_unzip = unzip and resource_json["is_zipped"].lower() == "true" - elif isinstance(resource_json["is_zipped"], bool): - run_unzip = unzip and resource_json["is_zipped"] - else: - raise Exception( - "The resource.json entry for '{}' has a value for the " - "'is_zipped' field which is neither a string or a boolean.".format( - resource_name + run_unzip = False + if "is_zipped" in resource_json: + if isinstance(resource_json["is_zipped"], str): + run_unzip = ( + unzip and resource_json["is_zipped"].lower() == "true" + ) + elif isinstance(resource_json["is_zipped"], bool): + run_unzip = unzip and resource_json["is_zipped"] + else: + raise Exception( + "The resource.json entry for '{}' has a value for the " + "'is_zipped' field which is neither a string or a boolean.".format( + resource_name + ) ) - ) run_tar_extract = ( untar @@ -306,42 +328,60 @@ def get_resource( if run_unzip: download_dest += zip_extension - # TODO: Might be nice to have some kind of download status bar here. - # TODO: There might be a case where this should be silenced. - print( - "Resource '{}' was not found locally. Downloading to '{}'...".format( - resource_name, download_dest + file_uri_path = _file_uri_to_path(resource_json["url"]) + if file_uri_path: + if not file_uri_path.exists(): + raise Exception( + f"Could not find file at path '{file_uri_path}'" + ) + print( + "Resource '{}' is being copied from '{}' to '{}'...".format( + resource_name, + urlparse(resource_json["url"]).path, + download_dest, + ) ) - ) + shutil.copy(file_uri_path, download_dest) + else: + # TODO: Might be nice to have some kind of download status bar here.. + if not quiet: + print( + f"Resource '{resource_name}' was not found locally. " + f"Downloading to '{download_dest}'..." + ) - # Get the URL. - url = resource_json["url"] + # Get the URL. + url = resource_json["url"] - _download(url=url, download_to=download_dest) - print(f"Finished downloading resource '{resource_name}'.") + _download(url=url, download_to=download_dest) + if not quiet: + print(f"Finished downloading resource '{resource_name}'.") if run_unzip: - print( - f"Decompressing resource '{resource_name}' ('{download_dest}')..." - ) + if not quiet: + print( + f"Decompressing resource '{resource_name}' " + f"('{download_dest}')..." + ) unzip_to = download_dest[: -len(zip_extension)] with gzip.open(download_dest, "rb") as f: with open(unzip_to, "wb") as o: shutil.copyfileobj(f, o) os.remove(download_dest) download_dest = unzip_to - print(f"Finished decompressing resource '{resource_name}'.") + if not quiet: + print(f"Finished decompressing resource '{resource_name}'.") if run_tar_extract: - print( - f"Unpacking the the resource '{resource_name}' " - f"('{download_dest}')" - ) + if not quiet: + print( + f"Unpacking the the resource '{resource_name}' " + f"('{download_dest}')" + ) unpack_to = download_dest[: -len(tar_extension)] with tarfile.open(download_dest) as f: def is_within_directory(directory, target): - abs_directory = os.path.abspath(directory) abs_target = os.path.abspath(target) @@ -352,7 +392,6 @@ def get_resource( def safe_extract( tar, path=".", members=None, *, numeric_owner=False ): - for member in tar.getmembers(): member_path = os.path.join(path, member.name) if not is_within_directory(path, member_path): @@ -364,3 +403,28 @@ def get_resource( safe_extract(f, unpack_to) os.remove(download_dest) + + +def _file_uri_to_path(uri: str) -> Optional[Path]: + """ + If the URI uses the File scheme (e.g, ``file://host/path``) then + a Path object for the local path is returned, otherwise ``None``. + + .. note:: + + Only files from localhost are permitted. An exception is thrown otherwise. + + :param uri: The file URI to convert. + + :returns: The path to the file. + """ + + if urlparse(uri).scheme == "file": + if urlparse(uri).netloc == "" or urlparse(uri).netloc == "localhost": + local_path = urlparse(uri).path + return Path(local_path) + raise Exception( + f"File URI '{uri}' specifies host '{urlparse(uri).netloc}'. " + "Only localhost is permitted." + ) + return None diff --git a/src/python/gem5/resources/elfie.py b/src/python/gem5/resources/elfie.py index ae51388d62..d90930d3f0 100644 --- a/src/python/gem5/resources/elfie.py +++ b/src/python/gem5/resources/elfie.py @@ -24,16 +24,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import PcCountPair -from m5.objects import PcCountTrackerManager - from typing import List +from m5.objects import PcCountTrackerManager +from m5.params import PcCountPair + class ELFieInfo: - """Stores information to load/run ELFies + """Stores information to load/run ELFies. - See https://github.com/intel/pinball2elf for more information + See https://github.com/intel/pinball2elf for more information. """ def __init__(self, start: PcCountPair, end: PcCountPair): @@ -50,6 +50,7 @@ class ELFieInfo: A function is used to setup a PC tracker in all the cores and connect all the tracker to the PC tracker manager to perform multithread PC tracking. + :param processor: The processor used in the simulation configuration. """ for core in processor.get_cores(): diff --git a/src/python/gem5/resources/looppoint.py b/src/python/gem5/resources/looppoint.py index 684faef37d..f054498dd9 100644 --- a/src/python/gem5/resources/looppoint.py +++ b/src/python/gem5/resources/looppoint.py @@ -24,23 +24,30 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import PcCountPair -from m5.objects import PcCountTrackerManager -import m5 - -import os import csv import json +import os from pathlib import Path -from typing import List, Optional, Dict, Union +from typing import ( + Dict, + List, + Optional, + Union, +) + +import m5 +from m5.objects import PcCountTrackerManager +from m5.params import PcCountPair class LooppointRegionPC: """A data structure for storing the Looppoint region's PC information. - **Note**: This is not intended to be a user-facing class. The classes - `LooppointJsonLoader` and `LooppointCSVLoader` can be used to load - and restore Simpoint data. + .. note:: + + This is not intended to be a user-facing class. The classes + LooppointJsonLoader and LooppointCSVLoader can be used to load + and restore Simpoint data. """ def __init__(self, pc: int, globl: int, relative: Optional[int] = None): @@ -92,9 +99,11 @@ class LooppointRegionPC: class LooppointRegionWarmup: """A data structure for storing a Looppoint region's warmup data. - **Note**: This is not intended to be a user-facing class. The classes - `LooppointJsonLoader` and `LooppointCSVLoader` can be used to load - and restore Simpoint data. + .. note:: + + This is not intended to be a user-facing class. The classes + LooppointJsonLoader and LooppointCSVLoader can be used to load + and restore SimPoint data. """ def __init__(self, start: PcCountPair, end: PcCountPair): @@ -135,9 +144,11 @@ class LooppointRegionWarmup: class LooppointSimulation: """A data structure to store the simulation region start and end region. - **Note**: This is not intended to be a user-facing class. The classes - `LooppointJsonLoader` and `LooppointCSVLoader` can be used to load - and restore Simpoint data. + .. note:: + + This is not intended to be a user-facing class. The classes + LooppointJsonLoader and LooppointCSVLoader can be used to load + and restore Simpoint data. """ def __init__(self, start: LooppointRegionPC, end: LooppointRegionPC): @@ -188,9 +199,11 @@ class LooppointSimulation: class LooppointRegion: """A data structure to store Looppoint region information. - **Note**: This is not intended to be a user-facing class. The classes - `LooppointJsonLoader` and `LooppointCSVLoader` can be used to load - and restore Simpoint data. + .. note:: + + This is not intended to be a user-facing class. The classes + LooppointJsonLoader and LooppointCSVLoader can be used to load + and restore SimPoint data. """ def __init__( @@ -200,11 +213,11 @@ class LooppointRegion: warmup: Optional[LooppointRegionWarmup] = None, ): """ - :param simulation: The simulation information for this Looppoint - region. - :param multiplier: The multiplier for this Looppoint region. - :param warmup: The warmup information for this Looppoint region. - Optional. + :param simulation: The simulation information for this LoopPoint + region. + :param multiplier: The multiplier for this LoopPoint region. + :param warmup: The warmup information for this LoopPoint region. + Optional. """ self._simulation = simulation self._multiplier = multiplier @@ -219,24 +232,24 @@ class LooppointRegion: return self._multiplier def get_warmup(self) -> Optional[LooppointRegionWarmup]: - """If set, returns the warmup region information. Otherwise None.""" + """If set, returns the warmup region information. Otherwise ``None``.""" return self._warmup def get_pc_count_pairs(self) -> List[PcCountPair]: - """Returns the PC count pairs for this Looppoint region.""" + """Returns the PC count pairs for this LoopPoint region.""" pc_count_pairs = self.get_simulation().get_pc_count_pairs() if self.get_warmup(): pc_count_pairs.extend(self.get_warmup().get_pc_count_pairs()) return pc_count_pairs def update_relatives_counts(self, manager: PcCountTrackerManager) -> None: - """Updates the relative counds of this Looppoint region.""" + """Updates the relative counds of this LoopPoint region.""" self.get_simulation().update_relatives_counts( manager=manager, include_start=bool(self.get_warmup()) ) def get_start(self) -> PcCountPair: - """Returns the correct starting PcCountPair for this Looppoint + """Returns the correct starting PcCountPair for this LoopPoint region.""" if self.get_warmup(): return self.get_warmup().get_start() @@ -255,19 +268,19 @@ class LooppointRegion: class Looppoint: - """Stores all the Looppoint information for a gem5 workload.""" + """Stores all the LoopPoint information for a gem5 workload.""" def __init__(self, regions: Dict[Union[str, int], LooppointRegion]): """ - :param regions: A dictionary mapping the region_ids with the - LooppointRegions. + :param regions: A dictionary mapping the ``region_ids`` with the + LoopPointRegions. """ self._regions = regions self._manager = PcCountTrackerManager() self._manager.targets = self.get_targets() def set_target_region_id(self, region_id: Union[str, int]) -> None: - """There are use-cases where we want to obtain a looppoint data + """There are use-cases where we want to obtain a LoopPoint data structure containing a single target region via its ID. This function will remove all irrelevant regions.""" @@ -281,7 +294,7 @@ class Looppoint: self._manager.targets = self.get_targets() def get_manager(self) -> PcCountTrackerManager: - """Returns the PcCountTrackerManager for this Looppoint data + """Returns the PcCountTrackerManager for this LoopPoint data structure.""" return self._manager @@ -318,7 +331,7 @@ class Looppoint: def get_current_region(self) -> Optional[Union[str, int]]: """Returns the region id if the current PC Count pair if significant - (e.g. beginning of the checkpoint), otherwise, it returns None to + (e.g. beginning of the checkpoint), otherwise, it returns ``None`` to indicate the current PC Count pair is not significant. """ current_pair = self.get_current_pair() @@ -354,7 +367,7 @@ class Looppoint: def to_json(self) -> Dict[Union[int, str], Dict]: """Returns this data-structure as a dictionary for serialization via - the `output_json_file` function.""" + the ``output_json_file`` function.""" to_return = {} for region_id in self.get_regions(): to_return[region_id] = self.get_regions()[region_id].to_json() @@ -366,18 +379,18 @@ class Looppoint: filepath: str = os.path.join(m5.options.outdir, "looppoint.json"), ) -> Dict[int, Dict]: """ - This function is used to output the _json_file into a json file + This function is used to output the ``_json_file`` into a json file. - :param input_indent: the indent value of the json file - :param filepath: the path of the output json file + :param input_indent: The indent value of the json file. + :param filepath: The path of the output json file. """ with open(filepath, "w") as file: json.dump(self.to_json(), file, indent=input_indent) class LooppointCsvLoader(Looppoint): - """This class will create a Looppoint data structure from data extracted - from a Looppoint pinpoints file.""" + """This class will create a LoopPoint data structure from data extracted + from a LoopPoint pinpoints file.""" def __init__( self, @@ -386,10 +399,10 @@ class LooppointCsvLoader(Looppoint): ): """ :params pinpoints_file: The pinpoints file in which the data is to be - expected. + expected. :params region_id: If set, will only load the specified region data. - Otherwise, all region info is loaded. Is used when restoring to a - particular region. + Otherwise, all region info is loaded. Is used when + restoring to a particular region. """ regions = {} @@ -465,8 +478,8 @@ class LooppointCsvLoader(Looppoint): class LooppointJsonLoader(Looppoint): - """This class will create a generate a Looppoint data structure from data - extracted from a Looppoint json file.""" + """This class will create a generate a LoopPoint data structure from data + extracted from a LoopPoint json file.""" def __init__( self, @@ -474,11 +487,11 @@ class LooppointJsonLoader(Looppoint): region_id: Optional[Union[str, int]] = None, ) -> None: """ - :param looppoint_file: a json file generated by gem5 that has all the - LoopPoint data information + :param looppoint_file: A json file generated by gem5 that has all the + LoopPoint data information. :params region_id: If set, will only load the specified region data. - Otherwise, all region info is loaded. Is used when restoring to a - particular region. + Otherwise, all region info is loaded. Is used when + restoring to a particular region. """ _path = ( @@ -491,7 +504,6 @@ class LooppointJsonLoader(Looppoint): with open(_path) as file: json_contents = json.load(file) for rid in json_contents: - start_pc = int(json_contents[rid]["simulation"]["start"]["pc"]) start_globl = int( json_contents[rid]["simulation"]["start"]["global"] diff --git a/src/python/gem5/resources/md5_utils.py b/src/python/gem5/resources/md5_utils.py index f4a1a87df5..fcbdedb967 100644 --- a/src/python/gem5/resources/md5_utils.py +++ b/src/python/gem5/resources/md5_utils.py @@ -1,4 +1,5 @@ # Copyright (c) 2022-2023 The Regents of the University of California +# Copyright (c) 2023 COSEDA Technologies GmbH # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -24,12 +25,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from pathlib import Path import hashlib -from _hashlib import HASH as Hash +from pathlib import Path +from typing import Type -def _md5_update_from_file(filename: Path, hash: Hash) -> Hash: +def _md5_update_from_file( + filename: Path, hash: Type[hashlib.md5] +) -> Type[hashlib.md5]: assert filename.is_file() if filename.stat().st_size < 1024 * 1024 * 100: @@ -52,7 +55,9 @@ def _md5_update_from_file(filename: Path, hash: Hash) -> Hash: return hash -def _md5_update_from_dir(directory: Path, hash: Hash) -> Hash: +def _md5_update_from_dir( + directory: Path, hash: Type[hashlib.md5] +) -> Type[hashlib.md5]: assert directory.is_dir() for path in sorted(directory.iterdir(), key=lambda p: str(p).lower()): hash.update(path.name.encode()) @@ -65,8 +70,8 @@ def _md5_update_from_dir(directory: Path, hash: Hash) -> Hash: def md5(path: Path) -> str: """ - Gets the md5 value of a file or directory. `md5_file` is used if the path - is a file and `md5_dir` is used if the path is a directory. An exception + Gets the md5 value of a file or directory. ``md5_file`` is used if the path + is a file and ``md5_dir`` is used if the path is a directory. An exception is returned if the path is not a valid file or directory. :param path: The path to get the md5 of. @@ -81,7 +86,7 @@ def md5(path: Path) -> str: def md5_file(filename: Path) -> str: """ - Gives the md5 hash of a file + Gives the md5 hash of a file. :filename: The file in which the md5 is to be calculated. """ @@ -94,7 +99,9 @@ def md5_dir(directory: Path) -> str: This is achieved by getting the md5 hash of all files in the directory. - Note: The path of files are also hashed so the md5 of the directory changes - if empty files are included or filenames are changed. + .. note:: + + The path of files are also hashed so the md5 of the directory changes + if empty files are included or filenames are changed. """ return str(_md5_update_from_dir(directory, hashlib.md5()).hexdigest()) diff --git a/src/python/gem5/resources/resource.py b/src/python/gem5/resources/resource.py index bc9f4480ba..591515a6b9 100644 --- a/src/python/gem5/resources/resource.py +++ b/src/python/gem5/resources/resource.py @@ -24,40 +24,62 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from abc import ABCMeta +import json import os +from abc import ABCMeta +from functools import partial from pathlib import Path -from m5.util import warn, fatal +from typing import ( + Any, + Dict, + Generator, + List, + Optional, + Set, + Tuple, + Type, + Union, +) + +from m5.util import ( + fatal, + warn, +) + from _m5 import core -from .downloader import get_resource - -from .looppoint import LooppointCsvLoader, LooppointJsonLoader -from ..isas import ISA, get_isa_from_str - -from typing import Optional, Dict, Union, Type, Tuple, List - +from ..isas import ( + ISA, + get_isa_from_str, +) from .client import get_resource_json_obj +from .downloader import get_resource +from .looppoint import ( + LooppointCsvLoader, + LooppointJsonLoader, +) """ Resources are items needed to run a simulation, such as a disk image, kernel, or binary. The gem5 project provides pre-built resources, with sources, at -. Here we provide the `AbstractResource` class and its +. Here we provide the AbstractResource class and its various implementations which are designed to encapsulate a resource for use in the gem5 Standard Library. These classes may be contructed directly. E.g.: -```python -binary = BinaryResource(local_path="/path/to/binary") -``` +.. code-block:: python -or obtained via the gem5-resources infrastructure with the `obtain_resource` + binary = BinaryResource(local_path="/path/to/binary") + + +or obtained via the gem5-resources infrastructure with the ``obtain_resource`` function: -```python -binary = obtain_resource("resource name here") -``` +.. code-block:: python + + binary = obtain_resource("resource name here") + """ @@ -70,39 +92,69 @@ class AbstractResource: def __init__( self, + id: Optional[str] = None, resource_version: Optional[str] = None, local_path: Optional[str] = None, description: Optional[str] = None, source: Optional[str] = None, + downloader: Optional[partial] = None, ): """ :param local_path: The path on the host system where this resource is - located. + located. :param description: Description describing this resource. Not a - required parameter. By default is None. + required parameter. By default is ``None``. :param source: The source (as in "source code") for this resource. This - string should navigate users to where the source for this resource - may be found. Not a required parameter. By default is None. + string should navigate users to where the source for this + resource may be found. Not a required parameter. By default + is ``None``. :param resource_version: Version of the resource itself. + :param downloader: A partial function which is used to download the + resource. If set, this is called if the resource is not present at the + specified `local_path`. """ - if local_path and not os.path.exists(local_path): - raise Exception( - f"Local path specified for resource, '{local_path}', does not " - "exist." - ) - + self._id = id self._local_path = local_path self._description = description self._source = source self._version = resource_version + self._downloader = downloader + + def get_id(self) -> str: + """Returns the ID of the resource.""" + return self._id + + def get_category_name(cls) -> str: + raise NotImplementedError + + def __str__(self): + message = ( + f"{self.get_category_name()}({self._id}, {self._version})\n" + "For more information, please visit " + f"https://resources.gem5.org/resources/{self._id}?" + f"version={self._version}" + ) + return message def get_resource_version(self) -> str: """Returns the version of the resource.""" return self._version def get_local_path(self) -> Optional[str]: - """Returns the local path of the resource.""" + """Returns the local path of the resource. + + If specified the `downloader` partial function is called to download + the resource if it is not present or up-to-date at the specified + `local_path`. + """ + if self._downloader: + self._downloader() + if self._local_path and not os.path.exists(self._local_path): + raise Exception( + f"Local path specified for resource, '{self._local_path}', " + "does not exist." + ) return self._local_path def get_description(self) -> Optional[str]: @@ -122,23 +174,38 @@ class FileResource(AbstractResource): def __init__( self, local_path: str, + id: Optional[str] = None, resource_version: Optional[str] = None, description: Optional[str] = None, source: Optional[str] = None, + downloader: Optional[partial] = None, **kwargs, ): - if not os.path.isfile(local_path): - raise Exception( - f"FileResource path specified, '{local_path}', is not a file." - ) - super().__init__( local_path=local_path, + id=id, description=description, source=source, resource_version=resource_version, + downloader=downloader, ) + def get_category_name(cls) -> str: + return "FileResource" + + def get_local_path(self) -> Optional[str]: + # Here we override get_local_path to ensure the file exists. + file_path = super().get_local_path() + + if not file_path: + raise Exception("FileResource path not specified.") + + if not os.path.isfile(file_path): + raise Exception( + f"FileResource path specified, '{file_path}', is not a file." + ) + return file_path + class DirectoryResource(AbstractResource): """A resource consisting of a directory.""" @@ -146,24 +213,39 @@ class DirectoryResource(AbstractResource): def __init__( self, local_path: str, + id: Optional[str] = None, resource_version: Optional[str] = None, description: Optional[str] = None, source: Optional[str] = None, + downloader: Optional[partial] = None, **kwargs, ): - if not os.path.isdir(local_path): - raise Exception( - f"DirectoryResource path specified, {local_path}, is not a " - "directory." - ) - super().__init__( local_path=local_path, + id=id, description=description, source=source, resource_version=resource_version, + downloader=downloader, ) + def get_category_name(cls) -> str: + return "DirectoryResource" + + def get_local_path(self) -> Optional[str]: + # Here we override get_local_path to ensure the directory exists. + dir_path = super().get_local_path() + + if not dir_path: + raise Exception("DirectoryResource path not specified.") + + if not os.path.isdir(dir_path): + raise Exception( + f"DirectoryResource path specified, {dir_path}, is not a " + "directory." + ) + return dir_path + class DiskImageResource(FileResource): """A Disk Image resource.""" @@ -171,17 +253,21 @@ class DiskImageResource(FileResource): def __init__( self, local_path: str, + id: Optional[str] = None, resource_version: Optional[str] = None, description: Optional[str] = None, source: Optional[str] = None, + downloader: Optional[partial] = None, root_partition: Optional[str] = None, **kwargs, ): super().__init__( local_path=local_path, + id=id, description=description, source=source, resource_version=resource_version, + downloader=downloader, ) self._root_partition = root_partition @@ -189,6 +275,9 @@ class DiskImageResource(FileResource): """Returns, if applicable, the Root Partition of the disk image.""" return self._root_partition + def get_category_name(cls) -> str: + return "DiskImageResource" + class BinaryResource(FileResource): """A binary resource.""" @@ -196,17 +285,21 @@ class BinaryResource(FileResource): def __init__( self, local_path: str, + id: Optional[str] = None, resource_version: Optional[str] = None, description: Optional[str] = None, source: Optional[str] = None, + downloader: Optional[partial] = None, architecture: Optional[Union[ISA, str]] = None, **kwargs, ): super().__init__( local_path=local_path, + id=id, description=description, source=source, resource_version=resource_version, + downloader=downloader, ) self._architecture = None @@ -216,6 +309,9 @@ class BinaryResource(FileResource): elif isinstance(architecture, ISA): self._architecture = architecture + def get_category_name(cls) -> str: + return "BinaryResource" + def get_architecture(self) -> Optional[ISA]: """Returns the ISA this binary is compiled to.""" return self._architecture @@ -227,20 +323,27 @@ class BootloaderResource(BinaryResource): def __init__( self, local_path: str, + id: Optional[str] = None, resource_version: Optional[str] = None, description: Optional[str] = None, source: Optional[str] = None, + downloader: Optional[partial] = None, architecture: Optional[Union[ISA, str]] = None, **kwargs, ): super().__init__( local_path=local_path, + id=id, description=description, architecture=architecture, source=source, resource_version=resource_version, + downloader=downloader, ) + def get_category_name(cls) -> str: + return "BootloaderResource" + class GitResource(DirectoryResource): """A git resource.""" @@ -248,18 +351,25 @@ class GitResource(DirectoryResource): def __init__( self, local_path: str, + id: Optional[str] = None, resource_version: Optional[str] = None, description: Optional[str] = None, source: Optional[str] = None, + downloader: Optional[partial] = None, **kwargs, ): super().__init__( local_path=local_path, + id=id, description=description, source=source, resource_version=resource_version, + downloader=downloader, ) + def get_category_name(cls) -> str: + return "GitResource" + class KernelResource(BinaryResource): """A kernel resource.""" @@ -267,20 +377,27 @@ class KernelResource(BinaryResource): def __init__( self, local_path: str, + id: Optional[str] = None, resource_version: Optional[str] = None, description: Optional[str] = None, source: Optional[str] = None, + downloader: Optional[partial] = None, architecture: Optional[Union[ISA, str]] = None, **kwargs, ): super().__init__( local_path=local_path, + id=id, description=description, source=source, architecture=architecture, resource_version=resource_version, + downloader=downloader, ) + def get_category_name(cls) -> str: + return "KernelResource" + class CheckpointResource(DirectoryResource): """A checkpoint resource. The following directory structure is expected: @@ -293,24 +410,31 @@ class CheckpointResource(DirectoryResource): def __init__( self, local_path: str, + id: Optional[str] = None, resource_version: Optional[str] = None, description: Optional[str] = None, source: Optional[str] = None, + downloader: Optional[partial] = None, **kwargs, ): super().__init__( local_path=local_path, + id=id, description=description, source=source, resource_version=resource_version, + downloader=downloader, ) + def get_category_name(cls) -> str: + return "CheckpointResource" + class SimpointResource(AbstractResource): - """A simpoint resource. This resource stores all information required to - perform a Simpoint creation and restore. It contains the Simpoint, the - Simpoint interval, the weight for each Simpoint, the full warmup length, - and the warmup length for each Simpoint. + """A SimPoint resource. This resource stores all information required to + perform a SimPoint creation and restore. It contains the SimPoint, the + SimPoint interval, the weight for each SimPoint, the full warmup length, + and the warmup length for each SimPoint. """ def __init__( @@ -320,29 +444,35 @@ class SimpointResource(AbstractResource): simpoint_list: List[int] = None, weight_list: List[float] = None, warmup_interval: int = 0, + id: Optional[str] = None, workload_name: Optional[str] = None, description: Optional[str] = None, source: Optional[str] = None, + downloader: Optional[partial] = None, local_path: Optional[str] = None, **kwargs, ): """ - :param simpoint_interval: The simpoint interval. - :param simpoint_list: The simpoint list. + :param simpoint_interval: The SimPoint interval. + :param simpoint_list: The SimPoint list. :param weight_list: The weight list. :param warmup_interval: The warmup interval. Default to zero (a value - of zero means effectively not set). - :param workload_name: Simpoints are typically associated with a - particular workload due to their dependency on chosen input parameters. - This field helps backtrack to that resource if required. This should - relate to a workload "name" field in the resource.json file. + of zero means effectively not set). + :param workload_name: SimPoints are typically associated with a + particular workload due to their dependency on + chosen input parameters. + This field helps backtrack to that resource if + required. This should relate to a workload "name" + field in the ``resource.json`` file. """ super().__init__( local_path=local_path, + id=id, description=description, source=source, resource_version=resource_version, + downloader=downloader, ) self._weight_list = weight_list @@ -361,13 +491,13 @@ class SimpointResource(AbstractResource): self._warmup_list = [0] * len(self.get_simpoint_start_insts) def get_simpoint_list(self) -> List[int]: - """Returns the a list containing all the Simpoints for the workload.""" + """Returns the a list containing all the SimPoints for the workload.""" return self._simpoint_list def get_simpoint_start_insts(self) -> List[int]: - """Returns a lst containing all the Simpoint starting instrunction + """Returns a lst containing all the SimPoint starting instrunction points for the workload. This was calculated by multiplying the - Simpoint with the Simpoint interval when it was generated.""" + SimPoint with the SimPoint interval when it was generated.""" return self._simpoint_start_insts def get_warmup_interval(self) -> int: @@ -375,35 +505,35 @@ class SimpointResource(AbstractResource): return self._warmup_interval def get_weight_list(self) -> List[float]: - """Returns the list that contains the weight for each Simpoint. The + """Returns the list that contains the weight for each SimPoint. The order of the weights matches that of the list returned by - `get_simpoint_list(). I.e. `get_weight_list()[3]` is the weight for - simpoint `get_simpoint_list()[3]`.""" + ``get_simpoint_list()``. I.e. ``get_weight_list()[3]`` is the weight for + SimPoint ``get_simpoint_list()[3]``.""" return self._weight_list def get_simpoint_interval(self) -> int: - """Returns the Simpoint interval value.""" + """Returns the SimPoint interval value.""" return self._simpoint_interval def get_warmup_list(self) -> List[int]: - """Returns the a list containing the warmup length for each Simpoint. - Each warmup length in this list corresponds to the Simpoint at the same - index in `get_simpoint_list()`. I.e., `get_warmup_list()[4]` is the - warmup length for Simpoint `get_simpoint_list()[4]`.""" + """Returns the a list containing the warmup length for each SimPoint. + Each warmup length in this list corresponds to the SimPoint at the same + index in ``get_simpoint_list()``. I.e., ``get_warmup_list()[4]`` is the + warmup length for SimPoint ``get_simpoint_list()[4]``.""" return self._warmup_list def get_workload_name(self) -> Optional[str]: - """Return the workload name this Simpoint is associated with.""" + """Return the workload name this SimPoint is associated with.""" return self._workload_name def _set_warmup_list(self) -> List[int]: """ - This function uses the warmup_interval, fits it into the - simpoint_start_insts, and outputs a list of warmup instruction lengths + This function uses the ``warmup_interval``, fits it into the + ``simpoint_start_insts``, and outputs a list of warmup instruction lengths for each SimPoint. The warmup instruction length is calculated using the starting - instruction of a SimPoint to minus the warmup_interval and the ending + instruction of a SimPoint to minus the ``warmup_interval`` and the ending instruction of the last SimPoint. If it is less than 0, then the warmup instruction length is the gap between the starting instruction of a SimPoint and the ending instruction of the last SimPoint. @@ -421,54 +551,73 @@ class SimpointResource(AbstractResource): self._simpoint_start_insts[index] = start_inst - warmup_inst return warmup_list + def get_category_name(cls) -> str: + return "SimpointResource" + class LooppointCsvResource(FileResource, LooppointCsvLoader): - """This Looppoint resource used to create a Looppoint resource from a - pinpoints CSV file""" + """This LoopPoint resource used to create a LoopPoint resource from a + pinpoints CSV file.""" def __init__( self, local_path: str, + id: Optional[str] = None, resource_version: Optional[str] = None, description: Optional[str] = None, source: Optional[str] = None, + downloader: Optional[partial] = None, **kwargs, ): FileResource.__init__( self, local_path=local_path, + id=id, description=description, source=source, resource_version=resource_version, + downloader=downloader, ) - LooppointCsvLoader.__init__(self, pinpoints_file=Path(local_path)) + LooppointCsvLoader.__init__( + self, pinpoints_file=Path(self.get_local_path()) + ) + + def get_category_name(cls) -> str: + return "LooppointCsvResource" class LooppointJsonResource(FileResource, LooppointJsonLoader): def __init__( self, local_path: str, + id: Optional[str] = None, resource_version: Optional[str] = None, region_id: Optional[Union[str, int]] = None, description: Optional[str] = None, source: Optional[str] = None, + downloader: Optional[partial] = None, **kwargs, ): FileResource.__init__( self, local_path=local_path, + id=id, description=description, source=source, resource_version=resource_version, + downloader=downloader, ) LooppointJsonLoader.__init__( - self, looppoint_file=local_path, region_id=region_id + self, looppoint_file=self.get_local_path(), region_id=region_id ) + def get_category_name(cls) -> str: + return "LooppointJsonResource" + class SimpointDirectoryResource(SimpointResource): - """A Simpoint diretory resource. This Simpoint Resource assumes the - existance of a directory containing a simpoint file and a weight file.""" + """A SimPoint diretory resource. This SimPoint Resource assumes the + existance of a directory containing a SimPoint file and a weight file.""" def __init__( self, @@ -477,18 +626,20 @@ class SimpointDirectoryResource(SimpointResource): weight_file: str, simpoint_interval: int, warmup_interval: int, + id: Optional[str] = None, resource_version: Optional[str] = None, workload_name: Optional[str] = None, description: Optional[str] = None, source: Optional[str] = None, + downloader: Optional[partial] = None, **kwargs, ): """ - :param simpoint_file: The Simpoint file. This file is a list of - Simpoints, each on its own line. It should map 1-to-1 to the weights - file. - :param weight_file: The Simpoint weights file. This file is a list of - weights, each on its own line. + :param simpoint_file: The SimPoint file. This file is a list of + SimPoints, each on its own line. It should map + 1-to-1 to the weights file. + :param weight_file: The SimPoint weights file. This file is a list of + weights, each on its own line. """ self._simpoint_file = simpoint_file self._weight_file = weight_file @@ -510,13 +661,15 @@ class SimpointDirectoryResource(SimpointResource): warmup_interval=warmup_interval, workload_name=workload_name, local_path=local_path, + id=id, description=description, source=source, + downloader=downloader, resource_version=resource_version, ) def get_simpoint_file(self) -> Path: - """Return the Simpoint File path.""" + """Return the SimPoint File path.""" return Path(Path(self._local_path) / self._simpoint_file) def get_weight_file(self) -> Path: @@ -526,7 +679,7 @@ class SimpointDirectoryResource(SimpointResource): def _get_weights_and_simpoints_from_file( self, ) -> Tuple[List[int], List[int]]: - """This is a helper function to extract the weights and simpoints from + """This is a helper function to extract the weights and SimPoints from the files. """ simpoint_weight_pair = [] @@ -553,6 +706,219 @@ class SimpointDirectoryResource(SimpointResource): weight_list.append(weight) return simpoint_list, weight_list + def get_category_name(cls) -> str: + return "SimpointDirectoryResource" + + +class SuiteResource(AbstractResource): + """ + A suite resource. This resource is used to specify a suite of workloads to + run on a board. It contains a list of workloads to run, along with their + IDs and versions. + + Each workload in a suite is used to create a `WorkloadResource` object. + These objects are stored in a list and can be iterated over. + """ + + def __init__( + self, + workloads: Dict["WorkloadResource", Set[str]] = {}, + resource_version: Optional[str] = None, + description: Optional[str] = None, + source: Optional[str] = None, + id: Optional[str] = None, + **kwargs, + ) -> None: + """ + :param workloads: A list of ``WorkloadResource`` objects + created from the ``_workloads`` parameter. + :param local_path: The path on the host system where this resource is + located. + :param description: Description describing this resource. Not a + required parameter. By default is ``None``. + :param source: The source (as in "source code") for this resource + on gem5-resources. Not a required parameter. By default + is ``None``. + :param resource_version: Version of the resource itself. + """ + self._workloads = workloads + self._description = description + self._source = source + self._resource_version = resource_version + + super().__init__( + id=id, + description=description, + source=source, + resource_version=resource_version, + ) + + def __iter__(self) -> Generator["WorkloadResource", None, None]: + """ + Returns a generator that iterates over the workloads in the suite. + + :yields: A generator that iterates over the workloads in the suite. + """ + yield from self._workloads.keys() + + def __len__(self): + """ + Returns the number of workloads in the suite. + + :returns: The number of workloads in the suite. + """ + return len(self._workloads) + + def get_category_name(cls) -> str: + return "SuiteResource" + + def with_input_group(self, input_group: str) -> "SuiteResource": + """ + :param input_group: The input group to filter the workloads by. + :returns: A new SuiteResource object with only the workloads that use + the specified input group. + """ + + if input_group not in self.get_input_groups(): + raise Exception( + f"Input group {input_group} not found in Suite.\n" + f"Available input groups are {self.get_input_groups()}" + ) + + filtered_workloads = {} + + for workload, input_groups in self._workloads.items(): + if input_group in input_groups: + filtered_workloads[workload] = input_groups + + return SuiteResource( + local_path=self._local_path, + resource_version=self._resource_version, + description=self._description, + source=self._source, + workloads=filtered_workloads, + ) + + def get_input_groups(self) -> Set[str]: + """ + :returns: A set of all input groups used by the workloads in a suite. + """ + return { + input_group + for input_groups in self._workloads.values() + for input_group in input_groups + } + + +class ShadowResource(AbstractResource): + """A special resource class which delays the `obtain_resource` call. It is, + in a sense, half constructed. Only when a function or attribute is called + which is is neither `get_id` or `get_resource_version` does this class + fully construct itself by calling the `obtain_resource_call` partial + function. + + **Note:** This class is a hack. The ideal solution to this would be to + enable the bundled obtaining of resources in the gem5 Standard Library. + Use of the class is discouraged and should not be depended on. Issue + https://github.com/gem5/gem5/issues/644 is tracking the implementation of + an alternative. + """ + + def __init__( + self, + id: str, + resource_version: str, + obtain_resource_call: partial, + ): + super().__init__( + id=id, + resource_version=resource_version, + ) + self._workload: Optional[AbstractResource] = None + self._obtain_resource_call = obtain_resource_call + + def __getattr__(self, attr): + """if getting the id or resource version, we keep the object in the + "shdow state" where the `obtain_resource` function has not been called. + When more information is needed by calling another attribute, we call + the `obtain_resource` function and store the result in the `_workload`. + """ + if attr in {"get_id", "get_resource_version"}: + return getattr(super(), attr) + if not self._workload: + self._workload = self._obtain_resource_call() + return getattr(self._workload, attr) + + +class WorkloadResource(AbstractResource): + """A workload resource. This resource is used to specify a workload to run + on a board. It contains the function to call and the parameters to pass to + that function. + """ + + def __init__( + self, + function: str = None, + id: Optional[str] = None, + resource_version: Optional[str] = None, + description: Optional[str] = None, + source: Optional[str] = None, + local_path: Optional[str] = None, + parameters: Optional[Dict[str, Any]] = {}, + **kwargs, + ): + """ + :param function: The function to call on the board. + :param parameters: The parameters to pass to the function. + """ + + super().__init__( + local_path=local_path, + id=id, + description=description, + source=source, + resource_version=resource_version, + ) + + self._id = id + self._func = function + self._params = parameters + + def get_id(self) -> str: + """Returns the ID of the workload.""" + return self._id + + def get_function_str(self) -> str: + """ + Returns the name of the workload function to be run. + + This function is called via the AbstractBoard's ``set_workload`` + function. The parameters from the ``get_parameters`` function are passed + to this function. + """ + return self._func + + def get_parameters(self) -> Dict[str, Any]: + """ + Returns a dictionary mapping the workload parameters to their values. + + These parameters are passed to the function specified by + ``get_function_str`` via the AbstractBoard's ``set_workload`` function. + """ + return self._params + + def set_parameter(self, parameter: str, value: Any) -> None: + """ + Used to set or override a workload parameter + + :param parameter: The parameter of the function to set. + :param value: The value to set to the parameter. + """ + self._params[parameter] = value + + def get_category_name(cls) -> str: + return "WorkloadResource" + def obtain_resource( resource_id: str, @@ -561,29 +927,41 @@ def obtain_resource( resource_version: Optional[str] = None, clients: Optional[List] = None, gem5_version=core.gem5Version, + to_path: Optional[str] = None, + quiet: bool = False, ) -> AbstractResource: """ This function primarily serves as a factory for resources. It will return - the correct `AbstractResource` implementation based on the resource + the correct AbstractResource implementation based on the resource requested. :param resource_name: The name of the gem5 resource as it appears under the - "id" field in the `resource.json` file. + "id" field in the ``resource.json`` file. :param resource_directory: The location of the directory in which the - resource is to be stored. If this parameter is not set, it will set to - the environment variable `GEM5_RESOURCE_DIR`. If the environment is not - set it will default to `~/.cache/gem5` if available, otherwise the CWD. - :param download_md5_mismatch: If the resource is present, but does not - have the correct md5 value, the resoruce will be deleted and - re-downloaded if this value is True. Otherwise an exception will be - thrown. True by default. + resource is to be stored. If this parameter is not + set, it will set to the environment variable + ``GEM5_RESOURCE_DIR``. If the environment is not set + it will default to ``~/.cache/gem5`` if available, + otherwise the CWD. *Note*: This argument is ignored + if the ``to_path`` parameter is specified. + :param download_md5_mismatch: If the resource is present, but does not have + the correct md5 value, the resource will be + deleted and re-downloaded if this value is ``True``. + Otherwise an exception will be thrown. ``True`` by + default. :param resource_version: Version of the resource itself. - Not a required parameter. None by default. + Not a required parameter. ``None`` by default. :param clients: A list of clients to search for the resource. If this - parameter is not set, it will default search all clients. + parameter is not set, it will default search all clients. :param gem5_version: The gem5 version to use to filter incompatible - resource versions. By default set to the current gem5 version. If None, - this filtering is not performed. + resource versions. By default set to the current gem5 + version. If `None`, this filtering is not performed. + :param to_path: The path to which the resource is to be downloaded. If + ``None``, the resource will be downloaded to the resource directory + with the file/directory name equal to the ID of the resource. + **Note**: Usage of this parameter will override the + ``resource_directory`` parameter. + :param quiet: If ``True``, suppress output. ``False`` by default. """ # Obtain the resource object entry for this resource @@ -594,47 +972,68 @@ def obtain_resource( gem5_version=gem5_version, ) - to_path = None + # This is is used to store the partial function which is used to download + # the resource when the `get_local_path` function is called. + downloader: Optional[partial] = None + # If the "url" field is specified, the resoruce must be downloaded. if "url" in resource_json and resource_json["url"]: + # If the `to_path` parameter is set, we use that as the path to which + # the resource is to be downloaded. Otherwise, default to the + # `resource_directory` parameter plus the resource ID. + if not to_path: + # If the `resource_directory` parameter is not set via this + # function, we heck the "GEM5_RESOURCE_DIR" environment variable. + # If this too is not set we call `_get_default_resource_dir()` to + # determine where the resource directory is, or should be, located. + if resource_directory == None: + resource_directory = os.getenv( + "GEM5_RESOURCE_DIR", _get_default_resource_dir() + ) - # If the `resource_directory` parameter is not set via this function, we - # check the "GEM5_RESOURCE_DIR" environment variable. If this too is not - # set we call `_get_default_resource_dir()` to determine where the - # resource directory is, or should be, located. - if resource_directory == None: - resource_directory = os.getenv( - "GEM5_RESOURCE_DIR", _get_default_resource_dir() + # Small checks here to ensure the resource directory is valid. + if os.path.exists(resource_directory): + if not os.path.isdir(resource_directory): + raise Exception( + "gem5 resource directory, " + "'{}', exists but is not a directory".format( + resource_directory + ) + ) + + # This is the path to which the resource is to be stored. + to_path = os.path.join(resource_directory, resource_id) + + assert to_path + + # Here we ensure the directory in which the resource is to be stored + # is created. + # + # `exist_ok=True` here as, occasionally, if multiple instance of gem5 + # are started simultaneously, a race condition can exist to create the + # resource directory. Without `exit_ok=True`, threads which lose this + # race will thrown a `FileExistsError` exception. `exit_ok=True` + # ensures no exception is thrown. + try: + Path(to_path).parent.mkdir(parents=True, exist_ok=True) + except Exception as e: + fatal( + f"Recursive creation of the directory " + f"'{Path(to_path).parent.absolute}' failed. \n" + f"Perhaps the path specified, '{to_path}', is incorrect?\n" + f"Failed with Exception:\n{e}" ) - # Small checks here to ensure the resource directory is valid. - if os.path.exists(resource_directory): - if not os.path.isdir(resource_directory): - raise Exception( - "gem5 resource directory, " - "'{}', exists but is not a directory".format( - resource_directory - ) - ) - else: - # `exist_ok=True` here as, occasionally, if multiple instance of - # gem5 are started simultaneously, a race condition can exist to - # create the resource directory. Without `exit_ok=True`, threads - # which lose this race will thrown a `FileExistsError` exception. - # `exit_ok=True` ensures no exception is thrown. - os.makedirs(resource_directory, exist_ok=True) - - # This is the path to which the resource is to be stored. - to_path = os.path.join(resource_directory, resource_id) - # Download the resource if it does not already exist. - get_resource( + downloader = partial( + get_resource, resource_name=resource_id, - to_path=os.path.join(resource_directory, resource_id), + to_path=to_path, download_md5_mismatch=download_md5_mismatch, resource_version=resource_version, clients=clients, gem5_version=gem5_version, + quiet=quiet, ) # Obtain the type from the JSON. From this we will determine what subclass @@ -651,17 +1050,64 @@ def obtain_resource( return DiskImageResource( local_path=to_path, root_partition=root_partition, + downloader=downloader, **resource_json, ) - return CustomResource(local_path=to_path) + return CustomResource(local_path=to_path, downloader=downloader) assert resources_category in _get_resource_json_type_map resource_class = _get_resource_json_type_map[resources_category] + if resources_category == "suite": + workloads = resource_json["workloads"] + workloads_obj = {} + for workload in workloads: + workloads_obj[ + ShadowResource( + id=workload["id"], + resource_version=workload["resource_version"], + obtain_resource_call=partial( + obtain_resource, + workload["id"], + resource_version=workload["resource_version"], + resource_directory=resource_directory, + clients=clients, + gem5_version=gem5_version, + ), + ) + ] = set(workload["input_group"]) + resource_json["workloads"] = workloads_obj + + if resources_category == "workload": + # This parses the "resources" and "additional_params" fields of the + # workload resource into a dictionary of AbstractResource objects and + # strings respectively. + params = {} + if "resources" in resource_json: + for key in resource_json["resources"].keys(): + assert isinstance(key, str) + value = resource_json["resources"][key] + + assert isinstance(value, dict) + params[key] = obtain_resource( + value["id"], + resource_version=value["resource_version"], + resource_directory=resource_directory, + clients=clients, + gem5_version=gem5_version, + ) + if "additional_params" in resource_json: + for key in resource_json["additional_params"].keys(): + assert isinstance(key, str) + value = resource_json["additional_params"][key] + params[key] = value + resource_json["parameters"] = params # Once we know what AbstractResource subclass we are using, we create it. # The fields in the JSON object are assumed to map like-for-like to the # subclass contructor, so we can pass the resource_json map directly. - return resource_class(local_path=to_path, **resource_json) + return resource_class( + local_path=to_path, downloader=downloader, **resource_json + ) def _get_default_resource_dir() -> str: @@ -703,16 +1149,17 @@ class CustomResource(AbstractResource): by a gem5 user as opposed to one available within the gem5 resources repository. - **Warning**: This class is deprecated and will be removed in future - releases of gem5. Please use the correct `AbstractResource` subclass - instead. + .. warning:: + + This class is deprecated and will be removed in future releases of gem5. + Please use the correct AbstractResource subclass instead. """ def __init__(self, local_path: str, metadata: Dict = {}): """ :param local_path: The path of the resource on the host system. :param metadata: Add metadata for the custom resource. **Warning:** - As of v22.1.1, this parameter is not used. + As of v22.1.1, this parameter is not used. """ warn( "The `CustomResource` class is deprecated. Please use an " @@ -731,9 +1178,11 @@ class CustomDiskImageResource(DiskImageResource): A custom disk image gem5 resource. It can be used to specify a custom, local disk image. - **Warning**: This class is deprecated and will be removed in future - releases of gem5. Please use the `DiskImageResource` class instead. This - class is merely a wrapper for it. + .. warning:: + + This class is deprecated and will be removed in future releases of gem5. + Please use the DiskImageResource class instead. This class is merely + a wrapper for it. """ def __init__( @@ -747,7 +1196,7 @@ class CustomDiskImageResource(DiskImageResource): :param local_path: The path of the disk image on the host system. :param root_partition: The root disk partition to use. :param metadata: Metadata for the resource. **Warning:** As of " - "v22.1.1, this parameter is not used. + "v22.1.1, this parameter is not used. :param resource_version: Version of the resource itself. """ warn( @@ -775,12 +1224,12 @@ def Resource( clients: Optional[List[str]] = None, ) -> AbstractResource: """ - This function was created to maintain backwards compability for v21.1.0 + This function was created to maintain backwards compatibility for v21.1.0 and prior releases of gem5 where `Resource` was a class. - In the interests of gem5-resource specialization, the `Resource` class - has been dropped. Instead users are advized to use the `obtain_resource` - function which will return the correct `AbstractResource` implementation. + In the interests of gem5-resource specialization, the ``Resource`` class + has been dropped. Instead users are advised to use the ``obtain_resource`` + function which will return the correct AbstractResource implementation. This function (disguised as a class) wraps this function. """ @@ -812,4 +1261,6 @@ _get_resource_json_type_map = { "resource": Resource, "looppoint-pinpoint-csv": LooppointCsvResource, "looppoint-json": LooppointJsonResource, + "suite": SuiteResource, + "workload": WorkloadResource, } diff --git a/src/python/gem5/resources/workload.py b/src/python/gem5/resources/workload.py index 0798b891ab..ccc6e4caf7 100644 --- a/src/python/gem5/resources/workload.py +++ b/src/python/gem5/resources/workload.py @@ -24,217 +24,80 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .resource import obtain_resource -from .client import get_resource_json_obj +from typing import ( + Any, + Dict, + List, + Optional, +) + +from m5.util import warn from _m5 import core -from typing import Dict, Any, List, Optional +from .client import get_resource_json_obj +from .resource import ( + WorkloadResource, + obtain_resource, +) -class AbstractWorkload: +def CustomWorkload(function: str, parameters: Dict[str, Any]): """ - Workloads contain information needed to build a workload. + A custom workload gem5 resource. It can be used to specify a custom, + local workload. - A workload specifies a function and its parameters to run on a board to - set a workload. Workload's are passed to board via the `AbstractBoard`'s - `set_workload` function. + .. warning:: - The `AbstractBoard` has a `set_workload` function which accepts an - AbstractWorkload. The `set_workload` function uses the `get_function_str` - to determine which function should be called on the board and the - `get_parameters` function specifies the parameters to be passed. + This ``CustomWorkload`` class is deprecated. It will be removed in a + future release of gem5. Please use the + ``gem5.resources.resource.WorkloadResource`` class instead. - Example - ------- - - ```py - workload = CustomWorkload( - function = "set_se_binary_workload", - parameters = { - "binary" : Resource("x86-print-this"), - "arguments" : ["hello", 6] - }, + The class has been stealthily converted to a function which wraps the + ``WorkloadResource`` class. + """ + warn( + "The `CustomWorkload` class is deprecated. Please use " + "the `gem5.resources.resource.WorkloadResource` class instead." ) + return WorkloadResource(function=function, parameters=parameters) - board.set_workload(workload) - ``` - The above is the equivalent of: +def Workload( + workload_name: str, + resource_directory: Optional[str] = None, + resource_version: Optional[str] = None, + clients: Optional[List] = None, + gem5_version: Optional[str] = core.gem5Version, +): + """ + .. warning:: - ```py - board.set_se_binary_workload( - binary = Resource("x86-print-this"), - arguments = ["hello", 6], + The ``Workload`` class is deprecated. It will be removed in a future + release of gem5. Please use the ``gem5.resources.resource.WorkloadResource`` + class instead. + + The class has been stealthily converted to a function which wraps the + ``WorkloadResource`` class. + """ + warn( + "`Workload` has been deprecated. Please use the `obtain_resource` " + "function instead:\n\n" + "```\n" + "from gem5.resources.resource import obtain_resource\n" + "workload = obtain_resource(\n" + f' resource_id="{workload_name}",\n' + f' resource_directory="{resource_directory}",\n' + f' gem5_version="{gem5_version}",\n' + f" clients={clients},\n" + f" resource_version={resource_version},\n" + ")\n" + "```" ) - ``` - - Notes - ----- - This class should not be used directly. Please use `Workload` or - `CustomWorkload`. - """ - - def __init__(self, function: str, parameters: Dict[str, Any]) -> None: - self._func = function - self._params = parameters - - def get_function_str(self) -> str: - """ - Returns the name of the workload function to be run. - - This function is called via the AbstractBoard's `set_workload` - function. The parameters from the `get_parameters` function are passed - to this function. - """ - return self._func - - def get_parameters(self) -> Dict[str, Any]: - """ - Returns a dictionary mapping the workload parameters to their values. - - These parameters are passed to the function specified by - `get_function_str` via the AbstractBoard's `set_workload` function. - """ - return self._params - - def set_parameter(self, parameter: str, value: Any) -> None: - """ - Used to set or override a workload parameter - - :param parameter: The parameter of the function to set. - :param value: The value to set to the parameter. - """ - self._params[parameter] = value - - -class CustomWorkload(AbstractWorkload): - """ - A workload specified locally (i.e., not via gem5-resources as with the - `Workload` class). Here the user specifies the function and the parameters - to be passed. - - Usage - ----- - - ```py - workload = CustomWorkload( - function = "set_se_binary_workload", - parameters = { - "binary" : Resource("x86-print-this"), - "arguments" : ["hello", 6] - }, + return obtain_resource( + workload_name, + resource_directory=resource_directory, + gem5_version=gem5_version, + clients=clients, + resource_version=resource_version, ) - - board.set_workload(workload) - ``` - """ - - def __init__(self, function: str, parameters: Dict[str, Any]) -> None: - super().__init__(function=function, parameters=parameters) - - -class Workload(AbstractWorkload): - """ - The `Workload` class loads a workload's information from gem5-resources - based on a name/id passed via the constructor. - - Usage - ----- - - ```py - # Determine what workload we want to run. - workload = Workload("example-workload-id") - - # Optionally we can override a parameter in the workload. In this example - # we are going to run this workload with a difference kernel. - workload.set_parameter("kernel", Resource("arm64-linux-kernel-4.14.134")) - - # We then set this workload to the board. - board.set_workload(workload) - ``` - - """ - - def __init__( - self, - workload_name: str, - resource_directory: Optional[str] = None, - resource_version: Optional[str] = None, - clients: Optional[List] = None, - gem5_version: Optional[str] = core.gem5Version, - ) -> None: - """ - This constructor will load the workload details from the workload with - the given name/id. - - This function assumes the dictionary returned by the downloader's - `get_workload_json_obj` is a dictionary. An example of the schema is - shown below: - - ```json - { - "category" : "workload", - "id" : "x86-ubuntu-18.04-echo-hello", - "description" : "Description of workload here", - "function" : "set_kernel_disk_workload", - "resources" : { - "kernel" : "x86-linux-kernel-5.4.49", - "disk-image" : "x86-ubuntu-18.04-img" - }, - "additional_params" : { - "readfile_contents" : "m5_exit; echo 'hello'; m5_exit" - } - } - ``` - - This resource will result in the equivalent of the following action - being taken: - - ```python - board.set_kernel_disk_workload( - kernel = Resource("x86-linux-kernel-5.4.49"), - disk-image = Resource("x86-ubuntu-18.04-img"), - readfile_contents = "m5_exit; echo 'hello'; m5_exit", - ) - ``` - - :param workload_name: The name of the workload in the resources.json - file to be loaded. - :param resource_directory: An optional parameter that specifies where - any resources should be download and accessed from. If None, a default - location will be used. None by default. - :param gem5_version: The gem5 version for the Workload to be loaded. - By default, the current gem5 version is used. This will filter - resources which are incompatible with the current gem5 version. If - None, no filtering will be done. - """ - - workload_json = get_resource_json_obj( - workload_name, - resource_version=resource_version, - clients=clients, - gem5_version=gem5_version, - ) - - func = workload_json["function"] - assert isinstance(func, str) - - params = {} - if "resources" in workload_json: - for key in workload_json["resources"].keys(): - assert isinstance(key, str) - value = workload_json["resources"][key] - assert isinstance(value, str) - params[key] = obtain_resource( - value, - resource_directory=resource_directory, - gem5_version=gem5_version, - ) - - if "additional_params" in workload_json: - for key in workload_json["additional_params"]: - assert isinstance(key, str) - params[key] = workload_json["additional_params"][key] - - super().__init__(function=func, parameters=params) diff --git a/src/python/gem5/runtime.py b/src/python/gem5/runtime.py index 6eed62a9da..a7a284f7ae 100644 --- a/src/python/gem5/runtime.py +++ b/src/python/gem5/runtime.py @@ -28,12 +28,17 @@ This file contains functions to extract gem5 runtime information. """ +from typing import Set + from m5.defines import buildEnv from m5.util import warn -from .isas import ISA, get_isa_from_str, get_isas_str_set from .coherence_protocol import CoherenceProtocol -from typing import Set +from .isas import ( + ISA, + get_isa_from_str, + get_isas_str_set, +) def get_supported_isas() -> Set[ISA]: @@ -42,6 +47,9 @@ def get_supported_isas() -> Set[ISA]: """ supported_isas = set() + if not buildEnv["BUILD_ISA"]: + return {ISA.NULL} + if "TARGET_ISA" in buildEnv.keys(): supported_isas.add(get_isa_from_str(buildEnv["TARGET_ISA"])) @@ -52,44 +60,9 @@ def get_supported_isas() -> Set[ISA]: return supported_isas -def get_runtime_isa() -> ISA: - """ - Returns a single target ISA at runtime. - - This determined via the "TARGET_ISA" parameter, which is set at - compilation. If not set, but only one ISA is compiled, we assume it's the - one ISA. If neither the "TARGET_ISA" parameter is set and there are - multiple ISA targets, an exception is thrown. - - **WARNING**: This function is deprecated and may be removed in future - versions of gem5. This function should not be relied upon to run gem5 - simulations. - - :returns: The target ISA. - """ - - warn( - "The `get_runtime_isa` function is deprecated. Please migrate away " - "from using this function." - ) - - if "TARGET_ISA" in buildEnv.keys(): - return get_isa_from_str(buildEnv["TARGET_ISA"]) - - supported_isas = get_supported_isas() - - if len(supported_isas) == 1: - return next(iter(supported_isas)) - - raise Exception( - "Cannot determine the the runtime ISA. Either the " - "'TARGET_ISA' parameter must be set or the binary only " - "compiled to one ISA." - ) - - def get_runtime_coherence_protocol() -> CoherenceProtocol: """Gets the cache coherence protocol. + This can be inferred at runtime. :returns: The cache coherence protocol. diff --git a/src/python/gem5/simulate/exit_event.py b/src/python/gem5/simulate/exit_event.py index cffe864f06..b902643a3f 100644 --- a/src/python/gem5/simulate/exit_event.py +++ b/src/python/gem5/simulate/exit_event.py @@ -53,6 +53,8 @@ class ExitEvent(Enum): PERF_COUNTER_DISABLE = "performance counter disabled" PERF_COUNTER_RESET = "performance counter reset" PERF_COUNTER_INTERRUPT = "performance counter interrupt" + KERNEL_PANIC = "kernel panic in simulated system" + KERNEL_OOPS = "kernel oops in simulated system" @classmethod def translate_exit_status(cls, exit_string: str) -> "ExitEvent": @@ -60,10 +62,11 @@ class ExitEvent(Enum): This function will translate common exit strings to their correct ExitEvent categorization. + .. note:: - **Note:** At present, we do not guarantee this list is complete, as - there are no bounds on what string may be returned by the simulator - given an exit event. + At present, we do not guarantee this list is complete, as + there are no bounds on what string may be returned by the simulator + given an exit event. """ if exit_string == "m5_workbegin instruction encountered": @@ -102,6 +105,10 @@ class ExitEvent(Enum): return ExitEvent.PERF_COUNTER_RESET elif exit_string == "performance counter interrupt": return ExitEvent.PERF_COUNTER_INTERRUPT + elif exit_string == "Kernel panic in simulated system.": + return ExitEvent.KERNEL_PANIC + elif exit_string == "Kernel oops in simulated system.": + return ExitEvent.KERNEL_OOPS elif exit_string.endswith("will terminate the simulation.\n"): # This is for the traffic generator exit event return ExitEvent.EXIT diff --git a/src/python/gem5/simulate/exit_event_generators.py b/src/python/gem5/simulate/exit_event_generators.py index 37998d3a9b..4d18b4cee0 100644 --- a/src/python/gem5/simulate/exit_event_generators.py +++ b/src/python/gem5/simulate/exit_event_generators.py @@ -24,14 +24,20 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from typing import Generator, Optional +from pathlib import Path +from typing import ( + Generator, + Optional, +) + import m5.stats +from m5.util import warn + +from gem5.resources.looppoint import Looppoint + from ..components.processors.abstract_processor import AbstractProcessor from ..components.processors.switchable_processor import SwitchableProcessor from ..resources.resource import SimpointResource -from gem5.resources.looppoint import Looppoint -from m5.util import warn -from pathlib import Path """ In this package we store generators for simulation exit events. @@ -48,15 +54,14 @@ def warn_default_decorator(gen: Generator, type: str, effect: str): f"No behavior was set by the user for {type}." f" Default behavior is {effect}." ) - for value in gen(*args, **kw_args): - yield value + yield from gen(*args, **kw_args) return wrapped_generator def exit_generator(): """ - A default generator for an exit event. It will return True, indicating that + A default generator for an exit event. It will return ``True``, indicating that the Simulator run loop should exit. """ while True: @@ -81,6 +86,7 @@ def dump_reset_generator(): """ A generator for doing statstic dump and reset. It will reset the simulation statistics and then dump simulation statistics. + The Simulation run loop will continue after executing the behavior of the generator. """ @@ -93,7 +99,8 @@ def dump_reset_generator(): def save_checkpoint_generator(checkpoint_dir: Optional[Path] = None): """ A generator for taking a checkpoint. It will take a checkpoint with the - input path and the current simulation Ticks. + input path and the current simulation ``Ticks``. + The Simulation run loop will continue after executing the behavior of the generator. """ @@ -128,6 +135,7 @@ def dump_stats_generator(): def skip_generator(): """ This generator does nothing when on the exit event. + The simulation will continue after this generator. """ while True: @@ -139,9 +147,9 @@ def simpoints_save_checkpoint_generator( ): """ A generator for taking multiple checkpoints for SimPoints. It will save the - checkpoints in the checkpoint_dir path with the SimPoints' index. + checkpoints in the ``checkpoint_dir`` path with the SimPoints' index. The Simulation run loop will continue after executing the behavior of the - generator until all the SimPoints in the simpoint_list has taken a + generator until all the SimPoints in the ``simpoint_list`` has taken a checkpoint. """ simpoint_list = simpoint.get_simpoint_start_insts() @@ -179,17 +187,19 @@ def looppoint_save_checkpoint_generator( """ A generator for taking a checkpoint for LoopPoint. It will save the checkpoints in the checkpoint_dir path with the Region id. + (i.e. "cpt.Region10) It only takes a checkpoint if the current PC Count pair is a significant PC Count Pair. This is determined in the LoopPoint module. The simulation loop continues after exiting this generator. - :param checkpoint_dir: where to save the checkpoints - :param loopoint: the looppoint object used in the configuration script - :param update_relative: if the generator should update the relative count - information in the output json file, then it should be True. It is default - as True. - :param exit_when_empty: if the generator should exit the simulation loop if - all PC paris have been discovered, then it should be True. It is default as - True. + + :param checkpoint_dir: Where to save the checkpoints. + :param loopoint: The LoopPoint object used in the configuration script + :param update_relative: If the generator should update the relative count + information in the output json file, then it should + be ``True``. It is default as ``True``. + :param exit_when_empty: If the generator should exit the simulation loop if + all PC paris have been discovered, then it should be + ``True``. It is default as ``True``. """ if exit_when_empty: total_pairs = len(looppoint.get_targets()) diff --git a/src/python/gem5/simulate/simulator.py b/src/python/gem5/simulate/simulator.py index 0551745b36..708a484ead 100644 --- a/src/python/gem5/simulate/simulator.py +++ b/src/python/gem5/simulate/simulator.py @@ -24,29 +24,37 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import m5 -import m5.ticks -from m5.stats import addStatVisitor -from m5.ext.pystats.simstat import SimStat -from m5.objects import Root -from m5.util import warn - import os import sys from pathlib import Path -from typing import Optional, List, Tuple, Dict, Generator, Union - -from .exit_event_generators import ( - warn_default_decorator, - exit_generator, - switch_generator, - save_checkpoint_generator, - reset_stats_generator, - dump_stats_generator, +from typing import ( + Callable, + Dict, + Generator, + List, + Optional, + Tuple, + Union, ) -from .exit_event import ExitEvent + +import m5 +import m5.ticks +from m5.ext.pystats.simstat import SimStat +from m5.objects import Root +from m5.stats import addStatVisitor +from m5.util import warn + from ..components.boards.abstract_board import AbstractBoard from ..components.processors.switchable_processor import SwitchableProcessor +from .exit_event import ExitEvent +from .exit_event_generators import ( + dump_stats_generator, + exit_generator, + reset_stats_generator, + save_checkpoint_generator, + switch_generator, + warn_default_decorator, +) class Simulator: @@ -55,15 +63,17 @@ class Simulator: Example ------- + Examples using the Simulator class can be found under - `configs/example/gem5_library`. + ``configs/example/gem5_library``. The most basic run would be as follows: - ``` - simulator = Simulator(board=board) - simulator.run() - ``` + .. code-block:: + + simulator = Simulator(board=board) + simulator.run() + This will run a simulation and execute default behavior for exit events. """ @@ -83,7 +93,14 @@ class Simulator: board: AbstractBoard, full_system: Optional[bool] = None, on_exit_event: Optional[ - Dict[ExitEvent, Generator[Optional[bool], None, None]] + Dict[ + ExitEvent, + Union[ + Generator[Optional[bool], None, None], + List[Callable], + Callable, + ], + ] ] = None, expected_execution_order: Optional[List[ExitEvent]] = None, checkpoint_path: Optional[Path] = None, @@ -91,64 +108,156 @@ class Simulator: """ :param board: The board to be simulated. :param full_system: Whether to run as a full-system simulation or not. - This is optional and used to override default behavior. If not set, - whether or not to run in FS mode will be determined via the board's - `is_fullsystem()` function. - :param on_exit_event: An optional map to specify the generator to - execute on each exit event. The generator may yield a boolean which, - if True, will have the Simulator exit the run loop. - :param expected_execution_order: May be specified to check the exit - events come in a specified order. If the order specified is not - encountered (e.g., 'Workbegin', 'Workend', then 'Exit'), an Exception - is thrown. If this parameter is not specified, any ordering of exit - events is valid. - :param checkpoint_path: An optional parameter specifying the directory - of the checkpoint to instantiate from. When the path is None, no - checkpoint will be loaded. By default, the path is None. **This - parameter is deprecated. Please set the checkpoint when setting the - board's workload**. + This is optional and used to override default + behavior. If not set, whether or not to run in FS + mode will be determined via the board's + ``is_fullsystem()`` function. + :param on_exit_event: An optional map to specify what to execute on + each exit event. There are three possibilities here: + a generator, a list of functions, or a single function. - `on_exit_event` usage notes + 1. Generator: The generator may yield a boolean each + time the associated exit event is encountered. If + ``True`` the simulator will exit the simulation loop. + + 2. List of functions: Each function must be callable + with no mandatory arguments and return a boolean + specifying if the Simulation should exit the + simulation loop. Upon each exit event the list will + pop the start of the list and execute it. If the list + is empty the default behavior for that exit event will + be executed. + + 3. Single function: The function must be callable with + no mandatory arguments and return a boolean specifying + if the Simulation should exit or not. This function is + executed each time the associated exit event is encountered. + :param checkpoint_path: An optional parameter specifying the directory of + the checkpoint to instantiate from. When the path + is ``None``, no checkpoint will be loaded. By default, + the path is ``None``. **This parameter is deprecated. + Please set the checkpoint when setting the board's + workload**. + + ``on_exit_event`` usage notes --------------------------- - The `on_exit_event` parameter specifies a Python generator for each + With Generators + =============== + + The ``on_exit_event`` parameter specifies a Python generator for each exit event. `next()` is run each time an exit event. The - generator may yield a boolean. If this value of this boolean is True + generator may yield a boolean. If this value of this boolean is ``True`` the Simulator run loop will exit, otherwise the Simulator run loop will continue execution. If the generator has - finished (i.e. a `StopIteration` exception is thrown when - `next()` is executed), then the default behavior for that + finished (i.e. a ``StopIteration`` exception is thrown when + ``next()`` is executed), then the default behavior for that exit event is run. As an example, a user may specify their own exit event setup like so: - ``` - def unique_exit_event(): - processor.switch() - yield False - m5.stats.dump() - yield False - yield True + .. code-block:: - simulator = Simulator( - board=board - on_exit_event = { - ExitEvent.Exit : unique_exit_event(), - }, - ) - ``` + def unique_exit_event(): + processor.switch() + yield False + m5.stats.dump() + yield False + yield True - This will execute `processor.switch()` the first time an exit event is + simulator = Simulator( + board=board + on_exit_event = { + ExitEvent.Exit : unique_exit_event(), + }, + ) + + + This will execute ``processor.switch()`` the first time an exit event is encountered, will dump gem5 statistics the second time an exit event is encountered, and will terminate the Simulator run loop the third time. + With a list of functions + ======================== + + Alternatively, instead of passing a generator per exit event, a list of + functions may be passed. Each function must take no mandatory arguments + and return True if the simulator is to exit after being called. + + An example: + + .. code-block:: + + def stop_simulation() -> bool: + return True + + def switch_cpus() -> bool: + processor.switch() + return False + + def print_hello() -> None: + # Here we don't explicitly return a boolean, but the simulator + # treats a None return as False. Ergo the Simulation loop is not + # terminated. + print("Hello") + + + simulator = Simulator( + board=board, + on_exit_event = { + ExitEvent.Exit : [ + print_hello, + switch_cpus, + print_hello, + stop_simulation + ], + }, + ) + + + Upon each ``EXIT`` type exit event the list will function as a queue, + with the top function of the list popped and executed. Therefore, in + this example, the first ``EXIT`` type exit event will cause ``print_hello`` + to be executed, and the second ``EXIT`` type exit event will cause the + ``switch_cpus`` function to run. The third will execute ``print_hello`` + again before finally, on the forth exit event will call + ``stop_simulation`` which will stop the simulation as it returns ``False``. + + With a function + =============== + A single function can be passed. In this case every exit event of that + type will execute that function every time. The function should not + accept any mandatory parameters and return a boolean specifying if the + simulation loop should end after it is executed. + An example: + + .. code-block:: + + def print_hello() -> bool: + print("Hello") + return False + simulator = Simulator( + board=board, + on_exit_event = { + ExitEvent.Exit : print_hello + }, + ) + + The above will print "Hello" on every ``Exit`` type Exit Event. As the + function returns False, the simulation loop will not end on these + events. + + + Exit Event defaults + =================== + Each exit event has a default behavior if none is specified by the user. These are as follows: * ExitEvent.EXIT: exit simulation * ExitEvent.CHECKPOINT: take a checkpoint * ExitEvent.FAIL : exit simulation - * ExitEvent.SWITCHCPU: call `switch` on the processor + * ExitEvent.SWITCHCPU: call ``switch`` on the processor * ExitEvent.WORKBEGIN: reset stats * ExitEvent.WORKEND: exit simulation * ExitEvent.USER_INTERRUPT: exit simulation @@ -157,7 +266,7 @@ class Simulator: * ExitEvent.SIMPOINT_BEGIN: reset stats * ExitEvent.MAX_INSTS: exit simulation - These generators can be found in the `exit_event_generator.py` module. + These generators can be found in the ``exit_event_generator.py`` module. """ @@ -199,10 +308,33 @@ class Simulator: "max instructions", "exiting the simulation", )(), + ExitEvent.KERNEL_PANIC: exit_generator(), + ExitEvent.KERNEL_OOPS: exit_generator(), } if on_exit_event: - self._on_exit_event = on_exit_event + self._on_exit_event = {} + for key, value in on_exit_event.items(): + if isinstance(value, Generator): + self._on_exit_event[key] = value + elif isinstance(value, List): + # In instances where we have a list of functions, we + # convert this to a generator. + self._on_exit_event[key] = (func() for func in value) + elif isinstance(value, Callable): + # In instances where the user passes a lone function, the + # function is called on every exit event of that type. Here + # we convert the function into an infinite generator. + def function_generator(func: Callable): + while True: + yield func() + + self._on_exit_event[key] = function_generator(func=value) + else: + raise Exception( + f"`on_exit_event` for '{key.value}' event is " + "not a Generator or List[Callable]." + ) else: self._on_exit_event = self._default_on_exit_dict @@ -229,12 +361,15 @@ class Simulator: def schedule_simpoint(self, simpoint_start_insts: List[int]) -> None: """ - Schedule SIMPOINT_BEGIN exit events + Schedule ``SIMPOINT_BEGIN`` exit events - **Warning:** SimPoints only work with one core + .. warning:: - :param simpoint_start_insts: a list of number of instructions - indicating the starting point of the simpoints + SimPoints only work with one core. + + :param simpoint_start_insts: A list of number of instructions + indicating the starting point of + the SimPoints. """ if self._board.get_processor().get_num_cores() > 1: warn("SimPoints only work with one core") @@ -244,10 +379,10 @@ class Simulator: def schedule_max_insts(self, inst: int) -> None: """ - Schedule a MAX_INSTS exit event when any thread in any core reaches the - given number of instructions. + Schedule a ``MAX_INSTS`` exit event when any thread in any core + reaches the given number of instructions. - :param insts: a number of instructions to run to. + :param insts: A number of instructions to run to. """ for core in self._board.get_processor().get_cores(): core._set_inst_stop_any_thread(inst, self._instantiated) @@ -258,19 +393,19 @@ class Simulator: to a JSON-style schema. :raises Exception: An exception is raised if this function is called - before `run()`. The board must be initialized before obtaining - statistics. + before ``run()``. The board must be initialized + before obtaining statistics. """ return self.get_simstats().to_json() def get_simstats(self) -> SimStat: """ - Obtains the SimStat of the current simulation. + Obtains the `SimStat` of the current simulation. :raises Exception: An exception is raised if this function is called - before `run()`. The board must be initialized before obtaining - statistics. + before ``run()``. The board must be initialized + before obtaining statistics. """ if not self._instantiated: @@ -355,7 +490,7 @@ class Simulator: """ start = 0 to_return = [] - for (exit_event, tick) in self._tick_stopwatch: + for exit_event, tick in self._tick_stopwatch: if exit_event == ExitEvent.WORKBEGIN: start = tick elif exit_event == ExitEvent.WORKEND: @@ -371,7 +506,6 @@ class Simulator: """ if not self._instantiated: - # Before anything else we run the AbstractBoard's # `_pre_instantiate` function. self._board._pre_instantiate() @@ -429,9 +563,10 @@ class Simulator: events accordingly. :param max_ticks: The maximum number of ticks to execute per simulation - run. If this max_ticks value is met, a MAX_TICK exit event is - received, if another simulation exit event is met the tick count is - reset. This is the **maximum number of ticks per simulation run**. + run. If this ``max_ticks`` value is met, a ``MAX_TICK`` + exit event is received, if another simulation exit + event is met the tick count is reset. This is the + **maximum number of ticks per simulation run**. """ # Check to ensure no banned module has been imported. @@ -448,7 +583,6 @@ class Simulator: # This while loop will continue until an a generator yields True. while True: - self._last_exit_event = m5.simulate(max_ticks) # Translate the exit event cause to the exit event enum. @@ -479,8 +613,8 @@ class Simulator: # If the user's generator has ended, throw a warning and use # the default generator for this exit event. warn( - "User-specified generator for the exit event " - f"'{exit_enum.value}' has ended. Using the default " + "User-specified generator/function list for the exit " + f"event'{exit_enum.value}' has ended. Using the default " "generator." ) exit_on_completion = next( @@ -496,7 +630,7 @@ class Simulator: self._exit_event_count += 1 # If the generator returned True we will return from the Simulator - # run loop. + # run loop. In the case of a function: if it returned True. if exit_on_completion: return @@ -505,6 +639,6 @@ class Simulator: This function will save the checkpoint to the specified directory. :param checkpoint_dir: The path to the directory where the checkpoint - will be saved. + will be saved. """ m5.checkpoint(str(checkpoint_dir)) diff --git a/src/python/gem5/utils/filelock.py b/src/python/gem5/utils/filelock.py index 6fb4e3e1d1..0c53422366 100644 --- a/src/python/gem5/utils/filelock.py +++ b/src/python/gem5/utils/filelock.py @@ -23,19 +23,19 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import errno import os import time -import errno class FileLockException(Exception): pass -class FileLock(object): +class FileLock: """A file locking mechanism that has context-manager support so you can use it in a with statement. This should be relatively cross - compatible as it doesn't rely on msvcrt or fcntl for the locking. + compatible as it doesn't rely on ``msvcrt`` or ``fcntl`` for the locking. """ def __init__(self, file_name, timeout=10, delay=0.05): @@ -54,8 +54,8 @@ class FileLock(object): def acquire(self): """Acquire the lock, if possible. If the lock is in use, it check again - every `wait` seconds. It does this until it either gets the lock or - exceeds `timeout` number of seconds, in which case it throws + every ``wait`` seconds. It does this until it either gets the lock or + exceeds ``timeout`` number of seconds, in which case it throws an exception. """ start_time = time.time() @@ -91,7 +91,8 @@ class FileLock(object): def release(self): """Get rid of the lock by deleting the lockfile. - When working in a `with` statement, this gets automatically + + When working in a ``with`` statement, this gets automatically called at the end. """ if self.is_locked: @@ -101,6 +102,7 @@ class FileLock(object): def __enter__(self): """Activated when used in the with statement. + Should automatically acquire a lock to be used in the with block. """ if not self.is_locked: @@ -109,6 +111,7 @@ class FileLock(object): def __exit__(self, type, value, traceback): """Activated at the end of the with statement. + It automatically releases the lock if it isn't locked. """ if self.is_locked: diff --git a/src/python/gem5/utils/multiprocessing/README.md b/src/python/gem5/utils/multiprocessing/README.md index da2116c44c..c6b0406e54 100644 --- a/src/python/gem5/utils/multiprocessing/README.md +++ b/src/python/gem5/utils/multiprocessing/README.md @@ -48,8 +48,8 @@ def run_sim(name): from gem5.simulate.simulator import Simulator board = X86DemoBoard() board.set_kernel_disk_workload( - kernel=Resource("x86-linux-kernel-5.4.49"), - disk_image=Resource("x86-ubuntu-18.04-img"), + kernel=obtain_resource("x86-linux-kernel-5.4.49"), + disk_image=obtain_resource("x86-ubuntu-18.04-img"), ) simulator = Simulator(board=board) simulator.run(max_ticks=10000000) diff --git a/src/python/gem5/utils/multiprocessing/__init__.py b/src/python/gem5/utils/multiprocessing/__init__.py index 680aeac314..07a55250fa 100644 --- a/src/python/gem5/utils/multiprocessing/__init__.py +++ b/src/python/gem5/utils/multiprocessing/__init__.py @@ -24,9 +24,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .context import Process - -from .context import gem5Context +from .context import ( + Process, + gem5Context, +) Pool = gem5Context().Pool diff --git a/src/python/gem5/utils/multiprocessing/_command_line.py b/src/python/gem5/utils/multiprocessing/_command_line.py index f68277540d..9922447295 100644 --- a/src/python/gem5/utils/multiprocessing/_command_line.py +++ b/src/python/gem5/utils/multiprocessing/_command_line.py @@ -32,7 +32,10 @@ multiprocessing module (i.e., cpython/Lib/multiprocessing/). """ import sys -from multiprocessing import spawn, util +from multiprocessing import ( + spawn, + util, +) def _gem5_args_for_multiprocessing(name): @@ -86,7 +89,7 @@ def _gem5_args_for_multiprocessing(name): def get_command_line(name, **kwds): """ - Returns prefix of command line used for spawning a child process + Returns prefix of command line used for spawning a child process. """ if getattr(sys, "frozen", False): return [sys.executable, "--multiprocessing-fork"] + [ diff --git a/src/python/gem5/utils/multiprocessing/context.py b/src/python/gem5/utils/multiprocessing/context.py index 87917d1bfb..03b554548d 100644 --- a/src/python/gem5/utils/multiprocessing/context.py +++ b/src/python/gem5/utils/multiprocessing/context.py @@ -30,9 +30,13 @@ Some code inspired by the Python standard library implementation of the multiprocessing module (i.e., cpython/Lib/multiprocessing/). """ -from multiprocessing import context, process +from multiprocessing import ( + context, + process, +) from multiprocessing.context import DefaultContext + # The `_start_method` must be `None` for the `Spawn_gem5Process` class. # Otherwise, in `_bootstrap` in the `BaseProcess` it will try to force the # `_start_method` to be gem5-specific, which the `multiprocessing` module diff --git a/src/python/gem5/utils/multiprocessing/popen_spawn_gem5.py b/src/python/gem5/utils/multiprocessing/popen_spawn_gem5.py index 13fb3362fc..b40b8b4eeb 100644 --- a/src/python/gem5/utils/multiprocessing/popen_spawn_gem5.py +++ b/src/python/gem5/utils/multiprocessing/popen_spawn_gem5.py @@ -33,11 +33,15 @@ multiprocessing module (i.e., cpython/Lib/multiprocessing/). import io import os - -from multiprocessing.context import reduction, set_spawning_popen -from multiprocessing import popen_spawn_posix -from multiprocessing import spawn -from multiprocessing import util +from multiprocessing import ( + popen_spawn_posix, + spawn, + util, +) +from multiprocessing.context import ( + reduction, + set_spawning_popen, +) from ._command_line import get_command_line diff --git a/src/python/gem5/utils/override.py b/src/python/gem5/utils/override.py index 6c28b18b43..01292a384a 100644 --- a/src/python/gem5/utils/override.py +++ b/src/python/gem5/utils/override.py @@ -30,6 +30,7 @@ def overrides(interface_class): Function override annotation. Corollary to @abc.abstractmethod where the override is not of an abstractmethod. + Modified from answer https://stackoverflow.com/a/8313042/471376 """ diff --git a/src/python/gem5/utils/progress_bar.py b/src/python/gem5/utils/progress_bar.py index 0ac13200b9..b11d3a17f2 100644 --- a/src/python/gem5/utils/progress_bar.py +++ b/src/python/gem5/utils/progress_bar.py @@ -26,7 +26,7 @@ class FakeTQDM: - """This is a fake wrapper so that the tqdm calls work whether or not it + """This is a fake wrapper so that the ``tqdm`` calls work whether or not it has been installed. """ @@ -55,6 +55,7 @@ except ImportError: tqdm = FakeTQDM() _have_tqdm = False + # Hook for the progress bar def progress_hook(t): if not _have_tqdm: diff --git a/src/python/gem5/utils/requires.py b/src/python/gem5/utils/requires.py index 9d271aafa2..f1f6053756 100644 --- a/src/python/gem5/utils/requires.py +++ b/src/python/gem5/utils/requires.py @@ -24,12 +24,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from ..runtime import get_runtime_coherence_protocol, get_supported_isas -from ..isas import ISA -from ..coherence_protocol import CoherenceProtocol -from typing import Optional -import os import inspect +import os +from typing import Optional + +from ..coherence_protocol import CoherenceProtocol +from ..isas import ISA +from ..runtime import ( + get_runtime_coherence_protocol, + get_supported_isas, +) def _get_exception_str(msg: str): @@ -61,11 +65,11 @@ def requires( :param isa_required: The ISA(s) gem5 must be compiled to. :param coherence_protocol_required: The coherence protocol gem5 must be - compiled to. + compiled to. :param kvm_required: The host system must have the Kernel-based Virtual - Machine available. + Machine available. :raises Exception: Raises an exception if the required ISA or coherence - protocol do not match that of the current gem5 binary. + protocol do not match that of the current gem5 binary. """ supported_isas = get_supported_isas() diff --git a/src/python/gem5/utils/simpoint.py b/src/python/gem5/utils/simpoint.py index 0d1af4b1cf..f3b8953bb3 100644 --- a/src/python/gem5/utils/simpoint.py +++ b/src/python/gem5/utils/simpoint.py @@ -24,16 +24,24 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.util import fatal, warn from pathlib import Path -from typing import List, Tuple +from typing import ( + List, + Tuple, +) + +from m5.util import ( + fatal, + warn, +) + from gem5.resources.resource import SimpointResource class SimPoint: """ This SimPoint class is used to manage the information needed for SimPoints - in workload + in workload. """ @@ -48,33 +56,33 @@ class SimPoint: warmup_interval: int = 0, ) -> None: """ - :param simpoint_interval: the length of each SimPoints interval - :param simpoint_file_path: the path to the SimPoints result file - generated by Simpoint3.2 or gem5 - :param weight_file_path: the path to the weight result file generated - by Simpoint3.2 or gem5 + :param simpoint_interval: The length of each SimPoints interval. + :param simpoint_file_path: The path to the SimPoints result file + generated by Simpoint3.2 or gem5. + :param weight_file_path: The path to the weight result file generated + by Simpoint3.2 or gem5. - :param simpoint_list: a list of SimPoints starting instructions - :param weight_list: a list of SimPoints weights - :param warmup_interval: a number of instructions for warming up before - restoring a SimPoints checkpoint + :param simpoint_list: A list of SimPoints starting instructions. + :param weight_list: A list of SimPoints weights. + :param warmup_interval: A number of instructions for warming up before + restoring a SimPoints checkpoint. - usage note - ----------- - Need to pass in the paths or the lists for the SimPoints and their - weights. If the paths are passed in, no actions will be done to the - list. + .. note:: - When passing in simpoint_list and weight_list, passing in sorted lists - (sorted by SimPoints in ascending order) is strongly suggested. - The warmup_list only works correctly with sorted simpoint_list. + Need to pass in the paths or the lists for the SimPoints and their + weights. If the paths are passed in, no actions will be done to the + list. + + When passing in ``simpoint_list`` and ``weight_list``, passing in sorted lists + (sorted by SimPoints in ascending order) is strongly suggested. + The ``warmup_list`` only works correctly with sorted ``simpoint_list``. """ warn( - "This `SimPoint` class has been deprecated in favor of " + "This SimPoint class has been deprecated in favor of " "`SimpointResource` and `SimpointDirectory` resource which may be " "found in `gem5.resources.resource`. Please utilize these. This " - "`SimPoint` class will be removed in future releases of gem5." + "SimPoint class will be removed in future releases of gem5." ) # initalize input if you're passing in a CustomResource @@ -125,7 +133,7 @@ class SimPoint: ) -> Tuple[List[int], List[int]]: """ This function takes in file paths and outputs a list of SimPoints - instruction starts and a list of weights + instruction starts and a list of weights. """ simpoint = [] with open(simpoint_path) as simpoint_file, open( @@ -152,12 +160,12 @@ class SimPoint: def set_warmup_intervals(self, warmup_interval: int) -> List[int]: """ - This function takes the warmup_interval, fits it into the - _simpoint_start_insts, and outputs a list of warmup instruction lengths + This function takes the ``warmup_interval``, fits it into the + ``_simpoint_start_insts``, and outputs a list of warmup instruction lengths for each SimPoint. The warmup instruction length is calculated using the starting - instruction of a SimPoint to minus the warmup_interval and the ending + instruction of a SimPoint to minus the ``warmup_interval`` and the ending instruction of the last SimPoint. If it is less than 0, then the warmup instruction length is the gap between the starting instruction of a SimPoint and the ending instruction of the last SimPoint. diff --git a/src/python/gem5py.cc b/src/python/gem5py.cc index f2d87596f0..37ddee2e7c 100644 --- a/src/python/gem5py.cc +++ b/src/python/gem5py.cc @@ -51,6 +51,21 @@ namespace py = pybind11; int main(int argc, const char **argv) { +#if PY_VERSION_HEX >= 0x03080000 + // Preinitialize Python for Python 3.8+ + // This ensures that the locale configuration takes effect + PyStatus status; + PyPreConfig preconfig; + PyPreConfig_InitPythonConfig(&preconfig); + + preconfig.utf8_mode = 1; + + status = Py_PreInitialize(&preconfig); + if (PyStatus_Exception(status)) { + Py_ExitStatusException(status); + } +#endif + py::scoped_interpreter guard; // Embedded python doesn't set up sys.argv, so we'll do that ourselves. diff --git a/src/python/gem5py_m5.cc b/src/python/gem5py_m5.cc new file mode 100644 index 0000000000..78337c1ffe --- /dev/null +++ b/src/python/gem5py_m5.cc @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2019 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +namespace py = pybind11; + +/* + * This wrapper program runs python scripts using the python interpretter which + * will be built into gem5. Its first argument is the script to run, and then + * all subsequent arguments are passed to the python script as its argv. + */ + +int +main(int argc, const char **argv) +{ + py::scoped_interpreter guard; + + // Embedded python doesn't set up sys.argv, so we'll do that ourselves. + py::list py_argv; + auto sys = py::module::import("sys"); + if (py::hasattr(sys, "argv")) { + // sys.argv already exists, so grab that. + py_argv = sys.attr("argv"); + } else { + // sys.argv doesn't exist, so create it. + sys.add_object("argv", py_argv); + } + + auto importer = py::module_::import("importer"); + importer.attr("install")(); + + // Clear out argv just in case it has something in it. + py_argv.attr("clear")(); + + if (argc < 2) { + std::cerr << "Usage: gem5py SCRIPT [arg] ..." << std::endl; + std::exit(1); + } + + // Fill it with our argvs. + for (int i = 1; i < argc; i++) + py_argv.append(argv[i]); + + // Actually call the script. + py::eval_file(argv[1]); + + return 0; +} diff --git a/src/python/importer.py b/src/python/importer.py index d3bdd593ef..6b64f1cb5c 100644 --- a/src/python/importer.py +++ b/src/python/importer.py @@ -38,11 +38,14 @@ class ByteCodeLoader(importlib.abc.Loader): def exec_module(self, module): exec(self.code, module.__dict__) + def get_code(self, _): + return self.code + # Simple importer that allows python to import data from a dict of # code objects. The keys are the module path, and the items are the # filename and bytecode of the file. -class CodeImporter(object): +class CodeImporter: def __init__(self): self.modules = {} override_var = os.environ.get("M5_OVERRIDE_PY_SOURCE", "false") @@ -61,7 +64,7 @@ class CodeImporter(object): abspath, code = self.modules[fullname] if self.override and os.path.exists(abspath): - src = open(abspath, "r").read() + src = open(abspath).read() code = compile(src, abspath, "exec") is_package = os.path.basename(abspath) == "__init__.py" diff --git a/src/python/m5/SimObject.py b/src/python/m5/SimObject.py index 08105d8833..608fc68492 100644 --- a/src/python/m5/SimObject.py +++ b/src/python/m5/SimObject.py @@ -38,36 +38,39 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import sys -from types import FunctionType, MethodType, ModuleType -from functools import wraps import inspect +import sys +from functools import wraps +from types import ( + FunctionType, + MethodType, + ModuleType, +) import m5 -from m5.util import * -from m5.util.pybind import * +from m5.citations import gem5_citations # Use the pyfdt and not the helper class, because the fdthelper # relies on the SimObject definition from m5.ext.pyfdt import pyfdt +# There are a few things we need that aren't in params.__all__ since +# normal users don't need them # Have to import params up top since Param is referenced on initial # load (when SimObject class references Param to create a class # variable, the 'name' param)... from m5.params import * - -# There are a few things we need that aren't in params.__all__ since -# normal users don't need them from m5.params import ( ParamDesc, + Port, + SimObjectVector, VectorParamDesc, isNullPointer, - SimObjectVector, - Port, ) - from m5.proxy import * from m5.proxy import isproxy +from m5.util import * +from m5.util.pybind import * ##################################################################### # @@ -215,6 +218,8 @@ class MetaSimObject(type): cls._instantiated = False # really instantiated, cloned, or subclassed cls._init_called = False # Used to check if __init__ overridden + cls._citations = gem5_citations # Default to gem5's citations + # We don't support multiple inheritance of sim objects. If you want # to, you must fix multidict to deal with it properly. Non sim-objects # are ok, though @@ -227,7 +232,10 @@ class MetaSimObject(type): "SimObjects do not support multiple inheritance" ) - base = bases[0] + # If the base class is not set, we assume type `object`. This ensures + # `class Foo(object): pass` is considered equivalent to + # `class Foo: pass`. + base = bases[0] if len(bases) > 0 else object # Set up general inheritance via multidicts. A subclass will # inherit all its settings from the base class. The only time @@ -517,7 +525,7 @@ def cxxMethod(*args, **kwargs): # This class holds information about each simobject parameter # that should be displayed on the command line for use in the # configuration system. -class ParamInfo(object): +class ParamInfo: def __init__(self, type, desc, type_str, example, default_val, access_str): self.type = type self.desc = desc @@ -542,7 +550,7 @@ class SimObjectCliWrapperException(Exception): super().__init__(message) -class SimObjectCliWrapper(object): +class SimObjectCliWrapper: """ Wrapper class to restrict operations that may be done from the command line on SimObjects. @@ -605,7 +613,7 @@ class SimObjectCliWrapper(object): # The SimObject class is the root of the special hierarchy. Most of # the code in this class deals with the configuration hierarchy itself # (parent/child node relationships). -class SimObject(object, metaclass=MetaSimObject): +class SimObject(metaclass=MetaSimObject): # Specify metaclass. Any class inheriting from SimObject will # get this metaclass. type = "SimObject" @@ -870,7 +878,7 @@ class SimObject(object, metaclass=MetaSimObject): hr_value = value value = param.convert(value) except Exception as e: - msg = "%s\nError setting param %s.%s to %s\n" % ( + msg = "{}\nError setting param {}.{} to {}\n".format( e, self.__class__.__name__, attr, @@ -1248,9 +1256,8 @@ class SimObject(object, metaclass=MetaSimObject): # The order of the dict is implementation dependent, so sort # it based on the key (name) to ensure the order is the same # on all hosts - for (name, child) in sorted(self._children.items()): - for obj in child.descendants(): - yield obj + for name, child in sorted(self._children.items()): + yield from child.descendants() # Call C++ to create C++ object corresponding to this object def createCCObject(self): @@ -1271,7 +1278,7 @@ class SimObject(object, metaclass=MetaSimObject): def connectPorts(self): # Sort the ports based on their attribute name to ensure the # order is the same on all hosts - for (attr, portRef) in sorted(self._port_refs.items()): + for attr, portRef in sorted(self._port_refs.items()): portRef.ccConnect() # Default function for generating the device structure. @@ -1283,8 +1290,7 @@ class SimObject(object, metaclass=MetaSimObject): def recurseDeviceTree(self, state): for child in self._children.values(): for item in child: # For looping over SimObjectVectors - for dt in item.generateDeviceTree(state): - yield dt + yield from item.generateDeviceTree(state) # On a separate method otherwise certain buggy Python versions # would fail with: SyntaxError: unqualified exec is not allowed @@ -1316,7 +1322,6 @@ class SimObject(object, metaclass=MetaSimObject): The format is the same as that supported by SimObjectCliWrapper. :param simobj_path: Current state to be in. - :type simobj_path: str """ d = self._apply_config_get_dict() return eval(simobj_path, d) diff --git a/src/python/m5/__init__.py b/src/python/m5/__init__.py index f029adffdc..a7bcf7a833 100644 --- a/src/python/m5/__init__.py +++ b/src/python/m5/__init__.py @@ -40,18 +40,19 @@ except ImportError: in_gem5 = False if in_gem5: - from . import SimObject - from . import core - from . import defines - from . import objects - from . import params - from . import stats + from . import ( + SimObject, + core, + defines, + objects, + params, + stats, + ) if defines.buildEnv["USE_SYSTEMC"]: from . import systemc from . import tlm from . import util - from .event import * from .main import main from .simulate import * diff --git a/src/python/m5/citations.py b/src/python/m5/citations.py new file mode 100644 index 0000000000..3df9de22a1 --- /dev/null +++ b/src/python/m5/citations.py @@ -0,0 +1,191 @@ +# Copyright (c) 2023 The Regents of The University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from pathlib import Path +from typing import Type + +import m5.options + + +def add_citation(sim_obj_cls: Type["SimObject"], citation: str): + """Add a citation to a SimObject class. + + :param sim_obj_cls: The SimObject class to add the citation to. + :param citation: The citation to add. Should be bibtex compatible + entry or entries + + This function will encode the citation into the SimObject class and it + will be included in the citations in the output directory when the + SimObject is used. If you have multiple citations, then you should include + one multiline string with all of the citations. + """ + + sim_obj_cls._citations += citation + + +def gather_citations(root: "SimObject"): + """Based on the root SimObject, walk the object hierarchy and gather all + of the citations together and then print them to citations.bib in the + output directory. + """ + + citations = {} + for obj in root.descendants(): + loc = 0 + while loc >= 0: + key, cite, loc = _get_next_key_entry(obj._citations, loc) + # If a key repeats, then just overwrite it + citations[key] = cite + + with open(Path(m5.options.outdir) / "citations.bib", "w") as output: + output.writelines(citations.values()) + + +def _get_next_key_entry(citations: str, loc: int = 0): + """Return the key, the citation, and the end of the citation location""" + + start = citations.find("@", loc) + key_start = citations.find("{", start) + key_end = citations.find(",", key_start) + end = citations.find("@", start + 1) + if end == -1: + end = len(citations) + next = -1 + else: + next = end + + return citations[key_start:key_end], citations[start:end], next + + +gem5_citations = """@article{Binkert:2011:gem5, + author = {Nathan L. Binkert and + Bradford M. Beckmann and + Gabriel Black and + Steven K. Reinhardt and + Ali G. Saidi and + Arkaprava Basu and + Joel Hestness and + Derek Hower and + Tushar Krishna and + Somayeh Sardashti and + Rathijit Sen and + Korey Sewell and + Muhammad Shoaib Bin Altaf and + Nilay Vaish and + Mark D. Hill and + David A. Wood}, + title = {The gem5 simulator}, + journal = {{SIGARCH} Comput. Archit. News}, + volume = {39}, + number = {2}, + pages = {1--7}, + year = {2011}, + url = {https://doi.org/10.1145/2024716.2024718}, + doi = {10.1145/2024716.2024718} +} +@article{Lowe-Power:2020:gem5-20, + author = {Jason Lowe{-}Power and + Abdul Mutaal Ahmad and + Ayaz Akram and + Mohammad Alian and + Rico Amslinger and + Matteo Andreozzi and + Adri{\\`{a}} Armejach and + Nils Asmussen and + Srikant Bharadwaj and + Gabe Black and + Gedare Bloom and + Bobby R. Bruce and + Daniel Rodrigues Carvalho and + Jer{\'{o}}nimo Castrill{\'{o}}n and + Lizhong Chen and + Nicolas Derumigny and + Stephan Diestelhorst and + Wendy Elsasser and + Marjan Fariborz and + Amin Farmahini Farahani and + Pouya Fotouhi and + Ryan Gambord and + Jayneel Gandhi and + Dibakar Gope and + Thomas Grass and + Bagus Hanindhito and + Andreas Hansson and + Swapnil Haria and + Austin Harris and + Timothy Hayes and + Adrian Herrera and + Matthew Horsnell and + Syed Ali Raza Jafri and + Radhika Jagtap and + Hanhwi Jang and + Reiley Jeyapaul and + Timothy M. Jones and + Matthias Jung and + Subash Kannoth and + Hamidreza Khaleghzadeh and + Yuetsu Kodama and + Tushar Krishna and + Tommaso Marinelli and + Christian Menard and + Andrea Mondelli and + Tiago M{\"{u}}ck and + Omar Naji and + Krishnendra Nathella and + Hoa Nguyen and + Nikos Nikoleris and + Lena E. Olson and + Marc S. Orr and + Binh Pham and + Pablo Prieto and + Trivikram Reddy and + Alec Roelke and + Mahyar Samani and + Andreas Sandberg and + Javier Setoain and + Boris Shingarov and + Matthew D. Sinclair and + Tuan Ta and + Rahul Thakur and + Giacomo Travaglini and + Michael Upton and + Nilay Vaish and + Ilias Vougioukas and + Zhengrong Wang and + Norbert Wehn and + Christian Weis and + David A. Wood and + Hongil Yoon and + {\'{E}}der F. Zulian}, + title = {The gem5 Simulator: Version 20.0+}, + journal = {CoRR}, + volume = {abs/2007.03152}, + year = {2020}, + url = {https://arxiv.org/abs/2007.03152}, + eprinttype = {arXiv}, + eprint = {2007.03152} +} +""" diff --git a/src/python/m5/debug.py b/src/python/m5/debug.py index 09a032aa50..c4ae167989 100644 --- a/src/python/m5/debug.py +++ b/src/python/m5/debug.py @@ -26,11 +26,15 @@ from collections.abc import Mapping -import _m5.debug -from _m5.debug import SimpleFlag, CompoundFlag -from _m5.debug import schedBreak from m5.util import printList +import _m5.debug +from _m5.debug import ( + CompoundFlag, + SimpleFlag, + schedBreak, +) + def help(): sorted_flags = sorted(flags.items(), key=lambda kv: kv[0]) diff --git a/src/python/m5/event.py b/src/python/m5/event.py index 7c3f9a7c42..0f4cb8d947 100644 --- a/src/python/m5/event.py +++ b/src/python/m5/event.py @@ -39,11 +39,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import m5 -import _m5.event +import _m5.event from _m5.event import GlobalSimLoopExitEvent as SimExit from _m5.event import PyEvent as Event -from _m5.event import getEventQueue, setEventQueue +from _m5.event import ( + getEventQueue, + setEventQueue, +) mainq = None diff --git a/src/python/m5/ext/pystats/__init__.py b/src/python/m5/ext/pystats/__init__.py index 32cee43296..0d15ee1ad1 100644 --- a/src/python/m5/ext/pystats/__init__.py +++ b/src/python/m5/ext/pystats/__init__.py @@ -25,13 +25,13 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from .abstract_stat import AbstractStat -from .serializable_stat import SerializableStat from .group import Group +from .jsonloader import JsonLoader +from .serializable_stat import SerializableStat from .simstat import SimStat from .statistic import Statistic from .storagetype import StorageType from .timeconversion import TimeConversion -from .jsonloader import JsonLoader __all__ = [ "AbstractStat", diff --git a/src/python/m5/ext/pystats/abstract_stat.py b/src/python/m5/ext/pystats/abstract_stat.py index f2a75fca1e..bae327fcf9 100644 --- a/src/python/m5/ext/pystats/abstract_stat.py +++ b/src/python/m5/ext/pystats/abstract_stat.py @@ -24,8 +24,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .serializable_stat import SerializableStat - import re from typing import ( Callable, @@ -35,6 +33,8 @@ from typing import ( Union, ) +from .serializable_stat import SerializableStat + class AbstractStat(SerializableStat): """ @@ -50,15 +50,16 @@ class AbstractStat(SerializableStat): ) -> List["AbstractStat"]: """Iterate through all of the children, optionally with a predicate - ``` - >>> system.children(lambda _name: 'cpu' in name) - [cpu0, cpu1, cpu2] - ``` + .. code-block:: - :param: predicate(str) -> bool: Optional. Each child's name is passed - to this function. If it returns true, then the child is - yielded. Otherwise, the child is skipped. - If not provided then all children are returned. + >>> system.children(lambda _name: 'cpu' in name) + [cpu0, cpu1, cpu2] + + + :param predicate: Optional. Each child's name is passed to this function. + If it returns ``True``, then the child is yielded. + Otherwise, the child is skipped. If not provided then + all children are returned. """ to_return = [] @@ -78,15 +79,18 @@ class AbstractStat(SerializableStat): """Find all stats that match the name, recursively through all the SimStats. + .. code-block:: - ``` - >>> system.find('cpu[0-9]') - [cpu0, cpu1, cpu2] - ``` - Note: The above will not match `cpu_other`. + >>> system.find('cpu[0-9]') + [cpu0, cpu1, cpu2] - :param: regex: The regular expression used to search. Can be a - precompiled regex or a string in regex format + + .. note:: + + The above will not match ``cpu_other``. + + :param regex: The regular expression used to search. Can be a + precompiled regex or a string in regex format. """ if isinstance(regex, str): pattern = re.compile(regex) diff --git a/src/python/m5/ext/pystats/group.py b/src/python/m5/ext/pystats/group.py index 0b71663565..bc292c9d0b 100644 --- a/src/python/m5/ext/pystats/group.py +++ b/src/python/m5/ext/pystats/group.py @@ -33,7 +33,10 @@ from typing import ( ) from .abstract_stat import AbstractStat -from .statistic import Scalar, Statistic +from .statistic import ( + Scalar, + Statistic, +) from .timeconversion import TimeConversion diff --git a/src/python/m5/ext/pystats/jsonloader.py b/src/python/m5/ext/pystats/jsonloader.py index 7461f8d7a8..260b978174 100644 --- a/src/python/m5/ext/pystats/jsonloader.py +++ b/src/python/m5/ext/pystats/jsonloader.py @@ -24,27 +24,41 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from json.decoder import JSONDecodeError -from .simstat import SimStat -from .statistic import Scalar, Distribution, Accumulator, Statistic -from .group import Group, Vector import json -from typing import IO, Union +from json.decoder import JSONDecodeError +from typing import ( + IO, + Union, +) + +from .group import ( + Group, + Vector, +) +from .simstat import SimStat +from .statistic import ( + Accumulator, + Distribution, + Scalar, + Statistic, +) class JsonLoader(json.JSONDecoder): """ - Subclass of JSONDecoder that overrides 'object_hook'. Converts JSON object + Subclass of JSONDecoder that overrides ``object_hook``. Converts JSON object into a SimStat object. Usage ----- - ``` - from m5.ext.pystats.jsonloader import JsonLoader - with open(path) as f: - simstat_object = json.load(f, cls=JsonLoader) - ``` + .. code-block:: + + from m5.ext.pystats.jsonloader import JsonLoader + + with open(path) as f: + simstat_object = json.load(f, cls=JsonLoader) + """ def __init__(self): @@ -87,12 +101,14 @@ def load(json_file: IO) -> SimStat: Usage ----- - ``` - import m5.ext.pystats as pystats - with open(path) as f: - pystats.jsonloader.load(f) - ``` + .. code-block:: + + import m5.ext.pystats as pystats + + with open(path) as f: + pystats.jsonloader.load(f) + """ simstat_object = json.load(json_file, cls=JsonLoader) diff --git a/src/python/m5/ext/pystats/serializable_stat.py b/src/python/m5/ext/pystats/serializable_stat.py index c4de181e70..faa70a5022 100644 --- a/src/python/m5/ext/pystats/serializable_stat.py +++ b/src/python/m5/ext/pystats/serializable_stat.py @@ -24,9 +24,15 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from datetime import datetime import json -from typing import Dict, List, Union, Any, IO +from datetime import datetime +from typing import ( + IO, + Any, + Dict, + List, + Union, +) from .storagetype import StorageType @@ -38,22 +44,21 @@ class SerializableStat: Usage ----- - ``` - import m5.pystats.gem5stats as gem5stats - simstat = gem5stats.get_simstat(root) - print(simstat.dumps()) - ``` + .. code-block:: + + import m5.pystats.gem5stats as gem5stats + + simstat = gem5stats.get_simstat(root) + print(simstat.dumps()) + """ def to_json(self) -> Dict: """ Translates the current object into a JSON dictionary. - Returns - ------- - Dict - The JSON dictionary. + :returns: The JSON dictionary. """ model_dct = {} @@ -69,15 +74,9 @@ class SerializableStat: Translate values into a value which can be handled by the Python stdlib JSON package. - Parameters - ---------- - value: Any - The value to be translated. + :param value: The value to be translated. - Returns - ------- - Union[str,int,float,Dict,List] - A value which can be handled by the Python stdlib JSON package. + :returns: A value which can be handled by the Python stdlib JSON package. """ if isinstance(value, SerializableStat): @@ -96,31 +95,26 @@ class SerializableStat: def dumps(self, **kwargs) -> str: """ This function mirrors the Python stdlib JSON module method - `json.dumps`. It is used to obtain the gem5 statistics output to a + ``json.dumps``. It is used to obtain the gem5 statistics output to a JSON string. - Parameters - ---------- - root: Root - The root of the simulation. + :param root: The root of the simulation. - kwargs: Dict[str, Any] - Additional parameters to be passed to the `json.dumps` method. + :param kwargs: Additional parameters to be passed to the ``json.dumps`` method. - Returns - ------- - str - A string of the gem5 Statistics in a JSON format. + :returns: A string of the gem5 Statistics in a JSON format. Usage Example ------------- - ``` - import m5.pystats.gem5stats as gem5stats - simstat = gem5stats.get_simstat(root) - print(simstat.dumps(indent=6)) - ``` + .. code-block:: + + import m5.pystats.gem5stats as gem5stats + + simstat = gem5stats.get_simstat(root) + print(simstat.dumps(indent=6)) + The above will print the simulation statistic JSON string. The indentation will be 6 (by default the indentation is 4). @@ -135,27 +129,24 @@ class SerializableStat: def dump(self, fp: IO[str], **kwargs) -> None: """ This function mirrors the Python stdlib JSON module method - `json.dump`. The root of the simulation is passed, and the JSON is + ``json.dump``. The root of the simulation is passed, and the JSON is output to the specified. + :param fp: The Text IO stream to output the JSON to. - Parameters - ---------- - fp: IO[str] - The Text IO stream to output the JSON to. - - **kwargs: - Additional parameters to be passed to the ``json.dump`` method. + :param kwargs: Additional parameters to be passed to the ``json.dump`` method. Usage ----- - ``` - import m5.pystats.gem5stats as gem5stats - simstat = gem5stats.get_simstat(root) - with open("test.json") as f: - simstat.dump(fp=f, indent=6) - ``` + .. code-block:: + + import m5.pystats.gem5stats as gem5stats + + simstat = gem5stats.get_simstat(root) + with open("test.json") as f: + simstat.dump(fp=f, indent=6) + The above will dump the json output to the 'test.json' file. The indentation will be of 6 (by default the indentation is 4). diff --git a/src/python/m5/ext/pystats/simstat.py b/src/python/m5/ext/pystats/simstat.py index c7c28f419a..06858bb9ad 100644 --- a/src/python/m5/ext/pystats/simstat.py +++ b/src/python/m5/ext/pystats/simstat.py @@ -25,7 +25,12 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from datetime import datetime -from typing import Dict, List, Optional, Union +from typing import ( + Dict, + List, + Optional, + Union, +) from .abstract_stat import AbstractStat from .group import Group diff --git a/src/python/m5/ext/pystats/statistic.py b/src/python/m5/ext/pystats/statistic.py index 4111bde23e..3d4c0c6246 100644 --- a/src/python/m5/ext/pystats/statistic.py +++ b/src/python/m5/ext/pystats/statistic.py @@ -25,7 +25,13 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from abc import ABC -from typing import Any, Iterable, Optional, Union, List +from typing import ( + Any, + Iterable, + List, + Optional, + Union, +) from .abstract_stat import AbstractStat from .storagetype import StorageType @@ -110,10 +116,7 @@ class BaseScalarVector(Statistic): """ Returns the mean of the value vector. - Returns - ------- - float - The mean value across all bins. + :returns: The mean value across all bins. """ assert self.value != None assert isinstance(self.value, List) @@ -126,10 +129,7 @@ class BaseScalarVector(Statistic): """ Returns the count across all the bins. - Returns - ------- - float - The sum of all bin values. + :returns: The sum of all bin values. """ assert self.value != None return sum(self.value) @@ -140,7 +140,7 @@ class Distribution(BaseScalarVector): A statistic type that stores information relating to distributions. Each distribution has a number of bins (>=1) between this range. The values correspond to the value of each bin. - E.g., value[3]` is the value of the 4th bin. + E.g., ``value[3]`` is the value of the 4th bin. It is assumed each bucket is of equal size. """ diff --git a/src/python/m5/main.py b/src/python/m5/main.py index e07e9562ab..e346b16a5c 100644 --- a/src/python/m5/main.py +++ b/src/python/m5/main.py @@ -39,6 +39,7 @@ import code import datetime import os +import runpy import socket import sys @@ -126,13 +127,13 @@ def parse_options(): option( "--stdout-file", metavar="FILE", - default="simout", + default="simout.txt", help="Filename for -r redirection [Default: %default]", ) option( "--stderr-file", metavar="FILE", - default="simerr", + default="simerr.txt", help="Filename for -e redirection [Default: %default]", ) option( @@ -183,6 +184,16 @@ def parse_options(): del parser.rargs[:] setattr(parser.values, option.dest, (value, extra_args)) + option( + "-m", + type=str, + help="run library module as a script (terminates option list)", + default="", + metavar="mod", + action="callback", + callback=collect_args, + ) + option( "-c", type=str, @@ -193,6 +204,14 @@ def parse_options(): callback=collect_args, ) + option( + "-P", + action="store_true", + default=False, + help="Don't prepend the script directory to the system path. " + "Mimics Python 3's `-P` option.", + ) + option( "-s", action="store_true", @@ -360,18 +379,24 @@ def _check_tracing(): def main(): import m5 + from m5.util.terminal_formatter import TerminalFormatter + import _m5.core - from . import core - from . import debug - from . import defines - from . import event - from . import info - from . import stats - from . import trace - - from .util import inform, panic, isInteractive - from m5.util.terminal_formatter import TerminalFormatter + from . import ( + core, + debug, + defines, + event, + info, + stats, + trace, + ) + from .util import ( + inform, + isInteractive, + panic, + ) options, arguments = parse_options() @@ -505,7 +530,7 @@ def main(): if os.name == "nt" and os.sep == "\\": # If a Windows machine, we manually quote the string. arg = arg.replace('"', '\\"') - if re.search("\s", args): + if re.search(r"\s", args): # We quote args which have whitespace. arg = '"' + arg + '"' return arg @@ -517,7 +542,9 @@ def main(): print() # check to make sure we can find the listed script - if not options.c and (not arguments or not os.path.isfile(arguments[0])): + if not (options.c or options.m) and ( + not arguments or not os.path.isfile(arguments[0]) + ): if arguments and not os.path.isfile(arguments[0]): print(f"Script {arguments[0]} not found") @@ -595,39 +622,51 @@ def main(): sys.argv = arguments - if options.c: - filedata = options.c[0] - filecode = compile(filedata, "", "exec") - sys.argv = ["-c"] + options.c[1] - scope = {"__name__": "__m5_main__"} + if options.m: + sys.argv = [options.m[0]] + options.m[1] + runpy.run_module(options.m[0], run_name="__m5_main__") else: - sys.path = [os.path.dirname(sys.argv[0])] + sys.path - filename = sys.argv[0] - filedata = open(filename, "r").read() - filecode = compile(filedata, filename, "exec") - scope = {"__file__": filename, "__name__": "__m5_main__"} + if options.c: + filedata = options.c[0] + filecode = compile(filedata, "", "exec") + sys.argv = ["-c"] + options.c[1] + scope = {"__name__": "__m5_main__"} + else: + # If `-P` was used (`options.P == true`), don't prepend the script + # directory to the `sys.path`. This mimics Python 3's `-P` option + # (https://docs.python.org/3/using/cmdline.html#cmdoption-P). + if not options.P: + sys.path = [os.path.dirname(sys.argv[0])] + sys.path + filename = sys.argv[0] + with open(filename, "rb") as fd: + # Handle config files with unicode characters + filedata = fd.read().decode("utf-8") + filecode = compile(filedata, filename, "exec") + scope = {"__file__": filename, "__name__": "__m5_main__"} - # if pdb was requested, execfile the thing under pdb, otherwise, - # just do the execfile normally - if options.pdb: - import pdb - import traceback + # if pdb was requested, execfile the thing under pdb, otherwise, + # just do the execfile normally + if options.pdb: + import pdb + import traceback - pdb = pdb.Pdb() - try: - pdb.run(filecode, scope) - except SystemExit: - print("The program exited via sys.exit(). Exit status: ", end=" ") - print(sys.exc_info()[1]) - except: - traceback.print_exc() - print("Uncaught exception. Entering post mortem debugging") - t = sys.exc_info()[2] - while t.tb_next is not None: - t = t.tb_next - pdb.interaction(t.tb_frame, t) - else: - exec(filecode, scope) + pdb = pdb.Pdb() + try: + pdb.run(filecode, scope) + except SystemExit: + print( + "The program exited via sys.exit(). Exit status: ", end=" " + ) + print(sys.exc_info()[1]) + except: + traceback.print_exc() + print("Uncaught exception. Entering post mortem debugging") + t = sys.exc_info()[2] + while t.tb_next is not None: + t = t.tb_next + pdb.interaction(t.tb_frame, t) + else: + exec(filecode, scope) # once the script is done if options.interactive: diff --git a/src/python/m5/objects/SimObject.py b/src/python/m5/objects/SimObject.py index 6cf44d07c2..af6b0998b9 100644 --- a/src/python/m5/objects/SimObject.py +++ b/src/python/m5/objects/SimObject.py @@ -23,8 +23,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import * - # The ByteOrder enum is defined in params. Expose it here so we can declare it # to SCons, since there's no normal SimObject file to make it a part of. from m5.params import ByteOrder +from m5.SimObject import * diff --git a/src/python/m5/options.py b/src/python/m5/options.py index ed0dcddc97..a4928960cc 100644 --- a/src/python/m5/options.py +++ b/src/python/m5/options.py @@ -26,15 +26,14 @@ import optparse import sys - from optparse import * -class nodefault(object): +class nodefault: pass -class splitter(object): +class splitter: def __init__(self, split): self.split = split diff --git a/src/python/m5/params.py b/src/python/m5/params.py index 2559b553d2..32723a7860 100644 --- a/src/python/m5/params.py +++ b/src/python/m5/params.py @@ -56,13 +56,15 @@ import copy import datetime +import math import re import sys import time -import math -from . import proxy -from . import ticks +from . import ( + proxy, + ticks, +) from .util import * @@ -101,7 +103,7 @@ class MetaParamValue(type): # Dummy base class to identify types that are legitimate for SimObject # parameters. -class ParamValue(object, metaclass=MetaParamValue): +class ParamValue(metaclass=MetaParamValue): cmd_line_settable = False # Generate the code needed as a prerequisite for declaring a C++ @@ -149,7 +151,7 @@ class ParamValue(object, metaclass=MetaParamValue): # Regular parameter description. -class ParamDesc(object): +class ParamDesc: def __init__(self, ptype_str, ptype, *args, **kwargs): self.ptype_str = ptype_str # remember ptype only if it is provided @@ -298,8 +300,7 @@ class SimObjectVector(VectorParamValue): # SimObjectVector directly. def descendants(self): for v in self: - for obj in v.descendants(): - yield obj + yield from v.descendants() def get_config_as_dict(self): a = [] @@ -415,7 +416,7 @@ class VectorParamDesc(ParamDesc): code("std::vector< ${{self.ptype.cxx_type}} > ${{self.name}};") -class ParamFactory(object): +class ParamFactory: def __init__(self, param_desc_class, ptype_str=None): self.param_desc_class = param_desc_class self.ptype_str = ptype_str @@ -453,6 +454,7 @@ VectorParam = ParamFactory(VectorParamDesc) # ##################################################################### + # String-valued parameter. Just mixin the ParamValue class with the # built-in str class. class String(ParamValue, str): @@ -965,7 +967,7 @@ class AddrRange(ParamValue): if len(self.masks) == 0: return f"{self.start}:{self.end}" else: - return "%s:%s:%s:%s" % ( + return "{}:{}:{}:{}".format( self.start, self.end, self.intlvMatch, @@ -1099,10 +1101,12 @@ class HostSocket(ParamValue): self.value = value def getValue(self): - from _m5.socket import listenSocketEmptyConfig - from _m5.socket import listenSocketInetConfig - from _m5.socket import listenSocketUnixFileConfig - from _m5.socket import listenSocketUnixAbstractConfig + from _m5.socket import ( + listenSocketEmptyConfig, + listenSocketInetConfig, + listenSocketUnixAbstractConfig, + listenSocketUnixFileConfig, + ) if isinstance(self.value, str): if self.value[0] == "@": @@ -1443,8 +1447,16 @@ time_formats = [ def parse_time(value): - from time import gmtime, strptime, struct_time, time - from datetime import datetime, date + from datetime import ( + date, + datetime, + ) + from time import ( + gmtime, + strptime, + struct_time, + time, + ) if isinstance(value, struct_time): return value @@ -1483,9 +1495,10 @@ class Time(ParamValue): return value def getValue(self): - from _m5.core import tm import calendar + from _m5.core import tm + return tm.gmtime(calendar.timegm(self.value)) def __str__(self): @@ -1524,10 +1537,11 @@ class Time(ParamValue): # derive the new type from the appropriate base class on the fly. allEnums = {} + + # Metaclass for Enum types class MetaEnum(MetaParamValue): def __new__(mcls, name, bases, dict): - cls = super().__new__(mcls, name, bases, dict) allEnums[name] = cls return cls @@ -1600,7 +1614,7 @@ class Enum(ParamValue, metaclass=MetaEnum): def cxx_ini_parse(cls, code, src, dest, ret): code("if (false) {") for elem_name in cls.map.keys(): - code('} else if (%s == "%s") {' % (src, elem_name)) + code(f'}} else if ({src} == "{elem_name}") {{') code.indent() name = cls.__name__ if cls.enum_name is None else cls.enum_name code(f"{dest} = {name if cls.is_class else 'enums'}::{elem_name};") @@ -1963,11 +1977,12 @@ class MemoryBandwidth(float, ParamValue): # "Constants"... handy aliases for various values. # + # Special class for NULL pointers. Note the special check in # make_param_value() above that lets these be assigned where a # SimObject is required. # only one copy of a particular node -class NullSimObject(object, metaclass=Singleton): +class NullSimObject(metaclass=Singleton): _name = "Null" def __call__(cls): @@ -2030,9 +2045,10 @@ AllMemory = AddrRange(0, MaxAddr) # ##################################################################### + # Port reference: encapsulates a reference to a particular port on a # particular SimObject. -class PortRef(object): +class PortRef: def __init__(self, simobj, name, role, is_source): assert isSimObject(simobj) or isSimObjectClass(simobj) self.simobj = simobj @@ -2202,7 +2218,7 @@ class VectorPortElementRef(PortRef): # A reference to a complete vector-valued port (not just a single element). # Can be indexed to retrieve individual VectorPortElementRef instances. -class VectorPortRef(object): +class VectorPortRef: def __init__(self, simobj, name, role, is_source): assert isSimObject(simobj) or isSimObjectClass(simobj) self.simobj = simobj @@ -2284,7 +2300,7 @@ class VectorPortRef(object): # Port description object. Like a ParamDesc object, this represents a # logical port in the SimObject class, not a particular port on a # SimObject instance. The latter are represented by PortRef objects. -class Port(object): +class Port: # Port("role", "description") _compat_dict = {} @@ -2371,15 +2387,16 @@ SlavePort = ResponsePort VectorMasterPort = VectorRequestPort VectorSlavePort = VectorResponsePort + # 'Fake' ParamDesc for Port references to assign to the _pdesc slot of # proxy objects (via set_param_desc()) so that proxy error messages # make sense. -class PortParamDesc(object, metaclass=Singleton): +class PortParamDesc(metaclass=Singleton): ptype_str = "Port" ptype = Port -class DeprecatedParam(object): +class DeprecatedParam: """A special type for deprecated parameter variable names. There are times when we need to change the name of parameter, but this diff --git a/src/python/m5/proxy.py b/src/python/m5/proxy.py index 78862346b4..1ae579190e 100644 --- a/src/python/m5/proxy.py +++ b/src/python/m5/proxy.py @@ -45,7 +45,7 @@ import copy -class BaseProxy(object): +class BaseProxy: def __init__(self, search_self, search_up): self._search_self = search_self self._search_up = search_up @@ -272,7 +272,7 @@ def isproxy(obj): return False -class ProxyFactory(object): +class ProxyFactory: def __init__(self, search_self, search_up): self.search_self = search_self self.search_up = search_up diff --git a/src/python/m5/simulate.py b/src/python/m5/simulate.py index 587bfa0202..ebeb74b8bf 100644 --- a/src/python/m5/simulate.py +++ b/src/python/m5/simulate.py @@ -41,21 +41,31 @@ import atexit import os import sys -# import the wrapped C++ functions -import _m5.drain -import _m5.core -from _m5.stats import updateEvents as updateStatEvents - -from . import stats -from . import SimObject -from . import ticks -from . import objects -from . import params -from m5.util.dot_writer import do_dot, do_dvfs_dot +from m5.util.dot_writer import ( + do_dot, + do_dvfs_dot, +) from m5.util.dot_writer_ruby import do_ruby_dot -from .util import fatal, warn -from .util import attrdict +import _m5.core + +# import the wrapped C++ functions +import _m5.drain +from _m5.stats import updateEvents as updateStatEvents + +from . import ( + SimObject, + objects, + params, + stats, + ticks, +) +from .citations import gather_citations +from .util import ( + attrdict, + fatal, + warn, +) # define a MaxTick parameter, unsigned 64 bit MaxTick = 2**64 - 1 @@ -64,6 +74,7 @@ _drain_manager = _m5.drain.DrainManager.instance() _instantiated = False # Has m5.instantiate() been called? + # The final call to instantiate the SimObject graph and initialize the # system. def instantiate(ckpt_dir=None): @@ -164,6 +175,8 @@ def instantiate(ckpt_dir=None): # a checkpoint, If so, this call will shift them to be at a valid time. updateStatEvents() + gather_citations(root) + need_startup = True @@ -231,15 +244,15 @@ def scheduleTickExitFromCurrent( ticks: int, exit_string: str = "Tick exit reached" ) -> None: """Schedules a tick exit event from the current tick. I.e., if ticks == 100 - then an exit event will be scheduled at tick `curTick() + 100`. + then an exit event will be scheduled at tick ``curTick() + 100``. - The default `exit_string` value is used by the stdlib Simulator module to - declare this exit event as `ExitEvent.SCHEDULED_TICK`. + The default ``exit_string`` value is used by the stdlib Simulator module to + declare this exit event as ``ExitEvent.SCHEDULED_TICK``. - :param ticks: The simulation ticks, from `curTick()` to schedule the exit - event. + :param ticks: The simulation ticks, from ``curTick()`` to schedule the exit + event. :param exit_string: The exit string to return when the exit event is - triggered. + triggered. """ scheduleTickExitAbsolute(tick=ticks + curTick(), exit_string=exit_string) @@ -250,12 +263,12 @@ def scheduleTickExitAbsolute( """Schedules a tick exit event using absolute ticks. I.e., if tick == 100 then an exit event will be scheduled at tick 100. - The default `exit_string` value is used by the stdlib Simulator module to - declare this exit event as `ExitEvent.SCHEDULED_TICK`. + The default ``exit_string`` value is used by the stdlib Simulator module to + declare this exit event as ``ExitEvent.SCHEDULED_TICK``. :param tick: The absolute simulation tick to schedule the exit event. :param exit_string: The exit string to return when the exit event is - triggered. + triggered. """ if tick <= curTick(): warn("Tick exit scheduled for the past. This will not be triggered.") @@ -315,6 +328,10 @@ def checkpoint(dir): drain() memWriteback(root) + + # Recursively create the checkpoint directory if it does not exist. + os.makedirs(dir, exist_ok=True) + print("Writing checkpoint") _m5.core.serializeAll(dir) @@ -334,9 +351,11 @@ def _changeMemoryMode(system, mode): def switchCpus(system, cpuList, verbose=True): """Switch CPUs in a system. - Note: This method may switch the memory mode of the system if that - is required by the CPUs. It may also flush all caches in the - system. + .. note:: + + This method may switch the memory mode of the system if that + is required by the CPUs. It may also flush all caches in the + system. Arguments: system -- Simulated system. @@ -474,6 +493,9 @@ def fork(simout="%(parent)s.f%(fork_seq)i"): return pid -from _m5.core import disableAllListeners, listenersDisabled -from _m5.core import listenersLoopbackOnly -from _m5.core import curTick +from _m5.core import ( + curTick, + disableAllListeners, + listenersDisabled, + listenersLoopbackOnly, +) diff --git a/src/python/m5/stats/__init__.py b/src/python/m5/stats/__init__.py index ce7a2d267d..de129c1817 100644 --- a/src/python/m5/stats/__init__.py +++ b/src/python/m5/stats/__init__.py @@ -38,16 +38,20 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import m5 - -import _m5.stats from m5.objects import Root from m5.params import isNullPointer -from .gem5stats import JsonOutputVistor -from m5.util import attrdict, fatal +from m5.util import ( + attrdict, + fatal, +) + +import _m5.stats # Stat exports -from _m5.stats import schedStatEvent as schedEvent from _m5.stats import periodicStatDump +from _m5.stats import schedStatEvent as schedEvent + +from .gem5stats import JsonOutputVistor outputList = [] diff --git a/src/python/m5/stats/gem5stats.py b/src/python/m5/stats/gem5stats.py index 07636e3e3f..682149f380 100644 --- a/src/python/m5/stats/gem5stats.py +++ b/src/python/m5/stats/gem5stats.py @@ -30,20 +30,25 @@ the Python Stats model. """ from datetime import datetime -from typing import IO, List, Union +from typing import ( + IO, + List, + Union, +) -import _m5.stats -from m5.objects import * from m5.ext.pystats.group import * from m5.ext.pystats.simstat import * from m5.ext.pystats.statistic import * from m5.ext.pystats.storagetype import * +from m5.objects import * + +import _m5.stats class JsonOutputVistor: """ This is a helper vistor class used to include a JSON output via the stats - API (`src/python/m5/stats/__init__.py`). + API (``src/python/m5/stats/__init__.py``). """ file: str @@ -51,14 +56,9 @@ class JsonOutputVistor: def __init__(self, file: str, **kwargs): """ - Parameters - ---------- + :param file: The output file location in which the JSON will be dumped. - file: str - The output file location in which the JSON will be dumped. - - kwargs: Dict[str, Any] - Additional parameters to be passed to the `json.dumps` method. + :param kwargs: Additional parameters to be passed to the ``json.dumps`` method. """ self.file = file @@ -69,14 +69,13 @@ class JsonOutputVistor: Dumps the stats of a simulation root (or list of roots) to the output JSON file specified in the JsonOutput constructor. - WARNING: This dump assumes the statistics have already been prepared - for the target root. + .. warning:: - Parameters - ---------- + This dump assumes the statistics have already been prepared + for the target root. - roots: Union[List[Root], Root]] - The Root, or List of roots, whose stats are are to be dumped JSON. + + :param roots: The Root, or List of roots, whose stats are are to be dumped JSON. """ with open(self.file, "w") as fp: @@ -88,20 +87,14 @@ def get_stats_group(group: _m5.stats.Group) -> Group: """ Translates a gem5 Group object into a Python stats Group object. A Python statistic Group object is a dictionary of labeled Statistic objects. Any - gem5 object passed to this will have its `getStats()` and `getStatGroups` + gem5 object passed to this will have its ``getStats()`` and ``getStatGroups`` function called, and all the stats translated (inclusive of the stats further down the hierarchy). - Parameters - ---------- - group: _m5.stats.Group - The gem5 _m5.stats.Group object to be translated to be a Python stats - Group object. Typically this will be a gem5 SimObject. + :param group: The gem5 _m5.stats.Group object to be translated to be a Python + stats Group object. Typically this will be a gem5 SimObject. - Returns - ------- - Group - The stats group object translated from the input gem5 object. + :returns: The stats group object translated from the input gem5 object. """ stats_dict = {} @@ -122,16 +115,10 @@ def __get_statistic(statistic: _m5.stats.Info) -> Optional[Statistic]: Translates a _m5.stats.Info object into a Statistic object, to process statistics at the Python level. - Parameters - ---------- - statistic: Info - The Info object to be translated to a Statistic object. + :param statistic: The Info object to be translated to a Statistic object. - Returns - ------- - Optional[Statistic] - The Statistic object of the Info object. Returns None if Info object - cannot be translated. + :returns: The Statistic object of the Info object. Returns ``None`` if + Info object cannot be translated. """ assert isinstance(statistic, _m5.stats.Info) @@ -245,22 +232,16 @@ def get_simstat( SimStat object will contain all the stats for all the SimObjects contained within the "root", inclusive of the "root" SimObject/SimObjects. - Parameters - ---------- - root: Union[SimObject, List[SimObject]] - A SimObject, or list of SimObjects, of the simulation for translation - into a SimStat object. Typically this is the simulation's Root - SimObject as this will obtain the entirety of a run's statistics in a - single SimStat object. + :param root: A SimObject, or list of SimObjects, of the simulation for + translation into a SimStat object. Typically this is the + simulation's Root SimObject as this will obtain the entirety + of a run's statistics in a single SimStat object. - prepare_stats: bool - Dictates whether the stats are to be prepared prior to creating the - SimStat object. By default this is 'True'. + :param prepare_stats: Dictates whether the stats are to be prepared prior + to creating the SimStat object. By default this is + ``True``. - Returns - ------- - SimStat - The SimStat Object of the current simulation. + :Returns: The SimStat Object of the current simulation. """ stats_map = {} diff --git a/src/python/m5/ticks.py b/src/python/m5/ticks.py index 47b033cfb4..c44ac7a13f 100644 --- a/src/python/m5/ticks.py +++ b/src/python/m5/ticks.py @@ -25,10 +25,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import decimal - import sys + from m5.util import warn + # fix the global frequency def fixGlobalFrequency(): import _m5.core @@ -38,6 +39,7 @@ def fixGlobalFrequency(): def setGlobalFrequency(ticksPerSecond): from m5.util import convert + import _m5.core if isinstance(ticksPerSecond, int): diff --git a/src/python/m5/trace.py b/src/python/m5/trace.py index 759f96e5bf..6aa88e7644 100644 --- a/src/python/m5/trace.py +++ b/src/python/m5/trace.py @@ -25,4 +25,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Export native methods to Python -from _m5.trace import output, activate, ignore, disable, enable +from _m5.trace import ( + activate, + disable, + enable, + ignore, + output, +) diff --git a/src/python/m5/util/__init__.py b/src/python/m5/util/__init__.py index 34c5ee8a49..3fab2d8931 100644 --- a/src/python/m5/util/__init__.py +++ b/src/python/m5/util/__init__.py @@ -40,14 +40,17 @@ import os import re import sys - from functools import wraps from . import convert - -from .attrdict import attrdict, multiattrdict, optiondict +from .attrdict import ( + attrdict, + multiattrdict, + optiondict, +) from .multidict import multidict + # panic() should be called when something happens that should never # ever happen regardless of what the user does (i.e., an acutal m5 # bug). diff --git a/src/python/m5/util/attrdict.py b/src/python/m5/util/attrdict.py index 0ff2103277..726f1add94 100644 --- a/src/python/m5/util/attrdict.py +++ b/src/python/m5/util/attrdict.py @@ -69,7 +69,7 @@ class multiattrdict(attrdict): class optiondict(attrdict): - """Modify attrdict so that a missing attribute just returns None""" + """Modify attrdict so that a missing attribute just returns ``None``.""" def __getattr__(self, attr): try: diff --git a/src/python/m5/util/dot_writer.py b/src/python/m5/util/dot_writer.py index b491a98448..1257470306 100644 --- a/src/python/m5/util/dot_writer.py +++ b/src/python/m5/util/dot_writer.py @@ -53,9 +53,18 @@ # ##################################################################### -import m5, os, re -from m5.SimObject import isRoot, isSimObjectVector -from m5.params import PortRef, isNullPointer +import os +import re + +import m5 +from m5.params import ( + PortRef, + isNullPointer, +) +from m5.SimObject import ( + isRoot, + isSimObjectVector, +) from m5.util import warn try: @@ -82,7 +91,7 @@ def dot_create_nodes(simNode, callgraph): label = "root" else: label = simNode._name - full_path = re.sub("\.", "_", simNode.path()) + full_path = re.sub(r"\.", "_", simNode.path()) # add class name under the label label = '"' + label + " \\n: " + simNode.__class__.__name__ + '"' @@ -109,7 +118,7 @@ def dot_create_edges(simNode, callgraph): for port_name in simNode._ports.keys(): port = simNode._port_refs.get(port_name, None) if port != None: - full_path = re.sub("\.", "_", simNode.path()) + full_path = re.sub(r"\.", "_", simNode.path()) full_port_name = full_path + "_" + port_name port_node = dot_create_node(simNode, full_port_name, port_name) # create edges @@ -128,7 +137,7 @@ def dot_create_edges(simNode, callgraph): def dot_add_edge(simNode, callgraph, full_port_name, port): peer = port.peer - full_peer_path = re.sub("\.", "_", peer.simobj.path()) + full_peer_path = re.sub(r"\.", "_", peer.simobj.path()) full_peer_port_name = full_peer_path + "_" + peer.name # Each edge is encountered twice, once for each peer. We only want one @@ -290,9 +299,9 @@ def dot_rgb_to_html(r, g, b): # We need to create all of the clock domains. We abuse the alpha channel to get # the correct domain colouring. def dot_add_clk_domain(c_dom, v_dom): - label = '"' + str(c_dom) + "\ :\ " + str(v_dom) + '"' - label = re.sub("\.", "_", str(label)) - full_path = re.sub("\.", "_", str(c_dom)) + label = '"' + str(c_dom) + r"\ :\ " + str(v_dom) + '"' + label = re.sub(r"\.", "_", str(label)) + full_path = re.sub(r"\.", "_", str(c_dom)) return pydot.Cluster( full_path, shape="box", @@ -311,7 +320,7 @@ def dot_create_dvfs_nodes(simNode, callgraph, domain=None): label = "root" else: label = simNode._name - full_path = re.sub("\.", "_", simNode.path()) + full_path = re.sub(r"\.", "_", simNode.path()) # add class name under the label label = '"' + label + " \\n: " + simNode.__class__.__name__ + '"' diff --git a/src/python/m5/util/dot_writer_ruby.py b/src/python/m5/util/dot_writer_ruby.py index fa21ae1a01..be5d406278 100644 --- a/src/python/m5/util/dot_writer_ruby.py +++ b/src/python/m5/util/dot_writer_ruby.py @@ -36,6 +36,7 @@ # Creates a visual representation of a Ruby network topology import os + import m5 from m5.util import warn diff --git a/src/python/m5/util/fdthelper.py b/src/python/m5/util/fdthelper.py index 136936c512..8a265d1d4e 100644 --- a/src/python/m5/util/fdthelper.py +++ b/src/python/m5/util/fdthelper.py @@ -35,9 +35,10 @@ # # Author: Glenn Bergmans -from m5.ext.pyfdt import pyfdt -import re import os +import re + +from m5.ext.pyfdt import pyfdt from m5.SimObject import SimObject from m5.util import fatal @@ -86,7 +87,7 @@ class FdtPropertyBytes(pyfdt.FdtPropertyBytes): super().__init__(name, values) -class FdtState(object): +class FdtState: """Class for maintaining state while recursively generating a flattened device tree. The state tracks address, size and CPU address cell sizes, and maintains a dictionary of allocated phandles.""" @@ -270,7 +271,7 @@ class Fdt(pyfdt.Fdt): with open(filename, "wb") as f: f.write(self.to_dtb()) return filename - except IOError: + except OSError: raise RuntimeError("Failed to open DTB output file") def writeDtsFile(self, filename): @@ -280,5 +281,5 @@ class Fdt(pyfdt.Fdt): with open(filename, "w") as f: f.write(self.to_dts()) return filename - except IOError: + except OSError: raise RuntimeError("Failed to open DTS output file") diff --git a/src/python/m5/util/multidict.py b/src/python/m5/util/multidict.py index f6ca6ba90a..cff47aeda1 100644 --- a/src/python/m5/util/multidict.py +++ b/src/python/m5/util/multidict.py @@ -27,7 +27,7 @@ __all__ = ["multidict"] -class multidict(object): +class multidict: def __init__(self, parent={}, **kwargs): self.local = dict(**kwargs) self.parent = parent @@ -80,8 +80,7 @@ class multidict(object): return key in self def items(self): - for item in self.next(): - yield item + yield from self.next() def keys(self): for key, value in self.next(): diff --git a/src/python/m5/util/pybind.py b/src/python/m5/util/pybind.py index 54fd111f38..1be9bb604b 100644 --- a/src/python/m5/util/pybind.py +++ b/src/python/m5/util/pybind.py @@ -36,7 +36,7 @@ from abc import * -class PyBindExport(object, metaclass=ABCMeta): +class PyBindExport(metaclass=ABCMeta): @abstractmethod def export(self, code, cname): pass diff --git a/src/python/m5/util/terminal.py b/src/python/m5/util/terminal.py index f3d53ac460..0486b14378 100644 --- a/src/python/m5/util/terminal.py +++ b/src/python/m5/util/terminal.py @@ -84,7 +84,7 @@ except: cap_string = null_cap_string -class ColorStrings(object): +class ColorStrings: def __init__(self, cap_string): for i, c in enumerate(color_names): setattr(self, c, cap_string("setaf", i)) diff --git a/src/python/m5/util/terminal_formatter.py b/src/python/m5/util/terminal_formatter.py index 8d533f8bb7..a8a8e21914 100644 --- a/src/python/m5/util/terminal_formatter.py +++ b/src/python/m5/util/terminal_formatter.py @@ -34,7 +34,9 @@ class TerminalFormatter: self.__text_width = min(max_width, self.__terminal_size()[0]) def __terminal_size(self): - import fcntl, termios, struct + import fcntl + import struct + import termios h, w, hp, wp = struct.unpack( "HHHH", @@ -45,7 +47,6 @@ class TerminalFormatter: return w, h def __get_paragraphs(self, text, flatten=False): - """ This function takes a text and returns a list of constituent paragraphs, defining a paragraph as a block of text separated from diff --git a/src/sim/ClockDomain.py b/src/sim/ClockDomain.py index d71252e1bc..4c4c6e765f 100644 --- a/src/sim/ClockDomain.py +++ b/src/sim/ClockDomain.py @@ -34,8 +34,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.params import * -from m5.SimObject import SimObject from m5.proxy import * +from m5.SimObject import SimObject + # Abstract clock domain class ClockDomain(SimObject): diff --git a/src/sim/ClockedObject.py b/src/sim/ClockedObject.py index 5d1656e520..75a9fae597 100644 --- a/src/sim/ClockedObject.py +++ b/src/sim/ClockedObject.py @@ -34,9 +34,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.objects.PowerState import PowerState -from m5.SimObject import SimObject from m5.params import * from m5.proxy import * +from m5.SimObject import SimObject class ClockedObject(SimObject): diff --git a/src/sim/DVFSHandler.py b/src/sim/DVFSHandler.py index f7064221e1..41aaffc897 100644 --- a/src/sim/DVFSHandler.py +++ b/src/sim/DVFSHandler.py @@ -34,8 +34,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.params import * -from m5.SimObject import SimObject from m5.proxy import * +from m5.SimObject import SimObject + # The handler in its current form is design to be centeralized, one per system # and manages all the source clock domains (SrcClockDomain) it is configured to diff --git a/src/sim/InstTracer.py b/src/sim/InstTracer.py index 34c97dd43e..4470cd1125 100644 --- a/src/sim/InstTracer.py +++ b/src/sim/InstTracer.py @@ -1,3 +1,15 @@ +# Copyright (c) 2023 Arm Limited +# All rights reserved. +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# # Copyright (c) 2007 The Regents of The University of Michigan # All rights reserved. # @@ -24,8 +36,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * +from m5.SimObject import SimObject + + +class InstDisassembler(SimObject): + type = "InstDisassembler" + cxx_header = "sim/insttracer.hh" + cxx_class = "gem5::trace::InstDisassembler" class InstTracer(SimObject): @@ -33,3 +51,7 @@ class InstTracer(SimObject): cxx_header = "sim/insttracer.hh" cxx_class = "gem5::trace::InstTracer" abstract = True + + disassembler = Param.InstDisassembler( + InstDisassembler(), "Instruction Disassembler" + ) diff --git a/src/sim/PowerDomain.py b/src/sim/PowerDomain.py index 2f42343870..cda7a300ee 100644 --- a/src/sim/PowerDomain.py +++ b/src/sim/PowerDomain.py @@ -36,8 +36,9 @@ import sys -from m5.params import * from m5.objects.PowerState import PowerState +from m5.params import * + # A power domain groups multiple ClockedObjects and creates a # hierarchy in which follower ClockedObjects (caches for example) can diff --git a/src/sim/PowerState.py b/src/sim/PowerState.py index ca285fc68b..5e6d900710 100644 --- a/src/sim/PowerState.py +++ b/src/sim/PowerState.py @@ -34,9 +34,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -from m5.SimObject import SimObject from m5.params import * from m5.proxy import * +from m5.SimObject import SimObject + # Enumerate set of allowed power states that can be used by a clocked object. # The list is kept generic to express a base minimal set. diff --git a/src/sim/Process.py b/src/sim/Process.py index 0b87b09485..0f911f9f65 100644 --- a/src/sim/Process.py +++ b/src/sim/Process.py @@ -24,10 +24,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import * +from os import getcwd + from m5.params import * from m5.proxy import * -from os import getcwd +from m5.SimObject import * class Process(SimObject): diff --git a/src/sim/RedirectPath.py b/src/sim/RedirectPath.py index c6c63e2558..91ed5891da 100644 --- a/src/sim/RedirectPath.py +++ b/src/sim/RedirectPath.py @@ -24,9 +24,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * from m5.proxy import * +from m5.SimObject import SimObject class RedirectPath(SimObject): diff --git a/src/sim/Root.py b/src/sim/Root.py index 5002cdcf81..fae560491f 100644 --- a/src/sim/Root.py +++ b/src/sim/Root.py @@ -26,13 +26,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * +from m5.SimObject import SimObject from m5.util import fatal class Root(SimObject): - _the_instance = None def __new__(cls, **kwargs): diff --git a/src/sim/SConscript b/src/sim/SConscript index e26676c00a..a9216de824 100644 --- a/src/sim/SConscript +++ b/src/sim/SConscript @@ -1,5 +1,16 @@ # -*- mode:python -*- - +# Copyright (c) 2023 Arm Limited +# All rights reserved. +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# # Copyright (c) 2006 The Regents of The University of Michigan # All rights reserved. # @@ -31,7 +42,8 @@ Import('*') SimObject('ClockedObject.py', sim_objects=['ClockedObject']) SimObject('TickedObject.py', sim_objects=['TickedObject']) SimObject('Workload.py', sim_objects=[ - 'Workload', 'StubWorkload', 'KernelWorkload', 'SEWorkload']) + 'Workload', 'StubWorkload', 'KernelWorkload', 'SEWorkload'], + enums=['KernelPanicOopsBehaviour']) SimObject('Root.py', sim_objects=['Root']) SimObject('ClockDomain.py', sim_objects=[ 'ClockDomain', 'SrcClockDomain', 'DerivedClockDomain']) @@ -105,7 +117,7 @@ GTest('proxy_ptr.test', 'proxy_ptr.test.cc') GTest('serialize.test', 'serialize.test.cc', with_tag('gem5 serialize')) GTest('serialize_handlers.test', 'serialize_handlers.test.cc') -SimObject('InstTracer.py', sim_objects=['InstTracer']) +SimObject('InstTracer.py', sim_objects=['InstTracer', 'InstDisassembler']) SimObject('Process.py', sim_objects=['Process', 'EmulatedDriver']) Source('faults.cc') Source('process.cc') diff --git a/src/sim/SignalPort.py b/src/sim/SignalPort.py index fc529a8b45..bdb6d5eae1 100644 --- a/src/sim/SignalPort.py +++ b/src/sim/SignalPort.py @@ -23,7 +23,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import Port, VectorPort +from m5.params import ( + Port, + VectorPort, +) SIGNAL_SOURCE_ROLE_TEMPLATE = "Signal source <%s>" SIGNAL_SINK_ROLE_TEMPLATE = "Signal sink <%s>" diff --git a/src/sim/SubSystem.py b/src/sim/SubSystem.py index fa0063ba1f..19df9d2b83 100644 --- a/src/sim/SubSystem.py +++ b/src/sim/SubSystem.py @@ -33,8 +33,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * +from m5.SimObject import SimObject + # An empty simobject. Used for organizing simobjects # into logical groups as subsystems of a larger diff --git a/src/sim/System.py b/src/sim/System.py index eb1280f248..fb6ff8622c 100644 --- a/src/sim/System.py +++ b/src/sim/System.py @@ -37,13 +37,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import * -from m5.params import * -from m5.proxy import * - from m5.objects.DVFSHandler import * from m5.objects.SimpleMemory import * from m5.objects.Workload import StubWorkload +from m5.params import * +from m5.proxy import * +from m5.SimObject import * class MemoryMode(Enum): diff --git a/src/sim/VoltageDomain.py b/src/sim/VoltageDomain.py index 79116edfd3..9d2c503a81 100644 --- a/src/sim/VoltageDomain.py +++ b/src/sim/VoltageDomain.py @@ -33,8 +33,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * +from m5.SimObject import SimObject class VoltageDomain(SimObject): diff --git a/src/sim/Workload.py b/src/sim/Workload.py index 31ea7382dd..50625e8085 100644 --- a/src/sim/Workload.py +++ b/src/sim/Workload.py @@ -1,3 +1,15 @@ +# Copyright (c) 2023 Arm Limited +# All rights reserved. +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# # Copyright 2019 Google Inc. # # Redistribution and use in source and binary forms, with or without @@ -23,10 +35,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.SimObject import SimObject, cxxMethod - from m5.objects.SimpleMemory import * +from m5.params import * +from m5.SimObject import ( + SimObject, + cxxMethod, +) class Workload(SimObject): @@ -60,6 +74,16 @@ class StubWorkload(Workload): ) +class KernelPanicOopsBehaviour(ScopedEnum): + "Define what gem5 should do after a Kernel Panic or Oops." + vals = [ + "Continue", + "DumpDmesgAndContinue", + "DumpDmesgAndExit", + "DumpDmesgAndPanic", + ] + + class KernelWorkload(Workload): type = "KernelWorkload" cxx_header = "sim/kernel_workload.hh" @@ -84,6 +108,18 @@ class KernelWorkload(Workload): command_line = Param.String("a", "boot flags to pass to the kernel") + on_panic = Param.KernelPanicOopsBehaviour( + "DumpDmesgAndExit", + "Define how gem5 should behave after a Linux Kernel Panic. " + "Handler might not be implemented for all architectures.", + ) + + on_oops = Param.KernelPanicOopsBehaviour( + "DumpDmesgAndExit", + "Define how gem5 should behave after a Linux Kernel Oops. " + "Handler might not be implemented for all architectures.", + ) + class SEWorkloadMeta(type(Workload)): all_se_workload_classes = [] diff --git a/src/sim/fd_array.cc b/src/sim/fd_array.cc index 5ca9370054..ea58299587 100644 --- a/src/sim/fd_array.cc +++ b/src/sim/fd_array.cc @@ -42,6 +42,7 @@ #include "base/output.hh" #include "params/Process.hh" #include "sim/fd_entry.hh" +#include "sim/process.hh" namespace gem5 { @@ -367,7 +368,7 @@ FDArray::serialize(CheckpointOut &cp) const { } void -FDArray::unserialize(CheckpointIn &cp) { +FDArray::unserialize(CheckpointIn &cp, Process* process_ptr) { ScopedCheckpointSection sec(cp, "fdarray"); uint64_t size; paramIn(cp, "size", size); @@ -418,11 +419,24 @@ FDArray::unserialize(CheckpointIn &cp) { setFDEntry(tgt_fd, fdep); mode_t mode = this_ffd->getFileMode(); - std::string const& path = this_ffd->getFileName(); + + std::string path; + + if (process_ptr) { + // Check if it is needed to redirect the app path to another host + // path + path = process_ptr->checkPathRedirect(this_ffd->getFileName()); + } + else { + path = this_ffd->getFileName(); + } + int flags = this_ffd->getFlags(); // Re-open the file and assign a new sim_fd - int sim_fd = openFile(path, flags, mode); + int sim_fd; + sim_fd = openFile(path, flags, mode); + this_ffd->setSimFD(sim_fd); // Restore the file offset to the proper value diff --git a/src/sim/fd_array.hh b/src/sim/fd_array.hh index d6d1b3cfbe..c2a6b64dea 100644 --- a/src/sim/fd_array.hh +++ b/src/sim/fd_array.hh @@ -43,6 +43,8 @@ namespace gem5 { +class Process; + class FDArray : public Serializable { public: @@ -117,7 +119,11 @@ class FDArray : public Serializable * Serialization methods for file descriptors */ void serialize(CheckpointOut &cp) const override; - void unserialize(CheckpointIn &cp) override; + void unserialize(CheckpointIn &cp, Process* process_ptr ); + void unserialize(CheckpointIn &cp) override { + unserialize(cp, nullptr); + }; + private: /** diff --git a/src/sim/init_signals.cc b/src/sim/init_signals.cc index b9aa6f6e4d..b574f12ea1 100644 --- a/src/sim/init_signals.cc +++ b/src/sim/init_signals.cc @@ -84,7 +84,8 @@ setupAltStack() static void installSignalHandler(int signal, void (*handler)(int sigtype), - int flags = SA_RESTART) + int flags = SA_RESTART, + struct sigaction *old_sa = NULL) { struct sigaction sa; @@ -93,7 +94,7 @@ installSignalHandler(int signal, void (*handler)(int sigtype), sa.sa_handler = handler; sa.sa_flags = flags; - if (sigaction(signal, &sa, NULL) == -1) + if (sigaction(signal, &sa, old_sa) == -1) panic("Failed to setup handler for signal %i\n", signal); } @@ -196,9 +197,6 @@ initSignals() // Dump intermediate stats and reset them installSignalHandler(SIGUSR2, dumprstStatsHandler); - // Exit cleanly on Interrupt (Ctrl-C) - installSignalHandler(SIGINT, exitNowHandler); - // Print the current cycle number and a backtrace on abort. Make // sure the signal is unmasked and the handler reset when a signal // is delivered to be able to invoke the default handler. @@ -218,4 +216,19 @@ initSignals() installSignalHandler(SIGIO, ioHandler); } +struct sigaction old_int_sa; + +void initSigInt() +{ + // Exit cleanly on Interrupt (Ctrl-C) + installSignalHandler(SIGINT, exitNowHandler, SA_RESTART, &old_int_sa); +} + +void restoreSigInt() +{ + // Restore the old SIGINT handler + sigaction(SIGINT, &old_int_sa, NULL); +} + + } // namespace gem5 diff --git a/src/sim/init_signals.hh b/src/sim/init_signals.hh index ea9f3d5229..2da3aae877 100644 --- a/src/sim/init_signals.hh +++ b/src/sim/init_signals.hh @@ -38,6 +38,10 @@ void exitNowHandler(int sigtype); void abortHandler(int sigtype); void initSignals(); +// separate out sigint handler so that we can restore the python one +void initSigInt(); +void restoreSigInt(); + } // namespace gem5 #endif // __SIM_INIT_SIGNALS_HH__ diff --git a/src/sim/insttracer.hh b/src/sim/insttracer.hh index 9c9bca7692..37e29756a2 100644 --- a/src/sim/insttracer.hh +++ b/src/sim/insttracer.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, 2020 ARM Limited + * Copyright (c) 2014, 2017, 2020, 2023 Arm Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -48,6 +48,7 @@ #include "cpu/inst_res.hh" #include "cpu/inst_seq.hh" #include "cpu/static_inst.hh" +#include "params/InstTracer.hh" #include "sim/sim_object.hh" namespace gem5 @@ -286,10 +287,37 @@ class InstRecord bool getFaulting() const { return faulting; } }; +/** + * The base InstDisassembler class provides a one-API interface + * to disassemble the instruction passed as a first argument. + * It also provides a base implementation which is + * simply calling the StaticInst::disassemble method, which + * is the usual interface for disassembling + * a gem5 instruction. + */ +class InstDisassembler : public SimObject +{ + public: + InstDisassembler(const SimObjectParams ¶ms) + : SimObject(params) + {} + + virtual std::string + disassemble(StaticInstPtr inst, + const PCStateBase &pc, + const loader::SymbolTable *symtab) const + { + return inst->disassemble(pc.instAddr(), symtab); + } +}; + class InstTracer : public SimObject { public: - InstTracer(const Params &p) : SimObject(p) {} + PARAMS(InstTracer); + InstTracer(const Params &p) + : SimObject(p), disassembler(p.disassembler) + {} virtual ~InstTracer() {} @@ -297,6 +325,17 @@ class InstTracer : public SimObject getInstRecord(Tick when, ThreadContext *tc, const StaticInstPtr staticInst, const PCStateBase &pc, const StaticInstPtr macroStaticInst=nullptr) = 0; + + std::string + disassemble(StaticInstPtr inst, + const PCStateBase &pc, + const loader::SymbolTable *symtab=nullptr) const + { + return disassembler->disassemble(inst, pc, symtab); + } + + private: + InstDisassembler *disassembler; }; } // namespace trace diff --git a/src/sim/kernel_workload.cc b/src/sim/kernel_workload.cc index c338b23b16..27e27137a9 100644 --- a/src/sim/kernel_workload.cc +++ b/src/sim/kernel_workload.cc @@ -66,8 +66,8 @@ KernelWorkload::KernelWorkload(const Params &p) : Workload(p), kernelSymtab = kernelObj->symtab(); auto initKernelSymtab = kernelSymtab.mask(_loadAddrMask) ->offset(_loadAddrOffset) - ->rename([](std::string &name) { - name = "kernel_init." + name; + ->rename([](const std::string &name) { + return "kernel_init." + name; }); loader::debugSymbolTable.insert(*initKernelSymtab); diff --git a/src/sim/main.cc b/src/sim/main.cc index 81a691d15d..1c42891816 100644 --- a/src/sim/main.cc +++ b/src/sim/main.cc @@ -50,6 +50,7 @@ main(int argc, char **argv) // Initialize gem5 special signal handling. initSignals(); +#if PY_VERSION_HEX < 0x03080000 // Convert argv[0] to a wchar_t string, using python's locale and cleanup // functions. std::unique_ptr program( @@ -59,6 +60,23 @@ main(int argc, char **argv) // This can help python find libraries at run time relative to this binary. // It's probably not necessary, but is mostly harmless and might be useful. Py_SetProgramName(program.get()); +#else + // Preinitialize Python for Python 3.8+ + // This ensures that the locale configuration takes effect + PyStatus status; + + PyConfig config; + PyConfig_InitPythonConfig(&config); + + /* Set the program name. Implicitly preinitialize Python. */ + status = PyConfig_SetBytesString(&config, &config.program_name, + argv[0]); + if (PyStatus_Exception(status)) { + PyConfig_Clear(&config); + Py_ExitStatusException(status); + return 1; + } +#endif py::scoped_interpreter guard(true, argc, argv); diff --git a/src/sim/mem_state.cc b/src/sim/mem_state.cc index 801226cfb1..93e7ca3773 100644 --- a/src/sim/mem_state.cc +++ b/src/sim/mem_state.cc @@ -49,7 +49,7 @@ MemState::MemState(Process *owner, Addr brk_point, Addr stack_base, _stackBase(stack_base), _stackSize(max_stack_size), _maxStackSize(max_stack_size), _stackMin(stack_base - max_stack_size), _nextThreadStackBase(next_thread_stack_base), - _mmapEnd(mmap_end), _endBrkPoint(brk_point) + _mmapEnd(mmap_end) { } @@ -67,7 +67,6 @@ MemState::operator=(const MemState &in) _stackMin = in._stackMin; _nextThreadStackBase = in._nextThreadStackBase; _mmapEnd = in._mmapEnd; - _endBrkPoint = in._endBrkPoint; _vmaList = in._vmaList; /* This assignment does a deep copy. */ return *this; @@ -107,26 +106,34 @@ MemState::isUnmapped(Addr start_addr, Addr length) void MemState::updateBrkRegion(Addr old_brk, Addr new_brk) { - /** - * To make this simple, avoid reducing the heap memory area if the - * new_brk point is less than the old_brk; this occurs when the heap is - * receding because the application has given back memory. The brk point - * is still tracked in the MemState class as an independent field so that - * it can be returned to the application; we just do not update the - * region unless we expand it out. - */ - if (new_brk < old_brk) { - _brkPoint = new_brk; - return; - } - /** * The regions must be page aligned but the break point can be set on * byte boundaries. Ensure that the restriction is maintained here by * extending the request out to the end of the page. (The roundUp * function will not round up an already aligned page.) */ - auto page_aligned_brk = roundUp(new_brk, _pageBytes); + auto page_aligned_new_brk = roundUp(new_brk, _pageBytes); + auto page_aligned_old_brk = roundUp(old_brk, _pageBytes); + + /** + * Reduce the heap memory area if the new_brk point is less than + * the old_brk; this occurs when the heap is receding because the + * application has given back memory. This may involve unmapping + * heap pages, if new_brk rounds to a lower-address page. The + * previous behavior was to leave such pages mapped for simplicity; + * however, that was not what Linux does in practice and may + * violate the assumptions of applications like glibc malloc, + * whose default configuration for Linux requires all pages + * allocated via brk(2) to be zero-filled (specifically, + * by setting MORECORE_CLEARS to 2). + */ + if (new_brk < old_brk) { + const auto length = page_aligned_old_brk - page_aligned_new_brk; + if (length > 0) + unmapRegion(page_aligned_new_brk, length); + _brkPoint = new_brk; + return; + } /** * Create a new mapping for the heap region. We only create a mapping @@ -135,17 +142,16 @@ MemState::updateBrkRegion(Addr old_brk, Addr new_brk) * * Since we do not track the type of the region and we also do not * coalesce the regions together, we can create a fragmented set of - * heap regions. To resolve this, we keep the furthest point ever mapped - * by the _endBrkPoint field. + * heap regions. */ - if (page_aligned_brk > _endBrkPoint) { - auto length = page_aligned_brk - _endBrkPoint; + if (page_aligned_new_brk > page_aligned_old_brk) { + auto length = page_aligned_new_brk - page_aligned_old_brk; /** * Check if existing mappings impede the expansion of brk expansion. * If brk cannot expand, it must return the original, unmodified brk * address and should not modify the mappings here. */ - if (!isUnmapped(_endBrkPoint, length)) { + if (!isUnmapped(page_aligned_old_brk, length)) { return; } @@ -156,8 +162,7 @@ MemState::updateBrkRegion(Addr old_brk, Addr new_brk) * implemented if it actually becomes necessary; probably only * necessary if the list becomes too long to walk. */ - mapRegion(_endBrkPoint, length, "heap"); - _endBrkPoint = page_aligned_brk; + mapRegion(page_aligned_old_brk, length, "heap"); } _brkPoint = new_brk; diff --git a/src/sim/mem_state.hh b/src/sim/mem_state.hh index b2b50d0760..8c01f7a765 100644 --- a/src/sim/mem_state.hh +++ b/src/sim/mem_state.hh @@ -277,11 +277,6 @@ class MemState : public Serializable Addr _nextThreadStackBase; Addr _mmapEnd; - /** - * Keeps record of the furthest mapped heap location. - */ - Addr _endBrkPoint; - /** * The _vmaList member is a list of virtual memory areas in the target * application space that have been allocated by the target. In most diff --git a/src/sim/power/MathExprPowerModel.py b/src/sim/power/MathExprPowerModel.py index 755b3953ee..baa5b0dae6 100644 --- a/src/sim/power/MathExprPowerModel.py +++ b/src/sim/power/MathExprPowerModel.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject -from m5.params import * from m5.objects.PowerModelState import PowerModelState +from m5.params import * +from m5.SimObject import SimObject + # Represents a power model for a simobj class MathExprPowerModel(PowerModelState): diff --git a/src/sim/power/PowerModel.py b/src/sim/power/PowerModel.py index 8dba29795d..df09672966 100644 --- a/src/sim/power/PowerModel.py +++ b/src/sim/power/PowerModel.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import * from m5.params import * from m5.proxy import Parent +from m5.SimObject import * + # Enum for a type of power model class PMType(Enum): diff --git a/src/sim/power/PowerModelState.py b/src/sim/power/PowerModelState.py index 081cd652d2..2a427242fa 100644 --- a/src/sim/power/PowerModelState.py +++ b/src/sim/power/PowerModelState.py @@ -33,8 +33,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import * from m5.params import * +from m5.SimObject import * + # Represents a power model for a simobj class PowerModelState(SimObject): diff --git a/src/sim/power/ThermalDomain.py b/src/sim/power/ThermalDomain.py index ddb8d4455b..eace9c101d 100644 --- a/src/sim/power/ThermalDomain.py +++ b/src/sim/power/ThermalDomain.py @@ -33,8 +33,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import * from m5.params import * +from m5.SimObject import * + # Represents a group of simobj which produce heat class ThermalDomain(SimObject): diff --git a/src/sim/power/ThermalModel.py b/src/sim/power/ThermalModel.py index a3d4a804cc..823a72ea4f 100644 --- a/src/sim/power/ThermalModel.py +++ b/src/sim/power/ThermalModel.py @@ -33,11 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import * -from m5.objects.ClockedObject import ClockedObject - -from m5.params import * from m5.objects import ThermalDomain +from m5.objects.ClockedObject import ClockedObject +from m5.params import * +from m5.SimObject import * # Represents a thermal node @@ -74,7 +73,7 @@ class ThermalCapacitor(SimObject): # Represents a fixed temperature node (ie. air) -class ThermalReference(SimObject, object): +class ThermalReference(SimObject): type = "ThermalReference" cxx_header = "sim/power/thermal_model.hh" cxx_class = "gem5::ThermalReference" diff --git a/src/sim/probe/Probe.py b/src/sim/probe/Probe.py index 006149a0b9..38664213fb 100644 --- a/src/sim/probe/Probe.py +++ b/src/sim/probe/Probe.py @@ -35,9 +35,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject from m5.params import * from m5.proxy import * +from m5.SimObject import SimObject class ProbeListenerObject(SimObject): diff --git a/src/sim/probe/probe.hh b/src/sim/probe/probe.hh index 3dd428effd..8d5366670a 100644 --- a/src/sim/probe/probe.hh +++ b/src/sim/probe/probe.hh @@ -1,5 +1,6 @@ /* * Copyright (c) 2013 ARM Limited + * Copyright (c) 2022-2023 The University of Edinburgh * All rights reserved * * The license below extends only to copyright in the software and shall @@ -317,6 +318,47 @@ class ProbePointArg : public ProbePoint } }; + +/** + * ProbeListenerArgFunc generates a listener for the class of Arg and + * a lambda callback function that is called by the notify. + * + * Note that the function is passed as lambda function on construction + * Example: + * ProbeListenerArgFunc (myobj->getProbeManager(), + * "MyProbePointName", [this](const MyArg &arg) + * { my_own_func(arg, xyz...); // do something with arg + * })); + */ +template +class ProbeListenerArgFunc : public ProbeListenerArgBase +{ + typedef std::function NotifyFunction; + private: + NotifyFunction function; + + public: + /** + * @param obj the class of type Tcontaining the method to call on notify. + * @param pm A probe manager that is not part of the obj + * @param name the name of the ProbePoint to add this listener to. + * @param func a pointer to the function on obj (called on notify). + */ + ProbeListenerArgFunc(ProbeManager *pm, const std::string &name, + const NotifyFunction &func) + : ProbeListenerArgBase(pm, name), + function(func) + {} + + /** + * @brief called when the ProbePoint calls notify. This is a shim through + * to the function passed during construction. + * @param val the argument value to pass. + */ + void notify(const Arg &val) override { function(val); } +}; + + } // namespace gem5 #endif//__SIM_PROBE_PROBE_HH__ diff --git a/src/sim/process.cc b/src/sim/process.cc index a348b450b0..f47dbd59c6 100644 --- a/src/sim/process.cc +++ b/src/sim/process.cc @@ -387,7 +387,7 @@ Process::unserialize(CheckpointIn &cp) { memState->unserialize(cp); pTable->unserialize(cp); - fds->unserialize(cp); + fds->unserialize(cp, this); /** * Checkpoints for pipes, device drivers or sockets currently diff --git a/src/sim/pseudo_inst.cc b/src/sim/pseudo_inst.cc index 55e44c7adc..29caba6661 100644 --- a/src/sim/pseudo_inst.cc +++ b/src/sim/pseudo_inst.cc @@ -244,9 +244,10 @@ loadsymbol(ThreadContext *tc) continue; if (!tc->getSystemPtr()->workload->insertSymbol( - { loader::Symbol::Binding::Global, symbol, addr })) { - continue; - } + { loader::Symbol::Binding::Global, + loader::Symbol::SymbolType::Function, symbol, addr })) { + continue; + } DPRINTF(Loader, "Loaded symbol: %s @ %#llx\n", symbol, addr); @@ -270,9 +271,13 @@ addsymbol(ThreadContext *tc, Addr addr, Addr symbolAddr) DPRINTF(Loader, "Loaded symbol: %s @ %#llx\n", symbol, addr); tc->getSystemPtr()->workload->insertSymbol( - { loader::Symbol::Binding::Global, symbol, addr }); + { loader::Symbol::Binding::Global, + loader::Symbol::SymbolType::Function, symbol, addr } + ); loader::debugSymbolTable.insert( - { loader::Symbol::Binding::Global, symbol, addr }); + { loader::Symbol::Binding::Global, + loader::Symbol::SymbolType::Function, symbol, addr } + ); } uint64_t diff --git a/src/sim/serialize.cc b/src/sim/serialize.cc index 2b1bd35f16..0f722a017e 100644 --- a/src/sim/serialize.cc +++ b/src/sim/serialize.cc @@ -145,8 +145,11 @@ CheckpointIn::setDir(const std::string &name) // appears to have a format placeholder in it. currentDirectory = (name.find("%") != std::string::npos) ? csprintf(name, curTick()) : name; - if (currentDirectory[currentDirectory.size() - 1] != '/') + auto isEmptyPath = currentDirectory.empty(); + auto endsWithSlash = !isEmptyPath && currentDirectory.back() == '/'; + if (!endsWithSlash) { currentDirectory += "/"; + } return currentDirectory; } diff --git a/src/sim/signal.hh b/src/sim/signal.hh index 3cb3f62c0d..233de07658 100644 --- a/src/sim/signal.hh +++ b/src/sim/signal.hh @@ -54,14 +54,16 @@ class SignalSinkPort : public Port OnChangeFunc _onChange; protected: + // if bypass_on_change is specified true, it will not call the _onChange + // function. Only _state will be updated if needed. void - set(const State &new_state) + set(const State &new_state, const bool bypass_on_change = false) { if (new_state == _state) return; _state = new_state; - if (_onChange) + if (!bypass_on_change && _onChange) _onChange(_state); } @@ -79,6 +81,8 @@ class SignalSinkPort : public Port _source = dynamic_cast *>(&peer); fatal_if(!_source, "Attempt to bind signal pin %s to " "incompatible pin %s", name(), peer.name()); + // The state of sink has to match the state of source. + _state = _source->state(); Port::bind(peer); } void @@ -94,18 +98,30 @@ class SignalSourcePort : public Port { private: SignalSinkPort *sink = nullptr; - State _state = {}; + State _state; public: - SignalSourcePort(const std::string &_name, PortID _id=InvalidPortID) : - Port(_name, _id) - {} + SignalSourcePort(const std::string &_name, PortID _id = InvalidPortID) + : Port(_name, _id) + { + _state = {}; + } + // Give an initial value to the _state instead of using a default value. + SignalSourcePort(const std::string &_name, PortID _id, + const State &init_state) + : SignalSourcePort(_name, _id) + { + _state = init_state; + } + + // if bypass_on_change is specified true, it will not call the _onChange + // function. Only _state will be updated if needed. void - set(const State &new_state) + set(const State &new_state, const bool bypass_on_change = false) { _state = new_state; - sink->set(new_state); + sink->set(new_state, bypass_on_change); } const State &state() const { return _state; } @@ -126,6 +142,6 @@ class SignalSourcePort : public Port } }; -} // namespace gem5 +} // namespace gem5 -#endif //__SIM_SIGNAL_HH__ +#endif //__SIM_SIGNAL_HH__ diff --git a/src/sim/simulate.cc b/src/sim/simulate.cc index abd2b1d391..6505f57904 100644 --- a/src/sim/simulate.cc +++ b/src/sim/simulate.cc @@ -50,6 +50,7 @@ #include "base/types.hh" #include "sim/async.hh" #include "sim/eventq.hh" +#include "sim/init_signals.hh" #include "sim/sim_events.hh" #include "sim/sim_exit.hh" #include "sim/stat_control.hh" @@ -187,6 +188,10 @@ GlobalSimLoopExitEvent *global_exit_event= nullptr; GlobalSimLoopExitEvent * simulate(Tick num_cycles) { + // install the sigint handler to catch ctrl-c and exit the sim loop cleanly + // Note: This should be done before initializing the threads + initSigInt(); + if (global_exit_event)//cleaning last global exit event global_exit_event->clean(); std::unique_ptr quantum_event; @@ -229,6 +234,9 @@ simulate(Tick num_cycles) Event *local_event = doSimLoop(mainEventQueue[0]); assert(local_event); + // Restore normal ctrl-c operation as soon as the event queue is done + restoreSigInt(); + inParallelMode = false; // locate the global exit event and return it to Python diff --git a/src/sim/syscall_emul.cc b/src/sim/syscall_emul.cc index c212d242fb..9794a4835e 100644 --- a/src/sim/syscall_emul.cc +++ b/src/sim/syscall_emul.cc @@ -959,7 +959,9 @@ chdirFunc(SyscallDesc *desc, ThreadContext *tc, VPtr<> pathname) tgt_cwd = path; } else { char buf[PATH_MAX]; - tgt_cwd = realpath((p->tgtCwd + "/" + path).c_str(), buf); + if (!realpath((p->tgtCwd + "/" + path).c_str(), buf)) + return -errno; + tgt_cwd = buf; } std::string host_cwd = p->checkPathRedirect(tgt_cwd); diff --git a/src/sim/syscall_emul.hh b/src/sim/syscall_emul.hh index b4550dd86b..97749f325d 100644 --- a/src/sim/syscall_emul.hh +++ b/src/sim/syscall_emul.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013, 2015, 2019-2021 Arm Limited + * Copyright (c) 2012-2013, 2015, 2019-2021, 2023 Arm Limited * Copyright (c) 2015 Advanced Micro Devices, Inc. * All rights reserved * @@ -866,7 +866,7 @@ openatFunc(SyscallDesc *desc, ThreadContext *tc, int sim_fd = -1; std::string used_path; std::vector special_paths = - { "/proc/meminfo/", "/system/", "/platform/", "/etc/passwd", + { "/proc/meminfo", "/system/", "/platform/", "/etc/passwd", "/proc/self/maps", "/dev/urandom", "/sys/devices/system/cpu/online" }; for (auto entry : special_paths) { @@ -1375,7 +1375,7 @@ statFunc(SyscallDesc *desc, ThreadContext *tc, template SyscallReturn newfstatatFunc(SyscallDesc *desc, ThreadContext *tc, int dirfd, - VPtr<> pathname, VPtr tgt_stat, + VPtr<> pathname, VPtr tgt_stat, int flags) { std::string path; @@ -1405,7 +1405,7 @@ newfstatatFunc(SyscallDesc *desc, ThreadContext *tc, int dirfd, if (result < 0) return -errno; - copyOutStatBuf(tgt_stat, &host_buf); + copyOutStat64Buf(tgt_stat, &host_buf); return 0; } @@ -2409,7 +2409,7 @@ tgkillFunc(SyscallDesc *desc, ThreadContext *tc, int tgid, int tid, int sig) } } - if (sig != 0 || sig != OS::TGT_SIGABRT) + if (sig != 0 && sig != OS::TGT_SIGABRT) return -EINVAL; if (tgt_proc == nullptr) diff --git a/src/sim/system.hh b/src/sim/system.hh index d2725c32a9..bb64f639b5 100644 --- a/src/sim/system.hh +++ b/src/sim/system.hh @@ -305,7 +305,7 @@ class System : public SimObject, public PCEventScope /** * Get the cache line size of the system. */ - unsigned int cacheLineSize() const { return _cacheLineSize; } + Addr cacheLineSize() const { return _cacheLineSize; } Threads threads; @@ -405,7 +405,7 @@ class System : public SimObject, public PCEventScope enums::MemoryMode memoryMode; - const unsigned int _cacheLineSize; + const Addr _cacheLineSize; uint64_t workItemsBegin = 0; uint64_t workItemsEnd = 0; diff --git a/src/sim/workload.hh b/src/sim/workload.hh index 10129379e0..727a283fd4 100644 --- a/src/sim/workload.hh +++ b/src/sim/workload.hh @@ -141,7 +141,7 @@ class Workload : public SimObject if (it == symtab.end()) return nullptr; - return new T(system, desc, fixFuncEventAddr(it->address), + return new T(system, desc, fixFuncEventAddr(it->address()), std::forward(args)...); } diff --git a/src/systemc/Kconfig b/src/systemc/Kconfig new file mode 100644 index 0000000000..3dde45d8da --- /dev/null +++ b/src/systemc/Kconfig @@ -0,0 +1,32 @@ +# Copyright 2022 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +config HAVE_SYSTEMC + def_bool "$(HAVE_SYSTEMC)" + +config USE_SYSTEMC + depends on HAVE_SYSTEMC + bool "Enable SystemC API support" + default y diff --git a/src/systemc/SConscript b/src/systemc/SConscript index 06bdb50456..4b9892a2f0 100644 --- a/src/systemc/SConscript +++ b/src/systemc/SConscript @@ -30,6 +30,9 @@ if not env['CONF']['USE_SYSTEMC']: env.UseSystemcCheck(warn=True) -env.Append(CPPPATH=Dir('ext')) +systemc_home = Dir('#/src/systemc/ext/systemc_home') +env['ENV']['SYSTEMC_HOME'] = systemc_home.abspath + +env.Append(CPPPATH=[systemc_home.Dir('include'),Dir('ext')]) SimObject('Tlm.py', sim_objects=[]) diff --git a/src/systemc/SConsopts b/src/systemc/SConsopts index e8702882e5..e7d9c04fd1 100644 --- a/src/systemc/SConsopts +++ b/src/systemc/SConsopts @@ -44,7 +44,4 @@ def use_systemc_check(env, warn=False): main.AddMethod(use_systemc_check, 'UseSystemcCheck') -main['CONF']['USE_SYSTEMC'] = main.UseSystemcCheck() - -sticky_vars.Add(BoolVariable('USE_SYSTEMC', 'Enable SystemC API support', - main.UseSystemcCheck())) +main['CONF']['HAVE_SYSTEMC'] = main.UseSystemcCheck() diff --git a/src/systemc/Tlm.py b/src/systemc/Tlm.py index 5b43a3b557..da4e4763b4 100644 --- a/src/systemc/Tlm.py +++ b/src/systemc/Tlm.py @@ -23,7 +23,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import Port, VectorPort +from m5.params import ( + Port, + VectorPort, +) def TLM_TARGET_ROLE(width): diff --git a/src/systemc/core/SystemC.py b/src/systemc/core/SystemC.py index f1f87a0583..75f59db597 100644 --- a/src/systemc/core/SystemC.py +++ b/src/systemc/core/SystemC.py @@ -23,7 +23,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.SimObject import SimObject, cxxMethod +from m5.SimObject import ( + SimObject, + cxxMethod, +) + # This class represents the systemc kernel. There should be exactly one in the # simulation. It receives gem5 SimObject lifecycle callbacks (init, regStats, diff --git a/src/systemc/ext/core/sc_port.hh b/src/systemc/ext/core/sc_port.hh index 796950e29b..bd57553559 100644 --- a/src/systemc/ext/core/sc_port.hh +++ b/src/systemc/ext/core/sc_port.hh @@ -114,6 +114,10 @@ class sc_port_base : public sc_object virtual sc_port_policy _portPolicy() const = 0; }; +// The overloaded virtual is intended in SystemC, so we'll disable the warning. +// Please check section 9.3 of SystemC 2.3.1 release note for more details. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverloaded-virtual" template class sc_port_b : public sc_port_base { @@ -244,6 +248,7 @@ class sc_port_b : public sc_port_base sc_port_b(const sc_port_b &) {} sc_port_b &operator = (const sc_port_b &) { return *this; } }; +#pragma GCC diagnostic pop template class sc_port : public sc_port_b diff --git a/src/systemc/python/systemc.py b/src/systemc/python/systemc.py index da189ffb29..d6c90534dc 100644 --- a/src/systemc/python/systemc.py +++ b/src/systemc/python/systemc.py @@ -24,13 +24,15 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import _m5.systemc - -from _m5.systemc import sc_main -from _m5.systemc import sc_time -from _m5.systemc import sc_main_result_code, sc_main_result_str +from _m5.systemc import ( + sc_main, + sc_main_result_code, + sc_main_result_str, + sc_time, +) -class ScMainResult(object): +class ScMainResult: def __init__(self, code, message): self.code = code self.message = message diff --git a/src/systemc/python/tlm.py b/src/systemc/python/tlm.py index 7b342811f6..e5ec7426f0 100644 --- a/src/systemc/python/tlm.py +++ b/src/systemc/python/tlm.py @@ -24,7 +24,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import _m5.systemc - from _m5.systemc import tlm_global_quantum diff --git a/src/systemc/tests/config.py b/src/systemc/tests/config.py index 1c2e021830..0be50d12c9 100755 --- a/src/systemc/tests/config.py +++ b/src/systemc/tests/config.py @@ -24,12 +24,15 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import argparse -import m5 import os import re import sys -from m5.objects import SystemC_Kernel, Root +import m5 +from m5.objects import ( + Root, + SystemC_Kernel, +) # pylint:disable=unused-variable diff --git a/src/systemc/tests/verify.py b/src/systemc/tests/verify.py index c0e072e3c2..acd9cb15e8 100755 --- a/src/systemc/tests/verify.py +++ b/src/systemc/tests/verify.py @@ -508,7 +508,7 @@ class VerifyPhase(TestPhaseBase): missing = [] log_file = ".".join([test.name, "log"]) log_path = gd.entry(log_file) - simout_path = os.path.join(out_dir, "simout") + simout_path = os.path.join(out_dir, "simout.txt") if not os.path.exists(simout_path): missing.append("log output") elif log_path: diff --git a/src/systemc/tlm_bridge/TlmBridge.py b/src/systemc/tlm_bridge/TlmBridge.py index 546b4c0790..bc0491132e 100644 --- a/src/systemc/tlm_bridge/TlmBridge.py +++ b/src/systemc/tlm_bridge/TlmBridge.py @@ -24,11 +24,13 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from m5.objects.SystemC import SystemC_ScModule +from m5.objects.Tlm import ( + TlmInitiatorSocket, + TlmTargetSocket, +) from m5.params import * from m5.proxy import * -from m5.objects.Tlm import TlmTargetSocket, TlmInitiatorSocket - class Gem5ToTlmBridgeBase(SystemC_ScModule): type = "Gem5ToTlmBridgeBase" diff --git a/src/systemc/tlm_bridge/gem5_to_tlm.cc b/src/systemc/tlm_bridge/gem5_to_tlm.cc index 515975224e..461be11051 100644 --- a/src/systemc/tlm_bridge/gem5_to_tlm.cc +++ b/src/systemc/tlm_bridge/gem5_to_tlm.cc @@ -152,6 +152,7 @@ packet2payload(PacketPtr packet) trans->acquire(); trans->set_address(packet->getAddr()); + trans->set_response_status(tlm::TLM_INCOMPLETE_RESPONSE); /* Check if this transaction was allocated by mm */ sc_assert(trans->has_mm()); @@ -480,7 +481,8 @@ Gem5ToTlmBridge::recvRespRetry() tlm::tlm_generic_payload *trans = blockingResponse; blockingResponse = nullptr; - PacketPtr packet = packetMap[blockingResponse]; + + PacketPtr packet = packetMap[trans]; sc_assert(packet); bool need_retry = !bridgeResponsePort.sendTimingResp(packet); diff --git a/src/systemc/tlm_bridge/tlm_to_gem5.cc b/src/systemc/tlm_bridge/tlm_to_gem5.cc index c02efe7437..036ca738ca 100644 --- a/src/systemc/tlm_bridge/tlm_to_gem5.cc +++ b/src/systemc/tlm_bridge/tlm_to_gem5.cc @@ -540,6 +540,7 @@ TlmToGem5Bridge::recvReqRetry() trans.release(); pendingRequest = nullptr; + pendingPacket = nullptr; } } diff --git a/tests/configs/dram-lowp.py b/tests/configs/dram-lowp.py deleted file mode 100644 index 25e7cc3087..0000000000 --- a/tests/configs/dram-lowp.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) 2017 ARM Limited -# All rights reserved. -# -# The license below extends only to copyright in the software and shall -# not be construed as granting a license to any other intellectual -# property including but not limited to intellectual property relating -# to a hardware implementation of the functionality of the software -# licensed hereunder. You may use the software subject to the license -# terms below provided that you ensure that this notice is replicated -# unmodified and in its entirety in all distributions of the software, -# modified or unmodified, in source code or in binary form. -# -# Copyright (c) 2015 Jason Lowe-Power -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# A wrapper around configs/dram/low_power_sweep.py - -# For some reason, this is implicitly needed by run.py -root = None - -import m5 - - -def run_test(root): - # Called from tests/run.py - - import sys - - argv = [ - sys.argv[0], - # Add a specific page policy and specify the number of ranks - f"-p{page_policy}", - "-r 2", - ] - - # Execute the script we are wrapping - run_config("configs/dram/low_power_sweep.py", argv=argv) diff --git a/tests/configs/gpu-randomtest-ruby.py b/tests/configs/gpu-randomtest-ruby.py deleted file mode 100644 index cfc65526e5..0000000000 --- a/tests/configs/gpu-randomtest-ruby.py +++ /dev/null @@ -1,164 +0,0 @@ -# -# Copyright (c) 2010-2015 Advanced Micro Devices, Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import m5 -from m5.objects import * -from m5.defines import buildEnv -from m5.util import addToPath -import os, argparse, sys - -m5.util.addToPath("../configs/") - -from ruby import Ruby -from common import Options - -parser = argparse.ArgumentParser() -Options.addCommonOptions(parser) - -# add the gpu specific options expected by the the gpu and gpu_RfO -parser.add_argument( - "-u", - "--num-compute-units", - type=int, - default=8, - help="number of compute units in the GPU", -) -parser.add_argument( - "--num-cp", - type=int, - default=0, - help="Number of GPU Command Processors (CP)", -) -parser.add_argument( - "--simds-per-cu", type=int, default=4, help="SIMD unitsper CU" -) -parser.add_argument( - "--wf-size", type=int, default=64, help="Wavefront size(in workitems)" -) -parser.add_argument( - "--wfs-per-simd", - type=int, - default=10, - help="Number of WF slots per SIMD", -) - -# Add the ruby specific and protocol specific options -Ruby.define_options(parser) - -args = parser.parse_args() - -# -# Set the default cache size and associativity to be very small to encourage -# races between requests and writebacks. -# -args.l1d_size = "256B" -args.l1i_size = "256B" -args.l2_size = "512B" -args.l3_size = "1kB" -args.l1d_assoc = 2 -args.l1i_assoc = 2 -args.l2_assoc = 2 -args.l3_assoc = 2 -args.num_compute_units = 8 -args.num_sqc = 2 - -# Check to for the GPU_RfO protocol. Other GPU protocols are non-SC and will -# not work with the Ruby random tester. -assert buildEnv["PROTOCOL"] == "GPU_RfO" - -# -# create the tester and system, including ruby -# -tester = RubyTester( - check_flush=False, - checks_to_complete=100, - wakeup_frequency=10, - num_cpus=args.num_cpus, -) - -# We set the testers as cpu for ruby to find the correct clock domains -# for the L1 Objects. -system = System(cpu=tester) - -# Dummy voltage domain for all our clock domains -system.voltage_domain = VoltageDomain(voltage=args.sys_voltage) -system.clk_domain = SrcClockDomain( - clock="1GHz", voltage_domain=system.voltage_domain -) - -system.mem_ranges = AddrRange("256MB") - -# the ruby tester reuses num_cpus to specify the -# number of cpu ports connected to the tester object, which -# is stored in system.cpu. because there is only ever one -# tester object, num_cpus is not necessarily equal to the -# size of system.cpu -cpu_list = [system.cpu] * args.num_cpus -Ruby.create_system(args, False, system, cpus=cpu_list) - -# Create a separate clock domain for Ruby -system.ruby.clk_domain = SrcClockDomain( - clock="1GHz", voltage_domain=system.voltage_domain -) - -tester.num_cpus = len(system.ruby._cpu_ports) - -# -# The tester is most effective when randomization is turned on and -# artifical delay is randomly inserted on messages -# -system.ruby.randomization = True - -for ruby_port in system.ruby._cpu_ports: - # - # Tie the ruby tester ports to the ruby cpu read and write ports - # - if ruby_port.support_data_reqs and ruby_port.support_inst_reqs: - tester.cpuInstDataPort = ruby_port.in_ports - elif ruby_port.support_data_reqs: - tester.cpuDataPort = ruby_port.in_ports - elif ruby_port.support_inst_reqs: - tester.cpuInstPort = ruby_port.in_ports - - # Do not automatically retry stalled Ruby requests - ruby_port.no_retry_on_stall = True - - # - # Tell the sequencer this is the ruby tester so that it - # copies the subblock back to the checker - # - ruby_port.using_ruby_tester = True - -# ----------------------- -# run simulation -# ----------------------- - -root = Root(full_system=False, system=system) -root.system.mem_mode = "timing" diff --git a/tests/configs/gpu-ruby.py b/tests/configs/gpu-ruby.py deleted file mode 100644 index 7606168a98..0000000000 --- a/tests/configs/gpu-ruby.py +++ /dev/null @@ -1,433 +0,0 @@ -# -# Copyright (c) 2015 Advanced Micro Devices, Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import m5 -from m5.objects import * -from m5.defines import buildEnv -from m5.util import addToPath -import os, argparse, sys, math, glob - -m5.util.addToPath("../configs/") - -from ruby import Ruby -from common import Options -from common import GPUTLBOptions, GPUTLBConfig - - -def run_test(root): - """gpu test requires a specialized run_test implementation to set up the - mmio space.""" - - # instantiate configuration - m5.instantiate() - - # Now that the system has been constructed, setup the mmio space - root.system.cpu[0].workload[0].map(0x10000000, 0x200000000, 4096) - - # simulate until program terminates - exit_event = m5.simulate(maxtick) - print("Exiting @ tick", m5.curTick(), "because", exit_event.getCause()) - - -parser = argparse.ArgumentParser() -Options.addCommonOptions(parser) -Options.addSEOptions(parser) - -parser.add_argument( - "-k", - "--kernel-files", - help="file(s) containing GPU kernel code (colon separated)", -) -parser.add_argument( - "-u", - "--num-compute-units", - type=int, - default=2, - help="number of GPU compute units", -), -parser.add_argument( - "--num-cp", - type=int, - default=0, - help="Number of GPU Command Processors (CP)", -) -parser.add_argument( - "--simds-per-cu", type=int, default=4, help="SIMD unitsper CU" -) -parser.add_argument( - "--cu-per-sqc", - type=int, - default=4, - help="number of CUssharing an SQC (icache, and thus icache TLB)", -) -parser.add_argument( - "--wf-size", type=int, default=64, help="Wavefront size(in workitems)" -) -parser.add_argument( - "--wfs-per-simd", - type=int, - default=8, - help="Number of WF slots per SIMD", -) -parser.add_argument( - "--sp-bypass-path-length", - type=int, - default=4, - help="Number of stages of bypass path in vector ALU for Single " - "Precision ops", -) -parser.add_argument( - "--dp-bypass-path-length", - type=int, - default=4, - help="Number of stages of bypass path in vector ALU for Double " - "Precision ops", -) -parser.add_argument( - "--issue-period", - type=int, - default=4, - help="Number of cycles per vector instruction issue period", -) -parser.add_argument( - "--glbmem-wr-bus-width", - type=int, - default=32, - help="VGPR to Coalescer (Global Memory) data bus width in bytes", -) -parser.add_argument( - "--glbmem-rd-bus-width", - type=int, - default=32, - help="Coalescer to VGPR (Global Memory) data bus width in bytes", -) -parser.add_argument( - "--shr-mem-pipes-per-cu", - type=int, - default=1, - help="Number of Shared Memory pipelines per CU", -) -parser.add_argument( - "--glb-mem-pipes-per-cu", - type=int, - default=1, - help="Number of Global Memory pipelines per CU", -) -parser.add_argument( - "--vreg-file-size", - type=int, - default=2048, - help="number of physical vector registers per SIMD", -) -parser.add_argument( - "--bw-scalor", - type=int, - default=0, - help="bandwidth scalor for scalability analysis", -) -parser.add_argument("--CPUClock", type=str, default="2GHz", help="CPU clock") -parser.add_argument("--GPUClock", type=str, default="1GHz", help="GPU clock") -parser.add_argument( - "--cpu-voltage", - action="store", - type=str, - default="1.0V", - help="""CPU voltage domain""", -) -parser.add_argument( - "--gpu-voltage", - action="store", - type=str, - default="1.0V", - help="""CPU voltage domain""", -) -parser.add_argument( - "--CUExecPolicy", - type=str, - default="OLDEST-FIRST", - help="WF exec policy (OLDEST-FIRST, ROUND-ROBIN)", -) -parser.add_argument( - "--xact-cas-mode", - action="store_true", - help="enable load_compare mode (transactional CAS)", -) -parser.add_argument( - "--SegFaultDebug", - action="store_true", - help="checks for GPU seg fault before TLB access", -) -parser.add_argument( - "--LocalMemBarrier", - action="store_true", - help="Barrier does not wait for writethroughs to complete", -) -parser.add_argument( - "--countPages", - action="store_true", - help="Count Page Accesses and output in per-CU output files", -) -parser.add_argument("--TLB-prefetch", type=int, help="prefetch depth forTLBs") -parser.add_argument( - "--pf-type", - type=str, - help="type of prefetch: PF_CU, PF_WF, PF_PHASE, PF_STRIDE", -) -parser.add_argument("--pf-stride", type=int, help="set prefetch stride") -parser.add_argument( - "--numLdsBanks", - type=int, - default=32, - help="number of physical banks per LDS module", -) -parser.add_argument( - "--ldsBankConflictPenalty", - type=int, - default=1, - help="number of cycles per LDS bank conflict", -) - -# Add the ruby specific and protocol specific options -Ruby.define_options(parser) - -GPUTLBOptions.tlb_options(parser) - -args = parser.parse_args() - -# The GPU cache coherence protocols only work with the backing store -args.access_backing_store = True - -# Currently, the sqc (I-Cache of GPU) is shared by -# multiple compute units(CUs). The protocol works just fine -# even if sqc is not shared. Overriding this option here -# so that the user need not explicitly set this (assuming -# sharing sqc is the common usage) -n_cu = args.num_compute_units -num_sqc = int(math.ceil(float(n_cu) / args.cu_per_sqc)) -args.num_sqc = num_sqc # pass this to Ruby - -########################## Creating the GPU system ######################## -# shader is the GPU -shader = Shader( - n_wf=args.wfs_per_simd, - clk_domain=SrcClockDomain( - clock=args.GPUClock, - voltage_domain=VoltageDomain(voltage=args.gpu_voltage), - ), - timing=True, -) - -# GPU_RfO(Read For Ownership) implements SC/TSO memory model. -# Other GPU protocols implement release consistency at GPU side. -# So, all GPU protocols other than GPU_RfO should make their writes -# visible to the global memory and should read from global memory -# during kernal boundary. The pipeline initiates(or do not initiate) -# the acquire/release operation depending on this impl_kern_boundary_sync -# flag. This flag=true means pipeline initiates a acquire/release operation -# at kernel boundary. -if buildEnv["PROTOCOL"] == "GPU_RfO": - shader.impl_kern_boundary_sync = False -else: - shader.impl_kern_boundary_sync = True - -# Switching off per-lane TLB by default -per_lane = False -if args.TLB_config == "perLane": - per_lane = True - -# List of compute units; one GPU can have multiple compute units -compute_units = [] -for i in range(n_cu): - compute_units.append( - ComputeUnit( - cu_id=i, - perLaneTLB=per_lane, - num_SIMDs=args.simds_per_cu, - wfSize=args.wf_size, - spbypass_pipe_length=args.sp_bypass_path_length, - dpbypass_pipe_length=args.dp_bypass_path_length, - issue_period=args.issue_period, - coalescer_to_vrf_bus_width=args.glbmem_rd_bus_width, - vrf_to_coalescer_bus_width=args.glbmem_wr_bus_width, - num_global_mem_pipes=args.glb_mem_pipes_per_cu, - num_shared_mem_pipes=args.shr_mem_pipes_per_cu, - n_wf=args.wfs_per_simd, - execPolicy=args.CUExecPolicy, - xactCasMode=args.xact_cas_mode, - debugSegFault=args.SegFaultDebug, - functionalTLB=True, - localMemBarrier=args.LocalMemBarrier, - countPages=args.countPages, - localDataStore=LdsState( - banks=args.numLdsBanks, - bankConflictPenalty=args.ldsBankConflictPenalty, - ), - ) - ) - wavefronts = [] - vrfs = [] - for j in range(args.simds_per_cu): - for k in range(int(shader.n_wf)): - wavefronts.append(Wavefront(simdId=j, wf_slot_id=k)) - vrfs.append( - VectorRegisterFile( - simd_id=j, num_regs_per_simd=args.vreg_file_size - ) - ) - compute_units[-1].wavefronts = wavefronts - compute_units[-1].vector_register_file = vrfs - if args.TLB_prefetch: - compute_units[-1].prefetch_depth = args.TLB_prefetch - compute_units[-1].prefetch_prev_type = args.pf_type - - # attach the LDS and the CU to the bus (actually a Bridge) - compute_units[-1].ldsPort = compute_units[-1].ldsBus.slave - compute_units[-1].ldsBus.master = compute_units[-1].localDataStore.cuPort - -# Attach compute units to GPU -shader.CUs = compute_units - -# this is a uniprocessor only test, thus the shader is the second index in the -# list of "system.cpus" -args.num_cpus = 1 -shader_idx = 1 -cpu = TimingSimpleCPU(cpu_id=0) - -########################## Creating the GPU dispatcher ######################## -# Dispatcher dispatches work from host CPU to GPU -host_cpu = cpu -dispatcher = GpuDispatcher() - -# Currently does not test for command processors -cpu_list = [cpu] + [shader] + [dispatcher] - -system = System( - cpu=cpu_list, - mem_ranges=[AddrRange(args.mem_size)], - mem_mode="timing", - workload=SEWorkload(), -) - -# Dummy voltage domain for all our clock domains -system.voltage_domain = VoltageDomain(voltage=args.sys_voltage) -system.clk_domain = SrcClockDomain( - clock="1GHz", voltage_domain=system.voltage_domain -) - -# Create a seperate clock domain for components that should run at -# CPUs frequency -system.cpu[0].clk_domain = SrcClockDomain( - clock="2GHz", voltage_domain=system.voltage_domain -) - -# configure the TLB hierarchy -GPUTLBConfig.config_tlb_hierarchy(args, system, shader_idx) - -# create Ruby system -system.piobus = IOXBar( - width=32, response_latency=0, frontend_latency=0, forward_latency=0 -) -Ruby.create_system(args, None, system) - -# Create a separate clock for Ruby -system.ruby.clk_domain = SrcClockDomain( - clock=args.ruby_clock, voltage_domain=system.voltage_domain -) - -# create the interrupt controller -cpu.createInterruptController() - -# -# Tie the cpu cache ports to the ruby cpu ports and -# physmem, respectively -# -cpu.connectAllPorts( - system.ruby._cpu_ports[0].in_ports, - system.ruby._cpu_ports[0].in_ports, - system.ruby._cpu_ports[0].interrupt_out_port, -) -system.ruby._cpu_ports[0].mem_request_port = system.piobus.cpu_side_ports - -# attach CU ports to Ruby -# Because of the peculiarities of the CP core, you may have 1 CPU but 2 -# sequencers and thus 2 _cpu_ports created. Your GPUs shouldn't be -# hooked up until after the CP. To make this script generic, figure out -# the index as below, but note that this assumes there is one sequencer -# per compute unit and one sequencer per SQC for the math to work out -# correctly. -gpu_port_idx = ( - len(system.ruby._cpu_ports) - args.num_compute_units - args.num_sqc -) -gpu_port_idx = gpu_port_idx - args.num_cp * 2 - -wavefront_size = args.wf_size -for i in range(n_cu): - # The pipeline issues wavefront_size number of uncoalesced requests - # in one GPU issue cycle. Hence wavefront_size mem ports. - for j in range(wavefront_size): - system.cpu[shader_idx].CUs[i].memory_port[j] = system.ruby._cpu_ports[ - gpu_port_idx - ].slave[j] - gpu_port_idx += 1 - -for i in range(n_cu): - if i > 0 and not i % args.cu_per_sqc: - gpu_port_idx += 1 - system.cpu[shader_idx].CUs[i].sqc_port = system.ruby._cpu_ports[ - gpu_port_idx - ].slave -gpu_port_idx = gpu_port_idx + 1 - -# Current regression tests do not support the command processor -assert args.num_cp == 0 - -# connect dispatcher to the system.piobus -dispatcher.pio = system.piobus.mem_side_ports -dispatcher.dma = system.piobus.cpu_side_ports - -################# Connect the CPU and GPU via GPU Dispatcher ################### -# CPU rings the GPU doorbell to notify a pending task -# using this interface. -# And GPU uses this interface to notify the CPU of task completion -# The communcation happens through emulated driver. - -# Note this implicit setting of the cpu_pointer, shader_pointer and tlb array -# parameters must be after the explicit setting of the System cpu list -shader.cpu_pointer = host_cpu -dispatcher.cpu = host_cpu -dispatcher.shader_pointer = shader - -# ----------------------- -# run simulation -# ----------------------- - -root = Root(full_system=False, system=system) -m5.ticks.setGlobalFrequency("1THz") -root.system.mem_mode = "timing" diff --git a/tests/configs/memtest-filter.py b/tests/configs/memtest-filter.py deleted file mode 100644 index 1080853f7b..0000000000 --- a/tests/configs/memtest-filter.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import m5 -from m5.objects import * - -m5.util.addToPath("../configs/") -from common.Caches import * - -# MAX CORES IS 8 with the fals sharing method -nb_cores = 8 -cpus = [MemTest() for i in range(nb_cores)] - -# system simulated -system = System( - cpu=cpus, - physmem=SimpleMemory(), - membus=SystemXBar(width=16, snoop_filter=SnoopFilter()), -) -# Dummy voltage domain for all our clock domains -system.voltage_domain = VoltageDomain() -system.clk_domain = SrcClockDomain( - clock="1GHz", voltage_domain=system.voltage_domain -) - -# Create a seperate clock domain for components that should run at -# CPUs frequency -system.cpu_clk_domain = SrcClockDomain( - clock="2GHz", voltage_domain=system.voltage_domain -) - -system.toL2Bus = L2XBar( - clk_domain=system.cpu_clk_domain, snoop_filter=SnoopFilter() -) -system.l2c = L2Cache(clk_domain=system.cpu_clk_domain, size="64kB", assoc=8) -system.l2c.cpu_side = system.toL2Bus.mem_side_ports - -# connect l2c to membus -system.l2c.mem_side = system.membus.cpu_side_ports - -# add L1 caches -for cpu in cpus: - # All cpus are associated with cpu_clk_domain - cpu.clk_domain = system.cpu_clk_domain - cpu.l1c = L1Cache(size="32kB", assoc=4) - cpu.l1c.cpu_side = cpu.port - cpu.l1c.mem_side = system.toL2Bus.cpu_side_ports - -system.system_port = system.membus.cpu_side_ports - -# connect memory to membus -system.physmem.port = system.membus.mem_side_ports - - -# ----------------------- -# run simulation -# ----------------------- - -root = Root(full_system=False, system=system) -root.system.mem_mode = "timing" diff --git a/tests/configs/memtest-ruby.py b/tests/configs/memtest-ruby.py deleted file mode 100644 index dac165e288..0000000000 --- a/tests/configs/memtest-ruby.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# Copyright (c) 2010 Advanced Micro Devices, Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import m5 -from m5.objects import * -from m5.defines import buildEnv -from m5.util import addToPath -import os, argparse, sys - -m5.util.addToPath("../configs/") - -from ruby import Ruby -from common import Options - -parser = argparse.ArgumentParser() -Options.addCommonOptions(parser) - -# Add the ruby specific and protocol specific options -Ruby.define_options(parser) - -args = parser.parse_args() - -# -# Set the default cache size and associativity to be very small to encourage -# races between requests and writebacks. -# -args.l1d_size = "256B" -args.l1i_size = "256B" -args.l2_size = "512B" -args.l3_size = "1kB" -args.l1d_assoc = 2 -args.l1i_assoc = 2 -args.l2_assoc = 2 -args.l3_assoc = 2 -args.ports = 32 - -# MAX CORES IS 8 with the fals sharing method -nb_cores = 8 - -# ruby does not support atomic, functional, or uncacheable accesses -cpus = [ - MemTest( - percent_functional=50, percent_uncacheable=0, suppress_func_errors=True - ) - for i in range(nb_cores) -] - -# overwrite args.num_cpus with the nb_cores value -args.num_cpus = nb_cores - -# system simulated -system = System(cpu=cpus) -# Dummy voltage domain for all our clock domains -system.voltage_domain = VoltageDomain() -system.clk_domain = SrcClockDomain( - clock="1GHz", voltage_domain=system.voltage_domain -) - -# Create a seperate clock domain for components that should run at -# CPUs frequency -system.cpu_clk_domain = SrcClockDomain( - clock="2GHz", voltage_domain=system.voltage_domain -) - -# All cpus are associated with cpu_clk_domain -for cpu in cpus: - cpu.clk_domain = system.cpu_clk_domain - -system.mem_ranges = AddrRange("256MB") - -Ruby.create_system(args, False, system) - -# Create a separate clock domain for Ruby -system.ruby.clk_domain = SrcClockDomain( - clock=args.ruby_clock, voltage_domain=system.voltage_domain -) - -assert len(cpus) == len(system.ruby._cpu_ports) - -for (i, ruby_port) in enumerate(system.ruby._cpu_ports): - # - # Tie the cpu port to the ruby cpu ports and - # physmem, respectively - # - cpus[i].port = ruby_port.in_ports - - # - # Since the memtester is incredibly bursty, increase the deadlock - # threshold to 1 million cycles - # - ruby_port.deadlock_threshold = 1000000 - -# ----------------------- -# run simulation -# ----------------------- - -root = Root(full_system=False, system=system) -root.system.mem_mode = "timing" diff --git a/tests/configs/memtest.py b/tests/configs/memtest.py deleted file mode 100644 index 10f3fbe50d..0000000000 --- a/tests/configs/memtest.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import m5 -from m5.objects import * - -m5.util.addToPath("../configs/") -from common.Caches import * - -# MAX CORES IS 8 with the fals sharing method -nb_cores = 8 -cpus = [MemTest() for i in range(nb_cores)] - -# system simulated -system = System(cpu=cpus, physmem=SimpleMemory(), membus=SystemXBar()) -# Dummy voltage domain for all our clock domains -system.voltage_domain = VoltageDomain() -system.clk_domain = SrcClockDomain( - clock="1GHz", voltage_domain=system.voltage_domain -) - -# Create a seperate clock domain for components that should run at -# CPUs frequency -system.cpu_clk_domain = SrcClockDomain( - clock="2GHz", voltage_domain=system.voltage_domain -) - -system.toL2Bus = L2XBar(clk_domain=system.cpu_clk_domain) -system.l2c = L2Cache(clk_domain=system.cpu_clk_domain, size="64kB", assoc=8) -system.l2c.cpu_side = system.toL2Bus.mem_side_ports - -# connect l2c to membus -system.l2c.mem_side = system.membus.cpu_side_ports - -# add L1 caches -for cpu in cpus: - # All cpus are associated with cpu_clk_domain - cpu.clk_domain = system.cpu_clk_domain - cpu.l1c = L1Cache(size="32kB", assoc=4) - cpu.l1c.cpu_side = cpu.port - cpu.l1c.mem_side = system.toL2Bus.cpu_side_ports - -system.system_port = system.membus.cpu_side_ports - -# connect memory to membus -system.physmem.port = system.membus.mem_side_ports - - -# ----------------------- -# run simulation -# ----------------------- - -root = Root(full_system=False, system=system) -root.system.mem_mode = "timing" diff --git a/tests/configs/o3-timing-checker.py b/tests/configs/o3-timing-checker.py deleted file mode 100644 index 9b328ce9e8..0000000000 --- a/tests/configs/o3-timing-checker.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2013 ARM Limited -# All rights reserved. -# -# The license below extends only to copyright in the software and shall -# not be construed as granting a license to any other intellectual -# property including but not limited to intellectual property relating -# to a hardware implementation of the functionality of the software -# licensed hereunder. You may use the software subject to the license -# terms below provided that you ensure that this notice is replicated -# unmodified and in its entirety in all distributions of the software, -# modified or unmodified, in source code or in binary form. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from m5.objects import * -from base_config import * - -root = BaseSESystemUniprocessor( - mem_mode="timing", - mem_class=DDR3_1600_8x8, - cpu_class=DerivO3CPU, - checker=True, -).create_root() diff --git a/tests/configs/o3-timing-mp-ruby.py b/tests/configs/o3-timing-mp-ruby.py deleted file mode 100644 index 10725e36ad..0000000000 --- a/tests/configs/o3-timing-mp-ruby.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import m5 -from m5.objects import * - -nb_cores = 4 -cpus = [DerivO3CPU(cpu_id=i) for i in range(nb_cores)] - -import ruby_config - -ruby_memory = ruby_config.generate("TwoLevel_SplitL1UnifiedL2.rb", nb_cores) - -# system simulated -system = System( - cpu=cpus, - physmem=ruby_memory, - membus=SystemXBar(), - mem_mode="timing", - clk_domain=SrcClockDomain(clock="1GHz"), -) - -# Create a seperate clock domain for components that should run at -# CPUs frequency -system.cpu_clk_domain = SrcClockDomain(clock="2GHz") - -for cpu in cpus: - # create the interrupt controller - cpu.createInterruptController() - cpu.connectBus(system.membus) - # All cpus are associated with cpu_clk_domain - cpu.clk_domain = system.cpu_clk_domain - -# connect memory to membus -system.physmem.port = system.membus.mem_side_ports - -# Connect the system port for loading of binaries etc -system.system_port = system.membus.cpu_side_ports - -# ----------------------- -# run simulation -# ----------------------- - -root = Root(full_system=False, system=system) -root.system.mem_mode = "timing" diff --git a/tests/configs/o3-timing-mp.py b/tests/configs/o3-timing-mp.py deleted file mode 100644 index 9b58c9d416..0000000000 --- a/tests/configs/o3-timing-mp.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2013 ARM Limited -# All rights reserved. -# -# The license below extends only to copyright in the software and shall -# not be construed as granting a license to any other intellectual -# property including but not limited to intellectual property relating -# to a hardware implementation of the functionality of the software -# licensed hereunder. You may use the software subject to the license -# terms below provided that you ensure that this notice is replicated -# unmodified and in its entirety in all distributions of the software, -# modified or unmodified, in source code or in binary form. -# -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from m5.objects import * -from base_config import * - -nb_cores = 4 -root = BaseSESystem( - mem_mode="timing", - mem_class=DDR3_1600_8x8, - cpu_class=DerivO3CPU, - num_cpus=nb_cores, -).create_root() diff --git a/tests/configs/o3-timing-mt.py b/tests/configs/o3-timing-mt.py deleted file mode 100644 index 9fda80de12..0000000000 --- a/tests/configs/o3-timing-mt.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) 2013, 2015 ARM Limited -# All rights reserved. -# -# The license below extends only to copyright in the software and shall -# not be construed as granting a license to any other intellectual -# property including but not limited to intellectual property relating -# to a hardware implementation of the functionality of the software -# licensed hereunder. You may use the software subject to the license -# terms below provided that you ensure that this notice is replicated -# unmodified and in its entirety in all distributions of the software, -# modified or unmodified, in source code or in binary form. -# -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from m5.objects import * -from m5.defines import buildEnv -from base_config import * -from arm_generic import * -from common.cores.arm.O3_ARM_v7a import O3_ARM_v7a_3 -from gem5.isas import ISA -from gem5.runtime import get_runtime_isa - -# If we are running ARM regressions, use a more sensible CPU -# configuration. This makes the results more meaningful, and also -# increases the coverage of the regressions. -if get_runtime_isa() == ISA.ARM: - root = ArmSESystemUniprocessor( - mem_mode="timing", - mem_class=DDR3_1600_8x8, - cpu_class=O3_ARM_v7a_3, - num_threads=2, - ).create_root() -else: - root = BaseSESystemUniprocessor( - mem_mode="timing", - mem_class=DDR3_1600_8x8, - cpu_class=DerivO3CPU, - num_threads=2, - ).create_root() diff --git a/tests/configs/o3-timing.py b/tests/configs/o3-timing.py deleted file mode 100644 index 26efe466d4..0000000000 --- a/tests/configs/o3-timing.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2013 ARM Limited -# All rights reserved. -# -# The license below extends only to copyright in the software and shall -# not be construed as granting a license to any other intellectual -# property including but not limited to intellectual property relating -# to a hardware implementation of the functionality of the software -# licensed hereunder. You may use the software subject to the license -# terms below provided that you ensure that this notice is replicated -# unmodified and in its entirety in all distributions of the software, -# modified or unmodified, in source code or in binary form. -# -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from m5.objects import * -from m5.defines import buildEnv -from base_config import * -from arm_generic import * -from common.cores.arm.O3_ARM_v7a import O3_ARM_v7a_3 -from gem5.isas import ISA -from gem5.runtime import get_runtime_isa - -# If we are running ARM regressions, use a more sensible CPU -# configuration. This makes the results more meaningful, and also -# increases the coverage of the regressions. -if get_runtime_isa() == ISA.ARM: - root = ArmSESystemUniprocessor( - mem_mode="timing", mem_class=DDR3_1600_8x8, cpu_class=O3_ARM_v7a_3 - ).create_root() -else: - root = BaseSESystemUniprocessor( - mem_mode="timing", mem_class=DDR3_1600_8x8, cpu_class=DerivO3CPU - ).create_root() diff --git a/tests/configs/pc-simple-atomic.py b/tests/configs/pc-simple-atomic.py deleted file mode 100644 index ac2c3c06b8..0000000000 --- a/tests/configs/pc-simple-atomic.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2012 ARM Limited -# All rights reserved. -# -# The license below extends only to copyright in the software and shall -# not be construed as granting a license to any other intellectual -# property including but not limited to intellectual property relating -# to a hardware implementation of the functionality of the software -# licensed hereunder. You may use the software subject to the license -# terms below provided that you ensure that this notice is replicated -# unmodified and in its entirety in all distributions of the software, -# modified or unmodified, in source code or in binary form. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from m5.objects import * -from x86_generic import * - -root = LinuxX86FSSystemUniprocessor( - mem_mode="atomic", mem_class=SimpleMemory, cpu_class=AtomicSimpleCPU -).create_root() diff --git a/tests/configs/pc-simple-timing-ruby.py b/tests/configs/pc-simple-timing-ruby.py deleted file mode 100644 index d0458b49cd..0000000000 --- a/tests/configs/pc-simple-timing-ruby.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (c) 2012 Mark D. Hill and David A. Wood -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import m5, os, argparse, sys -from m5.objects import * - -m5.util.addToPath("../configs/") -from common.Benchmarks import SysConfig -from common import FSConfig, SysPaths -from ruby import Ruby -from common import Options - -# Add the ruby specific and protocol specific options -parser = argparse.ArgumentParser() -Options.addCommonOptions(parser) -Ruby.define_options(parser) -args = parser.parse_args() - -# Set the default cache size and associativity to be very small to encourage -# races between requests and writebacks. -args.l1d_size = "32kB" -args.l1i_size = "32kB" -args.l2_size = "4MB" -args.l1d_assoc = 2 -args.l1i_assoc = 2 -args.l2_assoc = 2 -args.num_cpus = 2 - -# the system -mdesc = SysConfig(disks=["linux-x86.img"]) -system = FSConfig.makeLinuxX86System( - "timing", args.num_cpus, mdesc=mdesc, Ruby=True -) -system.kernel = SysPaths.binary("x86_64-vmlinux-2.6.22.9") -# Dummy voltage domain for all our clock domains -system.voltage_domain = VoltageDomain(voltage=args.sys_voltage) - -system.kernel = FSConfig.binary("x86_64-vmlinux-2.6.22.9.smp") -system.clk_domain = SrcClockDomain( - clock="1GHz", voltage_domain=system.voltage_domain -) -system.cpu_clk_domain = SrcClockDomain( - clock="2GHz", voltage_domain=system.voltage_domain -) -system.cpu = [ - TimingSimpleCPU(cpu_id=i, clk_domain=system.cpu_clk_domain) - for i in range(args.num_cpus) -] - -Ruby.create_system(args, True, system, system.iobus, system._dma_ports) - -# Create a seperate clock domain for Ruby -system.ruby.clk_domain = SrcClockDomain( - clock=args.ruby_clock, voltage_domain=system.voltage_domain -) - -# Connect the ruby io port to the PIO bus, -# assuming that there is just one such port. -system.iobus.mem_side_ports = system.ruby._io_port.in_ports - -for (i, cpu) in enumerate(system.cpu): - # create the interrupt controller - cpu.createInterruptController() - # Tie the cpu ports to the correct ruby system ports - system.ruby._cpu_ports[i].connectCpuPorts(cpu) - -root = Root(full_system=True, system=system) -m5.ticks.setGlobalFrequency("1THz") diff --git a/tests/configs/pc-simple-timing.py b/tests/configs/pc-simple-timing.py deleted file mode 100644 index c095401381..0000000000 --- a/tests/configs/pc-simple-timing.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2012 ARM Limited -# All rights reserved. -# -# The license below extends only to copyright in the software and shall -# not be construed as granting a license to any other intellectual -# property including but not limited to intellectual property relating -# to a hardware implementation of the functionality of the software -# licensed hereunder. You may use the software subject to the license -# terms below provided that you ensure that this notice is replicated -# unmodified and in its entirety in all distributions of the software, -# modified or unmodified, in source code or in binary form. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from m5.objects import * -from x86_generic import * - -root = LinuxX86FSSystemUniprocessor( - mem_mode="timing", mem_class=DDR3_1600_8x8, cpu_class=TimingSimpleCPU -).create_root() diff --git a/tests/configs/rubytest-ruby.py b/tests/configs/rubytest-ruby.py deleted file mode 100644 index 9a382cce6e..0000000000 --- a/tests/configs/rubytest-ruby.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# Copyright (c) 2009 Advanced Micro Devices, Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import m5 -from m5.objects import * -from m5.defines import buildEnv -from m5.util import addToPath -import os, argparse, sys - -m5.util.addToPath("../configs/") - -from ruby import Ruby -from common import Options - -parser = argparse.ArgumentParser() -Options.addNoISAOptions(parser) - -# Add the ruby specific and protocol specific options -Ruby.define_options(parser) - -args = parser.parse_args() - -# -# Set the default cache size and associativity to be very small to encourage -# races between requests and writebacks. -# -args.l1d_size = "256B" -args.l1i_size = "256B" -args.l2_size = "512B" -args.l3_size = "1kB" -args.l1d_assoc = 2 -args.l1i_assoc = 2 -args.l2_assoc = 2 -args.l3_assoc = 2 -args.ports = 32 - -# Turn on flush check for the hammer protocol -check_flush = False -if buildEnv["PROTOCOL"] == "MOESI_hammer": - check_flush = True - -# -# create the tester and system, including ruby -# -tester = RubyTester( - check_flush=check_flush, - checks_to_complete=100, - wakeup_frequency=10, - num_cpus=args.num_cpus, -) - -# We set the testers as cpu for ruby to find the correct clock domains -# for the L1 Objects. -system = System(cpu=tester) - -# Dummy voltage domain for all our clock domains -system.voltage_domain = VoltageDomain(voltage=args.sys_voltage) -system.clk_domain = SrcClockDomain( - clock="1GHz", voltage_domain=system.voltage_domain -) - -system.mem_ranges = AddrRange("256MB") - -# the ruby tester reuses num_cpus to specify the -# number of cpu ports connected to the tester object, which -# is stored in system.cpu. because there is only ever one -# tester object, num_cpus is not necessarily equal to the -# size of system.cpu -cpu_list = [system.cpu] * args.num_cpus -Ruby.create_system(args, False, system, cpus=cpu_list) - -# Create a separate clock domain for Ruby -system.ruby.clk_domain = SrcClockDomain( - clock="1GHz", voltage_domain=system.voltage_domain -) - -assert args.num_cpus == len(system.ruby._cpu_ports) - -tester.num_cpus = len(system.ruby._cpu_ports) - -# -# The tester is most effective when randomization is turned on and -# artifical delay is randomly inserted on messages -# -system.ruby.randomization = True - -for ruby_port in system.ruby._cpu_ports: - # - # Tie the ruby tester ports to the ruby cpu read and write ports - # - if ruby_port.support_data_reqs and ruby_port.support_inst_reqs: - tester.cpuInstDataPort = ruby_port.in_ports - elif ruby_port.support_data_reqs: - tester.cpuDataPort = ruby_port.in_ports - elif ruby_port.support_inst_reqs: - tester.cpuInstPort = ruby_port.in_ports - - # Do not automatically retry stalled Ruby requests - ruby_port.no_retry_on_stall = True - - # - # Tell the sequencer this is the ruby tester so that it - # copies the subblock back to the checker - # - ruby_port.using_ruby_tester = True - -# ----------------------- -# run simulation -# ----------------------- - -root = Root(full_system=False, system=system) -root.system.mem_mode = "timing" diff --git a/tests/configs/simple-atomic-dummychecker.py b/tests/configs/simple-atomic-dummychecker.py deleted file mode 100644 index 7ec004765a..0000000000 --- a/tests/configs/simple-atomic-dummychecker.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2013 ARM Limited -# All rights reserved. -# -# The license below extends only to copyright in the software and shall -# not be construed as granting a license to any other intellectual -# property including but not limited to intellectual property relating -# to a hardware implementation of the functionality of the software -# licensed hereunder. You may use the software subject to the license -# terms below provided that you ensure that this notice is replicated -# unmodified and in its entirety in all distributions of the software, -# modified or unmodified, in source code or in binary form. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from m5.objects import * -from base_config import * - -root = BaseSESystemUniprocessor( - mem_mode="atomic", cpu_class=AtomicSimpleCPU, checker=True -).create_root() diff --git a/tests/configs/simple-atomic-mp.py b/tests/configs/simple-atomic-mp.py deleted file mode 100644 index 0d85b5af36..0000000000 --- a/tests/configs/simple-atomic-mp.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) 2013 ARM Limited -# All rights reserved. -# -# The license below extends only to copyright in the software and shall -# not be construed as granting a license to any other intellectual -# property including but not limited to intellectual property relating -# to a hardware implementation of the functionality of the software -# licensed hereunder. You may use the software subject to the license -# terms below provided that you ensure that this notice is replicated -# unmodified and in its entirety in all distributions of the software, -# modified or unmodified, in source code or in binary form. -# -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from m5.objects import * -from base_config import * - -nb_cores = 4 -root = BaseSESystem( - mem_mode="atomic", cpu_class=AtomicSimpleCPU, num_cpus=nb_cores -).create_root() diff --git a/tests/configs/simple-atomic.py b/tests/configs/simple-atomic.py deleted file mode 100644 index 6dd86ccf39..0000000000 --- a/tests/configs/simple-atomic.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2013 ARM Limited -# All rights reserved. -# -# The license below extends only to copyright in the software and shall -# not be construed as granting a license to any other intellectual -# property including but not limited to intellectual property relating -# to a hardware implementation of the functionality of the software -# licensed hereunder. You may use the software subject to the license -# terms below provided that you ensure that this notice is replicated -# unmodified and in its entirety in all distributions of the software, -# modified or unmodified, in source code or in binary form. -# -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from m5.objects import * -from base_config import * - -root = BaseSESystemUniprocessor( - mem_mode="atomic", cpu_class=AtomicSimpleCPU -).create_root() diff --git a/tests/configs/simple-timing-mp-ruby.py b/tests/configs/simple-timing-mp-ruby.py deleted file mode 100644 index 38488c409d..0000000000 --- a/tests/configs/simple-timing-mp-ruby.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import m5 -from m5.objects import * -from m5.defines import buildEnv -from m5.util import addToPath -import os, argparse, sys - -m5.util.addToPath("../configs/") - -from common import Options -from ruby import Ruby - -parser = argparse.ArgumentParser() -Options.addCommonOptions(parser) - -# Add the ruby specific and protocol specific options -Ruby.define_options(parser) - -args = parser.parse_args() - -# -# Set the default cache size and associativity to be very small to encourage -# races between requests and writebacks. -# -args.l1d_size = "256B" -args.l1i_size = "256B" -args.l2_size = "512B" -args.l3_size = "1kB" -args.l1d_assoc = 2 -args.l1i_assoc = 2 -args.l2_assoc = 2 -args.l3_assoc = 2 - -nb_cores = 4 -cpus = [TimingSimpleCPU(cpu_id=i) for i in range(nb_cores)] - -# overwrite the num_cpus to equal nb_cores -args.num_cpus = nb_cores - -# system simulated -system = System(cpu=cpus, clk_domain=SrcClockDomain(clock="1GHz")) - -# Create a seperate clock domain for components that should run at -# CPUs frequency -system.cpu.clk_domain = SrcClockDomain(clock="2GHz") - -Ruby.create_system(args, False, system) - -# Create a separate clock domain for Ruby -system.ruby.clk_domain = SrcClockDomain(clock=args.ruby_clock) - -assert args.num_cpus == len(system.ruby._cpu_ports) - -for (i, cpu) in enumerate(system.cpu): - # create the interrupt controller - cpu.createInterruptController() - - # - # Tie the cpu ports to the ruby cpu ports - # - cpu.connectAllPorts( - system.ruby._cpu_ports[i].in_ports, - system.ruby._cpu_ports[i].in_ports, - system.ruby._cpu_ports[i].interrupt_out_port, - ) - -# ----------------------- -# run simulation -# ----------------------- - -root = Root(full_system=False, system=system) -root.system.mem_mode = "timing" diff --git a/tests/configs/simple-timing-mp.py b/tests/configs/simple-timing-mp.py deleted file mode 100644 index 3988f4c2b9..0000000000 --- a/tests/configs/simple-timing-mp.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) 2013 ARM Limited -# All rights reserved. -# -# The license below extends only to copyright in the software and shall -# not be construed as granting a license to any other intellectual -# property including but not limited to intellectual property relating -# to a hardware implementation of the functionality of the software -# licensed hereunder. You may use the software subject to the license -# terms below provided that you ensure that this notice is replicated -# unmodified and in its entirety in all distributions of the software, -# modified or unmodified, in source code or in binary form. -# -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from m5.objects import * -from base_config import * - -nb_cores = 4 -root = BaseSESystem( - mem_mode="timing", cpu_class=TimingSimpleCPU, num_cpus=nb_cores -).create_root() diff --git a/tests/configs/simple-timing-ruby.py b/tests/configs/simple-timing-ruby.py deleted file mode 100644 index eb0f4e9ac7..0000000000 --- a/tests/configs/simple-timing-ruby.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import m5 -from m5.objects import * -from m5.defines import buildEnv -from m5.util import addToPath -import os, argparse, sys - -m5.util.addToPath("../configs/") - -from ruby import Ruby -from common import Options - -parser = argparse.ArgumentParser() -Options.addCommonOptions(parser) - -# Add the ruby specific and protocol specific options -Ruby.define_options(parser) - -args = parser.parse_args() - -# -# Set the default cache size and associativity to be very small to encourage -# races between requests and writebacks. -# -args.l1d_size = "256B" -args.l1i_size = "256B" -args.l2_size = "512B" -args.l3_size = "1kB" -args.l1d_assoc = 2 -args.l1i_assoc = 2 -args.l2_assoc = 2 -args.l3_assoc = 2 - -# this is a uniprocessor only test -args.num_cpus = 1 -cpu = TimingSimpleCPU(cpu_id=0) -system = System(cpu=cpu) - -# Dummy voltage domain for all our clock domains -system.voltage_domain = VoltageDomain(voltage=args.sys_voltage) -system.clk_domain = SrcClockDomain( - clock="1GHz", voltage_domain=system.voltage_domain -) - -# Create a seperate clock domain for components that should run at -# CPUs frequency -system.cpu.clk_domain = SrcClockDomain( - clock="2GHz", voltage_domain=system.voltage_domain -) - -system.mem_ranges = AddrRange("256MB") -Ruby.create_system(args, False, system) - -# Create a separate clock for Ruby -system.ruby.clk_domain = SrcClockDomain( - clock=args.ruby_clock, voltage_domain=system.voltage_domain -) - -assert len(system.ruby._cpu_ports) == 1 - -# create the interrupt controller -cpu.createInterruptController() - -# -# Tie the cpu cache ports to the ruby cpu ports and -# physmem, respectively -# -cpu.connectAllPorts( - system.ruby._cpu_ports[0].in_ports, - system.ruby._cpu_ports[0].in_ports, - system.ruby._cpu_ports[0].interrupt_out_port, -) - -# ----------------------- -# run simulation -# ----------------------- - -root = Root(full_system=False, system=system) -root.system.mem_mode = "timing" diff --git a/tests/configs/simple-timing.py b/tests/configs/simple-timing.py deleted file mode 100644 index bf3ced4463..0000000000 --- a/tests/configs/simple-timing.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2013 ARM Limited -# All rights reserved. -# -# The license below extends only to copyright in the software and shall -# not be construed as granting a license to any other intellectual -# property including but not limited to intellectual property relating -# to a hardware implementation of the functionality of the software -# licensed hereunder. You may use the software subject to the license -# terms below provided that you ensure that this notice is replicated -# unmodified and in its entirety in all distributions of the software, -# modified or unmodified, in source code or in binary form. -# -# Copyright (c) 2006-2007 The Regents of The University of Michigan -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from m5.objects import * -from base_config import * - -root = BaseSESystemUniprocessor( - mem_mode="timing", cpu_class=TimingSimpleCPU -).create_root() diff --git a/tests/configs/t1000-simple-atomic.py b/tests/configs/t1000-simple-atomic.py deleted file mode 100644 index 76d39327d2..0000000000 --- a/tests/configs/t1000-simple-atomic.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) 2007 The Regents of The University of Michigan -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import m5 -from m5.objects import * - -m5.util.addToPath("../configs/") -from common import FSConfig - -try: - system = FSConfig.makeSparcSystem("atomic") -except IOError as e: - skip_test(reason=str(e)) - -system.voltage_domain = VoltageDomain() -system.clk_domain = SrcClockDomain( - clock="1GHz", voltage_domain=system.voltage_domain -) -system.cpu_clk_domain = SrcClockDomain( - clock="1GHz", voltage_domain=system.voltage_domain -) -cpu = AtomicSimpleCPU(cpu_id=0, clk_domain=system.cpu_clk_domain) -system.cpu = cpu -# create the interrupt controller -cpu.createInterruptController() -cpu.connectBus(system.membus) - -# create the memory controllers and connect them, stick with -# the physmem name to avoid bumping all the reference stats -system.physmem = [SimpleMemory(range=r) for r in system.mem_ranges] -for i in range(len(system.physmem)): - system.physmem[i].port = system.membus.mem_side_ports - -root = Root(full_system=True, system=system) - -m5.ticks.setGlobalFrequency("2GHz") diff --git a/tests/configs/x86_generic.py b/tests/configs/x86_generic.py deleted file mode 100644 index 3c590860de..0000000000 --- a/tests/configs/x86_generic.py +++ /dev/null @@ -1,123 +0,0 @@ -# Copyright (c) 2012 ARM Limited -# All rights reserved. -# -# The license below extends only to copyright in the software and shall -# not be construed as granting a license to any other intellectual -# property including but not limited to intellectual property relating -# to a hardware implementation of the functionality of the software -# licensed hereunder. You may use the software subject to the license -# terms below provided that you ensure that this notice is replicated -# unmodified and in its entirety in all distributions of the software, -# modified or unmodified, in source code or in binary form. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from abc import ABCMeta, abstractmethod -import m5 -from m5.objects import * -from m5.proxy import * - -m5.util.addToPath("../configs/") -from common.Benchmarks import SysConfig -from common import FSConfig, SysPaths -from common.Caches import * -from base_config import * - - -class LinuxX86SystemBuilder(object): - """Mix-in that implements create_system. - - This mix-in is intended as a convenient way of adding an - X86-specific create_system method to a class deriving from one of - the generic base systems. - """ - - def __init__(self): - pass - - def create_system(self): - mdesc = SysConfig(disks=["linux-x86.img"]) - system = FSConfig.makeLinuxX86System( - self.mem_mode, numCPUs=self.num_cpus, mdesc=mdesc - ) - system.kernel = SysPaths.binary("x86_64-vmlinux-2.6.22.9") - - self.init_system(system) - return system - - -class LinuxX86FSSystem(LinuxX86SystemBuilder, BaseFSSystem): - """Basic X86 full system builder.""" - - def __init__(self, **kwargs): - """Initialize an X86 system that supports full system simulation. - - Note: Keyword arguments that are not listed below will be - passed to the BaseFSSystem. - - Keyword Arguments: - machine_type -- String describing the platform to simulate - """ - BaseSystem.__init__(self, **kwargs) - LinuxX86SystemBuilder.__init__(self) - - def create_caches_private(self, cpu): - cpu.addPrivateSplitL1Caches( - L1_ICache(size="32kB", assoc=1), - L1_DCache(size="32kB", assoc=4), - PageTableWalkerCache(), - PageTableWalkerCache(), - ) - - -class LinuxX86FSSystemUniprocessor( - LinuxX86SystemBuilder, BaseFSSystemUniprocessor -): - """Basic X86 full system builder for uniprocessor systems. - - Note: This class is a specialization of the X86FSSystem and is - only really needed to provide backwards compatibility for existing - test cases. - """ - - def __init__(self, **kwargs): - BaseFSSystemUniprocessor.__init__(self, **kwargs) - LinuxX86SystemBuilder.__init__(self) - - def create_caches_private(self, cpu): - cpu.addTwoLevelCacheHierarchy( - L1_ICache(size="32kB", assoc=1), - L1_DCache(size="32kB", assoc=4), - L2Cache(size="4MB", assoc=8), - PageTableWalkerCache(), - PageTableWalkerCache(), - ) - - -class LinuxX86FSSwitcheroo(LinuxX86SystemBuilder, BaseFSSwitcheroo): - """Uniprocessor X86 system prepared for CPU switching""" - - def __init__(self, **kwargs): - BaseFSSwitcheroo.__init__(self, **kwargs) - LinuxX86SystemBuilder.__init__(self) diff --git a/tests/compiler-tests.sh b/tests/deprecated/compiler-tests.sh similarity index 98% rename from tests/compiler-tests.sh rename to tests/deprecated/compiler-tests.sh index f5d4bb189f..e8da335de4 100755 --- a/tests/compiler-tests.sh +++ b/tests/deprecated/compiler-tests.sh @@ -18,7 +18,6 @@ images=("gcc-version-12" "gcc-version-10" "gcc-version-9" "gcc-version-8" - "gcc-version-7" "clang-version-14" "clang-version-13" "clang-version-12" @@ -27,7 +26,6 @@ images=("gcc-version-12" "clang-version-9" "clang-version-8" "clang-version-7" - "clang-version-6.0" # The following checks our support for Ubuntu 18.04, 20.04, and 22.04. "ubuntu-18.04_all-dependencies" "ubuntu-20.04_all-dependencies" @@ -58,6 +56,8 @@ builds=("ALL" "POWER" "RISCV" "SPARC" + "GCN3_X86" + "VEGA_X86" "X86" "X86_MI_example" "X86_MOESI_AMD_Base") @@ -73,7 +73,7 @@ opts=(".opt" builds_per_compiler=1 # Base URL of the gem5 testing images. -base_url="gcr.io/gem5-test" +base_url="ghcr.io/gem5" # Arguments passed into scons on every build target test. if [ $# -eq 0 ];then diff --git a/tests/jenkins/gem5art-tests.sh b/tests/deprecated/jenkins/gem5art-tests.sh similarity index 100% rename from tests/jenkins/gem5art-tests.sh rename to tests/deprecated/jenkins/gem5art-tests.sh diff --git a/tests/jenkins/presubmit-stage2.sh b/tests/deprecated/jenkins/presubmit-stage2.sh similarity index 100% rename from tests/jenkins/presubmit-stage2.sh rename to tests/deprecated/jenkins/presubmit-stage2.sh diff --git a/tests/jenkins/presubmit.cfg b/tests/deprecated/jenkins/presubmit.cfg similarity index 100% rename from tests/jenkins/presubmit.cfg rename to tests/deprecated/jenkins/presubmit.cfg diff --git a/tests/jenkins/presubmit.sh b/tests/deprecated/jenkins/presubmit.sh similarity index 95% rename from tests/jenkins/presubmit.sh rename to tests/deprecated/jenkins/presubmit.sh index 91eb95f81b..becf499d28 100755 --- a/tests/jenkins/presubmit.sh +++ b/tests/deprecated/jenkins/presubmit.sh @@ -37,8 +37,8 @@ set -e -DOCKER_IMAGE_ALL_DEP=gcr.io/gem5-test/ubuntu-22.04_all-dependencies:latest -DOCKER_IMAGE_CLANG_COMPILE=gcr.io/gem5-test/clang-version-14:latest +DOCKER_IMAGE_ALL_DEP=ghcr.io/gem5/ubuntu-22.04_all-dependencies:latest +DOCKER_IMAGE_CLANG_COMPILE=ghcr.io/gem5/clang-version-14:latest PRESUBMIT_STAGE2=tests/jenkins/presubmit-stage2.sh GEM5ART_TESTS=tests/jenkins/gem5art-tests.sh diff --git a/tests/nightly.sh b/tests/deprecated/nightly.sh similarity index 91% rename from tests/nightly.sh rename to tests/deprecated/nightly.sh index cea1ad0be9..53ad374c3c 100755 --- a/tests/nightly.sh +++ b/tests/deprecated/nightly.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2021 The Regents of the University of California +# Copyright (c) 2021-2023 The Regents of the University of California # All Rights Reserved. # # Redistribution and use in source and binary forms, with or without @@ -70,13 +70,13 @@ unit_test () { docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ "${gem5_root}" --memory="${docker_mem_limit}" --rm \ - gcr.io/gem5-test/ubuntu-22.04_all-dependencies:${tag} \ + ghcr.io/gem5/ubuntu-22.04_all-dependencies:${tag} \ scons build/ALL/unittests.${build} -j${compile_threads} \ --ignore-style } # Ensure we have the latest docker images. -docker pull gcr.io/gem5-test/ubuntu-22.04_all-dependencies:${tag} +docker pull ghcr.io/gem5/ubuntu-22.04_all-dependencies:${tag} # Run the unit tests. unit_test opt @@ -85,7 +85,7 @@ unit_test debug # Run the gem5 long tests. docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ "${gem5_root}"/tests --memory="${docker_mem_limit}" --rm \ - gcr.io/gem5-test/ubuntu-22.04_all-dependencies:${tag} \ + ghcr.io/gem5/ubuntu-22.04_all-dependencies:${tag} \ ./main.py run --length long -j${compile_threads} -t${run_threads} -vv # Unfortunately, due docker being unable run KVM, we do so separately. @@ -105,10 +105,10 @@ cd "${gem5_root}/tests" cd "${gem5_root}" # For the GPU tests we compile and run the GPU ISA inside a gcn-gpu container. -docker pull gcr.io/gem5-test/gcn-gpu:${tag} +docker pull ghcr.io/gem5/gcn-gpu:${tag} docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ "${gem5_root}" --memory="${docker_mem_limit}" \ - gcr.io/gem5-test/gcn-gpu:${tag} bash -c \ + ghcr.io/gem5/gcn-gpu:${tag} bash -c \ "scons build/${gpu_isa}/gem5.opt -j${compile_threads} --ignore-style \ || (rm -rf build && scons build/${gpu_isa}/gem5.opt \ -j${compile_threads} --ignore-style)" @@ -123,7 +123,7 @@ mkdir -p tests/testing-results # basic GPU functionality is working. docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ "${gem5_root}" --memory="${docker_mem_limit}" \ - gcr.io/gem5-test/gcn-gpu:${tag} build/${gpu_isa}/gem5.opt \ + ghcr.io/gem5/gcn-gpu:${tag} build/${gpu_isa}/gem5.opt \ configs/example/apu_se.py --reg-alloc-policy=dynamic -n3 -c square # get HeteroSync @@ -135,7 +135,7 @@ wget -qN http://dist.gem5.org/dist/develop/test-progs/heterosync/gcn3/allSyncPri # atomics are tested. docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ "${gem5_root}" --memory="${docker_mem_limit}" \ - gcr.io/gem5-test/gcn-gpu:${tag} build/${gpu_isa}/gem5.opt \ + ghcr.io/gem5/gcn-gpu:${tag} build/${gpu_isa}/gem5.opt \ configs/example/apu_se.py --reg-alloc-policy=dynamic -n3 -c \ allSyncPrims-1kernel --options="sleepMutex 10 16 4" @@ -146,7 +146,7 @@ docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ # atomics are tested. docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ "${gem5_root}" --memory="${docker_mem_limit}" \ - gcr.io/gem5-test/gcn-gpu:${tag} build/${gpu_isa}/gem5.opt \ + ghcr.io/gem5/gcn-gpu:${tag} build/${gpu_isa}/gem5.opt \ configs/example/apu_se.py --reg-alloc-policy=dynamic -n3 -c \ allSyncPrims-1kernel --options="lfTreeBarrUniq 10 16 4" @@ -157,10 +157,11 @@ build_and_run_SST () { docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ "${gem5_root}" --rm --memory="${docker_mem_limit}" \ - gcr.io/gem5-test/sst-env:${tag} bash -c "\ + ghcr.io/gem5/sst-env:${tag} bash -c "\ scons build/${isa}/libgem5_${variant}.so -j${compile_threads} \ --without-tcmalloc --duplicate-sources --ignore-style && \ cd ext/sst && \ +mv Makefile.linux Makefile && \ make clean; make -j ${compile_threads} && \ sst --add-lib-path=./ sst/example.py && \ cd -; @@ -172,7 +173,7 @@ build_and_run_systemc () { rm -rf "${gem5_root}/build/ARM" docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ "${gem5_root}" --memory="${docker_mem_limit}" --rm \ - gcr.io/gem5-test/ubuntu-22.04_min-dependencies:${tag} bash -c "\ + ghcr.io/gem5/ubuntu-22.04_min-dependencies:${tag} bash -c "\ scons -j${compile_threads} --ignore-style --duplicate-sources \ build/ARM/gem5.opt && \ scons --with-cxx-config --without-python --without-tcmalloc \ @@ -182,7 +183,7 @@ scons --with-cxx-config --without-python --without-tcmalloc \ docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ "${gem5_root}" --memory="${docker_mem_limit}" --rm \ - gcr.io/gem5-test/systemc-env:${tag} bash -c "\ + ghcr.io/gem5/systemc-env:${tag} bash -c "\ cd util/systemc/gem5_within_systemc && \ make -j${compile_threads} && \ ../../../build/ARM/gem5.opt ../../../configs/deprecated/example/se.py -c \ diff --git a/tests/weekly.sh b/tests/deprecated/weekly.sh similarity index 95% rename from tests/weekly.sh rename to tests/deprecated/weekly.sh index 2bfd33741d..17d68426a6 100755 --- a/tests/weekly.sh +++ b/tests/deprecated/weekly.sh @@ -72,7 +72,7 @@ fi # Run the gem5 very-long tests. docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ "${gem5_root}"/tests --memory="${docker_mem_limit}" --rm \ - gcr.io/gem5-test/ubuntu-22.04_all-dependencies:${tag} \ + ghcr.io/gem5/ubuntu-22.04_all-dependencies:${tag} \ ./main.py run --length very-long -j${threads} -t${run_threads} -vv mkdir -p tests/testing-results @@ -81,7 +81,7 @@ mkdir -p tests/testing-results # before pulling gem5 resources, make sure it doesn't exist already docker run -u $UID:$GID --rm --volume "${gem5_root}":"${gem5_root}" -w \ "${gem5_root}" --memory="${docker_mem_limit}" \ - gcr.io/gem5-test/gcn-gpu:${tag} bash -c \ + ghcr.io/gem5/gcn-gpu:${tag} bash -c \ "rm -rf ${gem5_root}/gem5-resources" # delete m5out, Pannotia datasets, and output files in case a failed regression @@ -127,7 +127,7 @@ cd "${gem5_root}" # avoid needing to set all of these, we instead build a docker for it, which # has all these variables pre-set in its Dockerfile # To avoid compiling gem5 multiple times, all GPU benchmarks will use this -docker pull gcr.io/gem5-test/gcn-gpu:${tag} +docker pull ghcr.io/gem5/gcn-gpu:${tag} docker build -t hacc-test-weekly ${gem5_root}/gem5-resources/src/gpu/halo-finder docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ @@ -296,10 +296,15 @@ docker run --rm -v ${gem5_root}:${gem5_root} -w ${gem5_root} -u $UID:$GID \ -c color_maxmin.gem5 --options="1k_128k.gr 0" # build FW -docker run --rm -v ${gem5_root}:${gem5_root} -w \ - ${gem5_root}/gem5-resources/src/gpu/pannotia/fw -u $UID:$GID \ +docker run --rm -v ${gem5_root}:${gem5_root} -w ${gem5_root} -u $UID:$GID \ + ${gem5_root}/gem5-resources/src/gpu/pannotia/fw \ --memory="${docker_mem_limit}" hacc-test-weekly bash -c \ - "export GEM5_PATH=${gem5_root} ; make gem5-fusion" + "export GEM5_PATH=${gem5_root} ; make default; make gem5-fusion" + +# create input mmap file for FW +docker run --rm -v ${gem5_root}:${gem5_root} -w ${gem5_root} -u $UID:$GID \ + --memory="${docker_mem_limit}" hacc-test-weekly bash -c\ + "./gem5-resources/src/gpu/pannotia/fw/bin/fw_hip ./gem5-resources/src/gpu/pannotia/fw/1k_128k.gr 1" # run FW (use same input dataset as BC for faster testing) docker run --rm -v ${gem5_root}:${gem5_root} -w ${gem5_root} -u $UID:$GID \ @@ -308,7 +313,7 @@ docker run --rm -v ${gem5_root}:${gem5_root} -w ${gem5_root} -u $UID:$GID \ ${gem5_root}/configs/example/apu_se.py -n3 --mem-size=8GB \ --reg-alloc-policy=dynamic \ --benchmark-root=${gem5_root}/gem5-resources/src/gpu/pannotia/fw/bin \ - -c fw_hip.gem5 --options="1k_128k.gr" + -c fw_hip.gem5 --options="1k_128k.gr 2" # build MIS docker run --rm -v ${gem5_root}:${gem5_root} -w \ @@ -414,23 +419,23 @@ rm -rf "${gem5_root}/build/ALL" docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ "${gem5_root}" --memory="${docker_mem_limit}" --rm \ - gcr.io/gem5-test/ubuntu-22.04_all-dependencies:${tag} \ + ghcr.io/gem5/ubuntu-22.04_all-dependencies:${tag} \ scons build/ALL/gem5.opt -j${threads} docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ "${gem5_root}" --memory="${docker_mem_limit}" --rm \ - gcr.io/gem5-test/ubuntu-22.04_all-dependencies:${tag} \ + ghcr.io/gem5/ubuntu-22.04_all-dependencies:${tag} \ ./build/ALL/gem5.opt \ configs/example/gem5_library/dramsys/arm-hello-dramsys.py docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ "${gem5_root}" --memory="${docker_mem_limit}" --rm \ - gcr.io/gem5-test/ubuntu-22.04_all-dependencies:${tag} \ + ghcr.io/gem5/ubuntu-22.04_all-dependencies:${tag} \ ./build/ALL/gem5.opt \ configs/example/gem5_library/dramsys/dramsys-traffic.py docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \ "${gem5_root}" --memory="${docker_mem_limit}" --rm \ - gcr.io/gem5-test/ubuntu-22.04_all-dependencies:${tag} \ + ghcr.io/gem5/ubuntu-22.04_all-dependencies:${tag} \ ./build/ALL/gem5.opt \ configs/example/dramsys.py diff --git a/tests/gem5/__init__.py b/tests/gem5/__init__.py index 0955469d08..0526afd962 100644 --- a/tests/gem5/__init__.py +++ b/tests/gem5/__init__.py @@ -24,8 +24,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import testlib.suite import testlib.fixture +import testlib.suite -from .suite import * from .fixture import * +from .suite import * diff --git a/tests/gem5/arm_boot_tests/README.md b/tests/gem5/arm_boot_tests/README.md new file mode 100644 index 0000000000..68d5de97f1 --- /dev/null +++ b/tests/gem5/arm_boot_tests/README.md @@ -0,0 +1,9 @@ +# Arm Boot Tests + +These tests run a series of Linux boots on the ARMBoard. +It varies the CPU type, number of CPUs, and memory used for each run. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/arm_boot_tests --length=[length] +``` diff --git a/tests/gem5/configs/arm_boot_exit_run.py b/tests/gem5/arm_boot_tests/configs/arm_boot_exit_run.py similarity index 96% rename from tests/gem5/configs/arm_boot_exit_run.py rename to tests/gem5/arm_boot_tests/configs/arm_boot_exit_run.py index a8ea6eeea7..61931d1a7e 100644 --- a/tests/gem5/configs/arm_boot_exit_run.py +++ b/tests/gem5/arm_boot_tests/configs/arm_boot_exit_run.py @@ -33,24 +33,27 @@ Characteristics * Runs exclusively on the ARM ISA with the classic caches """ -from gem5.isas import ISA -from m5.objects import ArmDefaultRelease -from gem5.utils.requires import requires -from gem5.resources.resource import Resource -from gem5.simulate.simulator import Simulator -from m5.objects import VExpress_GEM5_Foundation -from gem5.coherence_protocol import CoherenceProtocol -from gem5.components.boards.arm_board import ArmBoard -from gem5.components.processors.simple_processor import SimpleProcessor -from gem5.components.processors.cpu_types import ( - get_cpu_types_str_set, - get_cpu_type_from_str, - CPUTypes, -) - import argparse import importlib +from m5.objects import ( + ArmDefaultRelease, + VExpress_GEM5_Foundation, +) + +from gem5.coherence_protocol import CoherenceProtocol +from gem5.components.boards.arm_board import ArmBoard +from gem5.components.processors.cpu_types import ( + CPUTypes, + get_cpu_type_from_str, + get_cpu_types_str_set, +) +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires + parser = argparse.ArgumentParser( description="A script to run the ARM boot exit tests." ) @@ -199,15 +202,15 @@ board = ArmBoard( # Set the Full System workload. board.set_kernel_disk_workload( - kernel=Resource( + kernel=obtain_resource( "arm64-linux-kernel-5.4.49", resource_directory=args.resource_directory, ), - bootloader=Resource( + bootloader=obtain_resource( "arm64-bootloader-foundation", resource_directory=args.resource_directory, ), - disk_image=Resource( + disk_image=obtain_resource( "arm64-ubuntu-20.04-img", resource_directory=args.resource_directory, ), diff --git a/tests/gem5/arm-boot-tests/test_linux_boot.py b/tests/gem5/arm_boot_tests/test_linux_boot.py similarity index 99% rename from tests/gem5/arm-boot-tests/test_linux_boot.py rename to tests/gem5/arm_boot_tests/test_linux_boot.py index 9e04e24d62..074f3364b5 100644 --- a/tests/gem5/arm-boot-tests/test_linux_boot.py +++ b/tests/gem5/arm_boot_tests/test_linux_boot.py @@ -25,7 +25,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import re - from typing import Optional from testlib import * @@ -44,9 +43,8 @@ def test_boot( length: str, to_tick: Optional[int] = None, ): - name = f"{cpu}-cpu_{num_cpus}-cores_{mem_system}_{memory_class}_\ -arm-boot-test" +arm_boot_test" verifiers = [] @@ -90,6 +88,7 @@ arm-boot-test" config.base_dir, "tests", "gem5", + "arm_boot_tests", "configs", "arm_boot_exit_run.py", ), diff --git a/tests/gem5/asmtest/README.md b/tests/gem5/asmtest/README.md new file mode 100644 index 0000000000..fcbe8ea65c --- /dev/null +++ b/tests/gem5/asmtest/README.md @@ -0,0 +1,9 @@ +# ASM Test + +These tests run a set of RISCV binaries on a bare bones syscall execution. +In addition, these test run these binaries against different CPU types. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/asmtest --length=[length] +``` diff --git a/tests/gem5/asmtest/configs/riscv_asmtest.py b/tests/gem5/asmtest/configs/riscv_asmtest.py new file mode 100644 index 0000000000..d6582e4698 --- /dev/null +++ b/tests/gem5/asmtest/configs/riscv_asmtest.py @@ -0,0 +1,121 @@ +# Copyright (c) 2021 The Regents of the University of California +# Copyright (c) 2022 Google Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +A run script for a very simple Syscall-Execution running simple binaries. +The system has no cache heirarchy and is as "bare-bones" as you can get in +gem5 while still being functinal. +""" + +import argparse + +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import ( + get_cpu_type_from_str, + get_cpu_types_str_set, +) +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource +from gem5.simulate.simulator import Simulator + +parser = argparse.ArgumentParser( + description="A gem5 script for testing RISC-V instructions" +) + +parser.add_argument( + "resource", type=str, help="The gem5 resource binary to run." +) + +parser.add_argument( + "cpu", type=str, choices=get_cpu_types_str_set(), help="The CPU type used." +) + +parser.add_argument( + "--riscv-32bits", + action="store_true", + help="Use 32 bits core of Riscv CPU", +) + +parser.add_argument( + "-r", + "--resource-directory", + type=str, + required=False, + help="The directory in which resources will be downloaded or exist.", +) + +parser.add_argument( + "-n", + "--num-cores", + type=int, + default=1, + required=False, + help="The number of CPU cores to run.", +) + +args = parser.parse_args() + +# Setup the system. +cache_hierarchy = NoCache() +memory = SingleChannelDDR3_1600() + +processor = SimpleProcessor( + cpu_type=get_cpu_type_from_str(args.cpu), + isa=ISA.RISCV, + num_cores=args.num_cores, +) + +if args.riscv_32bits: + for simple_core in processor.cores: + for i in range(len(simple_core.core.isa)): + simple_core.core.isa[i].riscv_type = "RV32" + +motherboard = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +# Set the workload +binary = obtain_resource( + args.resource, resource_directory=args.resource_directory +) +motherboard.set_se_binary_workload(binary) + +# Run the simulation +simulator = Simulator(board=motherboard) +simulator.run() + +print( + "Exiting @ tick {} because {}.".format( + simulator.get_current_tick(), simulator.get_last_exit_event_cause() + ) +) diff --git a/tests/gem5/asmtest/tests.py b/tests/gem5/asmtest/tests.py index b2a5992da0..62e4ef5859 100644 --- a/tests/gem5/asmtest/tests.py +++ b/tests/gem5/asmtest/tests.py @@ -34,7 +34,7 @@ else: # The following lists the RISCV binaries. Those commented out presently result # in a test failure. This is outlined in the following Jira issue: # https://gem5.atlassian.net/browse/GEM5-496 -binaries = ( +rv64_binaries = ( "rv64samt-ps-sysclone_d", "rv64samt-ps-sysfutex1_d", # 'rv64samt-ps-sysfutex2_d', @@ -69,6 +69,50 @@ binaries = ( "rv64uamt-ps-amoswap_d", "rv64uamt-ps-amoxor_d", "rv64uamt-ps-lrsc_d", + "rv64ub-ps-add_uw", + "rv64ub-ps-andn", + "rv64ub-ps-bclr", + "rv64ub-ps-bclri", + "rv64ub-ps-bext", + "rv64ub-ps-bexti", + "rv64ub-ps-binv", + "rv64ub-ps-binvi", + "rv64ub-ps-bset", + "rv64ub-ps-bseti", + "rv64ub-ps-clmul", + "rv64ub-ps-clmulh", + "rv64ub-ps-clmulr", + "rv64ub-ps-clz", + "rv64ub-ps-clzw", + "rv64ub-ps-cpop", + "rv64ub-ps-cpopw", + "rv64ub-ps-ctz", + "rv64ub-ps-ctzw", + "rv64ub-ps-max", + "rv64ub-ps-maxu", + "rv64ub-ps-min", + "rv64ub-ps-minu", + "rv64ub-ps-orc_b", + "rv64ub-ps-orn", + "rv64ub-ps-rev8", + "rv64ub-ps-rol", + "rv64ub-ps-rolw", + "rv64ub-ps-ror", + "rv64ub-ps-rori", + "rv64ub-ps-roriw", + "rv64ub-ps-rorw", + "rv64ub-ps-sext_b", + "rv64ub-ps-sext_h", + "rv64ub-ps-sh1add", + "rv64ub-ps-sh1add_uw", + "rv64ub-ps-sh2add", + "rv64ub-ps-sh2add_uw", + "rv64ub-ps-sh3add", + "rv64ub-ps-sh3add_uw", + "rv64ub-ps-slli_uw", + "rv64ub-ps-xnor", + "rv64ub-ps-zext_h", + "rv64uc-ps-rvc", "rv64ud-ps-fadd", "rv64ud-ps-fclass", "rv64ud-ps-fcmp", @@ -169,10 +213,145 @@ binaries = ( "rv64uzfh-ps-recoding", ) +rv32_binaries = ( + "rv32ua-ps-amoadd_w", + "rv32ua-ps-amoand_w", + "rv32ua-ps-amomaxu_w", + "rv32ua-ps-amomax_w", + "rv32ua-ps-amominu_w", + "rv32ua-ps-amomin_w", + "rv32ua-ps-amoor_w", + "rv32ua-ps-amoswap_w", + "rv32ua-ps-amoxor_w", + "rv32ua-ps-lrsc", + "rv32uamt-ps-amoadd_w", + "rv32uamt-ps-amoand_w", + "rv32uamt-ps-amomaxu_w", + "rv32uamt-ps-amomax_w", + "rv32uamt-ps-amominu_w", + "rv32uamt-ps-amomin_w", + "rv32uamt-ps-amoor_w", + "rv32uamt-ps-amoswap_w", + "rv32uamt-ps-amoxor_w", + "rv32uamt-ps-lrsc_w", + "rv32ub-ps-andn", + "rv32ub-ps-bclr", + "rv32ub-ps-bclri", + "rv32ub-ps-bext", + "rv32ub-ps-bexti", + "rv32ub-ps-binv", + "rv32ub-ps-binvi", + "rv32ub-ps-bset", + "rv32ub-ps-bseti", + "rv32ub-ps-clmul", + "rv32ub-ps-clmulh", + "rv32ub-ps-clmulr", + "rv32ub-ps-clz", + "rv32ub-ps-cpop", + "rv32ub-ps-ctz", + "rv32ub-ps-max", + "rv32ub-ps-maxu", + "rv32ub-ps-min", + "rv32ub-ps-minu", + "rv32ub-ps-orc_b", + "rv32ub-ps-orn", + "rv32ub-ps-rev8", + "rv32ub-ps-rol", + "rv32ub-ps-ror", + "rv32ub-ps-rori", + "rv32ub-ps-sext_b", + "rv32ub-ps-sext_h", + "rv32ub-ps-sh1add", + "rv32ub-ps-sh2add", + "rv32ub-ps-sh3add", + "rv32ub-ps-xnor", + "rv32ub-ps-zext_h", + "rv32uc-ps-rvc", + "rv32ud-ps-fadd", + "rv32ud-ps-fclass", + "rv32ud-ps-fcmp", + "rv32ud-ps-fcvt", + "rv32ud-ps-fcvt_w", + "rv32ud-ps-fdiv", + "rv32ud-ps-fmadd", + "rv32ud-ps-fmin", + "rv32ud-ps-ldst", + "rv32ud-ps-recoding", + "rv32uf-ps-fadd", + "rv32uf-ps-fclass", + "rv32uf-ps-fcmp", + "rv32uf-ps-fcvt", + "rv32uf-ps-fcvt_w", + "rv32uf-ps-fdiv", + "rv32uf-ps-fmadd", + "rv32uf-ps-fmin", + "rv32uf-ps-ldst", + "rv32uf-ps-move", + "rv32uf-ps-recoding", + "rv32ui-ps-add", + "rv32ui-ps-addi", + "rv32ui-ps-and", + "rv32ui-ps-andi", + "rv32ui-ps-auipc", + "rv32ui-ps-beq", + "rv32ui-ps-bge", + "rv32ui-ps-bgeu", + "rv32ui-ps-blt", + "rv32ui-ps-bltu", + "rv32ui-ps-bne", + "rv32ui-ps-fence_i", + "rv32ui-ps-jal", + "rv32ui-ps-jalr", + "rv32ui-ps-lb", + "rv32ui-ps-lbu", + "rv32ui-ps-lh", + "rv32ui-ps-lhu", + "rv32ui-ps-lui", + "rv32ui-ps-lw", + "rv32ui-ps-or", + "rv32ui-ps-ori", + "rv32ui-ps-sb", + "rv32ui-ps-sh", + "rv32ui-ps-simple", + "rv32ui-ps-sll", + "rv32ui-ps-slli", + "rv32ui-ps-slt", + "rv32ui-ps-slti", + "rv32ui-ps-sltiu", + "rv32ui-ps-sltu", + "rv32ui-ps-sra", + "rv32ui-ps-srai", + "rv32ui-ps-srl", + "rv32ui-ps-srli", + "rv32ui-ps-sub", + "rv32ui-ps-sw", + "rv32ui-ps-xor", + "rv32ui-ps-xori", + "rv32um-ps-div", + "rv32um-ps-divu", + "rv32um-ps-mul", + "rv32um-ps-mulh", + "rv32um-ps-mulhsu", + "rv32um-ps-mulhu", + "rv32um-ps-rem", + "rv32um-ps-remu", + "rv32uzfh-ps-fadd", + "rv32uzfh-ps-fclass", + "rv32uzfh-ps-fcmp", + "rv32uzfh-ps-fcvt", + "rv32uzfh-ps-fcvt_w", + "rv32uzfh-ps-fdiv", + "rv32uzfh-ps-fmadd", + "rv32uzfh-ps-fmin", + "rv32uzfh-ps-ldst", + "rv32uzfh-ps-move", + "rv32uzfh-ps-recoding", +) + cpu_types = ("atomic", "timing", "minor", "o3") for cpu_type in cpu_types: - for binary in binaries: + for binary in rv64_binaries: gem5_verify_config( name=f"asm-riscv-{binary}-{cpu_type}", verifiers=(), @@ -180,13 +359,13 @@ for cpu_type in cpu_types: config.base_dir, "tests", "gem5", + "asmtest", "configs", - "simple_binary_run.py", + "riscv_asmtest.py", ), config_args=[ binary, cpu_type, - "riscv", "--num-cores", "4", "--resource-directory", @@ -195,3 +374,27 @@ for cpu_type in cpu_types: valid_isas=(constants.all_compiled_tag,), valid_hosts=constants.supported_hosts, ) + for binary in rv32_binaries: + gem5_verify_config( + name=f"asm-riscv-{binary}-{cpu_type}", + verifiers=(), + config=joinpath( + config.base_dir, + "tests", + "gem5", + "asmtest", + "configs", + "riscv_asmtest.py", + ), + config_args=[ + binary, + cpu_type, + "--num-cores", + "4", + "--riscv-32bits", + "--resource-directory", + resource_path, + ], + valid_isas=(constants.all_compiled_tag,), + valid_hosts=constants.supported_hosts, + ) diff --git a/tests/gem5/checkpoint_tests/README.md b/tests/gem5/checkpoint_tests/README.md new file mode 100644 index 0000000000..64767169ba --- /dev/null +++ b/tests/gem5/checkpoint_tests/README.md @@ -0,0 +1,11 @@ +# Checkpoint tests + +These tests run hello world binary for arm, x86, and power isa and ubuntuboot workload for x86 isa using checkpoints. +Each binary is run in two parts: +- Save checkpoint: A binary is run for a set amount of ticks and then a checkpoint is taken. This test checks if the checkpoint is taken. + +- Resotre checkpoint: The same binary and board in the respective save test are used with the saved checkpoint (the checkpoint is uploaded to gem5 resources). This test checks if the binary ran properly. + +```bash +./main.py run gem5/checkpoint_tests/ +``` diff --git a/tests/gem5/checkpoint_tests/configs/arm-hello-restore-checkpoint.py b/tests/gem5/checkpoint_tests/configs/arm-hello-restore-checkpoint.py new file mode 100644 index 0000000000..f5dfe4cfc8 --- /dev/null +++ b/tests/gem5/checkpoint_tests/configs/arm-hello-restore-checkpoint.py @@ -0,0 +1,81 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +""" +This gem5 configuation script creates a simple board sharing the same +structure as the one in +tests/gem5/checkpoint-tests/arm-hello-save-checkpoint.py. +This script restores the checkpoint generated by the above script, and +runs the rest of "arm-hello64-static" binary simulation. +This configuration serves as a test of restoring a checkpoint with ARM ISA. +""" + +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.private_l1_private_l2_cache_hierarchy import ( + PrivateL1PrivateL2CacheHierarchy, +) +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import ( + CheckpointResource, + obtain_resource, +) +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires + +requires(isa_required=ISA.ARM) + +cache_hierarchy = PrivateL1PrivateL2CacheHierarchy( + l1d_size="16kB", l1i_size="16kB", l2_size="256kB" +) + +memory = SingleChannelDDR3_1600(size="32MB") + +processor = SimpleProcessor(cpu_type=CPUTypes.ATOMIC, isa=ISA.ARM, num_cores=2) + +board = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +board.set_se_binary_workload( + obtain_resource("arm-hello64-static"), + checkpoint=obtain_resource("arm-hello-test-checkpoint"), +) + +sim = Simulator(board=board, full_system=False) +sim.run() + +print( + "Exiting @ tick {} because {}.".format( + sim.get_current_tick(), sim.get_last_exit_event_cause() + ) +) diff --git a/tests/gem5/checkpoint_tests/configs/arm-hello-save-checkpoint.py b/tests/gem5/checkpoint_tests/configs/arm-hello-save-checkpoint.py new file mode 100644 index 0000000000..16fc2a00f0 --- /dev/null +++ b/tests/gem5/checkpoint_tests/configs/arm-hello-save-checkpoint.py @@ -0,0 +1,78 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import argparse + +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.private_l1_private_l2_cache_hierarchy import ( + PrivateL1PrivateL2CacheHierarchy, +) +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires + +parser = argparse.ArgumentParser() + +parser.add_argument( + "--checkpoint-path", + type=str, + required=False, + default="arm-hello-test-checkpoint/", + help="The directory to store the checkpoint.", +) + +args = parser.parse_args() +requires(isa_required=ISA.ARM) + +cache_hierarchy = PrivateL1PrivateL2CacheHierarchy( + l1d_size="16kB", l1i_size="16kB", l2_size="256kB" +) + +memory = SingleChannelDDR3_1600(size="32MB") +processor = SimpleProcessor(cpu_type=CPUTypes.ATOMIC, isa=ISA.ARM, num_cores=2) +board = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) +board.set_se_binary_workload(obtain_resource("arm-hello64-static")) + +sim = Simulator(board=board, full_system=False) +max_ticks = 10**6 +sim.run(max_ticks=max_ticks) +print( + "Exiting @ tick {} because {}.".format( + sim.get_current_tick(), sim.get_last_exit_event_cause() + ) +) +print("Taking checkpoint at", args.checkpoint_path) +sim.save_checkpoint(args.checkpoint_path) +print("Done taking checkpoint") diff --git a/tests/gem5/checkpoint_tests/configs/power-hello-restore-checkpoint.py b/tests/gem5/checkpoint_tests/configs/power-hello-restore-checkpoint.py new file mode 100644 index 0000000000..bd717f3c62 --- /dev/null +++ b/tests/gem5/checkpoint_tests/configs/power-hello-restore-checkpoint.py @@ -0,0 +1,74 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This gem5 configuation script creates a simple board sharing the same +structure as the one in +tests/gem5/checkpoint-tests/power-hello-save-checkpoint.py. +This script restores the checkpoint generated by the above script, and +runs the rest of "power-hello" binary simulation. +This configuration serves as a test of restoring a checkpoint with POWER ISA. +""" + +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import ( + CheckpointResource, + obtain_resource, +) +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires + +requires(isa_required=ISA.POWER) + +cache_hierarchy = NoCache() + +memory = SingleChannelDDR3_1600(size="32MB") +processor = SimpleProcessor( + cpu_type=CPUTypes.TIMING, isa=ISA.POWER, num_cores=2 +) +board = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) +board.set_se_binary_workload( + obtain_resource("power-hello"), + checkpoint=obtain_resource("power-hello-test-checkpoint"), +) + +sim = Simulator(board=board, full_system=False) +sim.run() +print( + "Exiting @ tick {} because {}.".format( + sim.get_current_tick(), sim.get_last_exit_event_cause() + ) +) diff --git a/tests/gem5/checkpoint_tests/configs/power-hello-save-checkpoint.py b/tests/gem5/checkpoint_tests/configs/power-hello-save-checkpoint.py new file mode 100644 index 0000000000..594d6610e0 --- /dev/null +++ b/tests/gem5/checkpoint_tests/configs/power-hello-save-checkpoint.py @@ -0,0 +1,84 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This gem5 test script creates a simple board to run the first +10^6 ticks of "power-hello" binary simulation and saves a checkpoint. +This configuration serves as a test to ensure that checkpoints work +with POWER ISA. +""" + +import argparse + +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires + +parser = argparse.ArgumentParser() + +parser.add_argument( + "--checkpoint-path", + type=str, + required=False, + default="power-hello-test-checkpoint/", + help="The directory to store the checkpoint.", +) + +args = parser.parse_args() +requires(isa_required=ISA.POWER) + +cache_hierarchy = NoCache() + +memory = SingleChannelDDR3_1600(size="32MB") +processor = SimpleProcessor( + cpu_type=CPUTypes.TIMING, isa=ISA.POWER, num_cores=2 +) + +board = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) +board.set_se_binary_workload(obtain_resource("power-hello")) + +sim = Simulator(board=board, full_system=False) +max_ticks = 10**6 +sim.run(max_ticks=max_ticks) +print( + "Exiting @ tick {} because {}.".format( + sim.get_current_tick(), sim.get_last_exit_event_cause() + ) +) +print("Taking checkpoint at", args.checkpoint_path) +sim.save_checkpoint(args.checkpoint_path) +print("Done taking checkpoint") diff --git a/tests/gem5/checkpoint_tests/configs/sparc-hello-restore-checkpoint.py b/tests/gem5/checkpoint_tests/configs/sparc-hello-restore-checkpoint.py new file mode 100644 index 0000000000..447c6bd325 --- /dev/null +++ b/tests/gem5/checkpoint_tests/configs/sparc-hello-restore-checkpoint.py @@ -0,0 +1,74 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This gem5 configuation script creates a simple board sharing the same +structure as the one in +tests/gem5/checkpoint-tests/sparc-hello-save-checkpoint.py. +This script restores the checkpoint generated by the above script, and +runs the rest of "sparc-hello" binary simulation. +This configuration serves as a test of restoring a checkpoint with SPARC ISA. +""" + +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import ( + CheckpointResource, + obtain_resource, +) +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires + +requires(isa_required=ISA.SPARC) + +cache_hierarchy = NoCache() + +memory = SingleChannelDDR3_1600(size="32MB") +processor = SimpleProcessor( + cpu_type=CPUTypes.TIMING, isa=ISA.SPARC, num_cores=2 +) +board = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) +board.set_se_binary_workload( + obtain_resource("sparc-hello"), + checkpoint=CheckpointResource(local_path="./sparc-hello-test-checkpoint"), +) + +sim = Simulator(board=board, full_system=False) +sim.run() +print( + "Exiting @ tick {} because {}.".format( + sim.get_current_tick(), sim.get_last_exit_event_cause() + ) +) diff --git a/tests/gem5/checkpoint_tests/configs/sparc-hello-save-checkpoint.py b/tests/gem5/checkpoint_tests/configs/sparc-hello-save-checkpoint.py new file mode 100644 index 0000000000..f85ff41dbc --- /dev/null +++ b/tests/gem5/checkpoint_tests/configs/sparc-hello-save-checkpoint.py @@ -0,0 +1,84 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This gem5 test script creates a simple board to run the first +10^6 ticks of "sparc-hello" binary simulation and saves a checkpoint. +This configuration serves as a test to ensure that checkpoints work +with SPARC ISA. +""" + +import argparse + +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires + +parser = argparse.ArgumentParser() + +parser.add_argument( + "--checkpoint-path", + type=str, + required=False, + default="sparc-hello-test-checkpoint/", + help="The directory to store the checkpoint.", +) + +args = parser.parse_args() +requires(isa_required=ISA.SPARC) + +cache_hierarchy = NoCache() + +memory = SingleChannelDDR3_1600(size="32MB") +processor = SimpleProcessor( + cpu_type=CPUTypes.TIMING, isa=ISA.SPARC, num_cores=2 +) + +board = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) +board.set_se_binary_workload(obtain_resource("sparc-hello")) + +sim = Simulator(board=board, full_system=False) +max_ticks = 10**6 +sim.run(max_ticks=max_ticks) +print( + "Exiting @ tick {} because {}.".format( + sim.get_current_tick(), sim.get_last_exit_event_cause() + ) +) +print("Taking checkpoint at", args.checkpoint_path) +sim.save_checkpoint(args.checkpoint_path) +print("Done taking checkpoint") diff --git a/tests/gem5/checkpoint_tests/configs/x86-fs-restore-checkpoint.py b/tests/gem5/checkpoint_tests/configs/x86-fs-restore-checkpoint.py new file mode 100644 index 0000000000..f2909b36f6 --- /dev/null +++ b/tests/gem5/checkpoint_tests/configs/x86-fs-restore-checkpoint.py @@ -0,0 +1,91 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This gem5 configuation script creates a simple board sharing the same +structure as the one in +tests/gem5/checkpoint-tests/x86-fs-save-checkpoint.py. +This script restores the checkpoint generated by the above script, and +runs the rest of full system simulation. +This configuration serves as a test of restoring a checkpoint with X86 ISA in fs mode. +""" + +from gem5.components.boards.x86_board import X86Board +from gem5.components.cachehierarchies.classic.private_l1_private_l2_cache_hierarchy import ( + PrivateL1PrivateL2CacheHierarchy, +) +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import ( + CheckpointResource, + obtain_resource, +) +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires + +# Run a check to ensure the right version of gem5 is being used. +requires(isa_required=ISA.X86) + +# Setup the cache hierarchy. +# For classic, PrivateL1PrivateL2 and NoCache have been tested. +# For Ruby, MESI_Two_Level and MI_example have been tested. +cache_hierarchy = PrivateL1PrivateL2CacheHierarchy( + l1d_size="32kB", l1i_size="32kB", l2_size="512kB" +) + +# Setup the system memory. +memory = SingleChannelDDR3_1600(size="1GB") + +# Setup a single core Processor. +processor = SimpleProcessor(cpu_type=CPUTypes.O3, isa=ISA.X86, num_cores=1) + +# Setup the board. +board = X86Board( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +# Set the Full System workload. +board.set_kernel_disk_workload( + kernel=obtain_resource("x86-linux-kernel-5.4.49"), + disk_image=obtain_resource("x86-ubuntu-18.04-img"), + checkpoint=obtain_resource("x86-fs-test-checkpoint"), +) + +sim = Simulator(board=board, full_system=True) +print("Beginning simulation!") + +sim.run(max_ticks=10**10) + +print( + "Exiting @ tick {} because {}.".format( + sim.get_current_tick(), sim.get_last_exit_event_cause() + ) +) diff --git a/tests/gem5/checkpoint_tests/configs/x86-fs-save-checkpoint.py b/tests/gem5/checkpoint_tests/configs/x86-fs-save-checkpoint.py new file mode 100644 index 0000000000..fb800209e0 --- /dev/null +++ b/tests/gem5/checkpoint_tests/configs/x86-fs-save-checkpoint.py @@ -0,0 +1,102 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This gem5 test script creates a simple board to run the first +10^6 ticks of x86 full system kernel disk workload simulation and saves a checkpoint. +This configuration serves as a test to ensure that checkpoints work +with X86 ISA in fs mode. +""" + +import argparse + +from gem5.components.boards.x86_board import X86Board +from gem5.components.cachehierarchies.classic.private_l1_private_l2_cache_hierarchy import ( + PrivateL1PrivateL2CacheHierarchy, +) +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires + +parser = argparse.ArgumentParser() + +parser.add_argument( + "--checkpoint-path", + type=str, + required=False, + default="x86-fs-test-checkpoint/", + help="The directory to store the checkpoint.", +) + +args = parser.parse_args() + +# Run a check to ensure the right version of gem5 is being used. +requires(isa_required=ISA.X86) + +# Setup the cache hierarchy. +# For classic, PrivateL1PrivateL2 and NoCache have been tested. +# For Ruby, MESI_Two_Level and MI_example have been tested. +cache_hierarchy = PrivateL1PrivateL2CacheHierarchy( + l1d_size="32kB", l1i_size="32kB", l2_size="512kB" +) + +# Setup the system memory. +memory = SingleChannelDDR3_1600(size="1GB") + +# Setup a single core Processor. +processor = SimpleProcessor(cpu_type=CPUTypes.O3, isa=ISA.X86, num_cores=1) + +# Setup the board. +board = X86Board( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +# Set the Full System workload. +board.set_kernel_disk_workload( + kernel=obtain_resource("x86-linux-kernel-5.4.49"), + disk_image=obtain_resource("x86-ubuntu-18.04-img"), +) + +sim = Simulator(board=board, full_system=True) +print("Beginning simulation!") + +max_ticks = 10**6 +sim.run(max_ticks=max_ticks) +print( + "Exiting @ tick {} because {}.".format( + sim.get_current_tick(), sim.get_last_exit_event_cause() + ) +) +print("Taking checkpoint at", args.checkpoint_path) +sim.save_checkpoint(args.checkpoint_path) +print("Done taking checkpoint") diff --git a/tests/gem5/checkpoint_tests/configs/x86-hello-restore-checkpoint.py b/tests/gem5/checkpoint_tests/configs/x86-hello-restore-checkpoint.py new file mode 100644 index 0000000000..5a36a01e35 --- /dev/null +++ b/tests/gem5/checkpoint_tests/configs/x86-hello-restore-checkpoint.py @@ -0,0 +1,74 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This gem5 configuation script creates a simple board sharing the same +structure as the one in +tests/gem5/checkpoint-tests/x86-hello-save-checkpoint.py. +This script restores the checkpoint generated by the above script, and +runs the rest of "x86-hello64-static" binary simulation. +This configuration serves as a test of restoring a checkpoint with X86 ISA. +""" + +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.private_l1_cache_hierarchy import ( + PrivateL1CacheHierarchy, +) +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import ( + CheckpointResource, + obtain_resource, +) +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires + +requires(isa_required=ISA.X86) + +cache_hierarchy = PrivateL1CacheHierarchy(l1d_size="16kB", l1i_size="16kB") + +memory = SingleChannelDDR3_1600(size="32MB") +processor = SimpleProcessor(cpu_type=CPUTypes.TIMING, isa=ISA.X86, num_cores=4) +board = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) +board.set_se_binary_workload( + obtain_resource("x86-hello64-static"), + checkpoint=obtain_resource("x86-hello-test-checkpoint"), +) + +sim = Simulator(board=board, full_system=False) +sim.run() +print( + "Exiting @ tick {} because {}.".format( + sim.get_current_tick(), sim.get_last_exit_event_cause() + ) +) diff --git a/tests/gem5/checkpoint_tests/configs/x86-hello-save-checkpoint.py b/tests/gem5/checkpoint_tests/configs/x86-hello-save-checkpoint.py new file mode 100644 index 0000000000..bc35e581df --- /dev/null +++ b/tests/gem5/checkpoint_tests/configs/x86-hello-save-checkpoint.py @@ -0,0 +1,84 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This gem5 test script creates a simple board to run the first +10^6 ticks of "x86-hello64-static" binary simulation and saves a checkpoint. +This configuration serves as a test to ensure that checkpoints work +with X86 ISA. +""" + +import argparse + +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.private_l1_cache_hierarchy import ( + PrivateL1CacheHierarchy, +) +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires + +parser = argparse.ArgumentParser() + +parser.add_argument( + "--checkpoint-path", + type=str, + required=False, + default="x86-hello-test-checkpoint/", + help="The directory to store the checkpoint.", +) + +args = parser.parse_args() +requires(isa_required=ISA.X86) + +cache_hierarchy = PrivateL1CacheHierarchy(l1d_size="16kB", l1i_size="16kB") + +memory = SingleChannelDDR3_1600(size="32MB") +processor = SimpleProcessor(cpu_type=CPUTypes.TIMING, isa=ISA.X86, num_cores=4) + +board = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) +board.set_se_binary_workload(obtain_resource("x86-hello64-static")) + +sim = Simulator(board=board, full_system=False) +max_ticks = 10**6 +sim.run(max_ticks=max_ticks) +print( + "Exiting @ tick {} because {}.".format( + sim.get_current_tick(), sim.get_last_exit_event_cause() + ) +) +print("Taking checkpoint at", args.checkpoint_path) +sim.save_checkpoint(args.checkpoint_path) +print("Done taking checkpoint") diff --git a/tests/gem5/checkpoint_tests/test-checkpoints.py b/tests/gem5/checkpoint_tests/test-checkpoints.py new file mode 100644 index 0000000000..f18cf29289 --- /dev/null +++ b/tests/gem5/checkpoint_tests/test-checkpoints.py @@ -0,0 +1,242 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This runs simple tests to ensure the examples in `configs/example/gem5_library` +still function. They simply check the simulation completed. +""" +import os +import re + +from testlib import * + +if config.bin_path: + resource_path = config.bin_path +else: + resource_path = joinpath(absdirpath(__file__), "..", "resources") + +hello_verifier = verifier.MatchRegex(re.compile(r"Hello world!")) +save_checkpoint_verifier = verifier.MatchRegex( + re.compile(r"Done taking checkpoint") +) + + +gem5_verify_config( + name="test-checkpoint-arm-hello-save-checkpoint", + fixtures=(), + verifiers=(save_checkpoint_verifier,), + config=joinpath( + config.base_dir, + "tests", + "gem5", + "checkpoint_tests", + "configs", + "arm-hello-save-checkpoint.py", + ), + config_args=[ + "--checkpoint-path", + joinpath(resource_path, "arm-hello-test-checkpoint"), + ], + valid_isas=(constants.all_compiled_tag,), + valid_hosts=constants.supported_hosts, + length=constants.quick_tag, +) + +gem5_verify_config( + name="test-checkpoint-arm-hello-restore-checkpoint", + fixtures=(), + verifiers=(hello_verifier,), + config=joinpath( + config.base_dir, + "tests", + "gem5", + "checkpoint_tests", + "configs", + "arm-hello-restore-checkpoint.py", + ), + config_args=[], + valid_isas=(constants.all_compiled_tag,), + valid_hosts=constants.supported_hosts, + length=constants.quick_tag, +) + +gem5_verify_config( + name="test-checkpoint-x86-hello-save-checkpoint", + fixtures=(), + verifiers=(save_checkpoint_verifier,), + config=joinpath( + config.base_dir, + "tests", + "gem5", + "checkpoint_tests", + "configs", + "x86-hello-save-checkpoint.py", + ), + config_args=[ + "--checkpoint-path", + joinpath(resource_path, "x86-hello-test-checkpoint"), + ], + valid_isas=(constants.all_compiled_tag,), + valid_hosts=constants.supported_hosts, + length=constants.quick_tag, +) + +gem5_verify_config( + name="test-checkpoint-x86-hello-restore-checkpoint", + fixtures=(), + verifiers=(hello_verifier,), + config=joinpath( + config.base_dir, + "tests", + "gem5", + "checkpoint_tests", + "configs", + "x86-hello-restore-checkpoint.py", + ), + config_args=[], + valid_isas=(constants.all_compiled_tag,), + valid_hosts=constants.supported_hosts, + length=constants.quick_tag, +) + +gem5_verify_config( + name="test-checkpoint-x86-fs-save-checkpoint", + fixtures=(), + verifiers=(save_checkpoint_verifier,), + config=joinpath( + config.base_dir, + "tests", + "gem5", + "checkpoint_tests", + "configs", + "x86-fs-save-checkpoint.py", + ), + config_args=[ + "--checkpoint-path", + joinpath(resource_path, "x86-fs-test-checkpoint"), + ], + valid_isas=(constants.all_compiled_tag,), + valid_hosts=constants.supported_hosts, + length=constants.quick_tag, +) + +gem5_verify_config( + name="test-checkpoint-x86-fs-restore-checkpoint", + fixtures=(), + verifiers=(), + config=joinpath( + config.base_dir, + "tests", + "gem5", + "checkpoint_tests", + "configs", + "x86-fs-restore-checkpoint.py", + ), + config_args=[], + valid_isas=(constants.all_compiled_tag,), + valid_hosts=constants.supported_hosts, + length=constants.quick_tag, +) + +gem5_verify_config( + name="test-checkpoint-power-hello-save-checkpoint", + fixtures=(), + verifiers=(save_checkpoint_verifier,), + config=joinpath( + config.base_dir, + "tests", + "gem5", + "checkpoint_tests", + "configs", + "power-hello-save-checkpoint.py", + ), + config_args=[ + "--checkpoint-path", + joinpath(resource_path, "power-hello-test-checkpoint"), + ], + valid_isas=(constants.all_compiled_tag,), + valid_hosts=constants.supported_hosts, + length=constants.quick_tag, +) + +gem5_verify_config( + name="test-checkpoint-power-hello-restore-checkpoint", + fixtures=(), + verifiers=(hello_verifier,), + config=joinpath( + config.base_dir, + "tests", + "gem5", + "checkpoint_tests", + "configs", + "power-hello-restore-checkpoint.py", + ), + config_args=[], + valid_isas=(constants.all_compiled_tag,), + valid_hosts=constants.supported_hosts, + length=constants.quick_tag, +) + +# There is a bug in sparc isa that causes the checkpoints to fail +# GitHub issue: https://github.com/gem5/gem5/issues/197 +# gem5_verify_config( +# name="test-checkpoint-sparc-hello-save-checkpoint", +# fixtures=(), +# verifiers=(save_checkpoint_verifier,), +# config=joinpath( +# config.base_dir, +# "tests", +# "gem5", +# "checkpoint_tests", +# "configs", +# "sparc-hello-save-checkpoint.py", +# ), +# config_args=[ +# # "--checkpoint-path", +# # joinpath(resource_path, "sparc-hello-test-checkpoint"), +# ], +# valid_isas=(constants.all_compiled_tag,), +# valid_hosts=constants.supported_hosts, +# length=constants.quick_tag, +# ) + +# gem5_verify_config( +# name="test-checkpoint-sparc-hello-restore-checkpoint", +# fixtures=(), +# verifiers=(hello_verifier,), +# config=joinpath( +# config.base_dir, +# "tests", +# "gem5", +# "checkpoint_tests", +# "configs", +# "sparc-hello-restore-checkpoint.py", +# ), +# config_args=[], +# valid_isas=(constants.all_compiled_tag,), +# valid_hosts=constants.supported_hosts, +# length=constants.quick_tag, +# ) diff --git a/tests/gem5/cpu_tests/README.md b/tests/gem5/cpu_tests/README.md new file mode 100644 index 0000000000..574d12ce0a --- /dev/null +++ b/tests/gem5/cpu_tests/README.md @@ -0,0 +1,8 @@ +# CPU Tests + +These tests run the Bubblesort and FloatMM workloads against the different CPU models. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/cpu_tests --length=[length] +``` diff --git a/tests/gem5/cpu_tests/run.py b/tests/gem5/cpu_tests/run.py index fb528e5e8c..2feddb0a51 100644 --- a/tests/gem5/cpu_tests/run.py +++ b/tests/gem5/cpu_tests/run.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2018 The Regents of the University of California # All Rights Reserved. # @@ -25,8 +24,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import os import argparse +import os import m5 from m5.objects import * diff --git a/tests/gem5/dram_lowp/README.md b/tests/gem5/dram_lowp/README.md new file mode 100644 index 0000000000..bff3f033fd --- /dev/null +++ b/tests/gem5/dram_lowp/README.md @@ -0,0 +1,8 @@ +# DRAM LowP + +These tests run the `configs/dram` scripts that trigger low power state transitions in the DRAM controller. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/dram_lowp --length=[length] +``` diff --git a/tests/gem5/dram-lowp/ref/simout b/tests/gem5/dram_lowp/ref/simout.txt similarity index 100% rename from tests/gem5/dram-lowp/ref/simout rename to tests/gem5/dram_lowp/ref/simout.txt diff --git a/tests/gem5/dram-lowp/test_dram_lowp.py b/tests/gem5/dram_lowp/test_dram_lowp.py similarity index 96% rename from tests/gem5/dram-lowp/test_dram_lowp.py rename to tests/gem5/dram_lowp/test_dram_lowp.py index 2e146bbe46..ec38acacc1 100644 --- a/tests/gem5/dram-lowp/test_dram_lowp.py +++ b/tests/gem5/dram_lowp/test_dram_lowp.py @@ -26,7 +26,9 @@ from testlib import * -verifiers = (verifier.MatchStdoutNoPerf(joinpath(getcwd(), "ref", "simout")),) +verifiers = ( + verifier.MatchStdoutNoPerf(joinpath(getcwd(), "ref", "simout.txt")), +) gem5_verify_config( name="test-low_power-close_adaptive", diff --git a/tests/gem5/fixture.py b/tests/gem5/fixture.py index d3312c9a63..f1fcc38dcc 100644 --- a/tests/gem5/fixture.py +++ b/tests/gem5/fixture.py @@ -36,36 +36,45 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import os -import tempfile -import shutil -import sys -import socket -import threading import gzip - +import os +import shutil +import socket +import sys +import tempfile +import threading import urllib.error import urllib.request +from typing import ( + List, + Optional, +) -from testlib.fixture import Fixture -from testlib.configuration import config, constants -from testlib.helper import log_call, cacheresult, joinpath, absdirpath import testlib.log as log +from testlib.configuration import ( + config, + constants, +) +from testlib.fixture import Fixture +from testlib.helper import ( + absdirpath, + cacheresult, + joinpath, + log_call, +) from testlib.state import Result class VariableFixture(Fixture): def __init__(self, value=None, name=None): - super(VariableFixture, self).__init__(name=name) + super().__init__(name=name) self.value = value class TempdirFixture(Fixture): def __init__(self): self.path = None - super(TempdirFixture, self).__init__( - name=constants.tempdir_fixture_name - ) + super().__init__(name=constants.tempdir_fixture_name) def setup(self, testitem): self.path = tempfile.mkdtemp(prefix="gem5out") @@ -74,7 +83,7 @@ class TempdirFixture(Fixture): suiteUID = testitem.metadata.uid.suite testUID = testitem.metadata.name testing_result_folder = os.path.join( - config.result_path, "SuiteUID:" + suiteUID, "TestUID:" + testUID + config.result_path, "SuiteUID-" + suiteUID, "TestUID-" + testUID ) # Copy the output files of the run from /tmp to testing-results @@ -111,7 +120,7 @@ class UniqueFixture(Fixture): if target in cls.fixtures: obj = cls.fixtures[target] else: - obj = super(UniqueFixture, cls).__new__(cls) + obj = super().__new__(cls) obj.lock = threading.Lock() obj.target = target cls.fixtures[target] = obj @@ -121,7 +130,7 @@ class UniqueFixture(Fixture): with self.lock: if hasattr(self, "_init_done"): return - super(UniqueFixture, self).__init__(self, **kwargs) + super().__init__(self, **kwargs) self._init(*args, **kwargs) self._init_done = True @@ -144,13 +153,52 @@ class SConsFixture(UniqueFixture): """ def __new__(cls, target): - obj = super(SConsFixture, cls).__new__(cls, target) + obj = super().__new__(cls, target) return obj def _setup(self, testitem): if config.skip_build: return + if not self.targets: + log.test_log.error("No SCons targets specified.") + else: + log.test_log.message( + "Building the following targets. This may take a while." + ) + log.test_log.message(f"{', '.join(self.targets)}") + log.test_log.message( + "You may want to use --skip-build, or use 'rerun'." + ) + + if self.protocol: + defconfig_command = [ + "scons", + "-C", + self.directory, + "--ignore-style", + "--no-compress-debug", + "defconfig", + self.target_dir, + joinpath(self.directory, "build_opts", self.isa.upper()), + ] + setconfig_command = [ + "scons", + "-C", + self.directory, + "--ignore-style", + "--no-compress-debug", + "setconfig", + self.target_dir, + f"RUBY_PROTOCOL_{self.protocol.upper()}=y", + ] + log_call( + log.test_log, defconfig_command, time=None, stderr=sys.stderr + ) + log_call( + log.test_log, setconfig_command, time=None, stderr=sys.stderr + ) + command = [ "scons", "-C", @@ -160,26 +208,7 @@ class SConsFixture(UniqueFixture): "--ignore-style", "--no-compress-debug", ] - - if not self.targets: - log.test_log.warn( - "No SCons targets specified, this will" - " build the default all target.\n" - "This is likely unintended, and you" - " may wish to kill testlib and reconfigure." - ) - else: - log.test_log.message( - "Building the following targets. This may take a while." - ) - log.test_log.message(f"{', '.join(self.targets)}") - log.test_log.message( - "You may want to use --skip-build, or use 'rerun'." - ) - command.extend(self.targets) - if self.options: - command.extend(self.options) log_call(log.test_log, command, time=None, stderr=sys.stderr) @@ -189,7 +218,8 @@ class Gem5Fixture(SConsFixture): if protocol: target_dir += "_" + protocol target = joinpath(target_dir, f"gem5.{variant}") - obj = super(Gem5Fixture, cls).__new__(cls, target) + obj = super().__new__(cls, target) + obj.target_dir = target_dir return obj def _init(self, isa, variant, protocol=None): @@ -199,23 +229,26 @@ class Gem5Fixture(SConsFixture): self.path = self.target self.directory = config.base_dir - self.options = [] - if protocol: - self.options = ["--default=" + isa.upper(), "PROTOCOL=" + protocol] + self.isa = isa + self.protocol = protocol self.set_global() + def get_get_build_info(self) -> Optional[str]: + build_target = self.target + return build_target + class MakeFixture(Fixture): def __init__(self, directory, *args, **kwargs): name = f"make -C {directory}" - super(MakeFixture, self).__init__( + super().__init__( build_once=True, lazy_init=False, name=name, *args, **kwargs ) self.targets = [] self.directory = directory def setup(self): - super(MakeFixture, self).setup() + super().setup() targets = set(self.required_by) command = ["make", "-C", self.directory] command.extend([target.target for target in targets]) @@ -230,7 +263,7 @@ class MakeTarget(Fixture): scons we need to know what invocation to attach to. If none given, creates its own. """ - super(MakeTarget, self).__init__(name=target, *args, **kwargs) + super().__init__(name=target, *args, **kwargs) self.target = self.name if make_fixture is None: @@ -244,7 +277,7 @@ class MakeTarget(Fixture): self.require(self.make_fixture) def setup(self, testitem): - super(MakeTarget, self).setup() + super().setup() self.make_fixture.setup() return self @@ -254,7 +287,7 @@ class TestProgram(MakeTarget): make_dir = joinpath(config.bin_dir, program) make_fixture = MakeFixture(make_dir) target = joinpath("bin", isa, os, program) - super(TestProgram, self).__init__(target, make_fixture) + super().__init__(target, make_fixture) self.path = joinpath(make_dir, target) self.recompile = recompile @@ -274,7 +307,7 @@ class DownloadedProgram(UniqueFixture): def __new__(cls, url, path, filename, gzip_decompress=False): target = joinpath(path, filename) - return super(DownloadedProgram, cls).__new__(cls, target) + return super().__new__(cls, target) def _init(self, url, path, filename, gzip_decompress=False, **kwargs): """ @@ -318,7 +351,9 @@ class DownloadedProgram(UniqueFixture): urllib.request.urlretrieve(self.url, self.filename) def _getremotetime(self): - import datetime, time + import datetime + import time + import _strptime # Needed for python threading bug u = urllib.request.urlopen(self.url, timeout=10) @@ -358,7 +393,6 @@ class DownloadedArchive(DownloadedProgram): with tarfile.open(self.filename) as tf: def is_within_directory(directory, target): - abs_directory = os.path.abspath(directory) abs_target = os.path.abspath(target) @@ -369,7 +403,6 @@ class DownloadedArchive(DownloadedProgram): def safe_extract( tar, path=".", members=None, *, numeric_owner=False ): - for member in tar.getmembers(): member_path = os.path.join(path, member.name) if not is_within_directory(path, member_path): diff --git a/tests/gem5/fs/linux/arm/README.md b/tests/gem5/fs/linux/arm/README.md new file mode 100644 index 0000000000..ba4bf07b36 --- /dev/null +++ b/tests/gem5/fs/linux/arm/README.md @@ -0,0 +1,8 @@ +# FS + +This is a set of full system ARM tests. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/fs/linux/arm --length=[length] +``` diff --git a/tests/gem5/configs/arm_generic.py b/tests/gem5/fs/linux/arm/configs/arm_generic.py similarity index 97% rename from tests/gem5/configs/arm_generic.py rename to tests/gem5/fs/linux/arm/configs/arm_generic.py index df118c7583..d5c9b8c501 100644 --- a/tests/gem5/configs/arm_generic.py +++ b/tests/gem5/fs/linux/arm/configs/arm_generic.py @@ -33,19 +33,24 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from abc import ABCMeta, abstractmethod +from abc import ( + ABCMeta, + abstractmethod, +) + import m5 from m5.objects import * from m5.proxy import * m5.util.addToPath("../configs/") -from common import FSConfig from base_caches import * from base_config import * -from common.cores.arm.O3_ARM_v7a import * +from common import ( + FSConfig, + SysPaths, +) from common.Benchmarks import SysConfig - -from common import SysPaths +from common.cores.arm.O3_ARM_v7a import * class ArmSESystemUniprocessor(BaseSESystemUniprocessor): @@ -56,7 +61,7 @@ class ArmSESystemUniprocessor(BaseSESystemUniprocessor): """ def __init__(self, **kwargs): - super(ArmSESystemUniprocessor, self).__init__(**kwargs) + super().__init__(**kwargs) def create_caches_private(self, cpu): # The atomic SE configurations do not use caches @@ -67,7 +72,7 @@ class ArmSESystemUniprocessor(BaseSESystemUniprocessor): ) -class LinuxArmSystemBuilder(object): +class LinuxArmSystemBuilder: """Mix-in that implements create_system. This mix-in is intended as a convenient way of adding an diff --git a/tests/gem5/configs/base_caches.py b/tests/gem5/fs/linux/arm/configs/base_caches.py similarity index 100% rename from tests/gem5/configs/base_caches.py rename to tests/gem5/fs/linux/arm/configs/base_caches.py diff --git a/tests/gem5/configs/base_config.py b/tests/gem5/fs/linux/arm/configs/base_config.py similarity index 96% rename from tests/gem5/configs/base_config.py rename to tests/gem5/fs/linux/arm/configs/base_config.py index 22987d5eff..8f7cffb255 100644 --- a/tests/gem5/configs/base_config.py +++ b/tests/gem5/fs/linux/arm/configs/base_config.py @@ -33,20 +33,27 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from abc import ABCMeta, abstractmethod import argparse +from abc import ( + ABCMeta, + abstractmethod, +) + +from base_caches import * +from common import ( + FSConfig, + Options, +) +from ruby import Ruby + import m5 from m5.objects import * from m5.proxy import * -from common import FSConfig -from common import Options -from base_caches import * -from ruby import Ruby _have_kvm_support = "BaseKvmCPU" in globals() -class BaseSystem(object, metaclass=ABCMeta): +class BaseSystem(metaclass=ABCMeta): """Base system builder. This class provides some basic functionality for creating an ARM @@ -254,10 +261,10 @@ class BaseSESystem(BaseSystem): """Basic syscall-emulation builder.""" def __init__(self, **kwargs): - super(BaseSESystem, self).__init__(**kwargs) + super().__init__(**kwargs) def init_system(self, system): - super(BaseSESystem, self).init_system(system) + super().init_system(system) def create_system(self): if issubclass(self.mem_class, m5.objects.DRAMInterface): @@ -291,7 +298,7 @@ class BaseSESystemUniprocessor(BaseSESystem): """ def __init__(self, **kwargs): - super(BaseSESystemUniprocessor, self).__init__(**kwargs) + super().__init__(**kwargs) def create_caches_private(self, cpu): # The atomic SE configurations do not use caches @@ -311,10 +318,10 @@ class BaseFSSystem(BaseSystem): """Basic full system builder.""" def __init__(self, **kwargs): - super(BaseFSSystem, self).__init__(**kwargs) + super().__init__(**kwargs) def init_system(self, system): - super(BaseFSSystem, self).init_system(system) + super().init_system(system) if self.use_ruby: # Connect the ruby io port to the PIO bus, @@ -356,7 +363,7 @@ class BaseFSSystemUniprocessor(BaseFSSystem): """ def __init__(self, **kwargs): - super(BaseFSSystemUniprocessor, self).__init__(**kwargs) + super().__init__(**kwargs) def create_caches_private(self, cpu): cpu.addTwoLevelCacheHierarchy( @@ -373,7 +380,7 @@ class BaseFSSwitcheroo(BaseFSSystem): """Uniprocessor system prepared for CPU switching""" def __init__(self, cpu_classes, **kwargs): - super(BaseFSSwitcheroo, self).__init__(**kwargs) + super().__init__(**kwargs) self.cpu_classes = tuple(cpu_classes) def create_cpus(self, cpu_clk_domain): diff --git a/tests/gem5/configs/checkpoint.py b/tests/gem5/fs/linux/arm/configs/checkpoint.py similarity index 100% rename from tests/gem5/configs/checkpoint.py rename to tests/gem5/fs/linux/arm/configs/checkpoint.py index f1b8a1bf72..6f57e3dc91 100644 --- a/tests/gem5/configs/checkpoint.py +++ b/tests/gem5/fs/linux/arm/configs/checkpoint.py @@ -33,9 +33,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from multiprocessing import Process -import sys import os +import sys +from multiprocessing import Process import m5 diff --git a/tests/gem5/configs/realview-minor-dual.py b/tests/gem5/fs/linux/arm/configs/realview-minor-dual.py similarity index 99% rename from tests/gem5/configs/realview-minor-dual.py rename to tests/gem5/fs/linux/arm/configs/realview-minor-dual.py index c6b4e45e9d..08f2be87dc 100644 --- a/tests/gem5/configs/realview-minor-dual.py +++ b/tests/gem5/fs/linux/arm/configs/realview-minor-dual.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystem( aarch64_kernel=False, machine_type="VExpress_GEM5_V1", diff --git a/tests/gem5/configs/realview-minor.py b/tests/gem5/fs/linux/arm/configs/realview-minor.py similarity index 99% rename from tests/gem5/configs/realview-minor.py rename to tests/gem5/fs/linux/arm/configs/realview-minor.py index a6351628fd..108b5ac500 100644 --- a/tests/gem5/configs/realview-minor.py +++ b/tests/gem5/fs/linux/arm/configs/realview-minor.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystemUniprocessor( aarch64_kernel=False, machine_type="VExpress_GEM5_V1", diff --git a/tests/gem5/configs/realview-o3-checker.py b/tests/gem5/fs/linux/arm/configs/realview-o3-checker.py similarity index 99% rename from tests/gem5/configs/realview-o3-checker.py rename to tests/gem5/fs/linux/arm/configs/realview-o3-checker.py index 89e5c66fd3..5f0ab44f9f 100644 --- a/tests/gem5/configs/realview-o3-checker.py +++ b/tests/gem5/fs/linux/arm/configs/realview-o3-checker.py @@ -33,10 +33,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * from common.cores.arm.O3_ARM_v7a import O3_ARM_v7a_3 +from m5.objects import * + root = LinuxArmFSSystemUniprocessor( aarch64_kernel=False, machine_type="VExpress_GEM5_V1", diff --git a/tests/gem5/configs/realview-o3-dual.py b/tests/gem5/fs/linux/arm/configs/realview-o3-dual.py similarity index 99% rename from tests/gem5/configs/realview-o3-dual.py rename to tests/gem5/fs/linux/arm/configs/realview-o3-dual.py index f4326dbda1..cebd29e998 100644 --- a/tests/gem5/configs/realview-o3-dual.py +++ b/tests/gem5/fs/linux/arm/configs/realview-o3-dual.py @@ -33,10 +33,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * from common.cores.arm.O3_ARM_v7a import O3_ARM_v7a_3 +from m5.objects import * + root = LinuxArmFSSystem( aarch64_kernel=False, machine_type="VExpress_GEM5_V1", diff --git a/tests/gem5/configs/realview-o3.py b/tests/gem5/fs/linux/arm/configs/realview-o3.py similarity index 99% rename from tests/gem5/configs/realview-o3.py rename to tests/gem5/fs/linux/arm/configs/realview-o3.py index 6a1b757300..9b6494e04a 100644 --- a/tests/gem5/configs/realview-o3.py +++ b/tests/gem5/fs/linux/arm/configs/realview-o3.py @@ -33,10 +33,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * from common.cores.arm.O3_ARM_v7a import O3_ARM_v7a_3 +from m5.objects import * + root = LinuxArmFSSystemUniprocessor( aarch64_kernel=False, machine_type="VExpress_GEM5_V1", diff --git a/tests/gem5/configs/realview-simple-atomic-checkpoint.py b/tests/gem5/fs/linux/arm/configs/realview-simple-atomic-checkpoint.py similarity index 99% rename from tests/gem5/configs/realview-simple-atomic-checkpoint.py rename to tests/gem5/fs/linux/arm/configs/realview-simple-atomic-checkpoint.py index a60fd96b27..b437ba21cc 100644 --- a/tests/gem5/configs/realview-simple-atomic-checkpoint.py +++ b/tests/gem5/fs/linux/arm/configs/realview-simple-atomic-checkpoint.py @@ -35,9 +35,10 @@ import functools -from m5.objects import * -from arm_generic import * import checkpoint +from arm_generic import * + +from m5.objects import * root = LinuxArmFSSystemUniprocessor( aarch64_kernel=False, diff --git a/tests/gem5/configs/realview-simple-atomic-dual.py b/tests/gem5/fs/linux/arm/configs/realview-simple-atomic-dual.py similarity index 99% rename from tests/gem5/configs/realview-simple-atomic-dual.py rename to tests/gem5/fs/linux/arm/configs/realview-simple-atomic-dual.py index c02f8727ea..0f588bb003 100644 --- a/tests/gem5/configs/realview-simple-atomic-dual.py +++ b/tests/gem5/fs/linux/arm/configs/realview-simple-atomic-dual.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystem( aarch64_kernel=False, machine_type="VExpress_GEM5_V1", diff --git a/tests/gem5/configs/realview-simple-atomic.py b/tests/gem5/fs/linux/arm/configs/realview-simple-atomic.py similarity index 99% rename from tests/gem5/configs/realview-simple-atomic.py rename to tests/gem5/fs/linux/arm/configs/realview-simple-atomic.py index 9c782ad97a..1e079e3c56 100644 --- a/tests/gem5/configs/realview-simple-atomic.py +++ b/tests/gem5/fs/linux/arm/configs/realview-simple-atomic.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystemUniprocessor( aarch64_kernel=False, machine_type="VExpress_GEM5_V1", diff --git a/tests/gem5/configs/realview-simple-timing-dual-ruby.py b/tests/gem5/fs/linux/arm/configs/realview-simple-timing-dual-ruby.py similarity index 99% rename from tests/gem5/configs/realview-simple-timing-dual-ruby.py rename to tests/gem5/fs/linux/arm/configs/realview-simple-timing-dual-ruby.py index 741ededdff..8afbc1a63d 100644 --- a/tests/gem5/configs/realview-simple-timing-dual-ruby.py +++ b/tests/gem5/fs/linux/arm/configs/realview-simple-timing-dual-ruby.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystem( aarch64_kernel=False, machine_type="VExpress_GEM5_V1", diff --git a/tests/gem5/configs/realview-simple-timing-dual.py b/tests/gem5/fs/linux/arm/configs/realview-simple-timing-dual.py similarity index 99% rename from tests/gem5/configs/realview-simple-timing-dual.py rename to tests/gem5/fs/linux/arm/configs/realview-simple-timing-dual.py index aaea9deb83..c3a76ebe8e 100644 --- a/tests/gem5/configs/realview-simple-timing-dual.py +++ b/tests/gem5/fs/linux/arm/configs/realview-simple-timing-dual.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystem( aarch64_kernel=False, machine_type="VExpress_GEM5_V1", diff --git a/tests/gem5/configs/realview-simple-timing-ruby.py b/tests/gem5/fs/linux/arm/configs/realview-simple-timing-ruby.py similarity index 99% rename from tests/gem5/configs/realview-simple-timing-ruby.py rename to tests/gem5/fs/linux/arm/configs/realview-simple-timing-ruby.py index fcca94361e..a99cec184b 100644 --- a/tests/gem5/configs/realview-simple-timing-ruby.py +++ b/tests/gem5/fs/linux/arm/configs/realview-simple-timing-ruby.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystemUniprocessor( aarch64_kernel=False, machine_type="VExpress_GEM5_V1", diff --git a/tests/gem5/configs/realview-simple-timing.py b/tests/gem5/fs/linux/arm/configs/realview-simple-timing.py similarity index 99% rename from tests/gem5/configs/realview-simple-timing.py rename to tests/gem5/fs/linux/arm/configs/realview-simple-timing.py index 2afbdd0a0e..9f40606292 100644 --- a/tests/gem5/configs/realview-simple-timing.py +++ b/tests/gem5/fs/linux/arm/configs/realview-simple-timing.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystemUniprocessor( aarch64_kernel=False, machine_type="VExpress_GEM5_V1", diff --git a/tests/gem5/configs/realview-switcheroo-atomic.py b/tests/gem5/fs/linux/arm/configs/realview-switcheroo-atomic.py similarity index 99% rename from tests/gem5/configs/realview-switcheroo-atomic.py rename to tests/gem5/fs/linux/arm/configs/realview-switcheroo-atomic.py index d2f2100f52..c7b8044246 100644 --- a/tests/gem5/configs/realview-switcheroo-atomic.py +++ b/tests/gem5/fs/linux/arm/configs/realview-switcheroo-atomic.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * -from arm_generic import * import switcheroo +from arm_generic import * + +from m5.objects import * root = LinuxArmFSSwitcheroo( mem_class=SimpleMemory, diff --git a/tests/gem5/configs/realview-switcheroo-full.py b/tests/gem5/fs/linux/arm/configs/realview-switcheroo-full.py similarity index 99% rename from tests/gem5/configs/realview-switcheroo-full.py rename to tests/gem5/fs/linux/arm/configs/realview-switcheroo-full.py index 6ed99a3772..2a468e63b6 100644 --- a/tests/gem5/configs/realview-switcheroo-full.py +++ b/tests/gem5/fs/linux/arm/configs/realview-switcheroo-full.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * -from arm_generic import * import switcheroo +from arm_generic import * + +from m5.objects import * root = LinuxArmFSSwitcheroo( machine_type="VExpress_GEM5_V1", diff --git a/tests/gem5/configs/realview-switcheroo-noncaching-timing.py b/tests/gem5/fs/linux/arm/configs/realview-switcheroo-noncaching-timing.py similarity index 99% rename from tests/gem5/configs/realview-switcheroo-noncaching-timing.py rename to tests/gem5/fs/linux/arm/configs/realview-switcheroo-noncaching-timing.py index cc77f440ab..8afda7b49a 100644 --- a/tests/gem5/configs/realview-switcheroo-noncaching-timing.py +++ b/tests/gem5/fs/linux/arm/configs/realview-switcheroo-noncaching-timing.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * -from arm_generic import * import switcheroo +from arm_generic import * + +from m5.objects import * root = LinuxArmFSSwitcheroo( cpu_classes=(ArmNonCachingSimpleCPU, ArmTimingSimpleCPU) diff --git a/tests/gem5/configs/realview-switcheroo-o3.py b/tests/gem5/fs/linux/arm/configs/realview-switcheroo-o3.py similarity index 99% rename from tests/gem5/configs/realview-switcheroo-o3.py rename to tests/gem5/fs/linux/arm/configs/realview-switcheroo-o3.py index 4fca57ea1e..e0b1a30c43 100644 --- a/tests/gem5/configs/realview-switcheroo-o3.py +++ b/tests/gem5/fs/linux/arm/configs/realview-switcheroo-o3.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * -from arm_generic import * import switcheroo +from arm_generic import * + +from m5.objects import * root = LinuxArmFSSwitcheroo( aarch64_kernel=False, diff --git a/tests/gem5/configs/realview-switcheroo-timing.py b/tests/gem5/fs/linux/arm/configs/realview-switcheroo-timing.py similarity index 99% rename from tests/gem5/configs/realview-switcheroo-timing.py rename to tests/gem5/fs/linux/arm/configs/realview-switcheroo-timing.py index 5157da6dac..a160e809a1 100644 --- a/tests/gem5/configs/realview-switcheroo-timing.py +++ b/tests/gem5/fs/linux/arm/configs/realview-switcheroo-timing.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * -from arm_generic import * import switcheroo +from arm_generic import * + +from m5.objects import * root = LinuxArmFSSwitcheroo( mem_class=DDR3_1600_8x8, diff --git a/tests/gem5/configs/realview64-kvm-dual.py b/tests/gem5/fs/linux/arm/configs/realview64-kvm-dual.py similarity index 97% rename from tests/gem5/configs/realview64-kvm-dual.py rename to tests/gem5/fs/linux/arm/configs/realview64-kvm-dual.py index e64ba2fbf5..91c0ebb0a2 100644 --- a/tests/gem5/configs/realview64-kvm-dual.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-kvm-dual.py @@ -33,9 +33,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * -from m5.ticks import fixGlobalFrequency, fromSeconds + +from m5.objects import * +from m5.ticks import ( + fixGlobalFrequency, + fromSeconds, +) root = LinuxArmFSSystem( mem_mode="atomic_noncaching", diff --git a/tests/gem5/configs/realview64-kvm.py b/tests/gem5/fs/linux/arm/configs/realview64-kvm.py similarity index 99% rename from tests/gem5/configs/realview64-kvm.py rename to tests/gem5/fs/linux/arm/configs/realview64-kvm.py index 8fa6997da8..ac7f7134fa 100644 --- a/tests/gem5/configs/realview64-kvm.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-kvm.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystemUniprocessor( mem_mode="atomic_noncaching", machine_type="VExpress_GEM5_V1", diff --git a/tests/gem5/configs/realview64-minor-dual.py b/tests/gem5/fs/linux/arm/configs/realview64-minor-dual.py similarity index 99% rename from tests/gem5/configs/realview64-minor-dual.py rename to tests/gem5/fs/linux/arm/configs/realview64-minor-dual.py index aed5d83c71..772ad986cf 100644 --- a/tests/gem5/configs/realview64-minor-dual.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-minor-dual.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystem( mem_mode="timing", mem_class=DDR3_1600_8x8, diff --git a/tests/gem5/configs/realview64-minor.py b/tests/gem5/fs/linux/arm/configs/realview64-minor.py similarity index 99% rename from tests/gem5/configs/realview64-minor.py rename to tests/gem5/fs/linux/arm/configs/realview64-minor.py index 7bad3c52ed..630a94bab0 100644 --- a/tests/gem5/configs/realview64-minor.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-minor.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystemUniprocessor( mem_mode="timing", mem_class=DDR3_1600_8x8, cpu_class=ArmMinorCPU ).create_root() diff --git a/tests/gem5/configs/realview64-o3-checker.py b/tests/gem5/fs/linux/arm/configs/realview64-o3-checker.py similarity index 99% rename from tests/gem5/configs/realview64-o3-checker.py rename to tests/gem5/fs/linux/arm/configs/realview64-o3-checker.py index 00d9a5773a..0c32aa6915 100644 --- a/tests/gem5/configs/realview64-o3-checker.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-o3-checker.py @@ -33,10 +33,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * from common.cores.arm.O3_ARM_v7a import O3_ARM_v7a_3 +from m5.objects import * + root = LinuxArmFSSystemUniprocessor( mem_mode="timing", machine_type="VExpress_GEM5_V1", diff --git a/tests/gem5/configs/realview64-o3-dual-ruby.py b/tests/gem5/fs/linux/arm/configs/realview64-o3-dual-ruby.py similarity index 99% rename from tests/gem5/configs/realview64-o3-dual-ruby.py rename to tests/gem5/fs/linux/arm/configs/realview64-o3-dual-ruby.py index a4bffe902e..db8edf3c4f 100644 --- a/tests/gem5/configs/realview64-o3-dual-ruby.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-o3-dual-ruby.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystem( mem_mode="timing", mem_class=DDR3_1600_8x8, diff --git a/tests/gem5/configs/realview64-o3-dual.py b/tests/gem5/fs/linux/arm/configs/realview64-o3-dual.py similarity index 99% rename from tests/gem5/configs/realview64-o3-dual.py rename to tests/gem5/fs/linux/arm/configs/realview64-o3-dual.py index 75fe10e316..1263035764 100644 --- a/tests/gem5/configs/realview64-o3-dual.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-o3-dual.py @@ -33,10 +33,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * from common.cores.arm.O3_ARM_v7a import O3_ARM_v7a_3 +from m5.objects import * + root = LinuxArmFSSystem( mem_mode="timing", mem_class=DDR3_1600_8x8, diff --git a/tests/gem5/configs/realview64-o3.py b/tests/gem5/fs/linux/arm/configs/realview64-o3.py similarity index 99% rename from tests/gem5/configs/realview64-o3.py rename to tests/gem5/fs/linux/arm/configs/realview64-o3.py index c8ae8ec5af..1c7c3b09fc 100644 --- a/tests/gem5/configs/realview64-o3.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-o3.py @@ -33,10 +33,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * from common.cores.arm.O3_ARM_v7a import O3_ARM_v7a_3 +from m5.objects import * + root = LinuxArmFSSystemUniprocessor( mem_mode="timing", mem_class=DDR3_1600_8x8, cpu_class=O3_ARM_v7a_3 ).create_root() diff --git a/tests/gem5/configs/realview64-simple-atomic-checkpoint.py b/tests/gem5/fs/linux/arm/configs/realview64-simple-atomic-checkpoint.py similarity index 99% rename from tests/gem5/configs/realview64-simple-atomic-checkpoint.py rename to tests/gem5/fs/linux/arm/configs/realview64-simple-atomic-checkpoint.py index fa73a0ee6e..ebf9ad7879 100644 --- a/tests/gem5/configs/realview64-simple-atomic-checkpoint.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-simple-atomic-checkpoint.py @@ -35,9 +35,10 @@ import functools -from m5.objects import * -from arm_generic import * import checkpoint +from arm_generic import * + +from m5.objects import * root = LinuxArmFSSystemUniprocessor( mem_mode="atomic", mem_class=SimpleMemory, cpu_class=ArmAtomicSimpleCPU diff --git a/tests/gem5/configs/realview64-simple-atomic-dual.py b/tests/gem5/fs/linux/arm/configs/realview64-simple-atomic-dual.py similarity index 99% rename from tests/gem5/configs/realview64-simple-atomic-dual.py rename to tests/gem5/fs/linux/arm/configs/realview64-simple-atomic-dual.py index 19cf751603..c208816dea 100644 --- a/tests/gem5/configs/realview64-simple-atomic-dual.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-simple-atomic-dual.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystem( mem_mode="atomic", mem_class=SimpleMemory, diff --git a/tests/gem5/configs/realview64-simple-atomic.py b/tests/gem5/fs/linux/arm/configs/realview64-simple-atomic.py similarity index 99% rename from tests/gem5/configs/realview64-simple-atomic.py rename to tests/gem5/fs/linux/arm/configs/realview64-simple-atomic.py index 299dd7b0a7..2595e88311 100644 --- a/tests/gem5/configs/realview64-simple-atomic.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-simple-atomic.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystemUniprocessor( mem_mode="atomic", mem_class=SimpleMemory, cpu_class=ArmAtomicSimpleCPU ).create_root() diff --git a/tests/gem5/configs/realview64-simple-timing-dual-ruby.py b/tests/gem5/fs/linux/arm/configs/realview64-simple-timing-dual-ruby.py similarity index 99% rename from tests/gem5/configs/realview64-simple-timing-dual-ruby.py rename to tests/gem5/fs/linux/arm/configs/realview64-simple-timing-dual-ruby.py index 96ad96355c..8b37b1a2a2 100644 --- a/tests/gem5/configs/realview64-simple-timing-dual-ruby.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-simple-timing-dual-ruby.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystem( mem_mode="timing", mem_class=DDR3_1600_8x8, diff --git a/tests/gem5/configs/realview64-simple-timing-dual.py b/tests/gem5/fs/linux/arm/configs/realview64-simple-timing-dual.py similarity index 99% rename from tests/gem5/configs/realview64-simple-timing-dual.py rename to tests/gem5/fs/linux/arm/configs/realview64-simple-timing-dual.py index 8b62cd3414..e6f1043b84 100644 --- a/tests/gem5/configs/realview64-simple-timing-dual.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-simple-timing-dual.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystem( mem_mode="timing", mem_class=DDR3_1600_8x8, diff --git a/tests/gem5/configs/realview64-simple-timing-ruby.py b/tests/gem5/fs/linux/arm/configs/realview64-simple-timing-ruby.py similarity index 99% rename from tests/gem5/configs/realview64-simple-timing-ruby.py rename to tests/gem5/fs/linux/arm/configs/realview64-simple-timing-ruby.py index f6024537a2..aa8fe14b4c 100644 --- a/tests/gem5/configs/realview64-simple-timing-ruby.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-simple-timing-ruby.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystemUniprocessor( mem_mode="timing", mem_class=DDR3_1600_8x8, diff --git a/tests/gem5/configs/realview64-simple-timing.py b/tests/gem5/fs/linux/arm/configs/realview64-simple-timing.py similarity index 99% rename from tests/gem5/configs/realview64-simple-timing.py rename to tests/gem5/fs/linux/arm/configs/realview64-simple-timing.py index 6897f3b1ad..7b0e7ce137 100644 --- a/tests/gem5/configs/realview64-simple-timing.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-simple-timing.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * from arm_generic import * +from m5.objects import * + root = LinuxArmFSSystemUniprocessor( mem_mode="timing", mem_class=DDR3_1600_8x8, cpu_class=ArmTimingSimpleCPU ).create_root() diff --git a/tests/gem5/configs/realview64-switcheroo-atomic.py b/tests/gem5/fs/linux/arm/configs/realview64-switcheroo-atomic.py similarity index 99% rename from tests/gem5/configs/realview64-switcheroo-atomic.py rename to tests/gem5/fs/linux/arm/configs/realview64-switcheroo-atomic.py index c2f67f0553..37c8d1e77d 100644 --- a/tests/gem5/configs/realview64-switcheroo-atomic.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-switcheroo-atomic.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * -from arm_generic import * import switcheroo +from arm_generic import * + +from m5.objects import * root = LinuxArmFSSwitcheroo( mem_class=SimpleMemory, diff --git a/tests/gem5/configs/realview64-switcheroo-full.py b/tests/gem5/fs/linux/arm/configs/realview64-switcheroo-full.py similarity index 99% rename from tests/gem5/configs/realview64-switcheroo-full.py rename to tests/gem5/fs/linux/arm/configs/realview64-switcheroo-full.py index 020957875d..0cb96586dc 100644 --- a/tests/gem5/configs/realview64-switcheroo-full.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-switcheroo-full.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * -from arm_generic import * import switcheroo +from arm_generic import * + +from m5.objects import * root = LinuxArmFSSwitcheroo( mem_class=DDR3_1600_8x8, diff --git a/tests/gem5/configs/realview64-switcheroo-o3.py b/tests/gem5/fs/linux/arm/configs/realview64-switcheroo-o3.py similarity index 99% rename from tests/gem5/configs/realview64-switcheroo-o3.py rename to tests/gem5/fs/linux/arm/configs/realview64-switcheroo-o3.py index f899337a8f..6da3b59eda 100644 --- a/tests/gem5/configs/realview64-switcheroo-o3.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-switcheroo-o3.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * -from arm_generic import * import switcheroo +from arm_generic import * + +from m5.objects import * root = LinuxArmFSSwitcheroo( mem_class=DDR3_1600_8x8, cpu_classes=(ArmO3CPU, ArmO3CPU) diff --git a/tests/gem5/configs/realview64-switcheroo-timing.py b/tests/gem5/fs/linux/arm/configs/realview64-switcheroo-timing.py similarity index 99% rename from tests/gem5/configs/realview64-switcheroo-timing.py rename to tests/gem5/fs/linux/arm/configs/realview64-switcheroo-timing.py index 4ccce5d953..8ac0a78335 100644 --- a/tests/gem5/configs/realview64-switcheroo-timing.py +++ b/tests/gem5/fs/linux/arm/configs/realview64-switcheroo-timing.py @@ -33,9 +33,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * -from arm_generic import * import switcheroo +from arm_generic import * + +from m5.objects import * root = LinuxArmFSSwitcheroo( mem_class=DDR3_1600_8x8, diff --git a/tests/gem5/configs/switcheroo.py b/tests/gem5/fs/linux/arm/configs/switcheroo.py similarity index 99% rename from tests/gem5/configs/switcheroo.py rename to tests/gem5/fs/linux/arm/configs/switcheroo.py index 72736a9d87..87ab6f4b2b 100644 --- a/tests/gem5/configs/switcheroo.py +++ b/tests/gem5/fs/linux/arm/configs/switcheroo.py @@ -34,9 +34,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import m5 -import _m5 from m5.objects import * +import _m5 + m5.util.addToPath("../configs/") from base_caches import * @@ -53,7 +54,7 @@ class Sequential: def __init__(self, cpus): self.first_cpu = None - for (cpuno, cpu) in enumerate(cpus): + for cpuno, cpu in enumerate(cpus): if not cpu.switched_out: if self.first_cpu != None: fatal("More than one CPU is switched in") @@ -141,7 +142,6 @@ def run_test(root, switcher=None, freq=1000, verbose=False): exit_cause == "target called exit()" or exit_cause == "m5_exit instruction encountered" ): - sys.exit(0) else: print(f"Test failed: Unknown exit cause: {exit_cause}") diff --git a/tests/gem5/fs/linux/arm/run.py b/tests/gem5/fs/linux/arm/run.py index 18a4e5e268..90349b33cd 100644 --- a/tests/gem5/fs/linux/arm/run.py +++ b/tests/gem5/fs/linux/arm/run.py @@ -36,9 +36,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import sys import os import os.path +import sys from os.path import join as joinpath import m5 @@ -62,7 +62,7 @@ gem5_root = sys.argv[3] # path setup sys.path.append(joinpath(gem5_root, "configs")) tests_root = joinpath(gem5_root, "tests") -sys.path.append(joinpath(tests_root, "gem5", "configs")) +sys.path.append(joinpath(tests_root, "gem5", "fs", "linux", "arm", "configs")) exec(compile(open(config).read(), config, "exec")) diff --git a/tests/gem5/fs/linux/arm/test.py b/tests/gem5/fs/linux/arm/test.py index 870024760e..aa5961bcb4 100644 --- a/tests/gem5/fs/linux/arm/test.py +++ b/tests/gem5/fs/linux/arm/test.py @@ -37,12 +37,11 @@ Arm FS simulation tests """ +import re from os.path import join as joinpath from testlib import * -import re - arm_fs_kvm_tests = ["realview64-kvm", "realview64-kvm-dual"] arm_fs_quick_tests = [ @@ -129,7 +128,16 @@ for name in arm_fs_quick_tests: valid_hosts = constants.supported_hosts args = [ - joinpath(config.base_dir, "tests", "gem5", "configs", name + ".py"), + joinpath( + config.base_dir, + "tests", + "gem5", + "fs", + "linux", + "arm", + "configs", + name + ".py", + ), path, config.base_dir, ] @@ -147,7 +155,16 @@ for name in arm_fs_quick_tests: for name in arm_fs_long_tests: args = [ - joinpath(config.base_dir, "tests", "gem5", "configs", name + ".py"), + joinpath( + config.base_dir, + "tests", + "gem5", + "fs", + "linux", + "arm", + "configs", + name + ".py", + ), path, config.base_dir, ] @@ -164,7 +181,16 @@ for name in arm_fs_long_tests: for name in arm_fs_long_tests_arm_target: args = [ - joinpath(config.base_dir, "tests", "gem5", "configs", name + ".py"), + joinpath( + config.base_dir, + "tests", + "gem5", + "fs", + "linux", + "arm", + "configs", + name + ".py", + ), path, config.base_dir, ] diff --git a/tests/gem5/gem5_library_example_tests/README.md b/tests/gem5/gem5_library_example_tests/README.md new file mode 100644 index 0000000000..3abba289f7 --- /dev/null +++ b/tests/gem5/gem5_library_example_tests/README.md @@ -0,0 +1,8 @@ +# gem5 Library Example Tests + +This set of tests checks the examples in `configs/example/gem5_library`, and makes sure they run to completion. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/gem5-resources --length=very-long +``` diff --git a/tests/gem5/gem5_library_example_tests/test_gem5_library_examples.py b/tests/gem5/gem5_library_example_tests/test_gem5_library_examples.py index e43d461b35..9c88ff4307 100644 --- a/tests/gem5/gem5_library_example_tests/test_gem5_library_examples.py +++ b/tests/gem5/gem5_library_example_tests/test_gem5_library_examples.py @@ -28,9 +28,11 @@ This runs simple tests to ensure the examples in `configs/example/gem5_library` still function. They simply check the simulation completed. """ -from testlib import * -import re import os +import re + +from testlib import * +from testlib.log import * if config.bin_path: resource_path = config.bin_path @@ -171,7 +173,14 @@ gem5_verify_config( length=constants.long_tag, ) -if os.access("/dev/kvm", mode=os.R_OK | os.W_OK): +log.test_log.message( + "PARSEC tests are disabled. This is due to our GitHub " + "Actions self-hosted runners only having 60GB of disk space. The " + "PARSEC Disk image is too big to use.", + level=LogLevel.Warn, +) +# 'False' is used to disable the tests. +if False: # os.access("/dev/kvm", mode=os.R_OK | os.W_OK): # The x86-parsec-benchmarks uses KVM cores, this test will therefore only # be run on systems that support KVM. gem5_verify_config( @@ -209,10 +218,7 @@ if os.access("/dev/kvm", mode=os.R_OK | os.W_OK): ), config_args=[ "--benchmark", - "bt", - "--size", - "A", - "--ticks", + "npb-bt-a" "--ticks", "5000000000", ], valid_isas=(constants.all_compiled_tag,), @@ -236,7 +242,7 @@ if os.access("/dev/kvm", mode=os.R_OK | os.W_OK): "gem5_library", "x86-gapbs-benchmarks.py", ), - config_args=["--benchmark", "bfs", "--synthetic", "1", "--size", "1"], + config_args=["--benchmark", "gapbs-bfs-test"], valid_isas=(constants.all_compiled_tag,), protocol="MESI_Two_Level", valid_hosts=(constants.host_x86_64_tag,), @@ -325,6 +331,23 @@ gem5_verify_config( length=constants.very_long_tag, ) +gem5_verify_config( + name="test-gem5-library-example-riscvmatched-microbenchmark-suite", + fixtures=(), + verifiers=(), + config=joinpath( + config.base_dir, + "configs", + "example", + "gem5_library", + "riscvmatched-microbenchmark-suite.py", + ), + config_args=[], + valid_isas=(constants.all_compiled_tag,), + valid_hosts=constants.supported_hosts, + length=constants.long_tag, +) + # The LoopPoint-Checkpointing feature is still under development, therefore # these tests are temporarily disabled until this feature is complete.# diff --git a/tests/gem5/gem5_resources/README.md b/tests/gem5/gem5_resources/README.md new file mode 100644 index 0000000000..8243c01b9f --- /dev/null +++ b/tests/gem5/gem5_resources/README.md @@ -0,0 +1,8 @@ +# gem5 Resources + +This test makes sure that resources you download within gem5 work properly, and the downloaded resource matches the input given. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/gem5_resources --length=very-long +``` diff --git a/tests/gem5/configs/download_check.py b/tests/gem5/gem5_resources/configs/download_check.py similarity index 89% rename from tests/gem5/configs/download_check.py rename to tests/gem5/gem5_resources/configs/download_check.py index e3b06a578d..f051638000 100644 --- a/tests/gem5/configs/download_check.py +++ b/tests/gem5/gem5_resources/configs/download_check.py @@ -24,20 +24,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from gem5.resources.downloader import ( - list_resources, - get_resource, -) - -from gem5.resources.client import get_resource_json_obj - -from gem5.resources.md5_utils import md5 - +import argparse import os import shutil -import argparse from pathlib import Path +from gem5.resources.client import get_resource_json_obj +from gem5.resources.downloader import ( + get_resource, + list_resources, +) +from gem5.resources.md5_utils import md5 + parser = argparse.ArgumentParser( description="A script that will checks that input resource IDs will " "download a resource and that resources md5 value is correct. " @@ -52,6 +50,14 @@ parser.add_argument( "checked", ) +parser.add_argument( + "--skip", + nargs="+", # Accepts 1 or more arguments. + type=str, + help="The resource IDs to skip. If not set, no resources will be skipped.", + required=False, +) + parser.add_argument( "--gem5-version", type=str, @@ -83,9 +89,11 @@ if len(ids) == 0: # We log all the errors as they occur then dump them at the end. This means we # can be aware of all download errors in a single failure. -errors = str() +errors = "" for id in ids: + if args.skip and id in args.skip: + continue if id not in resource_list: errors += ( f"Resource with ID '{id}' not found in " @@ -127,9 +135,14 @@ for id in ids: + f"({md5(Path(download_path))}) differs to that recorded in " + f" gem5-resources ({resource_json['md5sum']}).{os.linesep}" ) + # Remove the downloaded resource. + if os.path.isfile(download_path): + os.remove(download_path) + elif os.path.isdir(download_path): + shutil.rmtree(download_path, ignore_errors=True) + else: + raise Exception("{download_path} is not a file or directory.") -# Remove the downloaded resource. -shutil.rmtree(args.download_directory, ignore_errors=True) # If errors exist, raise an exception highlighting them. if errors: diff --git a/tests/gem5/gem5-resources/test_download_resources.py b/tests/gem5/gem5_resources/test_download_resources.py similarity index 88% rename from tests/gem5/gem5-resources/test_download_resources.py rename to tests/gem5/gem5_resources/test_download_resources.py index c0efc8baad..a15d498717 100644 --- a/tests/gem5/gem5-resources/test_download_resources.py +++ b/tests/gem5/gem5_resources/test_download_resources.py @@ -38,9 +38,19 @@ gem5_verify_config( fixtures=(), verifiers=(), config=joinpath( - config.base_dir, "tests", "gem5", "configs", "download_check.py" + config.base_dir, + "tests", + "gem5", + "gem5_resources", + "configs", + "download_check.py", ), - config_args=["--download-directory", resource_path], + config_args=[ + "--download-directory", + resource_path, + "--skip", + "x86-parsec", + ], valid_isas=(constants.all_compiled_tag,), length=constants.very_long_tag, ) diff --git a/tests/gem5/gpu/README.md b/tests/gem5/gpu/README.md new file mode 100644 index 0000000000..9722e30161 --- /dev/null +++ b/tests/gem5/gpu/README.md @@ -0,0 +1,8 @@ +# GPU + +These tests do random checks to the Ruby GPU protocol within gem5. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/gem5-resources --length=very-long +``` diff --git a/tests/gem5/insttest_se/README.md b/tests/gem5/insttest_se/README.md new file mode 100644 index 0000000000..3895316674 --- /dev/null +++ b/tests/gem5/insttest_se/README.md @@ -0,0 +1,8 @@ +# Inst Test SE + +These test the insttest binary running on the SPARC ISA, checking against different CPU models. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/insttest_se --length=[length] +``` diff --git a/tests/gem5/insttest_se/configs/simple_binary_run.py b/tests/gem5/insttest_se/configs/simple_binary_run.py new file mode 100644 index 0000000000..d48fdfc7ea --- /dev/null +++ b/tests/gem5/insttest_se/configs/simple_binary_run.py @@ -0,0 +1,134 @@ +# Copyright (c) 2021 The Regents of the University of California +# Copyright (c) 2022 Google Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +A run script for a very simple Syscall-Execution running simple binaries. +The system has no cache heirarchy and is as "bare-bones" as you can get in +gem5 while still being functinal. +""" + +import argparse +import importlib + +from m5.util import fatal + +from gem5.components.boards.mem_mode import MemMode +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.base_cpu_core import BaseCPUCore +from gem5.components.processors.base_cpu_processor import BaseCPUProcessor +from gem5.components.processors.cpu_types import ( + CPUTypes, + get_cpu_type_from_str, + get_cpu_types_str_set, +) +from gem5.components.processors.simple_core import SimpleCore +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ( + ISA, + get_isa_from_str, + get_isas_str_set, +) +from gem5.resources.resource import Resource +from gem5.simulate.simulator import Simulator + +cpu_types_string_map = { + CPUTypes.ATOMIC: "AtomicSimpleCPU", + CPUTypes.O3: "O3CPU", + CPUTypes.TIMING: "TimingSimpleCPU", + CPUTypes.KVM: "KvmCPU", + CPUTypes.MINOR: "MinorCPU", +} + +parser = argparse.ArgumentParser( + description="A gem5 script for running simple binaries in SE mode." +) + +parser.add_argument( + "resource", type=str, help="The gem5 resource binary to run." +) + +parser.add_argument( + "cpu", type=str, choices=get_cpu_types_str_set(), help="The CPU type used." +) + +parser.add_argument( + "isa", type=str, choices=get_isas_str_set(), help="The ISA used" +) + +parser.add_argument( + "--resource-directory", + type=str, + required=False, + help="The directory in which resources will be downloaded or exist.", +) + +parser.add_argument( + "--arguments", + type=str, + action="append", + default=[], + required=False, + help="The input arguments for the binary.", +) + +args = parser.parse_args() + +# Setup the system. +cache_hierarchy = NoCache() +memory = SingleChannelDDR3_1600() + +isa_enum = get_isa_from_str(args.isa) +cpu_enum = get_cpu_type_from_str(args.cpu) + +processor = SimpleProcessor( + cpu_type=cpu_enum, + isa=isa_enum, + num_cores=1, +) + +motherboard = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +# Set the workload +binary = Resource(args.resource, resource_directory=args.resource_directory) +motherboard.set_se_binary_workload(binary, arguments=args.arguments) + +# Run the simulation +simulator = Simulator(board=motherboard) +simulator.run() + +print( + "Exiting @ tick {} because {}.".format( + simulator.get_current_tick(), simulator.get_last_exit_event_cause() + ) +) diff --git a/tests/gem5/insttest_se/ref/simout b/tests/gem5/insttest_se/ref/simout.txt similarity index 100% rename from tests/gem5/insttest_se/ref/simout rename to tests/gem5/insttest_se/ref/simout.txt diff --git a/tests/gem5/insttest_se/test.py b/tests/gem5/insttest_se/test.py index 4dde9d6e94..bf59382b52 100644 --- a/tests/gem5/insttest_se/test.py +++ b/tests/gem5/insttest_se/test.py @@ -41,7 +41,9 @@ else: for isa in test_progs: for binary in test_progs[isa]: ref_path = joinpath(getcwd(), "ref") - verifiers = (verifier.MatchStdoutNoPerf(joinpath(ref_path, "simout")),) + verifiers = ( + verifier.MatchStdoutNoPerf(joinpath(ref_path, "simout.txt")), + ) for cpu in cpu_types[isa]: gem5_verify_config( @@ -52,6 +54,7 @@ for isa in test_progs: config.base_dir, "tests", "gem5", + "insttest_se", "configs", "simple_binary_run.py", ), diff --git a/tests/gem5/kvm_fork_tests/README.md b/tests/gem5/kvm_fork_tests/README.md new file mode 100644 index 0000000000..5d2d8e8ff8 --- /dev/null +++ b/tests/gem5/kvm_fork_tests/README.md @@ -0,0 +1,8 @@ +# KVM Fork Tests + +These tests check that gem5 can fork with the KVM cpu, then switch to a different CPU. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/kvm_fork_tests --length=[length] +``` diff --git a/tests/gem5/configs/boot_kvm_fork_run.py b/tests/gem5/kvm_fork_tests/configs/boot_kvm_fork_run.py similarity index 98% rename from tests/gem5/configs/boot_kvm_fork_run.py rename to tests/gem5/kvm_fork_tests/configs/boot_kvm_fork_run.py index 84e273d842..3dc6119454 100644 --- a/tests/gem5/configs/boot_kvm_fork_run.py +++ b/tests/gem5/kvm_fork_tests/configs/boot_kvm_fork_run.py @@ -43,19 +43,19 @@ from textwrap import dedent import m5 from m5.objects import Root -from gem5.components.boards.x86_board import X86Board from gem5.coherence_protocol import CoherenceProtocol -from gem5.isas import ISA +from gem5.components.boards.x86_board import X86Board from gem5.components.memory import SingleChannelDDR3_1600 from gem5.components.processors.cpu_types import ( CPUTypes, - get_cpu_types_str_set, get_cpu_type_from_str, + get_cpu_types_str_set, ) from gem5.components.processors.simple_switchable_processor import ( SimpleSwitchableProcessor, ) -from gem5.resources.resource import Resource +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource from gem5.runtime import get_runtime_coherence_protocol from gem5.utils.requires import requires @@ -179,10 +179,10 @@ kernel_args = motherboard.get_default_kernel_args() + [args.kernel_args] # Set the Full System workload. motherboard.set_kernel_disk_workload( - kernel=Resource( + kernel=obtain_resource( "x86-linux-kernel-5.4.49", resource_directory=args.resource_directory ), - disk_image=Resource( + disk_image=obtain_resource( "x86-ubuntu-18.04-img", resource_directory=args.resource_directory ), readfile_contents=dedent( diff --git a/tests/gem5/kvm-fork-tests/test_kvm_fork_run.py b/tests/gem5/kvm_fork_tests/test_kvm_fork_run.py similarity index 96% rename from tests/gem5/kvm-fork-tests/test_kvm_fork_run.py rename to tests/gem5/kvm_fork_tests/test_kvm_fork_run.py index 7dcfc8517c..be6821d6ef 100644 --- a/tests/gem5/kvm-fork-tests/test_kvm_fork_run.py +++ b/tests/gem5/kvm_fork_tests/test_kvm_fork_run.py @@ -41,7 +41,6 @@ else: def test_kvm_fork_run(cpu: str, num_cpus: int, mem_system: str, length: str): - if not os.access("/dev/kvm", mode=os.R_OK | os.W_OK): # Don't run the tests if KVM is unavailable. return @@ -64,7 +63,12 @@ def test_kvm_fork_run(cpu: str, num_cpus: int, mem_system: str, length: str): verifiers=verifiers, fixtures=(), config=joinpath( - config.base_dir, "tests", "gem5", "configs", "boot_kvm_fork_run.py" + config.base_dir, + "tests", + "gem5", + "kvm_fork_tests", + "configs", + "boot_kvm_fork_run.py", ), config_args=[ "--cpu", diff --git a/tests/gem5/kvm_switch_tests/README.md b/tests/gem5/kvm_switch_tests/README.md new file mode 100644 index 0000000000..9a46aa6c8b --- /dev/null +++ b/tests/gem5/kvm_switch_tests/README.md @@ -0,0 +1,8 @@ +# KVM Switch Tests + +These tests ensure that gem5 can switch processors during simulation. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/kvm_switch_tests --length=[length] +``` diff --git a/tests/gem5/configs/boot_kvm_switch_exit.py b/tests/gem5/kvm_switch_tests/configs/boot_kvm_switch_exit.py similarity index 98% rename from tests/gem5/configs/boot_kvm_switch_exit.py rename to tests/gem5/kvm_switch_tests/configs/boot_kvm_switch_exit.py index 1347e68ba4..c5b59b7f4e 100644 --- a/tests/gem5/configs/boot_kvm_switch_exit.py +++ b/tests/gem5/kvm_switch_tests/configs/boot_kvm_switch_exit.py @@ -33,22 +33,22 @@ import argparse import m5 from m5.objects import Root -from gem5.isas import ISA -from gem5.components.boards.x86_board import X86Board from gem5.coherence_protocol import CoherenceProtocol +from gem5.components.boards.x86_board import X86Board from gem5.components.memory import SingleChannelDDR3_1600 from gem5.components.processors.cpu_types import ( CPUTypes, - get_cpu_types_str_set, get_cpu_type_from_str, + get_cpu_types_str_set, ) from gem5.components.processors.simple_switchable_processor import ( SimpleSwitchableProcessor, ) -from gem5.resources.resource import Resource +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource from gem5.runtime import get_runtime_coherence_protocol -from gem5.simulate.simulator import Simulator from gem5.simulate.exit_event import ExitEvent +from gem5.simulate.simulator import Simulator from gem5.utils.requires import requires parser = argparse.ArgumentParser( @@ -165,10 +165,10 @@ kernal_args = motherboard.get_default_kernel_args() + [args.kernel_args] # Set the Full System workload. motherboard.set_kernel_disk_workload( - kernel=Resource( + kernel=obtain_resource( "x86-linux-kernel-5.4.49", resource_directory=args.resource_directory ), - disk_image=Resource( + disk_image=obtain_resource( "x86-ubuntu-18.04-img", resource_directory=args.resource_directory ), # The first exit signals to switch processors. diff --git a/tests/gem5/kvm-switch-tests/test_kvm_cpu_switch.py b/tests/gem5/kvm_switch_tests/test_kvm_cpu_switch.py similarity index 99% rename from tests/gem5/kvm-switch-tests/test_kvm_cpu_switch.py rename to tests/gem5/kvm_switch_tests/test_kvm_cpu_switch.py index 85e9268e2d..6cc53e6134 100644 --- a/tests/gem5/kvm-switch-tests/test_kvm_cpu_switch.py +++ b/tests/gem5/kvm_switch_tests/test_kvm_cpu_switch.py @@ -41,7 +41,6 @@ else: def test_kvm_switch(cpu: str, num_cpus: int, mem_system: str, length: str): - if not os.access("/dev/kvm", mode=os.R_OK | os.W_OK): # Don't run the tests if KVM is unavailable. return @@ -67,6 +66,7 @@ def test_kvm_switch(cpu: str, num_cpus: int, mem_system: str, length: str): config.base_dir, "tests", "gem5", + "kvm_switch_tests", "configs", "boot_kvm_switch_exit.py", ), diff --git a/tests/gem5/learning_gem5/README.md b/tests/gem5/learning_gem5/README.md new file mode 100644 index 0000000000..eda68d9d4c --- /dev/null +++ b/tests/gem5/learning_gem5/README.md @@ -0,0 +1,9 @@ +# Learning gem5 + +This set of tests ensures that the example scripts for the gem5 tutorial run properly. + +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/learning_gem5 --length=[length] +``` diff --git a/tests/gem5/m5_util/README.md b/tests/gem5/m5_util/README.md new file mode 100644 index 0000000000..133345a904 --- /dev/null +++ b/tests/gem5/m5_util/README.md @@ -0,0 +1,8 @@ +# m5 Util + +These test the util m5 exit assembly instruction. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/m5_util --length=[length] +``` diff --git a/tests/gem5/m5_util/configs/simple_binary_run.py b/tests/gem5/m5_util/configs/simple_binary_run.py new file mode 100644 index 0000000000..b6f52bcd34 --- /dev/null +++ b/tests/gem5/m5_util/configs/simple_binary_run.py @@ -0,0 +1,106 @@ +# Copyright (c) 2021 The Regents of the University of California +# Copyright (c) 2022 Google Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +A run script for a very simple Syscall-Execution running simple binaries. +The system has no cache heirarchy and is as "bare-bones" as you can get in +gem5 while still being functinal. +""" + +import argparse +import importlib + +from m5.util import fatal + +from gem5.components.boards.mem_mode import MemMode +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.base_cpu_core import BaseCPUCore +from gem5.components.processors.base_cpu_processor import BaseCPUProcessor +from gem5.components.processors.cpu_types import ( + CPUTypes, + get_cpu_type_from_str, + get_cpu_types_str_set, +) +from gem5.components.processors.simple_core import SimpleCore +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ( + ISA, + get_isa_from_str, + get_isas_str_set, +) +from gem5.resources.resource import Resource +from gem5.simulate.simulator import Simulator + +parser = argparse.ArgumentParser( + description="A gem5 script for running simple binaries in SE mode." +) + +parser.add_argument( + "resource", type=str, help="The gem5 resource binary to run." +) + +parser.add_argument( + "--resource-directory", + type=str, + required=False, + help="The directory in which resources will be downloaded or exist.", +) + +args = parser.parse_args() + +# Setup the system. +cache_hierarchy = NoCache() +memory = SingleChannelDDR3_1600() + +processor = SimpleProcessor( + cpu_type=CPUTypes.ATOMIC, + isa=ISA.X86, + num_cores=1, +) + +motherboard = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +# Set the workload +binary = Resource(args.resource, resource_directory=args.resource_directory) +motherboard.set_se_binary_workload(binary) + +# Run the simulation +simulator = Simulator(board=motherboard) +simulator.run() + +print( + "Exiting @ tick {} because {}.".format( + simulator.get_current_tick(), simulator.get_last_exit_event_cause() + ) +) diff --git a/tests/gem5/m5_util/test_exit.py b/tests/gem5/m5_util/test_exit.py index b79a8fadc2..9bb4238a57 100644 --- a/tests/gem5/m5_util/test_exit.py +++ b/tests/gem5/m5_util/test_exit.py @@ -40,6 +40,7 @@ Test file for the util m5 exit assembly instruction. """ import re + from testlib import * m5_exit_regex = re.compile( @@ -57,14 +58,17 @@ gem5_verify_config( verifiers=[a], fixtures=(), config=joinpath( - config.base_dir, "tests", "gem5", "configs", "simple_binary_run.py" + config.base_dir, + "tests", + "gem5", + "m5_util", + "configs", + "simple_binary_run.py", ), config_args=[ "x86-m5-exit", - "atomic", "--resource-directory", resource_path, - "x86", ], valid_isas=(constants.all_compiled_tag,), ) diff --git a/tests/gem5/m5threads_test_atomic/README.md b/tests/gem5/m5threads_test_atomic/README.md new file mode 100644 index 0000000000..5e52b91958 --- /dev/null +++ b/tests/gem5/m5threads_test_atomic/README.md @@ -0,0 +1,8 @@ +# m5 Threads Test Atomic + +These are m5threads atomic tests that run against different CPU types. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/m5threads_test_atomic --length=[length] +``` diff --git a/tests/gem5/m5threads_test_atomic/atomic_system.py b/tests/gem5/m5threads_test_atomic/atomic_system.py index b7bd67db10..62a698a025 100644 --- a/tests/gem5/m5threads_test_atomic/atomic_system.py +++ b/tests/gem5/m5threads_test_atomic/atomic_system.py @@ -24,11 +24,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import argparse +import sys + +from caches import * + import m5 from m5.objects import * -from caches import * -import sys -import argparse parser = argparse.ArgumentParser(description="m5threads atomic tester") parser.add_argument("--cpu-type", default="DerivO3CPU") diff --git a/tests/gem5/m5threads_test_atomic/caches.py b/tests/gem5/m5threads_test_atomic/caches.py index fd87d0484d..e9f112013b 100755 --- a/tests/gem5/m5threads_test_atomic/caches.py +++ b/tests/gem5/m5threads_test_atomic/caches.py @@ -31,8 +31,17 @@ gem5 configuration script. """ import m5 -from m5.objects import Cache, L2XBar, StridePrefetcher, SubSystem -from m5.params import AddrRange, AllMemory, MemorySize +from m5.objects import ( + Cache, + L2XBar, + StridePrefetcher, + SubSystem, +) +from m5.params import ( + AddrRange, + AllMemory, + MemorySize, +) from m5.util.convert import toMemorySize # Some specific options for caches @@ -51,7 +60,7 @@ class L1Cache(PrefetchCache): writeback_clean = True def __init__(self, options=None): - super(L1Cache, self).__init__(options) + super().__init__(options) pass def connectBus(self, bus): @@ -71,7 +80,7 @@ class L1ICache(L1Cache): size = "32kB" def __init__(self, opts=None): - super(L1ICache, self).__init__(opts) + super().__init__(opts) def connectCPU(self, cpu): """Connect this cache's port to a CPU icache port""" @@ -85,7 +94,7 @@ class L1DCache(L1Cache): size = "32kB" def __init__(self, opts=None): - super(L1DCache, self).__init__(opts) + super().__init__(opts) def connectCPU(self, cpu): """Connect this cache's port to a CPU dcache port""" @@ -106,7 +115,7 @@ class L2Cache(PrefetchCache): writeback_clean = True def __init__(self, opts=None): - super(L2Cache, self).__init__(opts) + super().__init__(opts) def connectCPUSideBus(self, bus): self.cpu_side = bus.mem_side_ports diff --git a/tests/gem5/m5threads_test_atomic/ref/sparc64/simout b/tests/gem5/m5threads_test_atomic/ref/sparc64/simout.txt similarity index 100% rename from tests/gem5/m5threads_test_atomic/ref/sparc64/simout rename to tests/gem5/m5threads_test_atomic/ref/sparc64/simout.txt diff --git a/tests/gem5/m5threads_test_atomic/test.py b/tests/gem5/m5threads_test_atomic/test.py index 531de83b2f..0af973bd14 100644 --- a/tests/gem5/m5threads_test_atomic/test.py +++ b/tests/gem5/m5threads_test_atomic/test.py @@ -45,7 +45,7 @@ url = config.resource_url + "/test-progs/pthreads/sparc64/" + binary test_atomic = DownloadedProgram(url, base_path, binary) verifiers = ( - verifier.MatchStdoutNoPerf(joinpath(getcwd(), "ref/sparc64/simout")), + verifier.MatchStdoutNoPerf(joinpath(getcwd(), "ref/sparc64/simout.txt")), ) for cpu in cpu_types: diff --git a/tests/gem5/memory/README.md b/tests/gem5/memory/README.md new file mode 100644 index 0000000000..892593394d --- /dev/null +++ b/tests/gem5/memory/README.md @@ -0,0 +1,9 @@ +# Memory + +These run a set of tests on memory within gem5. + +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/memory --length=[length] +``` diff --git a/tests/gem5/memory/simple-run.py b/tests/gem5/memory/simple-run.py index ec5b2d3385..910b632b11 100644 --- a/tests/gem5/memory/simple-run.py +++ b/tests/gem5/memory/simple-run.py @@ -33,11 +33,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import argparse + import m5 from m5.objects import * -import argparse - parser = argparse.ArgumentParser(description="Simple memory tester") parser.add_argument("--bandwidth", default=None) parser.add_argument("--latency", default=None) diff --git a/tests/gem5/multi_isa/README.md b/tests/gem5/multi_isa/README.md new file mode 100644 index 0000000000..1721ad59ec --- /dev/null +++ b/tests/gem5/multi_isa/README.md @@ -0,0 +1,9 @@ +# Multi ISA + +These tests check that all our ISAs are both currrently supported within gem5. + +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/multi_isa --length=[length] +``` diff --git a/tests/gem5/configs/runtime_isa_check.py b/tests/gem5/multi_isa/configs/runtime_isa_check.py similarity index 96% rename from tests/gem5/configs/runtime_isa_check.py rename to tests/gem5/multi_isa/configs/runtime_isa_check.py index 1076e99335..ffc50ee927 100644 --- a/tests/gem5/configs/runtime_isa_check.py +++ b/tests/gem5/multi_isa/configs/runtime_isa_check.py @@ -29,11 +29,15 @@ This is a very simple script to test the output given by `gem5.runtime.get_runtime_isa` """ -from gem5.runtime import get_runtime_isa -from gem5.isas import ISA, get_isas_str_set, get_isa_from_str - import argparse +from gem5.isas import ( + ISA, + get_isa_from_str, + get_isas_str_set, +) +from gem5.runtime import get_runtime_isa + parser = argparse.ArgumentParser( description="A simple script used to check the output of " "`gem5.runtime.get_runtime_isa`" diff --git a/tests/gem5/configs/supported_isa_check.py b/tests/gem5/multi_isa/configs/supported_isa_check.py similarity index 97% rename from tests/gem5/configs/supported_isa_check.py rename to tests/gem5/multi_isa/configs/supported_isa_check.py index 5f535e76a7..5b6dfc7c8a 100644 --- a/tests/gem5/configs/supported_isa_check.py +++ b/tests/gem5/multi_isa/configs/supported_isa_check.py @@ -29,11 +29,14 @@ This is a very simple script to test the output given by `gem5.runtime.get_supported_isas` """ -from gem5.runtime import get_supported_isas -from gem5.isas import get_isas_str_set, get_isa_from_str - -import os import argparse +import os + +from gem5.isas import ( + get_isa_from_str, + get_isas_str_set, +) +from gem5.runtime import get_supported_isas parser = argparse.ArgumentParser( description="A simple script used to check the output of " diff --git a/tests/gem5/multi_isa/test_multi_isa.py b/tests/gem5/multi_isa/test_multi_isa.py index 7d278b75ea..2727fe43de 100644 --- a/tests/gem5/multi_isa/test_multi_isa.py +++ b/tests/gem5/multi_isa/test_multi_isa.py @@ -39,25 +39,6 @@ isa_map = { for isa in isa_map.keys(): if isa in ("x86", "arm", "riscv"): - # We only do these checks for X86, ARM, and RISCV to save compiling - # other ISAs. - gem5_verify_config( - name=f"runtime-isa-check_{isa}-compiled-alone", - verifiers=(), - fixtures=(), - config=joinpath( - config.base_dir, - "tests", - "gem5", - "configs", - "runtime_isa_check.py", - ), - config_args=["-e", isa], - valid_isas=(isa_map[isa],), - valid_hosts=constants.supported_hosts, - length=constants.long_tag, - ) - gem5_verify_config( name=f"supported-isas-check_{isa}-compiled-alone", verifiers=(), @@ -66,6 +47,7 @@ for isa in isa_map.keys(): config.base_dir, "tests", "gem5", + "multi_isa", "configs", "supported_isa_check.py", ), @@ -86,6 +68,7 @@ for isa in isa_map.keys(): config.base_dir, "tests", "gem5", + "multi_isa", "configs", "supported_isa_check.py", ), diff --git a/tests/gem5/parsec_benchmarks/README.md b/tests/gem5/parsec_benchmarks/README.md new file mode 100644 index 0000000000..90dfd5a8e8 --- /dev/null +++ b/tests/gem5/parsec_benchmarks/README.md @@ -0,0 +1,9 @@ +# Parsec Benchmarks + +These tests run through a subset of the parsec benchmarks within gem5. + +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/parsec_benchmarks --length=[length] +``` diff --git a/tests/gem5/configs/parsec_disk_run.py b/tests/gem5/parsec_benchmarks/configs/parsec_disk_run.py similarity index 97% rename from tests/gem5/configs/parsec_disk_run.py rename to tests/gem5/parsec_benchmarks/configs/parsec_disk_run.py index 5c2fa75f65..606205f103 100644 --- a/tests/gem5/configs/parsec_disk_run.py +++ b/tests/gem5/parsec_benchmarks/configs/parsec_disk_run.py @@ -35,27 +35,26 @@ Notes * This will only function for the X86 ISA. """ +import argparse +import time + import m5.stats -from gem5.resources.resource import Resource from gem5.components.boards.x86_board import X86Board from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import ( + get_cpu_type_from_str, + get_cpu_types_str_set, +) from gem5.components.processors.simple_switchable_processor import ( SimpleSwitchableProcessor, ) -from gem5.components.processors.cpu_types import ( - get_cpu_types_str_set, - get_cpu_type_from_str, -) from gem5.isas import ISA -from gem5.runtime import get_runtime_isa, get_runtime_coherence_protocol -from gem5.simulate.simulator import Simulator +from gem5.resources.resource import obtain_resource from gem5.simulate.exit_event import ExitEvent +from gem5.simulate.simulator import Simulator from gem5.utils.requires import requires -import time -import argparse - requires(isa_required=ISA.X86) @@ -147,7 +146,6 @@ args = parser.parse_args() # Setup the cachie hierarchy. if args.mem_system == "classic": - from gem5.components.cachehierarchies.classic.private_l1_private_l2_cache_hierarchy import ( PrivateL1PrivateL2CacheHierarchy, ) @@ -205,15 +203,16 @@ command = ( ) board.set_kernel_disk_workload( - kernel=Resource( + kernel=obtain_resource( "x86-linux-kernel-5.4.49", resource_directory=args.resource_directory ), - disk_image=Resource( + disk_image=obtain_resource( "x86-parsec", resource_directory=args.resource_directory ), readfile_contents=command, ) + # Here we define some custom workbegin/workend exit event generators. Here we # want to switch to detailed CPUs at the beginning of the ROI, then continue to # the end of of the ROI. Then we exit the simulation. diff --git a/tests/gem5/parsec-benchmarks/test_parsec.py b/tests/gem5/parsec_benchmarks/test_parsec.py similarity index 93% rename from tests/gem5/parsec-benchmarks/test_parsec.py rename to tests/gem5/parsec_benchmarks/test_parsec.py index 11735ab43f..60aae0736d 100644 --- a/tests/gem5/parsec-benchmarks/test_parsec.py +++ b/tests/gem5/parsec_benchmarks/test_parsec.py @@ -43,13 +43,19 @@ def test_parsec( size: str, length: str, ): - if (boot_cpu == "kvm" or detailed_cpu == "kvm") and not os.access( "/dev/kvm", mode=os.R_OK | os.W_OK ): # Don't run the tests if KVM is unavailable. return + print( + "WARNING: PARSEC tests are disabled. This is due to our GitHub " + "Actions self-hosted runners only having 60GB of disk space. The " + "PARSEC Disk image is too big to use." + ) + return # Remove this line to re-enable PARSEC tests. + gem5_verify_config( name="{}-boot-cpu_{}-detailed-cpu_{}-cores_{}_{}_{}_parsec-test".format( boot_cpu, detailed_cpu, str(num_cpus), mem_system, benchmark, size @@ -57,7 +63,12 @@ def test_parsec( verifiers=(), fixtures=(), config=joinpath( - config.base_dir, "tests", "gem5", "configs", "parsec_disk_run.py" + config.base_dir, + "tests", + "gem5", + "parsec_benchmarks", + "configs", + "parsec_disk_run.py", ), config_args=[ "--cpu", diff --git a/tests/gem5/replacement-policies/README b/tests/gem5/replacement_policies/README similarity index 100% rename from tests/gem5/replacement-policies/README rename to tests/gem5/replacement_policies/README diff --git a/tests/gem5/replacement-policies/cache_hierarchies.py b/tests/gem5/replacement_policies/configs/cache_hierarchies.py similarity index 99% rename from tests/gem5/replacement-policies/cache_hierarchies.py rename to tests/gem5/replacement_policies/configs/cache_hierarchies.py index 6177dd4ac9..e6eab320a3 100644 --- a/tests/gem5/replacement-policies/cache_hierarchies.py +++ b/tests/gem5/replacement_policies/configs/cache_hierarchies.py @@ -27,12 +27,13 @@ from typing import Type -from gem5.utils.override import overrides +from m5.objects.ReplacementPolicies import BaseReplacementPolicy + +from gem5.components.boards.abstract_board import AbstractBoard from gem5.components.cachehierarchies.ruby.mi_example_cache_hierarchy import ( MIExampleCacheHierarchy, ) -from gem5.components.boards.abstract_board import AbstractBoard -from m5.objects.ReplacementPolicies import BaseReplacementPolicy +from gem5.utils.override import overrides class ModMIExampleCacheHierarchy(MIExampleCacheHierarchy): diff --git a/tests/gem5/replacement-policies/run_replacement_policy.py b/tests/gem5/replacement_policies/configs/run_replacement_policy.py similarity index 100% rename from tests/gem5/replacement-policies/run_replacement_policy.py rename to tests/gem5/replacement_policies/configs/run_replacement_policy.py index ec38bf382f..8f52a061f6 100644 --- a/tests/gem5/replacement-policies/run_replacement_policy.py +++ b/tests/gem5/replacement_policies/configs/run_replacement_policy.py @@ -30,9 +30,9 @@ from importlib.machinery import SourceFileLoader from cache_hierarchies import ModMIExampleCacheHierarchy import m5 - from m5.debug import flags from m5.objects import Root + from gem5.components.boards.test_board import TestBoard from gem5.components.memory.simple import SingleChannelSimpleMemory from gem5.components.processors.complex_generator import ComplexGenerator diff --git a/tests/gem5/replacement-policies/ref/fifo_test1_ld b/tests/gem5/replacement_policies/ref/fifo_test1_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/fifo_test1_ld rename to tests/gem5/replacement_policies/ref/fifo_test1_ld diff --git a/tests/gem5/replacement-policies/ref/fifo_test1_st b/tests/gem5/replacement_policies/ref/fifo_test1_st similarity index 100% rename from tests/gem5/replacement-policies/ref/fifo_test1_st rename to tests/gem5/replacement_policies/ref/fifo_test1_st diff --git a/tests/gem5/replacement-policies/ref/fifo_test2_ld b/tests/gem5/replacement_policies/ref/fifo_test2_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/fifo_test2_ld rename to tests/gem5/replacement_policies/ref/fifo_test2_ld diff --git a/tests/gem5/replacement-policies/ref/fifo_test2_st b/tests/gem5/replacement_policies/ref/fifo_test2_st similarity index 100% rename from tests/gem5/replacement-policies/ref/fifo_test2_st rename to tests/gem5/replacement_policies/ref/fifo_test2_st diff --git a/tests/gem5/replacement-policies/ref/lfu_test1_ld b/tests/gem5/replacement_policies/ref/lfu_test1_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/lfu_test1_ld rename to tests/gem5/replacement_policies/ref/lfu_test1_ld diff --git a/tests/gem5/replacement-policies/ref/lfu_test1_st b/tests/gem5/replacement_policies/ref/lfu_test1_st similarity index 100% rename from tests/gem5/replacement-policies/ref/lfu_test1_st rename to tests/gem5/replacement_policies/ref/lfu_test1_st diff --git a/tests/gem5/replacement-policies/ref/lfu_test2_ld b/tests/gem5/replacement_policies/ref/lfu_test2_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/lfu_test2_ld rename to tests/gem5/replacement_policies/ref/lfu_test2_ld diff --git a/tests/gem5/replacement-policies/ref/lfu_test2_st b/tests/gem5/replacement_policies/ref/lfu_test2_st similarity index 100% rename from tests/gem5/replacement-policies/ref/lfu_test2_st rename to tests/gem5/replacement_policies/ref/lfu_test2_st diff --git a/tests/gem5/replacement-policies/ref/lfu_test3_ld b/tests/gem5/replacement_policies/ref/lfu_test3_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/lfu_test3_ld rename to tests/gem5/replacement_policies/ref/lfu_test3_ld diff --git a/tests/gem5/replacement-policies/ref/lfu_test3_st b/tests/gem5/replacement_policies/ref/lfu_test3_st similarity index 100% rename from tests/gem5/replacement-policies/ref/lfu_test3_st rename to tests/gem5/replacement_policies/ref/lfu_test3_st diff --git a/tests/gem5/replacement-policies/ref/lip_test1_ld b/tests/gem5/replacement_policies/ref/lip_test1_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/lip_test1_ld rename to tests/gem5/replacement_policies/ref/lip_test1_ld diff --git a/tests/gem5/replacement-policies/ref/lip_test1_st b/tests/gem5/replacement_policies/ref/lip_test1_st similarity index 100% rename from tests/gem5/replacement-policies/ref/lip_test1_st rename to tests/gem5/replacement_policies/ref/lip_test1_st diff --git a/tests/gem5/replacement-policies/ref/lru_test1_ld b/tests/gem5/replacement_policies/ref/lru_test1_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/lru_test1_ld rename to tests/gem5/replacement_policies/ref/lru_test1_ld diff --git a/tests/gem5/replacement-policies/ref/lru_test1_st b/tests/gem5/replacement_policies/ref/lru_test1_st similarity index 100% rename from tests/gem5/replacement-policies/ref/lru_test1_st rename to tests/gem5/replacement_policies/ref/lru_test1_st diff --git a/tests/gem5/replacement-policies/ref/lru_test2_ld b/tests/gem5/replacement_policies/ref/lru_test2_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/lru_test2_ld rename to tests/gem5/replacement_policies/ref/lru_test2_ld diff --git a/tests/gem5/replacement-policies/ref/lru_test2_st b/tests/gem5/replacement_policies/ref/lru_test2_st similarity index 100% rename from tests/gem5/replacement-policies/ref/lru_test2_st rename to tests/gem5/replacement_policies/ref/lru_test2_st diff --git a/tests/gem5/replacement-policies/ref/lru_test3_ld b/tests/gem5/replacement_policies/ref/lru_test3_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/lru_test3_ld rename to tests/gem5/replacement_policies/ref/lru_test3_ld diff --git a/tests/gem5/replacement-policies/ref/lru_test3_st b/tests/gem5/replacement_policies/ref/lru_test3_st similarity index 100% rename from tests/gem5/replacement-policies/ref/lru_test3_st rename to tests/gem5/replacement_policies/ref/lru_test3_st diff --git a/tests/gem5/replacement-policies/ref/lru_test4_ld b/tests/gem5/replacement_policies/ref/lru_test4_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/lru_test4_ld rename to tests/gem5/replacement_policies/ref/lru_test4_ld diff --git a/tests/gem5/replacement-policies/ref/lru_test4_st b/tests/gem5/replacement_policies/ref/lru_test4_st similarity index 100% rename from tests/gem5/replacement-policies/ref/lru_test4_st rename to tests/gem5/replacement_policies/ref/lru_test4_st diff --git a/tests/gem5/replacement-policies/ref/mru_test1_ld b/tests/gem5/replacement_policies/ref/mru_test1_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/mru_test1_ld rename to tests/gem5/replacement_policies/ref/mru_test1_ld diff --git a/tests/gem5/replacement-policies/ref/mru_test1_st b/tests/gem5/replacement_policies/ref/mru_test1_st similarity index 100% rename from tests/gem5/replacement-policies/ref/mru_test1_st rename to tests/gem5/replacement_policies/ref/mru_test1_st diff --git a/tests/gem5/replacement-policies/ref/mru_test2_ld b/tests/gem5/replacement_policies/ref/mru_test2_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/mru_test2_ld rename to tests/gem5/replacement_policies/ref/mru_test2_ld diff --git a/tests/gem5/replacement-policies/ref/mru_test2_st b/tests/gem5/replacement_policies/ref/mru_test2_st similarity index 100% rename from tests/gem5/replacement-policies/ref/mru_test2_st rename to tests/gem5/replacement_policies/ref/mru_test2_st diff --git a/tests/gem5/replacement-policies/ref/nru_test1_ld b/tests/gem5/replacement_policies/ref/nru_test1_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/nru_test1_ld rename to tests/gem5/replacement_policies/ref/nru_test1_ld diff --git a/tests/gem5/replacement-policies/ref/nru_test1_st b/tests/gem5/replacement_policies/ref/nru_test1_st similarity index 100% rename from tests/gem5/replacement-policies/ref/nru_test1_st rename to tests/gem5/replacement_policies/ref/nru_test1_st diff --git a/tests/gem5/replacement-policies/ref/rrip_test1_ld b/tests/gem5/replacement_policies/ref/rrip_test1_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/rrip_test1_ld rename to tests/gem5/replacement_policies/ref/rrip_test1_ld diff --git a/tests/gem5/replacement-policies/ref/rrip_test1_st b/tests/gem5/replacement_policies/ref/rrip_test1_st similarity index 100% rename from tests/gem5/replacement-policies/ref/rrip_test1_st rename to tests/gem5/replacement_policies/ref/rrip_test1_st diff --git a/tests/gem5/replacement-policies/ref/rrip_test2_ld b/tests/gem5/replacement_policies/ref/rrip_test2_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/rrip_test2_ld rename to tests/gem5/replacement_policies/ref/rrip_test2_ld diff --git a/tests/gem5/replacement-policies/ref/rrip_test2_st b/tests/gem5/replacement_policies/ref/rrip_test2_st similarity index 100% rename from tests/gem5/replacement-policies/ref/rrip_test2_st rename to tests/gem5/replacement_policies/ref/rrip_test2_st diff --git a/tests/gem5/replacement-policies/ref/second_chance_test1_ld b/tests/gem5/replacement_policies/ref/second_chance_test1_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/second_chance_test1_ld rename to tests/gem5/replacement_policies/ref/second_chance_test1_ld diff --git a/tests/gem5/replacement-policies/ref/second_chance_test1_st b/tests/gem5/replacement_policies/ref/second_chance_test1_st similarity index 100% rename from tests/gem5/replacement-policies/ref/second_chance_test1_st rename to tests/gem5/replacement_policies/ref/second_chance_test1_st diff --git a/tests/gem5/replacement-policies/ref/second_chance_test2_ld b/tests/gem5/replacement_policies/ref/second_chance_test2_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/second_chance_test2_ld rename to tests/gem5/replacement_policies/ref/second_chance_test2_ld diff --git a/tests/gem5/replacement-policies/ref/second_chance_test2_st b/tests/gem5/replacement_policies/ref/second_chance_test2_st similarity index 100% rename from tests/gem5/replacement-policies/ref/second_chance_test2_st rename to tests/gem5/replacement_policies/ref/second_chance_test2_st diff --git a/tests/gem5/replacement-policies/ref/second_chance_test3_ld b/tests/gem5/replacement_policies/ref/second_chance_test3_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/second_chance_test3_ld rename to tests/gem5/replacement_policies/ref/second_chance_test3_ld diff --git a/tests/gem5/replacement-policies/ref/second_chance_test3_st b/tests/gem5/replacement_policies/ref/second_chance_test3_st similarity index 100% rename from tests/gem5/replacement-policies/ref/second_chance_test3_st rename to tests/gem5/replacement_policies/ref/second_chance_test3_st diff --git a/tests/gem5/replacement-policies/ref/tree_plru_test1_ld b/tests/gem5/replacement_policies/ref/tree_plru_test1_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/tree_plru_test1_ld rename to tests/gem5/replacement_policies/ref/tree_plru_test1_ld diff --git a/tests/gem5/replacement-policies/ref/tree_plru_test1_st b/tests/gem5/replacement_policies/ref/tree_plru_test1_st similarity index 100% rename from tests/gem5/replacement-policies/ref/tree_plru_test1_st rename to tests/gem5/replacement_policies/ref/tree_plru_test1_st diff --git a/tests/gem5/replacement-policies/ref/tree_plru_test2_ld b/tests/gem5/replacement_policies/ref/tree_plru_test2_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/tree_plru_test2_ld rename to tests/gem5/replacement_policies/ref/tree_plru_test2_ld diff --git a/tests/gem5/replacement-policies/ref/tree_plru_test2_st b/tests/gem5/replacement_policies/ref/tree_plru_test2_st similarity index 100% rename from tests/gem5/replacement-policies/ref/tree_plru_test2_st rename to tests/gem5/replacement_policies/ref/tree_plru_test2_st diff --git a/tests/gem5/replacement-policies/ref/tree_plru_test3_ld b/tests/gem5/replacement_policies/ref/tree_plru_test3_ld similarity index 100% rename from tests/gem5/replacement-policies/ref/tree_plru_test3_ld rename to tests/gem5/replacement_policies/ref/tree_plru_test3_ld diff --git a/tests/gem5/replacement-policies/ref/tree_plru_test3_st b/tests/gem5/replacement_policies/ref/tree_plru_test3_st similarity index 100% rename from tests/gem5/replacement-policies/ref/tree_plru_test3_st rename to tests/gem5/replacement_policies/ref/tree_plru_test3_st diff --git a/tests/gem5/replacement_policies/run_replacement_policy.py b/tests/gem5/replacement_policies/run_replacement_policy.py new file mode 100644 index 0000000000..8f52a061f6 --- /dev/null +++ b/tests/gem5/replacement_policies/run_replacement_policy.py @@ -0,0 +1,94 @@ +# Copyright (c) 2022 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import argparse +from importlib.machinery import SourceFileLoader + +from cache_hierarchies import ModMIExampleCacheHierarchy + +import m5 +from m5.debug import flags +from m5.objects import Root + +from gem5.components.boards.test_board import TestBoard +from gem5.components.memory.simple import SingleChannelSimpleMemory +from gem5.components.processors.complex_generator import ComplexGenerator + +argparser = argparse.ArgumentParser() + +argparser.add_argument( + "config_name", + type=str, + help="Name of the python file " + "including the defintion of a python generator and " + "importing the right replacement policy. The python " + "generator should only assume one positional argument " + "and be named python_generator. The replacement policy" + " should be imported as rp.", +) +argparser.add_argument( + "config_path", + type=str, + help="Path to the python file" "specified by config_name.", +) + +args = argparser.parse_args() + +module = SourceFileLoader(args.config_name, args.config_path).load_module() +python_generator = module.python_generator +rp_class = module.rp + +flags["RubyHitMiss"].enable() + +cache_hierarchy = ModMIExampleCacheHierarchy(rp_class) + +memory = SingleChannelSimpleMemory( + latency="30ns", + latency_var="0ns", + bandwidth="12.8GiB/s", + size="512MiB", +) + +generator = ComplexGenerator() +generator.set_traffic_from_python_generator(python_generator) + +# We use the Test Board. This is a special board to run traffic generation +# tasks +motherboard = TestBoard( + clk_freq="1GHz", + generator=generator, # We pass the traffic generator as the processor. + memory=memory, + cache_hierarchy=cache_hierarchy, +) +root = Root(full_system=False, system=motherboard) + +motherboard._pre_instantiate() +m5.instantiate() + +generator.start_traffic() +print("Beginning simulation!") +exit_event = m5.simulate() +print(f"Exiting @ tick {m5.curTick()} because {exit_event.getCause()}.") diff --git a/tests/gem5/replacement-policies/test_replacement_policies.py b/tests/gem5/replacement_policies/test_replacement_policies.py similarity index 98% rename from tests/gem5/replacement-policies/test_replacement_policies.py rename to tests/gem5/replacement_policies/test_replacement_policies.py index 4c74f72a2a..dd95c9c851 100644 --- a/tests/gem5/replacement-policies/test_replacement_policies.py +++ b/tests/gem5/replacement_policies/test_replacement_policies.py @@ -44,7 +44,8 @@ def test_replacement_policy(config_name: str, config_path: str) -> None: config.base_dir, "tests", "gem5", - "replacement-policies", + "replacement_policies", + "configs", "run_replacement_policy.py", ), config_args=[config_name, config_path], diff --git a/tests/gem5/replacement-policies/traces/fifo_test1_ld.py b/tests/gem5/replacement_policies/traces/fifo_test1_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/fifo_test1_ld.py rename to tests/gem5/replacement_policies/traces/fifo_test1_ld.py diff --git a/tests/gem5/replacement-policies/traces/fifo_test1_st.py b/tests/gem5/replacement_policies/traces/fifo_test1_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/fifo_test1_st.py rename to tests/gem5/replacement_policies/traces/fifo_test1_st.py diff --git a/tests/gem5/replacement-policies/traces/fifo_test2_ld.py b/tests/gem5/replacement_policies/traces/fifo_test2_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/fifo_test2_ld.py rename to tests/gem5/replacement_policies/traces/fifo_test2_ld.py diff --git a/tests/gem5/replacement-policies/traces/fifo_test2_st.py b/tests/gem5/replacement_policies/traces/fifo_test2_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/fifo_test2_st.py rename to tests/gem5/replacement_policies/traces/fifo_test2_st.py diff --git a/tests/gem5/replacement-policies/traces/lfu_test1_ld.py b/tests/gem5/replacement_policies/traces/lfu_test1_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lfu_test1_ld.py rename to tests/gem5/replacement_policies/traces/lfu_test1_ld.py diff --git a/tests/gem5/replacement-policies/traces/lfu_test1_st.py b/tests/gem5/replacement_policies/traces/lfu_test1_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lfu_test1_st.py rename to tests/gem5/replacement_policies/traces/lfu_test1_st.py diff --git a/tests/gem5/replacement-policies/traces/lfu_test2_ld.py b/tests/gem5/replacement_policies/traces/lfu_test2_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lfu_test2_ld.py rename to tests/gem5/replacement_policies/traces/lfu_test2_ld.py diff --git a/tests/gem5/replacement-policies/traces/lfu_test2_st.py b/tests/gem5/replacement_policies/traces/lfu_test2_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lfu_test2_st.py rename to tests/gem5/replacement_policies/traces/lfu_test2_st.py diff --git a/tests/gem5/replacement-policies/traces/lfu_test3_ld.py b/tests/gem5/replacement_policies/traces/lfu_test3_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lfu_test3_ld.py rename to tests/gem5/replacement_policies/traces/lfu_test3_ld.py diff --git a/tests/gem5/replacement-policies/traces/lfu_test3_st.py b/tests/gem5/replacement_policies/traces/lfu_test3_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lfu_test3_st.py rename to tests/gem5/replacement_policies/traces/lfu_test3_st.py diff --git a/tests/gem5/replacement-policies/traces/lip_test1_ld.py b/tests/gem5/replacement_policies/traces/lip_test1_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lip_test1_ld.py rename to tests/gem5/replacement_policies/traces/lip_test1_ld.py diff --git a/tests/gem5/replacement-policies/traces/lip_test1_st.py b/tests/gem5/replacement_policies/traces/lip_test1_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lip_test1_st.py rename to tests/gem5/replacement_policies/traces/lip_test1_st.py diff --git a/tests/gem5/replacement-policies/traces/lru_test1_ld.py b/tests/gem5/replacement_policies/traces/lru_test1_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lru_test1_ld.py rename to tests/gem5/replacement_policies/traces/lru_test1_ld.py diff --git a/tests/gem5/replacement-policies/traces/lru_test1_st.py b/tests/gem5/replacement_policies/traces/lru_test1_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lru_test1_st.py rename to tests/gem5/replacement_policies/traces/lru_test1_st.py diff --git a/tests/gem5/replacement-policies/traces/lru_test2_ld.py b/tests/gem5/replacement_policies/traces/lru_test2_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lru_test2_ld.py rename to tests/gem5/replacement_policies/traces/lru_test2_ld.py diff --git a/tests/gem5/replacement-policies/traces/lru_test2_st.py b/tests/gem5/replacement_policies/traces/lru_test2_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lru_test2_st.py rename to tests/gem5/replacement_policies/traces/lru_test2_st.py diff --git a/tests/gem5/replacement-policies/traces/lru_test3_ld.py b/tests/gem5/replacement_policies/traces/lru_test3_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lru_test3_ld.py rename to tests/gem5/replacement_policies/traces/lru_test3_ld.py diff --git a/tests/gem5/replacement-policies/traces/lru_test3_st.py b/tests/gem5/replacement_policies/traces/lru_test3_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lru_test3_st.py rename to tests/gem5/replacement_policies/traces/lru_test3_st.py diff --git a/tests/gem5/replacement-policies/traces/lru_test4_ld.py b/tests/gem5/replacement_policies/traces/lru_test4_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lru_test4_ld.py rename to tests/gem5/replacement_policies/traces/lru_test4_ld.py diff --git a/tests/gem5/replacement-policies/traces/lru_test4_st.py b/tests/gem5/replacement_policies/traces/lru_test4_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/lru_test4_st.py rename to tests/gem5/replacement_policies/traces/lru_test4_st.py diff --git a/tests/gem5/replacement-policies/traces/mru_test1_ld.py b/tests/gem5/replacement_policies/traces/mru_test1_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/mru_test1_ld.py rename to tests/gem5/replacement_policies/traces/mru_test1_ld.py diff --git a/tests/gem5/replacement-policies/traces/mru_test1_st.py b/tests/gem5/replacement_policies/traces/mru_test1_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/mru_test1_st.py rename to tests/gem5/replacement_policies/traces/mru_test1_st.py diff --git a/tests/gem5/replacement-policies/traces/mru_test2_ld.py b/tests/gem5/replacement_policies/traces/mru_test2_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/mru_test2_ld.py rename to tests/gem5/replacement_policies/traces/mru_test2_ld.py diff --git a/tests/gem5/replacement-policies/traces/mru_test2_st.py b/tests/gem5/replacement_policies/traces/mru_test2_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/mru_test2_st.py rename to tests/gem5/replacement_policies/traces/mru_test2_st.py diff --git a/tests/gem5/replacement-policies/traces/nru_test1_ld.py b/tests/gem5/replacement_policies/traces/nru_test1_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/nru_test1_ld.py rename to tests/gem5/replacement_policies/traces/nru_test1_ld.py diff --git a/tests/gem5/replacement-policies/traces/nru_test1_st.py b/tests/gem5/replacement_policies/traces/nru_test1_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/nru_test1_st.py rename to tests/gem5/replacement_policies/traces/nru_test1_st.py diff --git a/tests/gem5/replacement-policies/traces/rrip_test1_ld.py b/tests/gem5/replacement_policies/traces/rrip_test1_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/rrip_test1_ld.py rename to tests/gem5/replacement_policies/traces/rrip_test1_ld.py diff --git a/tests/gem5/replacement-policies/traces/rrip_test1_st.py b/tests/gem5/replacement_policies/traces/rrip_test1_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/rrip_test1_st.py rename to tests/gem5/replacement_policies/traces/rrip_test1_st.py diff --git a/tests/gem5/replacement-policies/traces/rrip_test2_ld.py b/tests/gem5/replacement_policies/traces/rrip_test2_ld.py similarity index 99% rename from tests/gem5/replacement-policies/traces/rrip_test2_ld.py rename to tests/gem5/replacement_policies/traces/rrip_test2_ld.py index b9f2ee026e..dcc8df90ee 100644 --- a/tests/gem5/replacement-policies/traces/rrip_test2_ld.py +++ b/tests/gem5/replacement_policies/traces/rrip_test2_ld.py @@ -52,7 +52,6 @@ from m5.objects.ReplacementPolicies import RRIPRP as rp def python_generator(generator): - yield generator.createLinear(60000, 0, 63, 64, 30000, 30000, 100, 0) yield generator.createLinear(60000, 0, 63, 64, 30000, 30000, 100, 0) yield generator.createLinear(60000, 0, 63, 64, 30000, 30000, 100, 0) diff --git a/tests/gem5/replacement-policies/traces/rrip_test2_st.py b/tests/gem5/replacement_policies/traces/rrip_test2_st.py similarity index 99% rename from tests/gem5/replacement-policies/traces/rrip_test2_st.py rename to tests/gem5/replacement_policies/traces/rrip_test2_st.py index be23756a95..b53ec4a076 100644 --- a/tests/gem5/replacement-policies/traces/rrip_test2_st.py +++ b/tests/gem5/replacement_policies/traces/rrip_test2_st.py @@ -52,7 +52,6 @@ from m5.objects.ReplacementPolicies import RRIPRP as rp def python_generator(generator): - yield generator.createLinear(60000, 0, 63, 64, 30000, 30000, 0, 0) yield generator.createLinear(60000, 0, 63, 64, 30000, 30000, 0, 0) yield generator.createLinear(60000, 0, 63, 64, 30000, 30000, 0, 0) diff --git a/tests/gem5/replacement-policies/traces/second_chance_test1_ld.py b/tests/gem5/replacement_policies/traces/second_chance_test1_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/second_chance_test1_ld.py rename to tests/gem5/replacement_policies/traces/second_chance_test1_ld.py diff --git a/tests/gem5/replacement-policies/traces/second_chance_test1_st.py b/tests/gem5/replacement_policies/traces/second_chance_test1_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/second_chance_test1_st.py rename to tests/gem5/replacement_policies/traces/second_chance_test1_st.py diff --git a/tests/gem5/replacement-policies/traces/second_chance_test2_ld.py b/tests/gem5/replacement_policies/traces/second_chance_test2_ld.py similarity index 99% rename from tests/gem5/replacement-policies/traces/second_chance_test2_ld.py rename to tests/gem5/replacement_policies/traces/second_chance_test2_ld.py index d187cbec3f..88c8e462a2 100644 --- a/tests/gem5/replacement-policies/traces/second_chance_test2_ld.py +++ b/tests/gem5/replacement_policies/traces/second_chance_test2_ld.py @@ -52,7 +52,6 @@ from m5.objects.ReplacementPolicies import SecondChanceRP as rp def python_generator(generator): - yield generator.createLinear(60000, 0, 63, 64, 30000, 30000, 100, 0) yield generator.createLinear(60000, 128, 191, 64, 30000, 30000, 100, 0) yield generator.createLinear(60000, 256, 319, 64, 30000, 30000, 100, 0) diff --git a/tests/gem5/replacement-policies/traces/second_chance_test2_st.py b/tests/gem5/replacement_policies/traces/second_chance_test2_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/second_chance_test2_st.py rename to tests/gem5/replacement_policies/traces/second_chance_test2_st.py diff --git a/tests/gem5/replacement-policies/traces/second_chance_test3_ld.py b/tests/gem5/replacement_policies/traces/second_chance_test3_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/second_chance_test3_ld.py rename to tests/gem5/replacement_policies/traces/second_chance_test3_ld.py diff --git a/tests/gem5/replacement-policies/traces/second_chance_test3_st.py b/tests/gem5/replacement_policies/traces/second_chance_test3_st.py similarity index 99% rename from tests/gem5/replacement-policies/traces/second_chance_test3_st.py rename to tests/gem5/replacement_policies/traces/second_chance_test3_st.py index 53dcbffe89..d40383e8fc 100644 --- a/tests/gem5/replacement-policies/traces/second_chance_test3_st.py +++ b/tests/gem5/replacement_policies/traces/second_chance_test3_st.py @@ -54,7 +54,6 @@ from m5.objects.ReplacementPolicies import SecondChanceRP as rp def python_generator(generator): - yield generator.createLinear(60000, 0, 63, 64, 30000, 30000, 0, 0) yield generator.createLinear(60000, 128, 191, 64, 30000, 30000, 0, 0) yield generator.createLinear(60000, 256, 319, 64, 30000, 30000, 0, 0) diff --git a/tests/gem5/replacement-policies/traces/tree_plru_test1_ld.py b/tests/gem5/replacement_policies/traces/tree_plru_test1_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/tree_plru_test1_ld.py rename to tests/gem5/replacement_policies/traces/tree_plru_test1_ld.py diff --git a/tests/gem5/replacement-policies/traces/tree_plru_test1_st.py b/tests/gem5/replacement_policies/traces/tree_plru_test1_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/tree_plru_test1_st.py rename to tests/gem5/replacement_policies/traces/tree_plru_test1_st.py diff --git a/tests/gem5/replacement-policies/traces/tree_plru_test2_ld.py b/tests/gem5/replacement_policies/traces/tree_plru_test2_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/tree_plru_test2_ld.py rename to tests/gem5/replacement_policies/traces/tree_plru_test2_ld.py diff --git a/tests/gem5/replacement-policies/traces/tree_plru_test2_st.py b/tests/gem5/replacement_policies/traces/tree_plru_test2_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/tree_plru_test2_st.py rename to tests/gem5/replacement_policies/traces/tree_plru_test2_st.py diff --git a/tests/gem5/replacement-policies/traces/tree_plru_test3_ld.py b/tests/gem5/replacement_policies/traces/tree_plru_test3_ld.py similarity index 100% rename from tests/gem5/replacement-policies/traces/tree_plru_test3_ld.py rename to tests/gem5/replacement_policies/traces/tree_plru_test3_ld.py diff --git a/tests/gem5/replacement-policies/traces/tree_plru_test3_st.py b/tests/gem5/replacement_policies/traces/tree_plru_test3_st.py similarity index 100% rename from tests/gem5/replacement-policies/traces/tree_plru_test3_st.py rename to tests/gem5/replacement_policies/traces/tree_plru_test3_st.py diff --git a/tests/gem5/riscv_boot_tests/README.md b/tests/gem5/riscv_boot_tests/README.md new file mode 100644 index 0000000000..002cc4d09d --- /dev/null +++ b/tests/gem5/riscv_boot_tests/README.md @@ -0,0 +1,9 @@ +# RISCV Boot Tests + +These tests run a series of Linux boots on the RISCVBoard. +It varies the CPU type, number of CPUs, and memory used for each run. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/riscv_boot_tests --length=[length] +``` diff --git a/tests/gem5/configs/riscv_boot_exit_run.py b/tests/gem5/riscv_boot_tests/configs/riscv_boot_exit_run.py similarity index 97% rename from tests/gem5/configs/riscv_boot_exit_run.py rename to tests/gem5/riscv_boot_tests/configs/riscv_boot_exit_run.py index e9fc06b27b..0192d3dbff 100644 --- a/tests/gem5/configs/riscv_boot_exit_run.py +++ b/tests/gem5/riscv_boot_tests/configs/riscv_boot_exit_run.py @@ -33,18 +33,20 @@ Characteristics * Runs exclusively on the RISC-V ISA with the classic caches """ -from gem5.isas import ISA -from gem5.utils.requires import requires -from gem5.resources.resource import Resource -from gem5.components.processors.cpu_types import CPUTypes -from gem5.components.boards.riscv_board import RiscvBoard -from gem5.components.processors.simple_processor import SimpleProcessor -from gem5.simulate.simulator import Simulator -from gem5.resources.workload import Workload - import argparse import importlib +from gem5.components.boards.riscv_board import RiscvBoard +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import ( + Resource, + obtain_resource, +) +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires + parser = argparse.ArgumentParser( description="A script to run the RISCV boot exit tests." ) @@ -160,7 +162,7 @@ board = RiscvBoard( ) # Set the workload. -workload = Workload( +workload = obtain_resource( "riscv-ubuntu-20.04-boot", resource_directory=args.resource_directory ) board.set_workload(workload) diff --git a/tests/gem5/riscv-boot-tests/test_linux_boot.py b/tests/gem5/riscv_boot_tests/test_linux_boot.py similarity index 99% rename from tests/gem5/riscv-boot-tests/test_linux_boot.py rename to tests/gem5/riscv_boot_tests/test_linux_boot.py index 55e0ae6109..bb2c3a4b04 100644 --- a/tests/gem5/riscv-boot-tests/test_linux_boot.py +++ b/tests/gem5/riscv_boot_tests/test_linux_boot.py @@ -25,7 +25,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import re - from typing import Optional from testlib import * @@ -44,7 +43,6 @@ def test_boot( length: str, to_tick: Optional[int] = None, ): - name = "{}-cpu_{}-cores_{}_{}_riscv-boot-test".format( cpu, str(num_cpus), cache_type, memory_class ) @@ -80,6 +78,7 @@ def test_boot( config.base_dir, "tests", "gem5", + "riscv_boot_tests", "configs", "riscv_boot_exit_run.py", ), diff --git a/tests/gem5/se_mode/hello_se/README.md b/tests/gem5/se_mode/hello_se/README.md new file mode 100644 index 0000000000..b3109692b0 --- /dev/null +++ b/tests/gem5/se_mode/hello_se/README.md @@ -0,0 +1,8 @@ +# SE Mode + +These tests use the SimpleBoard to test simple binaries in SE mode, both with single and multi cores. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/se_mode/hello_se --length=[length] +``` diff --git a/tests/gem5/configs/simple_binary_run.py b/tests/gem5/se_mode/hello_se/configs/simple_binary_run.py similarity index 79% rename from tests/gem5/configs/simple_binary_run.py rename to tests/gem5/se_mode/hello_se/configs/simple_binary_run.py index f5e097eaae..08569f05a0 100644 --- a/tests/gem5/configs/simple_binary_run.py +++ b/tests/gem5/se_mode/hello_se/configs/simple_binary_run.py @@ -1,4 +1,5 @@ # Copyright (c) 2021 The Regents of the University of California +# Copyright (c) 2022 Google Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -30,24 +31,39 @@ The system has no cache heirarchy and is as "bare-bones" as you can get in gem5 while still being functinal. """ -from gem5.resources.resource import Resource -from gem5.components.processors.cpu_types import ( - get_cpu_types_str_set, - get_cpu_type_from_str, -) -from gem5.components.memory import SingleChannelDDR3_1600 +import argparse +import importlib + +from m5.util import fatal + +from gem5.components.boards.mem_mode import MemMode from gem5.components.boards.simple_board import SimpleBoard from gem5.components.cachehierarchies.classic.no_cache import NoCache -from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.components.memory import SingleChannelDDR3_1600 from gem5.components.processors.base_cpu_core import BaseCPUCore from gem5.components.processors.base_cpu_processor import BaseCPUProcessor +from gem5.components.processors.cpu_types import ( + CPUTypes, + get_cpu_type_from_str, + get_cpu_types_str_set, +) from gem5.components.processors.simple_core import SimpleCore -from gem5.components.boards.mem_mode import MemMode -from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ( + ISA, + get_isa_from_str, + get_isas_str_set, +) +from gem5.resources.resource import obtain_resource from gem5.simulate.simulator import Simulator -from gem5.isas import get_isa_from_str, get_isas_str_set -import argparse +cpu_types_string_map = { + CPUTypes.ATOMIC: "AtomicSimpleCPU", + CPUTypes.O3: "O3CPU", + CPUTypes.TIMING: "TimingSimpleCPU", + CPUTypes.KVM: "KvmCPU", + CPUTypes.MINOR: "MinorCPU", +} parser = argparse.ArgumentParser( description="A gem5 script for running simple binaries in SE mode." @@ -65,13 +81,6 @@ parser.add_argument( "isa", type=str, choices=get_isas_str_set(), help="The ISA used" ) -parser.add_argument( - "-b", - "--base-cpu-processor", - action="store_true", - help="Use the BaseCPUProcessor instead of the SimpleProcessor.", -) - parser.add_argument( "-r", "--resource-directory", @@ -104,28 +113,14 @@ args = parser.parse_args() cache_hierarchy = NoCache() memory = SingleChannelDDR3_1600() -if args.base_cpu_processor: - cores = [ - BaseCPUCore( - core=SimpleCore.cpu_simobject_factory( - cpu_type=get_cpu_type_from_str(args.cpu), - isa=get_isa_from_str(args.isa), - core_id=i, - ), - isa=get_isa_from_str(args.isa), - ) - for i in range(args.num_cores) - ] +isa_enum = get_isa_from_str(args.isa) +cpu_enum = get_cpu_type_from_str(args.cpu) - processor = BaseCPUProcessor( - cores=cores, - ) -else: - processor = SimpleProcessor( - cpu_type=get_cpu_type_from_str(args.cpu), - isa=get_isa_from_str(args.isa), - num_cores=args.num_cores, - ) +processor = SimpleProcessor( + cpu_type=cpu_enum, + isa=isa_enum, + num_cores=args.num_cores, +) motherboard = SimpleBoard( clk_freq="3GHz", @@ -135,7 +130,9 @@ motherboard = SimpleBoard( ) # Set the workload -binary = Resource(args.resource, resource_directory=args.resource_directory) +binary = obtain_resource( + args.resource, resource_directory=args.resource_directory +) motherboard.set_se_binary_workload(binary, arguments=args.arguments) # Run the simulation diff --git a/tests/gem5/se_mode/hello_se/test_hello_se.py b/tests/gem5/se_mode/hello_se/test_hello_se.py index 1aaac4a435..3520f3c7cf 100644 --- a/tests/gem5/se_mode/hello_se/test_hello_se.py +++ b/tests/gem5/se_mode/hello_se/test_hello_se.py @@ -43,10 +43,10 @@ Tests which run simple binaries in gem5's SE mode. The stdlib's SimpleBoard is used to run these tests. """ -from testlib import * - import re +from testlib import * + isa_str_map = { constants.gcn3_x86_tag: "x86", constants.arm_tag: "arm", @@ -90,13 +90,18 @@ stdout_verifier = verifier.MatchRegex(regex) def verify_config(isa, binary, cpu, hosts, verifier, input): - gem5_verify_config( name="test-" + binary + "-" + cpu, fixtures=(), verifiers=(verifier,), config=joinpath( - config.base_dir, "tests", "gem5", "configs", "simple_binary_run.py" + config.base_dir, + "tests", + "gem5", + "se_mode", + "hello_se", + "configs", + "simple_binary_run.py", ), config_args=[ binary, diff --git a/tests/gem5/se_mode/hello_se/test_se_multicore.py b/tests/gem5/se_mode/hello_se/test_se_multicore.py index 55fc61fbf8..dc98a755f3 100644 --- a/tests/gem5/se_mode/hello_se/test_se_multicore.py +++ b/tests/gem5/se_mode/hello_se/test_se_multicore.py @@ -40,7 +40,13 @@ gem5_verify_config( fixtures=(), verifiers=(), config=joinpath( - config.base_dir, "tests", "gem5", "configs", "simple_binary_run.py" + config.base_dir, + "tests", + "gem5", + "se_mode", + "hello_se", + "configs", + "simple_binary_run.py", ), config_args=[ "x86-hello64-static", diff --git a/tests/gem5/stats/README.md b/tests/gem5/stats/README.md new file mode 100644 index 0000000000..c55600bed1 --- /dev/null +++ b/tests/gem5/stats/README.md @@ -0,0 +1,8 @@ +# Stats + +This test runs an SE simulation with the hdf5 stats and checks that the simulation succeeds and the stats file exists. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/stats --length=[length] +``` diff --git a/tests/gem5/stats/configs/simple_binary_run.py b/tests/gem5/stats/configs/simple_binary_run.py new file mode 100644 index 0000000000..4729202ae2 --- /dev/null +++ b/tests/gem5/stats/configs/simple_binary_run.py @@ -0,0 +1,116 @@ +# Copyright (c) 2021 The Regents of the University of California +# Copyright (c) 2022 Google Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +A run script for a very simple Syscall-Execution running simple binaries. +The system has no cache heirarchy and is as "bare-bones" as you can get in +gem5 while still being functinal. +""" + +import argparse +import importlib + +from m5.util import fatal + +from gem5.components.boards.mem_mode import MemMode +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.base_cpu_core import BaseCPUCore +from gem5.components.processors.base_cpu_processor import BaseCPUProcessor +from gem5.components.processors.cpu_types import ( + CPUTypes, + get_cpu_type_from_str, + get_cpu_types_str_set, +) +from gem5.components.processors.simple_core import SimpleCore +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ( + ISA, + get_isa_from_str, + get_isas_str_set, +) +from gem5.resources.resource import Resource +from gem5.simulate.simulator import Simulator + +parser = argparse.ArgumentParser( + description="A gem5 script for running simple binaries in SE mode." +) + +parser.add_argument( + "resource", type=str, help="The gem5 resource binary to run." +) + +parser.add_argument( + "-r", + "--resource-directory", + type=str, + required=False, + help="The directory in which resources will be downloaded or exist.", +) + +parser.add_argument( + "--arguments", + type=str, + action="append", + default=[], + required=False, + help="The input arguments for the binary.", +) + +args = parser.parse_args() + +# Setup the system. +cache_hierarchy = NoCache() +memory = SingleChannelDDR3_1600() + +processor = SimpleProcessor( + cpu_type=CPUTypes.ATOMIC, + isa=ISA.ARM, + num_cores=1, +) + +motherboard = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +# Set the workload +binary = Resource(args.resource, resource_directory=args.resource_directory) +motherboard.set_se_binary_workload(binary, arguments=args.arguments) + +# Run the simulation +simulator = Simulator(board=motherboard) +simulator.run() + +print( + "Exiting @ tick {} because {}.".format( + simulator.get_current_tick(), simulator.get_last_exit_event_cause() + ) +) diff --git a/tests/gem5/stats/test_hdf5.py b/tests/gem5/stats/test_hdf5.py index 8775d22ad8..a92c43e1b8 100644 --- a/tests/gem5/stats/test_hdf5.py +++ b/tests/gem5/stats/test_hdf5.py @@ -46,8 +46,9 @@ It will not run if the build/ARM/gem5.opt has not been built. As this is not built prior to this test being processed during the Weekly run, this test is not run. """ -import re import os +import re + from testlib import * if config.bin_path: @@ -96,14 +97,17 @@ if have_hdf5(): verifiers=[ok_verifier, err_verifier, h5_verifier], fixtures=(), config=joinpath( - config.base_dir, "tests", "gem5", "configs", "simple_binary_run.py" + config.base_dir, + "tests", + "gem5", + "stats", + "configs", + "simple_binary_run.py", ), config_args=[ "arm-hello64-static", - "atomic", "--resource-directory", resource_path, - "arm", ], gem5_args=["--stats-file=h5://stats.h5"], valid_isas=(constants.all_compiled_tag,), diff --git a/tests/gem5/stdlib/README.md b/tests/gem5/stdlib/README.md new file mode 100644 index 0000000000..0b0649f6b6 --- /dev/null +++ b/tests/gem5/stdlib/README.md @@ -0,0 +1,8 @@ +# Standard Library + +These tests check that the BaseCPUProcessor and the gem5.utils.requires function work as intended. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/stdlib --length=[length] +``` diff --git a/tests/gem5/configs/requires_check.py b/tests/gem5/stdlib/configs/requires_check.py similarity index 96% rename from tests/gem5/configs/requires_check.py rename to tests/gem5/stdlib/configs/requires_check.py index eb29f32aa3..76845174eb 100644 --- a/tests/gem5/configs/requires_check.py +++ b/tests/gem5/stdlib/configs/requires_check.py @@ -28,12 +28,15 @@ This is a very simple script to test the behavior of 'gem5.utils.requires'` """ -from gem5.utils.requires import requires -from gem5.isas import ISA, get_isas_str_set, get_isa_from_str - - import argparse +from gem5.isas import ( + ISA, + get_isa_from_str, + get_isas_str_set, +) +from gem5.utils.requires import requires + parser = argparse.ArgumentParser( description="A simple script used to check the behavior of " "`gem5.utils.requires`." diff --git a/tests/gem5/stdlib/configs/simple_binary_run.py b/tests/gem5/stdlib/configs/simple_binary_run.py new file mode 100644 index 0000000000..d02fac4533 --- /dev/null +++ b/tests/gem5/stdlib/configs/simple_binary_run.py @@ -0,0 +1,128 @@ +# Copyright (c) 2021 The Regents of the University of California +# Copyright (c) 2022 Google Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +A run script for a very simple Syscall-Execution running simple binaries. +The system has no cache heirarchy and is as "bare-bones" as you can get in +gem5 while still being functinal. +""" + +import argparse +import importlib + +from m5.util import fatal + +from gem5.components.boards.mem_mode import MemMode +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.base_cpu_core import BaseCPUCore +from gem5.components.processors.base_cpu_processor import BaseCPUProcessor +from gem5.components.processors.cpu_types import ( + CPUTypes, + get_cpu_type_from_str, + get_cpu_types_str_set, +) +from gem5.components.processors.simple_core import SimpleCore +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ( + ISA, + get_isa_from_str, + get_isas_str_set, +) +from gem5.resources.resource import Resource +from gem5.simulate.simulator import Simulator + +cpu_types_string_map = { + CPUTypes.ATOMIC: "AtomicSimpleCPU", + CPUTypes.O3: "O3CPU", + CPUTypes.TIMING: "TimingSimpleCPU", + CPUTypes.KVM: "KvmCPU", + CPUTypes.MINOR: "MinorCPU", +} + +parser = argparse.ArgumentParser( + description="A gem5 script for running simple binaries in SE mode." +) + +parser.add_argument( + "resource", type=str, help="The gem5 resource binary to run." +) + +parser.add_argument( + "cpu", type=str, choices=get_cpu_types_str_set(), help="The CPU type used." +) + +parser.add_argument( + "isa", type=str, choices=get_isas_str_set(), help="The ISA used" +) + +args = parser.parse_args() + +# Setup the system. +cache_hierarchy = NoCache() +memory = SingleChannelDDR3_1600() + +isa_enum = get_isa_from_str(args.isa) +cpu_enum = get_cpu_type_from_str(args.cpu) + +cores = [ + BaseCPUCore( + core=SimpleCore.cpu_simobject_factory( + cpu_type=cpu_enum, + isa=isa_enum, + core_id=i, + ), + isa=isa_enum, + ) + for i in range(1) +] + +processor = BaseCPUProcessor( + cores=cores, +) + +motherboard = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +# Set the workload +binary = Resource(args.resource) +motherboard.set_se_binary_workload(binary) + +# Run the simulation +simulator = Simulator(board=motherboard) +simulator.run() + +print( + "Exiting @ tick {} because {}.".format( + simulator.get_current_tick(), simulator.get_last_exit_event_cause() + ) +) diff --git a/tests/gem5/stdlib/configs/simulator_exit_event_run.py b/tests/gem5/stdlib/configs/simulator_exit_event_run.py new file mode 100644 index 0000000000..6b5e06890f --- /dev/null +++ b/tests/gem5/stdlib/configs/simulator_exit_event_run.py @@ -0,0 +1,161 @@ +# Copyright (c) 2022 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This script is used for testing the event_event handler of the Simulator +module. If the handler is working correctly the following output will be +received: + +``` +The program has started! +About to exit the simulation for the 1 st/nd/rd/th time +Handled exit event. +build/X86/sim/simulate.cc:194: info: Entering event queue @ 780559326. Starting simulation... +About to exit the simulation for the 2 st/nd/rd/th time +Handled exit event. +build/X86/sim/simulate.cc:194: info: Entering event queue @ 854152659. Starting simulation... +About to exit the simulation for the 3 st/nd/rd/th time +Handling the final exit event. We'll exit now. +``` + +By default a generator is passed to define the exit_event behavior. A list of +functions or a lone function can also be passed. This can be specified by the +`--exit-event-type` parameter. +""" + +import argparse + +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource +from gem5.simulate.exit_event import ExitEvent +from gem5.simulate.simulator import Simulator + +parser = argparse.ArgumentParser( + description="A gem5 script for running simple binaries in SE mode." +) + +parser.add_argument( + "-e", + "--exit-event-type", + type=str, + choices=("generator", "function-list", "function"), + default="generator", + help="Used to specify what exit event format is to be passed.", +) + +parser.add_argument( + "-r", + "--resource-directory", + type=str, + required=False, + help="The directory in which resources will be downloaded or exist.", +) + + +args = parser.parse_args() + +# Setup the system. +cache_hierarchy = NoCache() +memory = SingleChannelDDR3_1600() + +processor = SimpleProcessor( + cpu_type=CPUTypes.TIMING, + isa=ISA.X86, + num_cores=1, +) + +motherboard = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +# Set the workload +# Note: Here we're using the "x86-m5-exit-repeat" resource. This calls an +# `m5_exit(0)` command in an infinite while-loop. +binary = obtain_resource( + "x86-m5-exit-repeat", resource_directory=args.resource_directory +) +motherboard.set_se_binary_workload(binary) + +# Create the exit event handler. Here there are three kinds: either pass a +# generator, a list of functions, or a lone function. In this script they all +# do the same thing for testing purposes. + + +def event_handle() -> bool: + print("Handled exit event.") + return False + + +def event_handle_final() -> bool: + print("Handling the final exit event. We'll exit now.") + return True + + +def generator(): + yield event_handle() + yield event_handle() + yield event_handle_final() + + +func_list = [event_handle, event_handle, event_handle_final] + +i = 0 + + +def lone_function() -> bool: + global i + i += 1 + if i < 3: + return event_handle() + return event_handle_final() + + +exit_event_handler = None +if args.exit_event_type == "function-list": + exit_event_handler = func_list +elif args.exit_event_type == "generator": + exit_event_handler = generator() +elif args.exit_event_type == "function": + exit_event_handler = lone_function + +assert exit_event_handler is not None + +# Run the simulation +simulator = Simulator( + board=motherboard, + on_exit_event={ + ExitEvent.EXIT: exit_event_handler, + }, +) +simulator.run() diff --git a/tests/gem5/stdlib/simulator/ref/simout.txt b/tests/gem5/stdlib/simulator/ref/simout.txt new file mode 100644 index 0000000000..a58d513213 --- /dev/null +++ b/tests/gem5/stdlib/simulator/ref/simout.txt @@ -0,0 +1,8 @@ +Global frequency set at 1000000000000 ticks per second +The program has started! +About to exit the simulation for the 1 st/nd/rd/th time +Handled exit event. +About to exit the simulation for the 2 st/nd/rd/th time +Handled exit event. +About to exit the simulation for the 3 st/nd/rd/th time +Handling the final exit event. We'll exit now. diff --git a/tests/gem5/stdlib/simulator/test_event_event.py b/tests/gem5/stdlib/simulator/test_event_event.py new file mode 100644 index 0000000000..23204f0026 --- /dev/null +++ b/tests/gem5/stdlib/simulator/test_event_event.py @@ -0,0 +1,89 @@ +# Copyright (c) 2022 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from testlib import * + +""" +These tests are designed to test the BaseCPUProcessor. It utilizes the +tests/gem5/configs/simple_binary_run.py to run a simple SE-mode simualation +with different configurations of the BaseCPUProcessor. +""" + +verifiers = ( + verifier.MatchStdoutNoPerf(joinpath(getcwd(), "ref", "simout.txt")), +) + + +gem5_verify_config( + name="simulator-exit-event-handler-with-function-list", + verifiers=verifiers, + fixtures=(), + config=joinpath( + config.base_dir, + "tests", + "gem5", + "stdlib", + "configs", + "simulator_exit_event_run.py", + ), + config_args=["-e", "function-list"], + valid_isas=(constants.all_compiled_tag,), + length=constants.quick_tag, +) + +gem5_verify_config( + name="simulator-exit-event-handler-with-generator", + verifiers=verifiers, + fixtures=(), + config=joinpath( + config.base_dir, + "tests", + "gem5", + "stdlib", + "configs", + "simulator_exit_event_run.py", + ), + config_args=["-e", "generator"], + valid_isas=(constants.all_compiled_tag,), + length=constants.quick_tag, +) + +gem5_verify_config( + name="simulator-exit-event-handler-with-lone-function", + verifiers=verifiers, + fixtures=(), + config=joinpath( + config.base_dir, + "tests", + "gem5", + "stdlib", + "configs", + "simulator_exit_event_run.py", + ), + config_args=["-e", "function"], + valid_isas=(constants.all_compiled_tag,), + length=constants.quick_tag, +) diff --git a/tests/gem5/stdlib/test_base_cpu_processor.py b/tests/gem5/stdlib/test_base_cpu_processor.py index cbc6767481..554342b51f 100644 --- a/tests/gem5/stdlib/test_base_cpu_processor.py +++ b/tests/gem5/stdlib/test_base_cpu_processor.py @@ -37,9 +37,14 @@ gem5_verify_config( verifiers=(), fixtures=(), config=joinpath( - config.base_dir, "tests", "gem5", "configs", "simple_binary_run.py" + config.base_dir, + "tests", + "gem5", + "stdlib", + "configs", + "simple_binary_run.py", ), - config_args=["x86-hello64-static", "timing", "x86", "-b"], + config_args=["x86-hello64-static", "timing", "x86"], valid_isas=(constants.all_compiled_tag,), length=constants.quick_tag, ) @@ -49,9 +54,14 @@ gem5_verify_config( verifiers=(), fixtures=(), config=joinpath( - config.base_dir, "tests", "gem5", "configs", "simple_binary_run.py" + config.base_dir, + "tests", + "gem5", + "stdlib", + "configs", + "simple_binary_run.py", ), - config_args=["riscv-hello", "atomic", "riscv", "-b"], + config_args=["riscv-hello", "atomic", "riscv"], valid_isas=(constants.all_compiled_tag,), length=constants.quick_tag, ) @@ -61,9 +71,14 @@ gem5_verify_config( verifiers=(), fixtures=(), config=joinpath( - config.base_dir, "tests", "gem5", "configs", "simple_binary_run.py" + config.base_dir, + "tests", + "gem5", + "stdlib", + "configs", + "simple_binary_run.py", ), - config_args=["arm-hello64-static", "o3", "arm", "-b"], + config_args=["arm-hello64-static", "o3", "arm"], valid_isas=(constants.all_compiled_tag,), length=constants.quick_tag, ) diff --git a/tests/gem5/stdlib/test_requires.py b/tests/gem5/stdlib/test_requires.py index b729050b47..3011180679 100644 --- a/tests/gem5/stdlib/test_requires.py +++ b/tests/gem5/stdlib/test_requires.py @@ -58,6 +58,7 @@ for isa in isa_map.keys(): config.base_dir, "tests", "gem5", + "stdlib", "configs", "requires_check.py", ), @@ -75,6 +76,7 @@ for isa in isa_map.keys(): config.base_dir, "tests", "gem5", + "stdlib", "configs", "requires_check.py", ), diff --git a/tests/gem5/suite.py b/tests/gem5/suite.py index 7e0935d9eb..7b01366c23 100644 --- a/tests/gem5/suite.py +++ b/tests/gem5/suite.py @@ -36,18 +36,25 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import os import copy +import os import subprocess import sys -from testlib.test_util import TestFunction -from testlib.suite import TestSuite +from testlib.configuration import ( + config, + constants, +) from testlib.helper import log_call -from testlib.configuration import constants, config -from .fixture import TempdirFixture, Gem5Fixture, VariableFixture +from testlib.suite import TestSuite +from testlib.test_util import TestFunction from . import verifier +from .fixture import ( + Gem5Fixture, + TempdirFixture, + VariableFixture, +) def gem5_verify_config( @@ -98,7 +105,6 @@ def gem5_verify_config( for host in valid_hosts: for opt in valid_variants: for isa in valid_isas: - # Create a tempdir fixture to be shared throughout the test. tempdir = TempdirFixture() gem5_returncode = VariableFixture( diff --git a/tests/gem5/to_tick/README.md b/tests/gem5/to_tick/README.md new file mode 100644 index 0000000000..e675905120 --- /dev/null +++ b/tests/gem5/to_tick/README.md @@ -0,0 +1,8 @@ +# To Tick + +These tests check that setting the max tick in various ways behaves as expected, as well as that event scheduling at a certain tick works. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/to_tick --length=[length] +``` diff --git a/tests/gem5/to_tick/configs/tick-exit.py b/tests/gem5/to_tick/configs/tick-exit.py index 9b412cbfb6..b1da983abd 100644 --- a/tests/gem5/to_tick/configs/tick-exit.py +++ b/tests/gem5/to_tick/configs/tick-exit.py @@ -28,19 +28,19 @@ """ -from gem5.resources.resource import Resource -from gem5.isas import ISA -from gem5.components.memory import SingleChannelDDR3_1600 -from gem5.components.boards.simple_board import SimpleBoard -from gem5.components.cachehierarchies.classic.no_cache import NoCache -from gem5.components.processors.simple_processor import SimpleProcessor -from gem5.components.processors.cpu_types import CPUTypes -from gem5.simulate.simulator import Simulator -from gem5.simulate.exit_event import ExitEvent +import argparse import m5 -import argparse +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource +from gem5.simulate.exit_event import ExitEvent +from gem5.simulate.simulator import Simulator parser = argparse.ArgumentParser() @@ -76,7 +76,7 @@ motherboard = SimpleBoard( ) # Set the workload -binary = Resource( +binary = obtain_resource( "x86-hello64-static", resource_directory=args.resource_directory ) motherboard.set_se_binary_workload(binary) diff --git a/tests/gem5/to_tick/configs/tick-to-max.py b/tests/gem5/to_tick/configs/tick-to-max.py index 2b679df412..25e0014211 100644 --- a/tests/gem5/to_tick/configs/tick-to-max.py +++ b/tests/gem5/to_tick/configs/tick-to-max.py @@ -33,18 +33,18 @@ run before, at, or after the running of `simulator.run`. time. """ -from gem5.resources.resource import Resource -from gem5.isas import ISA -from gem5.components.memory import SingleChannelDDR3_1600 -from gem5.components.boards.simple_board import SimpleBoard -from gem5.components.cachehierarchies.classic.no_cache import NoCache -from gem5.components.processors.simple_processor import SimpleProcessor -from gem5.components.processors.cpu_types import CPUTypes -from gem5.simulate.simulator import Simulator +import argparse import m5 -import argparse +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.memory import SingleChannelDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource +from gem5.simulate.simulator import Simulator parser = argparse.ArgumentParser() @@ -97,7 +97,7 @@ motherboard = SimpleBoard( ) # Set the workload -binary = Resource( +binary = obtain_resource( "x86-hello64-static", resource_directory=args.resource_directory ) motherboard.set_se_binary_workload(binary) diff --git a/tests/gem5/traffic_gen/README.md b/tests/gem5/traffic_gen/README.md new file mode 100644 index 0000000000..dadb71b641 --- /dev/null +++ b/tests/gem5/traffic_gen/README.md @@ -0,0 +1,9 @@ +# Traffic Generator + +This tests the gem5 memory components with a simple traffic generator. +It also checks the correctness of the statistics outputted by gem5. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/traffic_gen --length=[length] +``` diff --git a/tests/gem5/traffic_gen/simple_traffic_run.py b/tests/gem5/traffic_gen/configs/simple_traffic_run.py similarity index 99% rename from tests/gem5/traffic_gen/simple_traffic_run.py rename to tests/gem5/traffic_gen/configs/simple_traffic_run.py index 3766d7314f..193b59e2d6 100644 --- a/tests/gem5/traffic_gen/simple_traffic_run.py +++ b/tests/gem5/traffic_gen/configs/simple_traffic_run.py @@ -31,14 +31,17 @@ subsystem. The reported values could be used to compare against a validated set of statistics. """ -import m5 - import argparse import importlib from pathlib import Path -from m5.objects import Root, MemorySize +import m5 +from m5.objects import ( + MemorySize, + Root, +) from m5.stats.gem5stats import get_simstat + from gem5.components.boards.test_board import TestBoard diff --git a/tests/gem5/traffic_gen/test_memory_traffic_gen.py b/tests/gem5/traffic_gen/test_memory_traffic_gen.py index 122204e3e9..74b31105b7 100644 --- a/tests/gem5/traffic_gen/test_memory_traffic_gen.py +++ b/tests/gem5/traffic_gen/test_memory_traffic_gen.py @@ -44,7 +44,6 @@ def test_memory( memory: str, *args, ) -> None: - name = ( "test-memory-" + f"{generator}-{generator_cores}-{cache}-{module}-{memory}" @@ -72,6 +71,7 @@ def test_memory( "tests", "gem5", "traffic_gen", + "configs", "simple_traffic_run.py", ), config_args=[generator, generator_cores, cache, module] diff --git a/tests/gem5/verifier.py b/tests/gem5/verifier.py index c725fc68b9..f77c15dded 100644 --- a/tests/gem5/verifier.py +++ b/tests/gem5/verifier.py @@ -40,16 +40,19 @@ """ Built in test cases that verify particular details about a gem5 run. """ -import re -import os import json +import os +import re from testlib import test_util from testlib.configuration import constants -from testlib.helper import joinpath, diff_out_file +from testlib.helper import ( + diff_out_file, + joinpath, +) -class Verifier(object): +class Verifier: def __init__(self, fixtures=tuple()): self.fixtures = fixtures @@ -67,7 +70,7 @@ class Verifier(object): class CheckH5StatsExist(Verifier): def __init__(self, stats_file="stats.h5"): - super(CheckH5StatsExist, self).__init__() + super().__init__() self.stats_file = stats_file def test(self, params): @@ -84,7 +87,7 @@ class MatchGoldStandard(Verifier): """ def __init__( - self, standard_filename, ignore_regex=None, test_filename="simout" + self, standard_filename, ignore_regex=None, test_filename="simout.txt" ): """ :param standard_filename: The path of the standard file to compare @@ -94,7 +97,7 @@ class MatchGoldStandard(Verifier): either which will be ignored in 'standard' and test output files when diffing. """ - super(MatchGoldStandard, self).__init__() + super().__init__() self.standard_filename = standard_filename self.test_filename = test_filename @@ -139,13 +142,12 @@ class DerivedGoldStandard(MatchGoldStandard): def __init__( self, standard_filename, ignore_regex=__ignore_regex_sentinel, **kwargs ): - if ignore_regex == self.__ignore_regex_sentinel: ignore_regex = self._default_ignore_regex self._generic_instance_warning(kwargs) - super(DerivedGoldStandard, self).__init__( + super().__init__( standard_filename, test_filename=self._file, ignore_regex=ignore_regex, @@ -156,7 +158,7 @@ class DerivedGoldStandard(MatchGoldStandard): class MatchStdout(DerivedGoldStandard): _file = constants.gem5_simulation_stdout _default_ignore_regex = [ - re.compile("^\s+$"), # Remove blank lines. + re.compile(r"^\s+$"), # Remove blank lines. re.compile("^gem5 Simulator System"), re.compile("^gem5 is copyrighted software"), re.compile("^Redirecting (stdout|stderr) to"), @@ -169,8 +171,8 @@ class MatchStdout(DerivedGoldStandard): re.compile("^info: kernel located at:"), re.compile("^info: Standard input is not a terminal"), re.compile("^Couldn't unlink "), - re.compile("^Using GPU kernel code file\(s\) "), - re.compile("^.* not found locally\. Downloading"), + re.compile(r"^Using GPU kernel code file\(s\) "), + re.compile(r"^.* not found locally\. Downloading"), re.compile("^Finished downloading"), re.compile("^info: Using default config"), ] @@ -219,12 +221,12 @@ class MatchFileRegex(Verifier): """ def __init__(self, regex, filenames): - super(MatchFileRegex, self).__init__() + super().__init__() self.regex = _iterable_regex(regex) self.filenames = filenames def parse_file(self, fname): - with open(fname, "r") as file_: + with open(fname) as file_: for line in file_: for regex in self.regex: if re.match(regex, line): @@ -253,7 +255,7 @@ class MatchRegex(MatchFileRegex): filenames.append(constants.gem5_simulation_stdout) if match_stderr: filenames.append(constants.gem5_simulation_stderr) - super(MatchRegex, self).__init__(regex, filenames) + super().__init__(regex, filenames) class NoMatchRegex(MatchRegex): @@ -262,7 +264,7 @@ class NoMatchRegex(MatchRegex): """ def __init__(self, regex, match_stderr=True, match_stdout=True): - super(NoMatchRegex, self).__init__(regex, match_stderr, match_stdout) + super().__init__(regex, match_stderr, match_stdout) def test(self, params): fixtures = params.fixtures @@ -291,7 +293,7 @@ class MatchJSONStats(Verifier): :param test_name_in_m5out: True if the 'test_name' dir is to found in the `m5.options.outdir`. """ - super(MatchJSONStats, self).__init__() + super().__init__() self.truth_name = truth_name self.test_name = test_name self.test_name_in_outdir = test_name_in_outdir @@ -319,13 +321,13 @@ class MatchJSONStats(Verifier): test_util.fail(err) def test(self, params): - trusted_file = open(self.truth_name, "r") + trusted_file = open(self.truth_name) if self.test_name_in_outdir: fixtures = params.fixtures tempdir = fixtures[constants.tempdir_fixture_name].path - test_file = open(joinpath(tempdir, self.test_name), "r") + test_file = open(joinpath(tempdir, self.test_name)) else: - test_file = open(self.test_name, "r") + test_file = open(self.test_name) return self._compare_stats(trusted_file, test_file) diff --git a/tests/gem5/x86_boot_tests/README.md b/tests/gem5/x86_boot_tests/README.md new file mode 100644 index 0000000000..92357b6bb2 --- /dev/null +++ b/tests/gem5/x86_boot_tests/README.md @@ -0,0 +1,9 @@ +# X86 Boot Tests + +These tests run a series of Linux boots on the X86Board. +It varies the CPU type, number of CPUs, and memory used for each run. +To run these tests by themselves, you can run the following command in the tests directory: + +```bash +./main.py run gem5/x86_boot_tests --length=[length] +``` diff --git a/tests/gem5/configs/x86_boot_exit_run.py b/tests/gem5/x86_boot_tests/configs/x86_boot_exit_run.py similarity index 98% rename from tests/gem5/configs/x86_boot_exit_run.py rename to tests/gem5/x86_boot_tests/configs/x86_boot_exit_run.py index e9eeacefd8..3a91c4d253 100644 --- a/tests/gem5/configs/x86_boot_exit_run.py +++ b/tests/gem5/x86_boot_tests/configs/x86_boot_exit_run.py @@ -28,24 +28,23 @@ This script will run a simple boot exit test. """ -import m5 - -from gem5.runtime import get_runtime_coherence_protocol -from gem5.isas import ISA -from gem5.utils.requires import requires -from gem5.coherence_protocol import CoherenceProtocol -from gem5.components.boards.x86_board import X86Board -from gem5.components.processors.cpu_types import ( - get_cpu_types_str_set, - get_cpu_type_from_str, -) -from gem5.components.processors.simple_processor import SimpleProcessor -from gem5.simulate.simulator import Simulator -from gem5.resources.workload import Workload - import argparse import importlib +import m5 + +from gem5.coherence_protocol import CoherenceProtocol +from gem5.components.boards.x86_board import X86Board +from gem5.components.processors.cpu_types import ( + get_cpu_type_from_str, + get_cpu_types_str_set, +) +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource +from gem5.runtime import get_runtime_coherence_protocol +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires parser = argparse.ArgumentParser( description="A script to run the gem5 boot test. This test boots the " @@ -184,7 +183,7 @@ if args.boot_type == "init": kernal_args.append("init=/root/exit.sh") # Set the workload. -workload = Workload( +workload = obtain_resource( "x86-ubuntu-18.04-boot", resource_directory=args.resource_directory ) workload.set_parameter("kernel_args", kernal_args) diff --git a/tests/gem5/x86-boot-tests/test_linux_boot.py b/tests/gem5/x86_boot_tests/test_linux_boot.py similarity index 98% rename from tests/gem5/x86-boot-tests/test_linux_boot.py rename to tests/gem5/x86_boot_tests/test_linux_boot.py index 1907aaf0e4..fb16896bf8 100644 --- a/tests/gem5/x86-boot-tests/test_linux_boot.py +++ b/tests/gem5/x86_boot_tests/test_linux_boot.py @@ -24,8 +24,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from typing import Optional import re +from typing import Optional from testlib import * @@ -44,7 +44,6 @@ def test_boot( boot_type: str = "init", to_tick: Optional[int] = None, ): - name = "{}-cpu_{}-cores_{}_{}_{}_x86-boot-test".format( cpu, str(num_cpus), mem_system, memory_class, boot_type ) @@ -75,7 +74,12 @@ def test_boot( verifiers=verifiers, fixtures=(), config=joinpath( - config.base_dir, "tests", "gem5", "configs", "x86_boot_exit_run.py" + config.base_dir, + "tests", + "gem5", + "x86_boot_tests", + "configs", + "x86_boot_exit_run.py", ), config_args=[ "--cpu", diff --git a/tests/main.py b/tests/main.py index 77ab73d3c0..063042d43d 100755 --- a/tests/main.py +++ b/tests/main.py @@ -6,8 +6,8 @@ loaders. Discovers and runs all tests from a given root directory. """ -import sys import os +import sys os.environ["PYTHONUNBUFFERED"] = "1" @@ -17,9 +17,9 @@ ext_path = os.path.join(base_dir, os.pardir, "ext") sys.path.insert(0, base_dir) sys.path.insert(0, ext_path) -import testlib.main as testlib import testlib.configuration as config import testlib.helper as helper +import testlib.main as testlib config.basedir = helper.absdirpath(__file__) sys.exit(testlib()) diff --git a/tests/pyunit/pyunit_jsonserializable_check.py b/tests/pyunit/pyunit_jsonserializable_check.py index 8d5d2fa857..3695374b8e 100644 --- a/tests/pyunit/pyunit_jsonserializable_check.py +++ b/tests/pyunit/pyunit_jsonserializable_check.py @@ -25,6 +25,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import unittest + from m5.ext.pystats.serializable_stat import SerializableStat @@ -54,18 +55,18 @@ class JsonSerializableTestSuite(unittest.TestCase): obj_json = obj.to_json() self.assertTrue("child_1" in obj_json) self.assertTrue("stat1" in obj_json["child_1"]) - self.assertEquals(2, obj_json["child_1"]["stat1"]) + self.assertEqual(2, obj_json["child_1"]["stat1"]) self.assertTrue("stat2" in obj_json["child_1"]) - self.assertEquals("3", obj_json["child_1"]["stat2"]) + self.assertEqual("3", obj_json["child_1"]["stat2"]) self.assertTrue("child_list" in obj_json) - self.assertEquals(2, len(obj_json["child_list"])) + self.assertEqual(2, len(obj_json["child_list"])) self.assertTrue("stat1" in obj_json["child_list"][0]) self.assertEqual("hello", obj_json["child_list"][0]["stat1"]) self.assertTrue("list_stat2" in obj_json["child_list"][1]) - self.assertEquals(6, len(obj_json["child_list"][1]["list_stat2"])) - self.assertEquals("1", obj_json["child_list"][1]["list_stat2"][0]) - self.assertEquals(2, obj_json["child_list"][1]["list_stat2"][1]) - self.assertEquals("3", obj_json["child_list"][1]["list_stat2"][2]) - self.assertEquals(4, obj_json["child_list"][1]["list_stat2"][3]) - self.assertEquals(5.2, obj_json["child_list"][1]["list_stat2"][4]) - self.assertEquals(None, obj_json["child_list"][1]["list_stat2"][5]) + self.assertEqual(6, len(obj_json["child_list"][1]["list_stat2"])) + self.assertEqual("1", obj_json["child_list"][1]["list_stat2"][0]) + self.assertEqual(2, obj_json["child_list"][1]["list_stat2"][1]) + self.assertEqual("3", obj_json["child_list"][1]["list_stat2"][2]) + self.assertEqual(4, obj_json["child_list"][1]["list_stat2"][3]) + self.assertEqual(5.2, obj_json["child_list"][1]["list_stat2"][4]) + self.assertEqual(None, obj_json["child_list"][1]["list_stat2"][5]) diff --git a/tests/pyunit/stdlib/pyunit_looppoint.py b/tests/pyunit/stdlib/pyunit_looppoint.py index 0cb708e8ac..398e771df6 100644 --- a/tests/pyunit/stdlib/pyunit_looppoint.py +++ b/tests/pyunit/stdlib/pyunit_looppoint.py @@ -24,22 +24,21 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import os import unittest from m5.params import PcCountPair from gem5.resources.looppoint import ( Looppoint, + LooppointCsvLoader, + LooppointJsonLoader, + LooppointRegion, LooppointRegionPC, LooppointRegionWarmup, LooppointSimulation, - LooppointRegion, - LooppointCsvLoader, - LooppointJsonLoader, ) -import os - class LooppointRegionPCTestSuite(unittest.TestCase): """Tests the resources.looppoint.LooppointRegionPC class.""" @@ -47,21 +46,21 @@ class LooppointRegionPCTestSuite(unittest.TestCase): def test_construction_with_relative(self) -> None: region_pc = LooppointRegionPC(pc=444, globl=65, relative=454) - self.assertEquals(444, region_pc.get_pc()) - self.assertEquals(65, region_pc.get_global()) - self.assertEquals(454, region_pc.get_relative()) + self.assertEqual(444, region_pc.get_pc()) + self.assertEqual(65, region_pc.get_global()) + self.assertEqual(454, region_pc.get_relative()) def test_construction_without_relative(self) -> None: region_pc = LooppointRegionPC(pc=43454, globl=653434) - self.assertEquals(43454, region_pc.get_pc()) - self.assertEquals(653434, region_pc.get_global()) + self.assertEqual(43454, region_pc.get_pc()) + self.assertEqual(653434, region_pc.get_global()) self.assertIsNone(region_pc.get_relative()) def test_get_pc_count_pair(self) -> None: region_pc = LooppointRegionPC(pc=1, globl=2) expected = PcCountPair(1, 2) - self.assertEquals(expected, region_pc.get_pc_count_pair()) + self.assertEqual(expected, region_pc.get_pc_count_pair()) def update_relative_count(self) -> None: pass # Not really sure what to do here... @@ -70,23 +69,23 @@ class LooppointRegionPCTestSuite(unittest.TestCase): region_pc = LooppointRegionPC(pc=100, globl=200, relative=300) json_contents = region_pc.to_json() - self.assertEquals(3, len(json_contents)) + self.assertEqual(3, len(json_contents)) self.assertTrue("pc" in json_contents) - self.assertEquals(100, json_contents["pc"]) + self.assertEqual(100, json_contents["pc"]) self.assertTrue("global" in json_contents) - self.assertEquals(200, json_contents["global"]) + self.assertEqual(200, json_contents["global"]) self.assertTrue("relative" in json_contents) - self.assertEquals(300, json_contents["relative"]) + self.assertEqual(300, json_contents["relative"]) def test_to_json_without_relative(self) -> None: region_pc = LooppointRegionPC(pc=1111, globl=2222) json_contents = region_pc.to_json() - self.assertEquals(2, len(json_contents)) + self.assertEqual(2, len(json_contents)) self.assertTrue("pc" in json_contents) - self.assertEquals(1111, json_contents["pc"]) + self.assertEqual(1111, json_contents["pc"]) self.assertTrue("global" in json_contents) - self.assertEquals(2222, json_contents["global"]) + self.assertEqual(2222, json_contents["global"]) self.assertFalse("relative" in json_contents) @@ -98,8 +97,8 @@ class LooppointRegionWarmupTestSuite(unittest.TestCase): start=PcCountPair(123, 456), end=PcCountPair(789, 1011) ) - self.assertEquals(PcCountPair(123, 456), region_warmup.get_start()) - self.assertEquals(PcCountPair(789, 1011), region_warmup.get_end()) + self.assertEqual(PcCountPair(123, 456), region_warmup.get_start()) + self.assertEqual(PcCountPair(789, 1011), region_warmup.get_end()) def test_get_pc_count_pairs(self) -> None: region_warmup = LooppointRegionWarmup( @@ -107,9 +106,9 @@ class LooppointRegionWarmupTestSuite(unittest.TestCase): ) output = region_warmup.get_pc_count_pairs() - self.assertEquals(2, len(output)) - self.assertEquals(PcCountPair(1, 1), output[0]) - self.assertEquals(PcCountPair(2, 2), output[1]) + self.assertEqual(2, len(output)) + self.assertEqual(PcCountPair(1, 1), output[0]) + self.assertEqual(PcCountPair(2, 2), output[1]) def test_to_json(self) -> None: region_warmup = LooppointRegionWarmup( @@ -135,14 +134,14 @@ class LooppointSimulationTestSuite(unittest.TestCase): sim_start = sim.get_start() - self.assertEquals(444, sim_start.get_pc()) - self.assertEquals(65, sim_start.get_global()) - self.assertEquals(454, sim_start.get_relative()) + self.assertEqual(444, sim_start.get_pc()) + self.assertEqual(65, sim_start.get_global()) + self.assertEqual(454, sim_start.get_relative()) sim_end = sim.get_end() - self.assertEquals(555, sim_end.get_pc()) - self.assertEquals(699, sim_end.get_global()) + self.assertEqual(555, sim_end.get_pc()) + self.assertEqual(699, sim_end.get_global()) self.assertIsNone(sim_end.get_relative()) def test_get_pc_count_pairs(self) -> None: @@ -152,9 +151,9 @@ class LooppointSimulationTestSuite(unittest.TestCase): ) sim_pc_count_pairs = sim.get_pc_count_pairs() - self.assertEquals(2, len(sim_pc_count_pairs)) - self.assertEquals(PcCountPair(56, 45), sim_pc_count_pairs[0]) - self.assertEquals(PcCountPair(23, 12), sim_pc_count_pairs[1]) + self.assertEqual(2, len(sim_pc_count_pairs)) + self.assertEqual(PcCountPair(56, 45), sim_pc_count_pairs[0]) + self.assertEqual(PcCountPair(23, 12), sim_pc_count_pairs[1]) def test_get_json(self) -> None: sim = LooppointSimulation( @@ -193,7 +192,7 @@ class LooppointRegionTestSuite(unittest.TestCase): self.assertTrue( isinstance(region.get_simulation(), LooppointSimulation) ) - self.assertEquals(5.6, region.get_multiplier()) + self.assertEqual(5.6, region.get_multiplier()) self.assertIsNotNone(region.get_warmup()) self.assertTrue(isinstance(region.get_warmup(), LooppointRegionWarmup)) @@ -209,7 +208,7 @@ class LooppointRegionTestSuite(unittest.TestCase): self.assertTrue( isinstance(region.get_simulation(), LooppointSimulation) ) - self.assertEquals(5444.4, region.get_multiplier()) + self.assertEqual(5444.4, region.get_multiplier()) self.assertIsNone(region.get_warmup()) def test_get_pc_count_pairs_with_warmup(self): @@ -225,11 +224,11 @@ class LooppointRegionTestSuite(unittest.TestCase): ) pc_count_pairs = region.get_pc_count_pairs() - self.assertEquals(4, len(pc_count_pairs)) - self.assertEquals(PcCountPair(1, 2), pc_count_pairs[0]) - self.assertEquals(PcCountPair(6, 7), pc_count_pairs[1]) - self.assertEquals(PcCountPair(100, 200), pc_count_pairs[2]) - self.assertEquals(PcCountPair(101, 202), pc_count_pairs[3]) + self.assertEqual(4, len(pc_count_pairs)) + self.assertEqual(PcCountPair(1, 2), pc_count_pairs[0]) + self.assertEqual(PcCountPair(6, 7), pc_count_pairs[1]) + self.assertEqual(PcCountPair(100, 200), pc_count_pairs[2]) + self.assertEqual(PcCountPair(101, 202), pc_count_pairs[3]) def test_get_pc_count_pairs_without_warmup(self): region = LooppointRegion( @@ -242,9 +241,9 @@ class LooppointRegionTestSuite(unittest.TestCase): pc_count_pairs = region.get_pc_count_pairs() - self.assertEquals(2, len(pc_count_pairs)) - self.assertEquals(PcCountPair(56, 2345), pc_count_pairs[0]) - self.assertEquals(PcCountPair(645, 457), pc_count_pairs[1]) + self.assertEqual(2, len(pc_count_pairs)) + self.assertEqual(PcCountPair(56, 2345), pc_count_pairs[0]) + self.assertEqual(PcCountPair(645, 457), pc_count_pairs[1]) class LooppointTestSuite(unittest.TestCase): @@ -276,11 +275,11 @@ class LooppointTestSuite(unittest.TestCase): } ) - self.assertEquals(2, len(looppoint.get_regions())) + self.assertEqual(2, len(looppoint.get_regions())) self.assertTrue(1 in looppoint.get_regions()) - self.assertEquals(region1, looppoint.get_regions()[1]) + self.assertEqual(region1, looppoint.get_regions()[1]) self.assertTrue(3 in looppoint.get_regions()) - self.assertEquals(region2, looppoint.get_regions()[3]) + self.assertEqual(region2, looppoint.get_regions()[3]) def test_get_targets(self): region1 = LooppointRegion( @@ -309,16 +308,15 @@ class LooppointTestSuite(unittest.TestCase): ) targets = looppoint.get_targets() - self.assertEquals(6, len(targets)) - self.assertEquals(PcCountPair(56, 2345), targets[0]) - self.assertEquals(PcCountPair(645, 457), targets[1]) - self.assertEquals(PcCountPair(67, 254), targets[2]) - self.assertEquals(PcCountPair(64554, 7454), targets[3]) - self.assertEquals(PcCountPair(100, 200), targets[4]) - self.assertEquals(PcCountPair(101, 202), targets[5]) + self.assertEqual(6, len(targets)) + self.assertEqual(PcCountPair(56, 2345), targets[0]) + self.assertEqual(PcCountPair(645, 457), targets[1]) + self.assertEqual(PcCountPair(67, 254), targets[2]) + self.assertEqual(PcCountPair(64554, 7454), targets[3]) + self.assertEqual(PcCountPair(100, 200), targets[4]) + self.assertEqual(PcCountPair(101, 202), targets[5]) def test_get_region_start_id_map(self): - region1 = LooppointRegion( simulation=LooppointSimulation( start=LooppointRegionPC(pc=56, globl=2345, relative=344), @@ -346,15 +344,15 @@ class LooppointTestSuite(unittest.TestCase): region_start_id_map = looppoint.get_region_start_id_map() - self.assertEquals(2, len(region_start_id_map)) + self.assertEqual(2, len(region_start_id_map)) # The start of region1. self.assertTrue(PcCountPair(56, 2345) in region_start_id_map) - self.assertEquals(1, region_start_id_map[PcCountPair(56, 2345)]) + self.assertEqual(1, region_start_id_map[PcCountPair(56, 2345)]) # The start of region2. Since this has a warmup, it's the warmup. self.assertTrue(PcCountPair(100, 200) in region_start_id_map) - self.assertEquals(3, region_start_id_map[PcCountPair(100, 200)]) + self.assertEqual(3, region_start_id_map[PcCountPair(100, 200)]) def test_to_json(self) -> None: region1 = LooppointRegion( @@ -441,60 +439,60 @@ class LooppointCSVLoaderTestSuite(unittest.TestCase): ) regions = looppoint.get_regions() - self.assertEquals(3, len(regions)) + self.assertEqual(3, len(regions)) region1 = regions[1] - self.assertEquals(4.0, region1.get_multiplier()) + self.assertEqual(4.0, region1.get_multiplier()) region1start = region1.get_simulation().get_start() - self.assertEquals(0x4069D0, region1start.get_pc()) - self.assertEquals(211076617, region1start.get_global()) + self.assertEqual(0x4069D0, region1start.get_pc()) + self.assertEqual(211076617, region1start.get_global()) self.assertIsNone(region1start.get_relative()) region1end = region1.get_simulation().get_end() - self.assertEquals(0x4069D0, region1end.get_pc()) - self.assertEquals(219060252, region1end.get_global()) + self.assertEqual(0x4069D0, region1end.get_pc()) + self.assertEqual(219060252, region1end.get_global()) self.assertIsNotNone(region1end.get_relative()) - self.assertEquals(1060676, region1end.get_relative()) + self.assertEqual(1060676, region1end.get_relative()) self.assertIsNone(region1.get_warmup()) region2 = regions[2] - self.assertEquals(5.001, region2.get_multiplier()) + self.assertEqual(5.001, region2.get_multiplier()) region2start = region2.get_simulation().get_start() - self.assertEquals(0x4069D0, region2start.get_pc()) - self.assertEquals(407294228, region2start.get_global()) + self.assertEqual(0x4069D0, region2start.get_pc()) + self.assertEqual(407294228, region2start.get_global()) self.assertIsNone(region2start.get_relative()) region2end = region2.get_simulation().get_end() - self.assertEquals(0x4069D0, region2end.get_pc()) - self.assertEquals(415282447, region2end.get_global()) + self.assertEqual(0x4069D0, region2end.get_pc()) + self.assertEqual(415282447, region2end.get_global()) self.assertIsNotNone(region2end.get_relative()) - self.assertEquals(1035231, region2end.get_relative()) + self.assertEqual(1035231, region2end.get_relative()) region2warmup = region2.get_warmup() self.assertIsNotNone(region2warmup) - self.assertEquals( + self.assertEqual( PcCountPair(0x406880, 48111518), region2warmup.get_start() ) - self.assertEquals( + self.assertEqual( PcCountPair(0x4069D0, 407294228), region2warmup.get_end() ) region3 = regions[3] - self.assertEquals(4.0, region3.get_multiplier()) + self.assertEqual(4.0, region3.get_multiplier()) region3start = region3.get_simulation().get_start() - self.assertEquals(0x4069D0, region3start.get_pc()) - self.assertEquals(187978221, region3start.get_global()) + self.assertEqual(0x4069D0, region3start.get_pc()) + self.assertEqual(187978221, region3start.get_global()) self.assertIsNone(region3start.get_relative()) region3end = region3.get_simulation().get_end() - self.assertEquals(0x406880, region3end.get_pc()) - self.assertEquals(23520614, region3end.get_global()) + self.assertEqual(0x406880, region3end.get_pc()) + self.assertEqual(23520614, region3end.get_global()) self.assertIsNotNone(region3end.get_relative()) - self.assertEquals(144352, region3end.get_relative()) + self.assertEqual(144352, region3end.get_relative()) self.assertIsNone(region3.get_warmup()) @@ -509,22 +507,22 @@ class LooppointCSVLoaderTestSuite(unittest.TestCase): ) regions = looppoint.get_regions() - self.assertEquals(1, len(regions)) + self.assertEqual(1, len(regions)) self.assertTrue(1 in regions) region1 = regions[1] - self.assertEquals(4.0, region1.get_multiplier()) + self.assertEqual(4.0, region1.get_multiplier()) region1start = region1.get_simulation().get_start() - self.assertEquals(0x4069D0, region1start.get_pc()) - self.assertEquals(211076617, region1start.get_global()) + self.assertEqual(0x4069D0, region1start.get_pc()) + self.assertEqual(211076617, region1start.get_global()) self.assertIsNone(region1start.get_relative()) region1end = region1.get_simulation().get_end() - self.assertEquals(0x4069D0, region1end.get_pc()) - self.assertEquals(219060252, region1end.get_global()) + self.assertEqual(0x4069D0, region1end.get_pc()) + self.assertEqual(219060252, region1end.get_global()) self.assertIsNotNone(region1end.get_relative()) - self.assertEquals(1060676, region1end.get_relative()) + self.assertEqual(1060676, region1end.get_relative()) self.assertIsNone(region1.get_warmup()) @@ -542,31 +540,31 @@ class LooppointJsonLoaderTestSuite(unittest.TestCase): region_id="1", ) - self.assertEquals(1, len(looppoint.get_regions())) + self.assertEqual(1, len(looppoint.get_regions())) self.assertTrue("1" in looppoint.get_regions()) region = looppoint.get_regions()["1"] - self.assertEquals(4.0, region.get_multiplier()) + self.assertEqual(4.0, region.get_multiplier()) region_start = region.get_simulation().get_start() - self.assertEquals(4221392, region_start.get_pc()) - self.assertEquals(211076617, region_start.get_global()) + self.assertEqual(4221392, region_start.get_pc()) + self.assertEqual(211076617, region_start.get_global()) self.assertIsNotNone(region_start.get_relative()) - self.assertEquals(15326617, region_start.get_relative()) + self.assertEqual(15326617, region_start.get_relative()) region_end = region.get_simulation().get_end() - self.assertEquals(4221392, region_end.get_pc()) - self.assertEquals(219060252, region_end.get_global()) + self.assertEqual(4221392, region_end.get_pc()) + self.assertEqual(219060252, region_end.get_global()) self.assertIsNotNone(region_end.get_relative()) - self.assertEquals(23310252, region_end.get_relative()) + self.assertEqual(23310252, region_end.get_relative()) region_warmup = region.get_warmup() self.assertIsNotNone(region_warmup) - self.assertEquals( + self.assertEqual( PcCountPair(4221056, 23520614), region_warmup.get_start() ) - self.assertEquals( + self.assertEqual( PcCountPair(4221392, 211076617), region_warmup.get_end() ) @@ -580,20 +578,20 @@ class LooppointJsonLoaderTestSuite(unittest.TestCase): region_id="2", ) - self.assertEquals(1, len(looppoint.get_regions())) + self.assertEqual(1, len(looppoint.get_regions())) self.assertTrue("2" in looppoint.get_regions()) region = looppoint.get_regions()["2"] - self.assertEquals(5.001, region.get_multiplier()) + self.assertEqual(5.001, region.get_multiplier()) region_start = region.get_simulation().get_start() - self.assertEquals(4221392, region_start.get_pc()) - self.assertEquals(407294228, region_start.get_global()) + self.assertEqual(4221392, region_start.get_pc()) + self.assertEqual(407294228, region_start.get_global()) self.assertIsNone(region_start.get_relative()) region_end = region.get_simulation().get_end() - self.assertEquals(4221392, region_end.get_pc()) - self.assertEquals(415282447, region_end.get_global()) + self.assertEqual(4221392, region_end.get_pc()) + self.assertEqual(415282447, region_end.get_global()) self.assertIsNone(region_end.get_relative()) region_warmup = region.get_warmup() diff --git a/tests/pyunit/stdlib/resources/pyunit_client_wrapper_checks.py b/tests/pyunit/stdlib/resources/pyunit_client_wrapper_checks.py index f190b1ed5f..f98005e546 100644 --- a/tests/pyunit/stdlib/resources/pyunit_client_wrapper_checks.py +++ b/tests/pyunit/stdlib/resources/pyunit_client_wrapper_checks.py @@ -24,15 +24,19 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import unittest -from gem5.resources.client import get_resource_json_obj -from gem5.resources.client_api.client_wrapper import ClientWrapper -from unittest.mock import patch -import json -from urllib.error import HTTPError -import io import contextlib +import io +import json +import unittest from pathlib import Path +from unittest.mock import patch +from urllib.error import HTTPError + +from gem5.resources.client import get_resource_json_obj +from gem5.resources.client_api.atlasclient import ( + AtlasClientHttpJsonRequestError, +) +from gem5.resources.client_api.client_wrapper import ClientWrapper mock_json_path = Path(__file__).parent / "refs/resources.json" mock_config_json = { @@ -63,12 +67,12 @@ mock_config_combined["sources"]["baba"] = mock_config_json["sources"]["baba"] mock_json = {} -with open(Path(__file__).parent / "refs/mongo-mock.json", "r") as f: +with open(Path(__file__).parent / "refs/mongo-mock.json") as f: mock_json = json.load(f) duplicate_mock_json = {} -with open(Path(__file__).parent / "refs/mongo-dup-mock.json", "r") as f: +with open(Path(__file__).parent / "refs/mongo-dup-mock.json") as f: duplicate_mock_json = json.load(f) @@ -419,21 +423,11 @@ class ClientWrapperTestSuite(unittest.TestCase): @patch("urllib.request.urlopen", side_effect=mocked_requests_post) def test_invalid_auth_url(self, mock_get): resource_id = "test-resource" - f = io.StringIO() - with self.assertRaises(Exception) as context: - with contextlib.redirect_stderr(f): - get_resource_json_obj( - resource_id, - gem5_version="develop", - ) - self.assertTrue( - "Error getting resources from client gem5-resources:" - " Panic: Not found" in str(f.getvalue()) - ) - self.assertTrue( - "Resource with ID 'test-resource' not found." - in str(context.exception) - ) + with self.assertRaises(AtlasClientHttpJsonRequestError) as context: + get_resource_json_obj( + resource_id, + gem5_version="develop", + ) @patch( "gem5.resources.client.clientwrapper", @@ -442,21 +436,11 @@ class ClientWrapperTestSuite(unittest.TestCase): @patch("urllib.request.urlopen", side_effect=mocked_requests_post) def test_invalid_url(self, mock_get): resource_id = "test-resource" - f = io.StringIO() - with self.assertRaises(Exception) as context: - with contextlib.redirect_stderr(f): - get_resource_json_obj( - resource_id, - gem5_version="develop", - ) - self.assertTrue( - "Error getting resources from client gem5-resources:" - " Panic: Not found" in str(f.getvalue()) - ) - self.assertTrue( - "Resource with ID 'test-resource' not found." - in str(context.exception) - ) + with self.assertRaises(AtlasClientHttpJsonRequestError) as context: + get_resource_json_obj( + resource_id, + gem5_version="develop", + ) @patch( "gem5.resources.client.clientwrapper", @@ -465,18 +449,8 @@ class ClientWrapperTestSuite(unittest.TestCase): @patch("urllib.request.urlopen", side_effect=mocked_requests_post) def test_invalid_url(self, mock_get): resource_id = "test-too-many" - f = io.StringIO() - with self.assertRaises(Exception) as context: - with contextlib.redirect_stderr(f): - get_resource_json_obj( - resource_id, - gem5_version="develop", - ) - self.assertTrue( - "Error getting resources from client gem5-resources:" - " Panic: Too many requests" in str(f.getvalue()) - ) - self.assertTrue( - "Resource with ID 'test-too-many' not found." - in str(context.exception) - ) + with self.assertRaises(AtlasClientHttpJsonRequestError) as context: + get_resource_json_obj( + resource_id, + gem5_version="develop", + ) diff --git a/tests/pyunit/stdlib/resources/pyunit_json_client_checks.py b/tests/pyunit/stdlib/resources/pyunit_json_client_checks.py index 88db3d4967..be2c6d7810 100644 --- a/tests/pyunit/stdlib/resources/pyunit_json_client_checks.py +++ b/tests/pyunit/stdlib/resources/pyunit_json_client_checks.py @@ -24,11 +24,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import unittest -import tempfile -import os -from typing import Dict import json +import os +import tempfile +import unittest +from typing import Dict from gem5.resources.client_api.jsonclient import JSONClient @@ -148,16 +148,16 @@ class JSONClientTestSuite(unittest.TestCase): "create_temp_resources_json" has been loaded correctly into a Python dictionary. """ - self.assertEquals(4, len(json)) + self.assertEqual(4, len(json)) self.assertTrue("id" in json[0]) - self.assertEquals("this-is-a-test-resource", json[0]["id"]) - self.assertEquals("binary", json[0]["category"]) + self.assertEqual("this-is-a-test-resource", json[0]["id"]) + self.assertEqual("binary", json[0]["category"]) self.assertTrue("id" in json[1]) - self.assertEquals("this-is-a-test-resource", json[1]["id"]) + self.assertEqual("this-is-a-test-resource", json[1]["id"]) self.assertTrue("id" in json[2]) - self.assertEquals("test-version", json[2]["id"]) + self.assertEqual("test-version", json[2]["id"]) self.assertTrue("id" in json[3]) - self.assertEquals("test-version", json[3]["id"]) + self.assertEqual("test-version", json[3]["id"]) def test_get_resources_json_at_path(self) -> None: # Tests JSONClient.get_resources_json() diff --git a/tests/pyunit/stdlib/resources/pyunit_local_file_path_check.py b/tests/pyunit/stdlib/resources/pyunit_local_file_path_check.py new file mode 100644 index 0000000000..03a7cb95f7 --- /dev/null +++ b/tests/pyunit/stdlib/resources/pyunit_local_file_path_check.py @@ -0,0 +1,73 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import unittest +from pathlib import Path + +from gem5.resources.downloader import _file_uri_to_path + + +class LocalPathTestSuite(unittest.TestCase): + def test_local_path_exists_single_slash(self): + # Test that a local path is returned as-is + path = "file:/test/test/file" + expected_path = Path("/test/test/file") + self.assertEqual(_file_uri_to_path(path), expected_path) + + def test_non_localhost_exception(self): + # Test that a local path with different netloc throws an exception + path = "file://test/test/file" + # should raise Exception because netloc is not '' or 'localhost' + with self.assertRaises(Exception) as exception: + _file_uri_to_path(path) + self.assertEqual( + str(exception.exception), + f"File URI '{path}' specifies host 'test'. " + "Only localhost is permitted.", + ) + + def test_localhost_accepted(self): + path = "file://localhost/test/test/file" + # should work as expected because netloc is 'localhost' + expected_path = Path("/test/test/file") + self.assertEqual(_file_uri_to_path(path), expected_path) + + def test_local_path_exists_triple_slash(self): + # Test that a local path is returned as-is + path = "file:///test/test/file" + expected_path = Path("/test/test/file") + self.assertEqual(_file_uri_to_path(path), expected_path) + + def test_local_path_exists_quadruple_slash(self): + # Test that a local path is returned as-is + path = "file:////test/test/file" + expected_path = Path("//test/test/file") + self.assertEqual(_file_uri_to_path(path), expected_path) + + def test_uri_not_file(self): + # Test that a URL returns None + path = "http://test/test/file" + self.assertIsNone(_file_uri_to_path(path)) diff --git a/tests/pyunit/stdlib/resources/pyunit_md5_utils_check.py b/tests/pyunit/stdlib/resources/pyunit_md5_utils_check.py index 65bf33544e..4570f74426 100644 --- a/tests/pyunit/stdlib/resources/pyunit_md5_utils_check.py +++ b/tests/pyunit/stdlib/resources/pyunit_md5_utils_check.py @@ -24,13 +24,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import unittest -import tempfile import os import shutil +import tempfile +import unittest from pathlib import Path -from gem5.resources.md5_utils import md5_file, md5_dir +from gem5.resources.md5_utils import ( + md5_dir, + md5_file, +) class MD5FileTestSuite(unittest.TestCase): @@ -46,7 +49,7 @@ class MD5FileTestSuite(unittest.TestCase): md5 = md5_file(Path(file.name)) os.remove(file.name) - self.assertEquals("b113b29fce251f2023066c3fda2ec9dd", md5) + self.assertEqual("b113b29fce251f2023066c3fda2ec9dd", md5) def test_identicalFilesIdenticalMd5(self) -> None: # This test ensures that two files with exactly the same contents have @@ -68,14 +71,13 @@ class MD5FileTestSuite(unittest.TestCase): os.remove(file.name) - self.assertEquals(first_file_md5, second_file_md5) + self.assertEqual(first_file_md5, second_file_md5) class MD5DirTestSuite(unittest.TestCase): """Test cases for gem5.resources.md5_utils.md5_dir()""" def _create_temp_directory(self) -> Path: - dir = tempfile.mkdtemp() with open(os.path.join(dir, "file1"), "w") as f: @@ -99,7 +101,7 @@ class MD5DirTestSuite(unittest.TestCase): md5 = md5_dir(dir) shutil.rmtree(dir) - self.assertEquals("ad5ac785de44c9fc2fe2798cab2d7b1a", md5) + self.assertEqual("ad5ac785de44c9fc2fe2798cab2d7b1a", md5) def test_identicalDirsIdenticalMd5(self) -> None: # This test ensures that two directories with exactly the same contents @@ -113,4 +115,4 @@ class MD5DirTestSuite(unittest.TestCase): second_md5 = md5_dir(dir2) shutil.rmtree(dir2) - self.assertEquals(first_md5, second_md5) + self.assertEqual(first_md5, second_md5) diff --git a/tests/pyunit/stdlib/resources/pyunit_obtain_resources_check.py b/tests/pyunit/stdlib/resources/pyunit_obtain_resources_check.py index b1eda4e6ed..b4bc5dba94 100644 --- a/tests/pyunit/stdlib/resources/pyunit_obtain_resources_check.py +++ b/tests/pyunit/stdlib/resources/pyunit_obtain_resources_check.py @@ -24,20 +24,21 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import unittest -import os -import io import contextlib +import io +import os +import unittest from pathlib import Path - -from gem5.resources.resource import obtain_resource, BinaryResource - -from gem5.isas import ISA +from unittest.mock import patch from _m5 import core +from gem5.isas import ISA from gem5.resources.client_api.client_wrapper import ClientWrapper -from unittest.mock import patch +from gem5.resources.resource import ( + BinaryResource, + obtain_resource, +) mock_json_path = Path(__file__).parent / "refs/obtain-resource.json" @@ -78,13 +79,11 @@ class TestObtainResourcesCheck(unittest.TestCase): resource_directory=self.get_resource_dir(), gem5_version="develop", ) - self.assertEquals("1.7.0", resource.get_resource_version()) + self.assertEqual("1.7.0", resource.get_resource_version()) self.assertIsInstance(resource, BinaryResource) - self.assertEquals( - "test description v1.7.0", resource.get_description() - ) - self.assertEquals("src/test-source", resource.get_source()) - self.assertEquals(ISA.ARM, resource.get_architecture()) + self.assertEqual("test description v1.7.0", resource.get_description()) + self.assertEqual("src/test-source", resource.get_source()) + self.assertEqual(ISA.ARM, resource.get_architecture()) def test_obtain_resources_with_version_compatible(self): resource = obtain_resource( @@ -93,13 +92,13 @@ class TestObtainResourcesCheck(unittest.TestCase): resource_version="1.5.0", gem5_version="develop", ) - self.assertEquals("1.5.0", resource.get_resource_version()) + self.assertEqual("1.5.0", resource.get_resource_version()) self.assertIsInstance(resource, BinaryResource) - self.assertEquals( + self.assertEqual( "test description for 1.5.0", resource.get_description() ) - self.assertEquals("src/test-source", resource.get_source()) - self.assertEquals(ISA.ARM, resource.get_architecture()) + self.assertEqual("src/test-source", resource.get_source()) + self.assertEqual(ISA.ARM, resource.get_architecture()) def test_obtain_resources_with_version_incompatible(self): resource = None @@ -110,12 +109,6 @@ class TestObtainResourcesCheck(unittest.TestCase): resource_directory=self.get_resource_dir(), resource_version="1.5.0", ) - self.assertTrue( - f"warn: Resource test-binary-resource with version 1.5.0 is not known to be compatible with gem5 version {core.gem5Version}. " - "This may cause problems with your simulation. This resource's compatibility with different gem5 versions can be found here: " - f"https://resources.gem5.org/resources/test-binary-resource/versions" - in f.getvalue() - ) resource = obtain_resource( resource_id="test-binary-resource", @@ -123,13 +116,13 @@ class TestObtainResourcesCheck(unittest.TestCase): resource_version="1.5.0", gem5_version="develop", ) - self.assertEquals("1.5.0", resource.get_resource_version()) + self.assertEqual("1.5.0", resource.get_resource_version()) self.assertIsInstance(resource, BinaryResource) - self.assertEquals( + self.assertEqual( "test description for 1.5.0", resource.get_description() ) - self.assertEquals("src/test-source", resource.get_source()) - self.assertEquals(ISA.ARM, resource.get_architecture()) + self.assertEqual("src/test-source", resource.get_source()) + self.assertEqual(ISA.ARM, resource.get_architecture()) def test_obtain_resources_no_version_invalid_id(self): with self.assertRaises(Exception) as context: diff --git a/tests/pyunit/stdlib/resources/pyunit_resource_specialization.py b/tests/pyunit/stdlib/resources/pyunit_resource_specialization.py index 93b83019b9..2401edbc3e 100644 --- a/tests/pyunit/stdlib/resources/pyunit_resource_specialization.py +++ b/tests/pyunit/stdlib/resources/pyunit_resource_specialization.py @@ -27,18 +27,15 @@ import os import unittest from pathlib import Path +from unittest.mock import patch -from gem5.resources.resource import * - +from gem5.isas import ISA +from gem5.resources.client_api.client_wrapper import ClientWrapper from gem5.resources.looppoint import ( LooppointCsvLoader, LooppointJsonLoader, ) - -from gem5.isas import ISA - -from gem5.resources.client_api.client_wrapper import ClientWrapper -from unittest.mock import patch +from gem5.resources.resource import * mock_json_path = Path(__file__).parent / "refs/resource-specialization.json" @@ -86,11 +83,11 @@ class ResourceSpecializationSuite(unittest.TestCase): self.assertIsInstance(resource, BinaryResource) - self.assertEquals( + self.assertEqual( "binary-example documentation.", resource.get_description() ) - self.assertEquals("src/simple", resource.get_source()) - self.assertEquals(ISA.ARM, resource.get_architecture()) + self.assertEqual("src/simple", resource.get_source()) + self.assertEqual(ISA.ARM, resource.get_architecture()) def test_kernel_resource(self) -> None: """Tests the loading of a KernelResource.""" @@ -102,11 +99,11 @@ class ResourceSpecializationSuite(unittest.TestCase): self.assertIsInstance(resource, KernelResource) - self.assertEquals( + self.assertEqual( "kernel-example documentation.", resource.get_description() ) - self.assertEquals("src/linux-kernel", resource.get_source()) - self.assertEquals(ISA.RISCV, resource.get_architecture()) + self.assertEqual("src/linux-kernel", resource.get_source()) + self.assertEqual(ISA.RISCV, resource.get_architecture()) def test_bootloader_resource(self) -> None: """Tests the loading of a BootloaderResource.""" @@ -118,7 +115,7 @@ class ResourceSpecializationSuite(unittest.TestCase): self.assertIsInstance(resource, BootloaderResource) - self.assertEquals( + self.assertEqual( "bootloader documentation.", resource.get_description() ) self.assertIsNone(resource.get_source()) @@ -134,11 +131,11 @@ class ResourceSpecializationSuite(unittest.TestCase): self.assertIsInstance(resource, DiskImageResource) - self.assertEquals( + self.assertEqual( "disk-image documentation.", resource.get_description() ) - self.assertEquals("src/x86-ubuntu", resource.get_source()) - self.assertEquals("1", resource.get_root_partition()) + self.assertEqual("src/x86-ubuntu", resource.get_source()) + self.assertEqual("1", resource.get_root_partition()) def test_checkpoint_resource(self) -> None: """Tests the loading of a CheckpointResource.""" @@ -150,7 +147,7 @@ class ResourceSpecializationSuite(unittest.TestCase): self.assertIsInstance(resource, CheckpointResource) - self.assertEquals( + self.assertEqual( "checkpoint-example documentation.", resource.get_description() ) self.assertIsNone(resource.get_source()) @@ -178,14 +175,14 @@ class ResourceSpecializationSuite(unittest.TestCase): self.assertIsInstance(resource, SimpointDirectoryResource) - self.assertEquals( + self.assertEqual( "simpoint directory documentation.", resource.get_description() ) self.assertIsNone(resource.get_source()) - self.assertEquals(1000000, resource.get_simpoint_interval()) - self.assertEquals(1000000, resource.get_warmup_interval()) - self.assertEquals( + self.assertEqual(1000000, resource.get_simpoint_interval()) + self.assertEqual(1000000, resource.get_warmup_interval()) + self.assertEqual( Path( Path(self.get_resource_dir()) / "simpoint-directory-example" @@ -193,7 +190,7 @@ class ResourceSpecializationSuite(unittest.TestCase): ), resource.get_simpoint_file(), ) - self.assertEquals( + self.assertEqual( Path( Path(self.get_resource_dir()) / "simpoint-directory-example" @@ -201,7 +198,7 @@ class ResourceSpecializationSuite(unittest.TestCase): ), resource.get_weight_file(), ) - self.assertEquals("Example Workload", resource.get_workload_name()) + self.assertEqual("Example Workload", resource.get_workload_name()) def test_simpoint_resource(self) -> None: """Tests the loading of a Simpoint resource.""" @@ -213,16 +210,14 @@ class ResourceSpecializationSuite(unittest.TestCase): self.assertIsInstance(resource, SimpointResource) - self.assertEquals( - "simpoint documentation.", resource.get_description() - ) + self.assertEqual("simpoint documentation.", resource.get_description()) self.assertIsNone(resource.get_source()) self.assertIsNone(resource.get_local_path()) - self.assertEquals(1000000, resource.get_simpoint_interval()) - self.assertEquals(23445, resource.get_warmup_interval()) - self.assertEquals([2, 3, 4, 15], resource.get_simpoint_list()) - self.assertEquals([0.1, 0.2, 0.4, 0.3], resource.get_weight_list()) + self.assertEqual(1000000, resource.get_simpoint_interval()) + self.assertEqual(23445, resource.get_warmup_interval()) + self.assertEqual([2, 3, 4, 15], resource.get_simpoint_list()) + self.assertEqual([0.1, 0.2, 0.4, 0.3], resource.get_weight_list()) def test_file_resource(self) -> None: """Tests the loading of a FileResource.""" @@ -247,7 +242,7 @@ class ResourceSpecializationSuite(unittest.TestCase): self.assertIsInstance(resource, DirectoryResource) - self.assertEquals( + self.assertEqual( "directory-example documentation.", resource.get_description() ) self.assertIsNone(resource.get_source()) @@ -268,7 +263,7 @@ class ResourceSpecializationSuite(unittest.TestCase): # LooppointCsvLoader. self.assertIsInstance(resource, LooppointCsvLoader) - self.assertEquals( + self.assertEqual( "A looppoint pinpoints csv file.", resource.get_description() ) self.assertIsNone(resource.get_source()) @@ -287,10 +282,10 @@ class ResourceSpecializationSuite(unittest.TestCase): self.assertIsInstance(resource, LooppointJsonResource) self.assertIsInstance(resource, LooppointJsonLoader) - self.assertEquals(1, len(resource.get_regions())) + self.assertEqual(1, len(resource.get_regions())) self.assertTrue("1" in resource.get_regions()) - self.assertEquals( + self.assertEqual( "A looppoint json file resource.", resource.get_description() ) self.assertIsNone(resource.get_source()) diff --git a/tests/pyunit/stdlib/resources/pyunit_suite_checks.py b/tests/pyunit/stdlib/resources/pyunit_suite_checks.py new file mode 100644 index 0000000000..79944a59fd --- /dev/null +++ b/tests/pyunit/stdlib/resources/pyunit_suite_checks.py @@ -0,0 +1,171 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import contextlib +import io +import os +import shutil +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +from gem5.resources.client_api.client_wrapper import ClientWrapper +from gem5.resources.resource import ( + SuiteResource, + WorkloadResource, + obtain_resource, +) + +mock_config_json = { + "sources": { + "baba": { + "url": Path(__file__).parent / "refs/suite-checks.json", + "isMongo": False, + } + }, +} + + +class CustomSuiteResourceTestSuite(unittest.TestCase): + @classmethod + @patch( + "gem5.resources.client.clientwrapper", + new=ClientWrapper(mock_config_json), + ) + def setUpClass(cls): + cls.workload1 = obtain_resource("simple-workload-1") + cls.workload2 = obtain_resource("simple-workload-2") + cls.SuiteResource = SuiteResource( + workloads={cls.workload1: set(), cls.workload2: set()} + ) + + @patch( + "gem5.resources.client.clientwrapper", + new=ClientWrapper(mock_config_json), + ) + def test_with_input_group(self) -> None: + """ + Tests the `with_input_group` function. + """ + # test if an input group can return a single workload in a suite resource + + with self.assertRaises(Exception) as context: + filtered_suite = self.SuiteResource.with_input_group("testtag2") + self.assertIsInstance(filtered_suite, SuiteResource) + self.assertEqual(len(filtered_suite), 0) + self.assertTrue( + f"Input group invalid not found in Suite.\n" + f"Available input groups are {filtered_suite.get_input_groups()}" + in str(context.exception) + ) + + def test_get_input_groups(self): + """ + Tests the `list_input_groups` function. + """ + self.assertEqual(self.SuiteResource.get_input_groups(), set()) + + +class SuiteResourceTestSuite(unittest.TestCase): + @classmethod + @patch( + "gem5.resources.client.clientwrapper", + new=ClientWrapper(mock_config_json), + ) + def setUpClass(cls): + cls.suite = obtain_resource("suite-example", gem5_version="develop") + + @patch( + "gem5.resources.client.clientwrapper", + new=ClientWrapper(mock_config_json), + ) + def test_with_input_group(self) -> None: + """ + Tests the `with_input_group` function. + """ + # test if an input group can return a single workload in a suite resource + filtered_suite = self.suite.with_input_group("testtag2") + self.assertIsInstance(filtered_suite, SuiteResource) + self.assertEqual(len(filtered_suite), 1) + for workload in filtered_suite: + self.assertIsInstance(workload, WorkloadResource) + + @patch( + "gem5.resources.client.clientwrapper", + new=ClientWrapper(mock_config_json), + ) + def test_with_input_group_multiple(self) -> None: + # test if an input group can return multiple workloads in a suite resource + filtered_suite = self.suite.with_input_group("testtag1") + self.assertIsInstance(filtered_suite, SuiteResource) + self.assertEqual(len(filtered_suite), 2) + for workload in filtered_suite: + self.assertIsInstance(workload, WorkloadResource) + + @patch( + "gem5.resources.client.clientwrapper", + new=ClientWrapper(mock_config_json), + ) + def test_with_input_group_invalid(self) -> None: + """ + Tests the `with_input_group` function with an invalid input group. + """ + with self.assertRaises(Exception) as context: + filtered_suite = self.suite.with_input_group("invalid") + # check if exception is raised + self.assertTrue( + f"Input group invalid not found in Suite.\n" + f"Available input groups are {filtered_suite.get_input_groups()}" + in str(context.exception) + ) + + @patch( + "gem5.resources.client.clientwrapper", + new=ClientWrapper(mock_config_json), + ) + def test_get_input_groups(self) -> None: + """ + Tests the `list_input_groups` function. + """ + expected_input_groups = {"testtag1", "testtag2", "testtag3"} + self.assertEqual(self.suite.get_input_groups(), expected_input_groups) + + @patch( + "gem5.resources.client.clientwrapper", + new=ClientWrapper(mock_config_json), + ) + def test_get_input_groups_not_found(self) -> None: + """ + Tests the `list_input_groups` function with an invalid input group. + """ + with self.assertRaises(Exception) as context: + self.suite.get_input_groups("invalid") + self.assertTrue( + f"Input group invalid not found in Suite.\n" + f"Available input groups are {self.suite.get_input_groups()}" + in str(context.exception) + ) diff --git a/tests/pyunit/stdlib/resources/pyunit_workload_checks.py b/tests/pyunit/stdlib/resources/pyunit_workload_checks.py index b374e7a8d3..f5917208d5 100644 --- a/tests/pyunit/stdlib/resources/pyunit_workload_checks.py +++ b/tests/pyunit/stdlib/resources/pyunit_workload_checks.py @@ -24,21 +24,23 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import unittest import os +import unittest +from pathlib import Path +from typing import Dict +from unittest.mock import patch -from gem5.resources.workload import Workload, CustomWorkload +from gem5.resources.client_api.client_wrapper import ClientWrapper from gem5.resources.resource import ( BinaryResource, DiskImageResource, + WorkloadResource, obtain_resource, ) - -from typing import Dict - -from gem5.resources.client_api.client_wrapper import ClientWrapper -from unittest.mock import patch -from pathlib import Path +from gem5.resources.workload import ( + CustomWorkload, + Workload, +) mock_config_json = { "sources": { @@ -61,7 +63,7 @@ class CustomWorkloadTestSuite(unittest.TestCase): new=ClientWrapper(mock_config_json), ) def setUpClass(cls) -> None: - cls.custom_workload = CustomWorkload( + cls.custom_workload = WorkloadResource( function="set_se_binary_workload", parameters={ "binary": obtain_resource( @@ -72,36 +74,36 @@ class CustomWorkloadTestSuite(unittest.TestCase): ) def test_get_function_str(self) -> None: - # Tests `CustomResource.get_function_str` + # Tests `CustomWorkload.get_function_str` self.assertEqual( "set_se_binary_workload", self.custom_workload.get_function_str() ) def test_get_parameters(self) -> None: - # Tests `CustomResource.get_parameter` + # Tests `CustomWorkload.get_parameter` parameters = self.custom_workload.get_parameters() self.assertTrue(isinstance(parameters, Dict)) - self.assertEquals(2, len(parameters)) + self.assertEqual(2, len(parameters)) self.assertTrue("binary" in parameters) self.assertTrue(isinstance(parameters["binary"], BinaryResource)) self.assertTrue("arguments" in parameters) self.assertTrue(isinstance(parameters["arguments"], list)) - self.assertEquals(2, len(parameters["arguments"])) - self.assertEquals("hello", parameters["arguments"][0]) - self.assertEquals(6, parameters["arguments"][1]) + self.assertEqual(2, len(parameters["arguments"])) + self.assertEqual("hello", parameters["arguments"][0]) + self.assertEqual(6, parameters["arguments"][1]) def test_add_parameters(self) -> None: - # Tests `CustomResource.set_parameter` for the case where we add a new + # Tests `CustomWorkload.set_parameter` for the case where we add a new # parameter value. self.custom_workload.set_parameter("test_param", 10) self.assertTrue("test_param" in self.custom_workload.get_parameters()) - self.assertEquals( + self.assertEqual( 10, self.custom_workload.get_parameters()["test_param"] ) @@ -109,14 +111,14 @@ class CustomWorkloadTestSuite(unittest.TestCase): del self.custom_workload.get_parameters()["test_param"] def test_override_parameter(self) -> None: - # Tests `CustomResource.set_parameter` for the case where we override + # Tests `CustomWorkload.set_parameter` for the case where we override # a parameter's value. old_value = self.custom_workload.get_parameters()["binary"] self.custom_workload.set_parameter("binary", "test") self.assertTrue("binary" in self.custom_workload.get_parameters()) - self.assertEquals( + self.assertEqual( "test", self.custom_workload.get_parameters()["binary"] ) @@ -135,12 +137,12 @@ class WorkloadTestSuite(unittest.TestCase): ClientWrapper(mock_config_json), ) def setUpClass(cls): - cls.workload = Workload("simple-boot", gem5_version="develop") + cls.workload = obtain_resource("simple-boot", gem5_version="develop") def test_get_function_str(self) -> None: # Tests `Resource.get_function_str` - self.assertEquals( + self.assertEqual( "set_kernel_disk_workload", self.workload.get_function_str() ) @@ -172,7 +174,7 @@ class WorkloadTestSuite(unittest.TestCase): self.workload.set_parameter("test_param", 10) self.assertTrue("test_param" in self.workload.get_parameters()) - self.assertEquals(10, self.workload.get_parameters()["test_param"]) + self.assertEqual(10, self.workload.get_parameters()["test_param"]) # Cleanup del self.workload.get_parameters()["test_param"] @@ -185,7 +187,7 @@ class WorkloadTestSuite(unittest.TestCase): self.workload.set_parameter("readfile_contents", "test") self.assertTrue("readfile_contents" in self.workload.get_parameters()) - self.assertEquals( + self.assertEqual( "test", self.workload.get_parameters()["readfile_contents"] ) diff --git a/tests/pyunit/stdlib/resources/refs/suite-checks.json b/tests/pyunit/stdlib/resources/refs/suite-checks.json new file mode 100644 index 0000000000..7583020292 --- /dev/null +++ b/tests/pyunit/stdlib/resources/refs/suite-checks.json @@ -0,0 +1,97 @@ +[ + { + "id": "suite-example", + "category": "suite", + "resource_version": "1.0.0", + "gem5_versions": ["develop","23.1"], + "workloads": [ + { + "id": "simple-workload-1", + "resource_version": "1.0.0", + "input_group": ["testtag1", "testtag2"] + }, + { + "id": "simple-workload-2", + "resource_version": "1.0.0", + "input_group": ["testtag1", "testtag3"] + } + ] + }, + { + "category": "workload", + "id": "simple-workload-1", + "description": "Description of workload here", + "function": "set_kernel_disk_workload", + "resources": { + "kernel": "x86-linux-kernel-5.2.3-example", + "disk-image": "x86-ubuntu-18.04-img-example" + }, + "additional_params": { + "readfile_contents": "echo 'Boot successful'; m5 exit" + }, + "resource_version": "1.0.0", + "gem5_versions": [ + "develop" + ] + }, + { + "category": "workload", + "id": "simple-workload-2", + "description": "Description of workload here", + "function": "set_kernel_disk_workload", + "resources": { + "kernel": "x86-linux-kernel-5.2.3-example", + "disk-image": "x86-ubuntu-18.04-img-example" + }, + "additional_params": { + "readfile_contents": "echo 'Boot successful'; m5 exit" + }, + "resource_version": "1.0.0", + "gem5_versions": [ + "develop" + ] + }, + { + "category": "kernel", + "id": "x86-linux-kernel-5.2.3-example", + "description": "The linux kernel (v5.2.3), compiled to X86.", + "architecture": "X86", + "is_zipped": false, + "md5sum": "4838c99b77d33c8307b939c16624e4ac", + "url": "http://dist.gem5.org/dist/develop/kernels/x86/static/vmlinux-5.2.3", + "source": "src/linux-kernel", + "resource_version": "1.0.0", + "gem5_versions": [ + "develop" + ] + }, + { + "category": "disk-image", + "id": "x86-ubuntu-18.04-img-example", + "description": "A disk image containing Ubuntu 18.04 for x86..", + "architecture": "X86", + "is_zipped": false, + "md5sum": "dbf120338b37153e3334603970cebd8c", + "url": "http://dist.gem5.org/dist/develop/test-progs/hello/bin/x86/linux/hello64-static", + "source": "src/x86-ubuntu", + "root_partition": "1", + "resource_version": "1.0.0", + "gem5_versions": [ + "develop" + ] + }, + { + "category": "binary", + "id": "x86-hello64-static-example", + "description": "A 'Hello World!' binary.", + "architecture": "X86", + "is_zipped": false, + "md5sum": "dbf120338b37153e3334603970cebd8c", + "url": "http://dist.gem5.org/dist/develop/test-progs/hello/bin/x86/linux/hello64-static", + "source": "src/simple", + "resource_version": "1.0.0", + "gem5_versions": [ + "develop" + ] + } +] diff --git a/tests/pyunit/test_run.py b/tests/pyunit/test_run.py index 76cd5f70cc..fd24f753bb 100644 --- a/tests/pyunit/test_run.py +++ b/tests/pyunit/test_run.py @@ -27,6 +27,7 @@ import os from testlib.configuration import constants + from gem5.suite import * """ diff --git a/tests/run.py b/tests/run.py index dde8f70749..cb0d42b996 100644 --- a/tests/run.py +++ b/tests/run.py @@ -37,13 +37,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import os -import sys +import os.path import re import string - +import sys from os.path import join as joinpath -import os.path -import os import m5 @@ -161,6 +159,7 @@ test_progs = os.environ.get("M5_TEST_PROGS", "/dist/m5/regression/test-progs") if not os.path.isdir(test_progs): test_progs = joinpath(tests_root, "test-progs") + # generate path to binary file def binpath(app, file=None): # executable has same name as app unless specified otherwise @@ -188,7 +187,7 @@ def run_config(config, argv=None): src_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "../")) abs_path = joinpath(src_root, config) - code = compile(open(abs_path, "r").read(), abs_path, "exec") + code = compile(open(abs_path).read(), abs_path, "exec") scope = {"__file__": config, "__name__": "__m5_main__"} # Set the working directory in case we are executing from @@ -234,6 +233,7 @@ exec( ) ) + # Initialize all CPUs in a system def initCPUs(sys): def initCPU(cpu): diff --git a/util/checkpoint-tester.py b/util/checkpoint-tester.py index 1e4024b858..7fffa4bdf5 100755 --- a/util/checkpoint-tester.py +++ b/util/checkpoint-tester.py @@ -64,9 +64,11 @@ # -import os, sys, re -import subprocess import argparse +import os +import re +import subprocess +import sys parser = argparse.ArgumentParser() @@ -102,7 +104,7 @@ print("===> Running initial simulation.") subprocess.call([m5_binary] + ["-red", cptdir] + args + checkpoint_args) dirs = os.listdir(cptdir) -expr = re.compile("cpt\.([0-9]*)") +expr = re.compile(r"cpt\.([0-9]*)") cpts = [] for dir in dirs: match = expr.match(dir) diff --git a/util/checkpoint_aggregator.py b/util/checkpoint_aggregator.py index 86892c87b1..8621330853 100755 --- a/util/checkpoint_aggregator.py +++ b/util/checkpoint_aggregator.py @@ -26,10 +26,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from configparser import ConfigParser import gzip - -import sys, re, os +import os +import re +import sys +from configparser import ConfigParser class myCP(ConfigParser): @@ -57,7 +58,7 @@ def aggregate(output_dir, cpts, no_compress, memory_size): max_curtick = 0 num_digits = len(str(len(cpts) - 1)) - for (i, arg) in enumerate(cpts): + for i, arg in enumerate(cpts): print(arg) merged_config = myCP() config = myCP() diff --git a/util/cpt_upgrader.py b/util/cpt_upgrader.py index a852294fbc..92e104fd25 100755 --- a/util/cpt_upgrader.py +++ b/util/cpt_upgrader.py @@ -70,8 +70,11 @@ import configparser -import glob, types, sys, os +import glob +import os import os.path as osp +import sys +import types verbose_print = False @@ -193,7 +196,7 @@ def process_file(path, **kwargs): if not osp.isfile(path): import errno - raise IOError(errno.ENOENT, "No such file", path) + raise OSError(errno.ENOENT, "No such file", path) verboseprint(f"Processing file {path}....") @@ -208,7 +211,7 @@ def process_file(path, **kwargs): cpt.optionxform = str # Read the current data - cpt_file = open(path, "r") + cpt_file = open(path) cpt.read_file(cpt_file) cpt_file.close() @@ -220,7 +223,7 @@ def process_file(path, **kwargs): # Legacy linear checkpoint version # convert to list of tags before proceeding - tags = set([]) + tags = set() for i in range(2, cpt_ver + 1): tags.add(Upgrader.legacy[i].tag) verboseprint("performed legacy version -> tags conversion") @@ -253,7 +256,7 @@ def process_file(path, **kwargs): # downgraders are present, respecting dependences to_apply = (Upgrader.tag_set - tags) | (Upgrader.untag_set & tags) while to_apply: - ready = set([t for t in to_apply if Upgrader.get(t).ready(tags)]) + ready = {t for t in to_apply if Upgrader.get(t).ready(tags)} if not ready: print("could not apply these upgrades:", " ".join(to_apply)) print("update dependences impossible to resolve; aborting") @@ -277,7 +280,10 @@ def process_file(path, **kwargs): if __name__ == "__main__": - from argparse import ArgumentParser, SUPPRESS + from argparse import ( + SUPPRESS, + ArgumentParser, + ) parser = ArgumentParser(usage="%(prog)s [args] ") parser.add_argument( diff --git a/util/cpt_upgraders/arm-ccregs.py b/util/cpt_upgraders/arm-ccregs.py index 435be7b0cb..abfbe2d5b3 100644 --- a/util/cpt_upgraders/arm-ccregs.py +++ b/util/cpt_upgraders/arm-ccregs.py @@ -5,13 +5,13 @@ def upgrader(cpt): for sec in cpt.sections(): import re - re_cpu_match = re.match("^(.*sys.*\.cpu[^.]*)\.xc\.(.+)$", sec) + re_cpu_match = re.match(r"^(.*sys.*\.cpu[^.]*)\.xc\.(.+)$", sec) # Search for all the execution contexts if not re_cpu_match: continue items = [] - for (item, value) in cpt.items(sec): + for item, value in cpt.items(sec): items.append(item) if "ccRegs" not in items: intRegs = cpt.get(sec, "intRegs").split() diff --git a/util/cpt_upgraders/arm-contextidr-el2.py b/util/cpt_upgraders/arm-contextidr-el2.py index 891fec5e0d..d016c1a224 100644 --- a/util/cpt_upgraders/arm-contextidr-el2.py +++ b/util/cpt_upgraders/arm-contextidr-el2.py @@ -5,7 +5,7 @@ def upgrader(cpt): import re # Search for all ISA sections - if re.search(".*sys.*\.cpu.*\.isa$", sec): + if re.search(r".*sys.*\.cpu.*\.isa$", sec): miscRegs = cpt.get(sec, "miscRegs").split() # CONTEXTIDR_EL2 defaults to 0b11111100000000000001 miscRegs[599:599] = [0xFC001] diff --git a/util/cpt_upgraders/arm-gem5-gic-ext.py b/util/cpt_upgraders/arm-gem5-gic-ext.py index fea852ff13..4e543a24e0 100644 --- a/util/cpt_upgraders/arm-gem5-gic-ext.py +++ b/util/cpt_upgraders/arm-gem5-gic-ext.py @@ -61,7 +61,7 @@ def upgrader(cpt): new_per_cpu_regs = (("cpuSgiPendingExt", "0"), ("cpuSgiActiveExt", "0")) for sec in cpt.sections(): - if re.search(".*\.gic$", sec): + if re.search(r".*\.gic$", sec): for reg, default in per_cpu_regs: value = cpt.get(sec, reg).split(" ") assert ( diff --git a/util/cpt_upgraders/arm-gicv2-banked-regs.py b/util/cpt_upgraders/arm-gicv2-banked-regs.py index 44a6146b58..6438a178d4 100644 --- a/util/cpt_upgraders/arm-gicv2-banked-regs.py +++ b/util/cpt_upgraders/arm-gicv2-banked-regs.py @@ -33,13 +33,14 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + # duplicate banked registers into new per-cpu arrays. def upgrader(cpt): if cpt.get("root", "isa", fallback="") == "arm": for sec in cpt.sections(): import re - if not re.search("\.gic$", sec): + if not re.search(r"\.gic$", sec): continue cpuEnabled = cpt.get(sec, "cpuEnabled").split() diff --git a/util/cpt_upgraders/arm-hdlcd-upgrade.py b/util/cpt_upgraders/arm-hdlcd-upgrade.py index 96d6368718..87431e8b9f 100644 --- a/util/cpt_upgraders/arm-hdlcd-upgrade.py +++ b/util/cpt_upgraders/arm-hdlcd-upgrade.py @@ -69,7 +69,7 @@ def upgrader(cpt): } for sec in cpt.sections(): - if re.search(".*\.hdlcd$", sec): + if re.search(r".*\.hdlcd$", sec): options = {} for new, old in list(option_names.items()): options[new] = cpt.get(sec, old) diff --git a/util/cpt_upgraders/arm-miscreg-teehbr.py b/util/cpt_upgraders/arm-miscreg-teehbr.py index d6e81e0da1..656757d036 100644 --- a/util/cpt_upgraders/arm-miscreg-teehbr.py +++ b/util/cpt_upgraders/arm-miscreg-teehbr.py @@ -5,7 +5,7 @@ def upgrader(cpt): import re # Search for all ISA sections - if re.search(".*sys.*\.cpu.*\.isa$", sec): + if re.search(r".*sys.*\.cpu.*\.isa$", sec): mr = cpt.get(sec, "miscRegs").split() if len(mr) == 161: print("MISCREG_TEEHBR already seems to be inserted.") diff --git a/util/cpt_upgraders/arm-sve.py b/util/cpt_upgraders/arm-sve.py index 45d2949aa8..c484bae057 100644 --- a/util/cpt_upgraders/arm-sve.py +++ b/util/cpt_upgraders/arm-sve.py @@ -12,8 +12,7 @@ def upgrader(cpt): import re # Search for all ISA sections - if re.search(".*sys.*\.cpu.*\.isa$", sec): - + if re.search(r".*sys.*\.cpu.*\.isa$", sec): # haveSVE = false cpt.set(sec, "haveSVE", "false") diff --git a/util/cpt_upgraders/arm-sysreg-mapping-ns.py b/util/cpt_upgraders/arm-sysreg-mapping-ns.py index fd02062039..c0a76549a0 100644 --- a/util/cpt_upgraders/arm-sysreg-mapping-ns.py +++ b/util/cpt_upgraders/arm-sysreg-mapping-ns.py @@ -33,6 +33,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + # reflect updated register mappings for ARM ISA def upgrader(cpt): if cpt.get("root", "isa", fallback="") == "arm": @@ -40,7 +41,7 @@ def upgrader(cpt): import re # Search for all ISA sections - if re.search(".*sys.*\.cpu.*\.isa\d*$", sec): + if re.search(r".*sys.*\.cpu.*\.isa\d*$", sec): mr = cpt.get(sec, "miscRegs").split() if int(mr[0]) & 16 == 0: # CPSR reg width; 0 for AArch64 mr[112] = mr[111] # ACTLR_NS = ACTLR diff --git a/util/cpt_upgraders/armv8.py b/util/cpt_upgraders/armv8.py index 6679beb88a..e5453770fa 100644 --- a/util/cpt_upgraders/armv8.py +++ b/util/cpt_upgraders/armv8.py @@ -12,7 +12,7 @@ def upgrader(cpt): ) # Find the CPU context's and upgrade their registers for sec in cpt.sections(): - re_xc_match = re.match("^.*?sys.*?\.cpu(\d+)*\.xc\.*", sec) + re_xc_match = re.match(r"^.*?sys.*?\.cpu(\d+)*\.xc\.*", sec) if not re_xc_match: continue @@ -38,7 +38,7 @@ def upgrader(cpt): # Update the cpu interrupt field for sec in cpt.sections(): - re_int_match = re.match("^.*?sys.*?\.cpu(\d+)*$", sec) + re_int_match = re.match(r"^.*?sys.*?\.cpu(\d+)*$", sec) if not re_int_match: continue @@ -49,7 +49,7 @@ def upgrader(cpt): # Update the per cpu interrupt structure for sec in cpt.sections(): - re_int_match = re.match("^.*?sys.*?\.cpu(\d+)*\.interrupts$", sec) + re_int_match = re.match(r"^.*?sys.*?\.cpu(\d+)*\.interrupts$", sec) if not re_int_match: continue @@ -60,7 +60,7 @@ def upgrader(cpt): # Update the misc regs and add in new isa specific fields for sec in cpt.sections(): - re_isa_match = re.match("^.*?sys.*?\.cpu(\d+)*\.isa$", sec) + re_isa_match = re.match(r"^.*?sys.*?\.cpu(\d+)*\.isa$", sec) if not re_isa_match: continue @@ -254,7 +254,7 @@ def upgrader(cpt): cpu_prefix = {} # Add in state for ITB/DTB for sec in cpt.sections(): - re_tlb_match = re.match("(^.*?sys.*?\.cpu(\d+)*)\.(dtb|itb)$", sec) + re_tlb_match = re.match(r"(^.*?sys.*?\.cpu(\d+)*)\.(dtb|itb)$", sec) if not re_tlb_match: continue @@ -271,7 +271,7 @@ def upgrader(cpt): # Add in extra state for the new TLB Entries for sec in cpt.sections(): re_tlbentry_match = re.match( - "(^.*?sys.*?\.cpu(\d+)*)\.(dtb|itb).TlbEntry\d+$", sec + r"(^.*?sys.*?\.cpu(\d+)*)\.(dtb|itb).TlbEntry\d+$", sec ) if not re_tlbentry_match: continue diff --git a/util/cpt_upgraders/isa-is-simobject.py b/util/cpt_upgraders/isa-is-simobject.py index 0fd33f733e..fa6734cbfa 100644 --- a/util/cpt_upgraders/isa-is-simobject.py +++ b/util/cpt_upgraders/isa-is-simobject.py @@ -60,7 +60,7 @@ def upgrader(cpt): for sec in cpt.sections(): import re - re_cpu_match = re.match("^(.*sys.*\.cpu[^.]*)\.xc\.(.+)$", sec) + re_cpu_match = re.match(r"^(.*sys.*\.cpu[^.]*)\.xc\.(.+)$", sec) # Search for all the execution contexts if not re_cpu_match: continue @@ -75,17 +75,17 @@ def upgrader(cpt): isa_section = [] for fspec in isa_fields: - for (key, value) in cpt.items(sec, raw=True): + for key, value in cpt.items(sec, raw=True): if key in isa_fields: isa_section.append((key, value)) name = f"{re_cpu_match.group(1)}.isa" isa_sections.append((name, isa_section)) - for (key, value) in isa_section: + for key, value in isa_section: cpt.remove_option(sec, key) - for (sec, options) in isa_sections: + for sec, options in isa_sections: # Some intermediate versions of gem5 have empty ISA sections # (after we made the ISA a SimObject, but before we started to # serialize into a separate ISA section). @@ -97,7 +97,7 @@ def upgrader(cpt): "Unexpected populated ISA section in old checkpoint" ) - for (key, value) in options: + for key, value in options: cpt.set(sec, key, value) diff --git a/util/cpt_upgraders/memory-per-range.py b/util/cpt_upgraders/memory-per-range.py index d75a4acf8c..24fa0b592e 100644 --- a/util/cpt_upgraders/memory-per-range.py +++ b/util/cpt_upgraders/memory-per-range.py @@ -6,7 +6,7 @@ def upgrader(cpt): import re # Search for a physical memory - if re.search(".*sys.*\.physmem$", sec): + if re.search(r".*sys.*\.physmem$", sec): # Add the number of stores attribute to the global physmem cpt.set(sec, "nbr_of_stores", "1") @@ -24,7 +24,7 @@ def upgrader(cpt): cpt.set(section_name, "store_id", "0") cpt.set(section_name, "range_size", mem_size) cpt.set(section_name, "filename", mem_filename) - elif re.search(".*sys.*\.\w*mem$", sec): + elif re.search(r".*sys.*\.\w*mem$", sec): # Due to the lack of information about a start address, # this migration only works if there is a single memory in # the system, thus starting at 0 diff --git a/util/cpt_upgraders/mempool-sections.py b/util/cpt_upgraders/mempool-sections.py index dec2e02799..55f1d591dc 100644 --- a/util/cpt_upgraders/mempool-sections.py +++ b/util/cpt_upgraders/mempool-sections.py @@ -12,7 +12,6 @@ def upgrader(cpt): systems[sec] = ptrs, limits for sec, (ptrs, limits) in systems.items(): - ptrs = list(map(int, ptrs.split())) limits = list(map(int, limits.split())) diff --git a/util/cpt_upgraders/remove-arm-cpsr-mode-miscreg.py b/util/cpt_upgraders/remove-arm-cpsr-mode-miscreg.py index 8eba866f1a..66b6cd8bd7 100644 --- a/util/cpt_upgraders/remove-arm-cpsr-mode-miscreg.py +++ b/util/cpt_upgraders/remove-arm-cpsr-mode-miscreg.py @@ -5,7 +5,7 @@ def upgrader(cpt): import re # Search for all ISA sections - if re.search(".*sys.*\.cpu.*\.isa$", sec): + if re.search(r".*sys.*\.cpu.*\.isa$", sec): mr = cpt.get(sec, "miscRegs").split() # Remove MISCREG_CPSR_MODE del mr[137] diff --git a/tests/configs/o3-timing-ruby.py b/util/cpt_upgraders/riscv-dyn-vlen.py similarity index 64% rename from tests/configs/o3-timing-ruby.py rename to util/cpt_upgraders/riscv-dyn-vlen.py index 30ee69ef23..ea2de9d19d 100644 --- a/tests/configs/o3-timing-ruby.py +++ b/util/cpt_upgraders/riscv-dyn-vlen.py @@ -1,6 +1,6 @@ -# Copyright (c) 2006-2007 The Regents of The University of Michigan +# Copyright (c) 2023 Barcelona Supercomputing Center (BSC) # All rights reserved. -# + # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: redistributions of source code must retain the above copyright @@ -11,7 +11,7 @@ # neither the name of the copyright holders nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. -# + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -24,33 +24,26 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import m5 -from m5.objects import * -import ruby_config +def upgrader(cpt): + """ + Update the checkpoint to support initial RVV implemtation. + The updater is taking the following steps. -ruby_memory = ruby_config.generate("TwoLevel_SplitL1UnifiedL2.rb", 1) + Set vector registers to occupy 327680 bytes (40regs * 8192bytes). + Vector registers now ocupy this space regardless of VLEN as the + VecRegContainer is always MaxVecLenInBytes. + """ -cpu = DerivO3CPU(cpu_id=0) + for sec in cpt.sections(): + import re -system = System( - cpu=cpu, - physmem=ruby_memory, - membus=SystemXBar(), - mem_mode="timing", - clk_domain=SrcClockDomain(clock="1GHz"), -) + # Search for all XC sections -# Create a seperate clock domain for components that should run at -# CPUs frequency -system.cpu.clk_domain = SrcClockDomain(clock="2GHz") - -system.physmem.port = system.membus.mem_side_ports -# create the interrupt controller -cpu.createInterruptController() -cpu.connectBus(system.membus) - -# Connect the system port for loading of binaries etc -system.system_port = system.membus.cpu_side_ports - -root = Root(full_system=False, system=system) + if re.search(r".*processor.*\.core.*\.xc.*", sec): + # Updating RVV vector registers (dummy values) + mr = cpt.get(sec, "regs.vector").split() + if len(mr) != 327680: + cpt.set( + sec, "regs.vector", " ".join("0" for i in range(327680)) + ) diff --git a/tests/configs/pc-o3-timing.py b/util/cpt_upgraders/riscv-pcstate.py similarity index 65% rename from tests/configs/pc-o3-timing.py rename to util/cpt_upgraders/riscv-pcstate.py index 24abcd2de6..4182355419 100644 --- a/tests/configs/pc-o3-timing.py +++ b/util/cpt_upgraders/riscv-pcstate.py @@ -1,15 +1,6 @@ -# Copyright (c) 2012 ARM Limited +# Copyright (c) 2023 Google LLC # All rights reserved. -# -# The license below extends only to copyright in the software and shall -# not be construed as granting a license to any other intellectual -# property including but not limited to intellectual property relating -# to a hardware implementation of the functionality of the software -# licensed hereunder. You may use the software subject to the license -# terms below provided that you ensure that this notice is replicated -# unmodified and in its entirety in all distributions of the software, -# modified or unmodified, in source code or in binary form. -# + # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: redistributions of source code must retain the above copyright @@ -20,7 +11,7 @@ # neither the name of the copyright holders nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. -# + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -33,9 +24,26 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.objects import * -from x86_generic import * -root = LinuxX86FSSystemUniprocessor( - mem_mode="timing", mem_class=DDR3_1600_8x8, cpu_class=DerivO3CPU -).create_root() +def upgrader(cpt): + # Update the RISC-V pcstate to match the new version of + # PCState + + for sec in cpt.sections(): + import re + + if re.search(r".*processor.*\.core.*\.xc.*", sec): + if cpt.get(sec, "_rvType", fallback="") == "": + cpt.set(sec, "_rvType", "1") + + if cpt.get(sec, "_vlenb", fallback="") == "": + cpt.set(sec, "_vlenb", "32") + + if cpt.get(sec, "_vtype", fallback="") == "": + cpt.set(sec, "_vtype", str(1 << 63)) + + if cpt.get(sec, "_vl", fallback="") == "": + cpt.set(sec, "_vl", "0") + + if cpt.get(sec, "_compressed", fallback="") == "": + cpt.set(sec, "_compressed", "false") diff --git a/util/cpt_upgraders/riscv-vext.py b/util/cpt_upgraders/riscv-vext.py new file mode 100644 index 0000000000..d335f74b83 --- /dev/null +++ b/util/cpt_upgraders/riscv-vext.py @@ -0,0 +1,84 @@ +# Copyright (c) 2023 Barcelona Supercomputing Center (BSC) +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +def upgrader(cpt): + """ + Update the checkpoint to support initial RVV implemtation. + The updater is taking the following steps. + + 1) Set vector registers to occupy 1280 bytes (40regs * 32bytes) + 2) Clear vector_element, vector_predicate and matrix registers + 3) Add RVV misc registers in the checkpoint + """ + + for sec in cpt.sections(): + import re + + # Search for all XC sections + if re.search(r".*processor.*\.core.*\.xc.*", sec): + # Updating RVV vector registers (dummy values) + # Assuming VLEN = 256 bits (32 bytes) + mr = cpt.get(sec, "regs.vector").split() + if len(mr) <= 8: + cpt.set(sec, "regs.vector", " ".join("0" for i in range(1280))) + + # Updating RVV vector element (dummy values) + cpt.set(sec, "regs.vector_element", "") + + # Updating RVV vector predicate (dummy values) + cpt.set(sec, "regs.vector_predicate", "") + + # Updating RVV matrix (dummy values) + cpt.set(sec, "regs.matrix", "") + + # Search for all ISA sections + if re.search(r".*processor.*\.core.*\.isa$", sec): + # Updating RVV misc registers (dummy values) + mr = cpt.get(sec, "miscRegFile").split() + if len(mr) == 164: + print( + "MISCREG_* RVV registers already seem " "to be inserted." + ) + else: + # Add dummy value for MISCREG_VSTART + mr.insert(121, 0) + # Add dummy value for MISCREG_VXSAT + mr.insert(121, 0) + # Add dummy value for MISCREG_VXRM + mr.insert(121, 0) + # Add dummy value for MISCREG_VCSR + mr.insert(121, 0) + # Add dummy value for MISCREG_VL + mr.insert(121, 0) + # Add dummy value for MISCREG_VTYPE + mr.insert(121, 0) + # Add dummy value for MISCREG_VLENB + mr.insert(121, 0) + cpt.set(sec, "miscRegFile", " ".join(str(x) for x in mr)) + + +legacy_version = 17 diff --git a/util/cpt_upgraders/smt-interrupts.py b/util/cpt_upgraders/smt-interrupts.py index d8366c2aa4..0e0e0e963a 100644 --- a/util/cpt_upgraders/smt-interrupts.py +++ b/util/cpt_upgraders/smt-interrupts.py @@ -5,7 +5,7 @@ def upgrader(cpt): for sec in cpt.sections(): import re - re_cpu_match = re.match("^(.*sys.*\.cpu[^._]*)$", sec) + re_cpu_match = re.match(r"^(.*sys.*\.cpu[^._]*)$", sec) if re_cpu_match != None: interrupts = cpt.get(sec, "interrupts") intStatus = cpt.get(sec, "intStatus") diff --git a/util/cpt_upgraders/x86-add-tlb.py b/util/cpt_upgraders/x86-add-tlb.py index 5b6778bcbf..4903704731 100644 --- a/util/cpt_upgraders/x86-add-tlb.py +++ b/util/cpt_upgraders/x86-add-tlb.py @@ -5,11 +5,11 @@ def upgrader(cpt): import re # Search for all ISA sections - if re.search(".*sys.*\.cpu.*\.dtb$", sec): + if re.search(r".*sys.*\.cpu.*\.dtb$", sec): cpt.set(sec, "_size", "0") cpt.set(sec, "lruSeq", "0") - if re.search(".*sys.*\.cpu.*\.itb$", sec): + if re.search(r".*sys.*\.cpu.*\.itb$", sec): cpt.set(sec, "_size", "0") cpt.set(sec, "lruSeq", "0") else: diff --git a/util/decode_inst_dep_trace.py b/util/decode_inst_dep_trace.py index ded0051ae1..0ae8e9e71b 100755 --- a/util/decode_inst_dep_trace.py +++ b/util/decode_inst_dep_trace.py @@ -86,9 +86,10 @@ # 8,35670,1,STORE,1748748,4,74,0:,6,3:,7 # 9,35670,1,COMP,500::,7 -import protolib import sys +import protolib + # Import the packet proto definitions. If they are not found, attempt # to generate them automatically. This assumes that the script is # executed from the gem5 root. @@ -125,12 +126,12 @@ def main(): try: ascii_out = open(sys.argv[2], "w") - except IOError: + except OSError: print("Failed to open ", sys.argv[2], " for writing") exit(-1) # Read the magic number in 4-byte Little Endian - magic_number = proto_in.read(4) + magic_number = proto_in.read(4).decode() if magic_number != "gem5": print("Unrecognized file") diff --git a/util/decode_inst_trace.py b/util/decode_inst_trace.py index 8e59f6955d..6aeb5ee3b7 100755 --- a/util/decode_inst_trace.py +++ b/util/decode_inst_trace.py @@ -42,9 +42,10 @@ # protoc --python_out=. inst.proto # The ASCII trace format uses one line per request. -import protolib import sys +import protolib + # Import the packet proto definitions try: import inst_pb2 @@ -85,7 +86,7 @@ def main(): try: ascii_out = open(sys.argv[2], "w") - except IOError: + except OSError: print("Failed to open ", sys.argv[2], " for writing") exit(-1) @@ -153,7 +154,9 @@ def main(): for mem_acc in inst.mem_access: ascii_out.write( - " %#x-%#x;" % (mem_acc.addr, mem_acc.addr + mem_acc.size) + " {:#x}-{:#x};".format( + mem_acc.addr, mem_acc.addr + mem_acc.size + ) ) ascii_out.write("\n") diff --git a/util/decode_packet_trace.py b/util/decode_packet_trace.py index 66a74c6f01..67febce411 100755 --- a/util/decode_packet_trace.py +++ b/util/decode_packet_trace.py @@ -39,10 +39,11 @@ # format. import os -import protolib import subprocess import sys +import protolib + util_dir = os.path.dirname(os.path.realpath(__file__)) # Make sure the proto definitions are up to date. subprocess.check_call(["make", "--quiet", "-C", util_dir, "packet_pb2.py"]) @@ -59,7 +60,7 @@ def main(): try: ascii_out = open(sys.argv[2], "w") - except IOError: + except OSError: print("Failed to open ", sys.argv[2], " for writing") exit(-1) diff --git a/util/dockerfiles/docker-bake.hcl b/util/dockerfiles/docker-bake.hcl new file mode 100644 index 0000000000..8218290bd2 --- /dev/null +++ b/util/dockerfiles/docker-bake.hcl @@ -0,0 +1,189 @@ +# Copyright (c) 2023 The Regents of the University of California +# All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# docker buildx bake --push +# https://docs.docker.com/build/bake/reference + +variable "IMAGE_URI" { + default = "ghcr.io/gem5" # The gem5 GitHub container registry. +} + +variable "TAG" { + default = "latest" +} + +# A group of targets to be built. Note: groups can contain other groups. +# Any target or group can be build individually. I.e.: +# `docker buildx bake --push ubuntu-20-04_all-dependencies` or +# `docker buildx bake --push ubuntu-releases`. +group "default" { + targets=["clang-compilers", "ubuntu-releases", "gcc-compilers", "gcn-gpu", "gpu-fs", "sst", "systemc", "llvm-gnu-cross-compiler-riscv64", "gem5-all-min-dependencies"] +} + +group "ubuntu-releases" { + targets=["ubuntu-22-04_all-dependencies", "ubuntu-20-04_all-dependencies", "ubuntu-22-04_min-dependencies"] +} + +group "clang-compilers" { + targets = ["clang-compilers-base-20-04", "clang-compilers-base-22-04", "clang-compilers-16"] +} + +group "gcc-compilers" { + targets = ["gcc-compilers-base-20-04", "gcc-compilers-base-22-04"] +} + +# Common attributes across all targets. Note: these can be overwritten. +target "common" { + # Here we are enabling multi-platform builds. We are compiling to both ARM + # amd X86. + platforms = ["linux/amd64", "linux/arm64"] +} + +target "gcn-gpu" { + inherits = ["common"] + dockerfile = "Dockerfile" + context = "gcn-gpu" + tags = ["${IMAGE_URI}/gcn-gpu:${TAG}"] +} + +target "gpu-fs" { + inherits = ["common"] + dockerfile = "Dockerfile" + context = "gpu-fs" + tags = ["${IMAGE_URI}/gpu-fs:${TAG}"] +} + +target "sst" { + inherits = ["common"] + dockerfile = "Dockerfile" + context = "sst-11.1.0" + tags = ["${IMAGE_URI}/sst-env:${TAG}"] +} + +target "systemc" { + inherits = ["common"] + dockerfile = "Dockerfile" + context = "systemc-2.3.3" + tags = ["${IMAGE_URI}/systemc-env:${TAG}"] +} + +target "ubuntu-22-04_all-dependencies" { + inherits = ["common"] + dockerfile = "Dockerfile" + context = "ubuntu-22.04_all-dependencies" + tags = ["${IMAGE_URI}/ubuntu-22.04_all-dependencies:${TAG}"] +} + +target "ubuntu-20-04_all-dependencies" { + inherits = ["common"] + dockerfile = "Dockerfile" + context = "ubuntu-20.04_all-dependencies" + tags = ["${IMAGE_URI}/ubuntu-20.04_all-dependencies:${TAG}"] +} + +target "ubuntu-22-04_min-dependencies" { + inherits = ["common"] + dockerfile = "Dockerfile" + context = "ubuntu-22.04_min-dependencies" + tags = ["${IMAGE_URI}/ubuntu-22.04_min-dependencies:${TAG}"] +} + +target "gcc-compilers-base-20-04" { + name = "gcc-compilers-${replace(ver, ".", "-")}" + inherits = ["common"] + context = "ubuntu-20.04_gcc-version" + dockerfile = "Dockerfile" + matrix = { + ver = ["8", "9", "10"] + } + args = { + version = ver + } + tags = ["${IMAGE_URI}/gcc-version-${ver}:${TAG}"] +} + +target "gcc-compilers-base-22-04" { + name = "gcc-compilers-${replace(ver, ".", "-")}" + inherits = ["common"] + context = "ubuntu-22.04_gcc-version" + dockerfile = "Dockerfile" + matrix = { + ver = ["11", "12"] + } + args = { + version = ver + } + tags = ["${IMAGE_URI}/gcc-version-${ver}:${TAG}"] +} + +target "clang-compilers-base-20-04" { + name = "clang-compilers-${replace(ver, ".", "-")}" + inherits = ["common"] + context = "ubuntu-20.04_clang-version" + dockerfile = "Dockerfile" + matrix = { + ver = ["7", "8", "9", "10", "11", "12"] + } + args = { + version = ver + } + tags = ["${IMAGE_URI}/clang-version-${ver}:${TAG}"] +} + +target "clang-compilers-base-22-04" { + name = "clang-compilers-${replace(ver, ".", "-")}" + inherits = ["common"] + context = "ubuntu-22.04_clang-version" + dockerfile = "Dockerfile" + matrix = { + ver = ["13", "14", "15"] + } + args = { + version = ver + } + tags = ["${IMAGE_URI}/clang-version-${ver}:${TAG}"] +} + +target "clang-compilers-16" { + inherits = ["common"] + dockerfile = "Dockerfile" + context = "ubuntu-22.04_clang_16" + tags = ["${IMAGE_URI}/clang-version-16:${TAG}"] +} + +target "llvm-gnu-cross-compiler-riscv64" { + inherits = ["common"] + dockerfile = "Dockerfile" + context = "llvm-gnu-cross-compiler-riscv64" + tags = ["${IMAGE_URI}/llvm-gnu-cross-compiler-riscv64:${TAG}"] +} + +target "gem5-all-min-dependencies" { + inherits = ["common"] + dockerfile = "Dockerfile" + context = "gem5-all-min-dependencies" + tags = ["${IMAGE_URI}/gem5-all-min-dependencies:${TAG}"] +} diff --git a/util/dockerfiles/docker-compose.yaml b/util/dockerfiles/docker-compose.yaml index 39579962b1..5c63e101ba 100644 --- a/util/dockerfiles/docker-compose.yaml +++ b/util/dockerfiles/docker-compose.yaml @@ -1,3 +1,4 @@ +--- version: '2' services: @@ -13,7 +14,7 @@ services: image: gcr.io/gem5-test/gpu-fs:latest sst: build: - context: sst-11.1.0 + context: sst dockerfile: Dockerfile image: gcr.io/gem5-test/sst-env:latest systemc: @@ -21,11 +22,6 @@ services: context: systemc-2.3.3 dockerfile: Dockerfile image: gcr.io/gem5-test/systemc-env:latest - ubuntu-18.04_all-dependencies: - build: - context: ubuntu-18.04_all-dependencies - dockerfile: Dockerfile - image: gcr.io/gem5-test/ubuntu-18.04_all-dependencies:latest ubuntu-20.04_all-dependencies: build: context: ubuntu-20.04_all-dependencies @@ -41,27 +37,13 @@ services: context: ubuntu-22.04_min-dependencies dockerfile: Dockerfile image: gcr.io/gem5-test/ubuntu-22.04_min-dependencies:latest - gcc-7: - build: - context: ubuntu-18.04_gcc-version - dockerfile: Dockerfile - args: - - version=7 - image: gcr.io/gem5-test/gcc-version-7:latest gcc-8: - build: - context: ubuntu-18.04_gcc-version - dockerfile: Dockerfile - args: - - version=8 - image: gcr.io/gem5-test/gcc-version-8:latest - gcc-9: build: context: ubuntu-20.04_gcc-version dockerfile: Dockerfile args: - - version=9 - image: gcr.io/gem5-test/gcc-version-9:latest + - version=8 + image: gcr.io/gem5-test/gcc-version-8:latest gcc-10: build: context: ubuntu-20.04_gcc-version @@ -83,30 +65,23 @@ services: args: - version=12 image: gcr.io/gem5-test/gcc-version-12:latest - clang-6: - build: - context: ubuntu-18.04_clang-version - dockerfile: Dockerfile - args: - - version=6.0 - image: gcr.io/gem5-test/clang-version-6.0:latest clang-7: build: - context: ubuntu-18.04_clang-version + context: ubuntu-20.04_clang-version dockerfile: Dockerfile args: - version=7 image: gcr.io/gem5-test/clang-version-7:latest clang-8: build: - context: ubuntu-18.04_clang-version + context: ubuntu-20.04_clang-version dockerfile: Dockerfile args: - version=8 image: gcr.io/gem5-test/clang-version-8:latest clang-9: build: - context: ubuntu-18.04_clang-version + context: ubuntu-20.04_clang-version dockerfile: Dockerfile args: - version=9 @@ -146,6 +121,18 @@ services: args: - version=14 image: gcr.io/gem5-test/clang-version-14:latest + clang-15: + build: + context: ubuntu-22.04_clang-version + dockerfile: Dockerfile + args: + - version=15 + image: gcr.io/gem5-test/clang-version-15:latest + clang-16: + build: + context: ubuntu-22.04_clang-16 + dockerfile: Dockerfile + image: gcr.io/gem5-test/clang-version-16:latest llvm-gnu-cross-compiler-riscv64: build: context: llvm-gnu-cross-compiler-riscv64 diff --git a/util/dockerfiles/gcn-gpu/Dockerfile b/util/dockerfiles/gcn-gpu/Dockerfile index c5db8963a8..9aead08c43 100644 --- a/util/dockerfiles/gcn-gpu/Dockerfile +++ b/util/dockerfiles/gcn-gpu/Dockerfile @@ -23,13 +23,13 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -FROM ubuntu:20.04 +FROM --platform=${BUILDPLATFORM} ubuntu:20.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt -y update && apt -y upgrade && \ apt -y install build-essential git m4 scons zlib1g zlib1g-dev \ libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev \ python3-dev python-is-python3 doxygen libboost-all-dev \ - libhdf5-serial-dev python3-pydot libpng-dev libelf-dev pkg-config + libhdf5-serial-dev python3-pydot libpng-dev libelf-dev pkg-config gdb # Requirements for ROCm RUN apt -y install cmake mesa-common-dev libgflags-dev libgoogle-glog-dev @@ -121,3 +121,41 @@ RUN git clone -b rocm-4.0.1 https://github.com/ROCmSoftwarePlatform/MIOpen.git # when linking in the database file RUN mkdir -p /root/.cache/miopen/2.9.0.8252-rocm-rel-4.0-26-64506314 && \ ln -s /root/.cache/miopen/2.9.0.8252-rocm-rel-4.0-26-64506314 /root/.cache/miopen/2.9.0 + +# Add commands from halofinder Dockerfile +RUN apt-get update && apt-get -y install libopenmpi-dev libomp-dev + +ENV HCC_AMDGPU_TARGET="gfx801,gfx803,gfx900" + +ENV HIPCC_BIN=/opt/rocm/bin +ENV MPI_INCLUDE=/usr/lib/x86_64-linux-gnu/openmpi/include + +ENV OPT="-O3 -g -DRCB_UNTHREADED_BUILD -DUSE_SERIAL_COSMO" +ENV OMP="-I/usr/lib/llvm-10/include/openmp -L/usr/lib/llvm-10/lib -fopenmp" + +ENV HIPCC_FLAGS="-v -ffast_math -DINLINE_FORCE -I${MPI_INCLUDE}" +ENV HIPCC_FLAGS="-v -I${MPI_INCLUDE} -I/opt/rocm/hip/include" + +ENV HACC_PLATFORM="hip" +ENV HACC_OBJDIR="${HACC_PLATFORM}" + +ENV HACC_CFLAGS="$OPT $OMP $HIPCC_FLAGS" +ENV HACC_CC="${HIPCC_BIN}/hipcc -x c -Xclang -std=c99" + +ENV HACC_CXXFLAGS="$OPT $OMP $HIPCC_FLAGS" +ENV HACC_CXX="${HIPCC_BIN}/hipcc -Xclang" + +ENV HACC_LDFLAGS="-lm -lrt" + +# USE_SERIAL_COSMO must be set to avoid building the code with MPI, which isn't +# supported on the GPU model in gem5. +ENV USE_SERIAL_COSMO="1" +ENV HACC_NUM_CUDA_DEV="1" +ENV HACC_MPI_CFLAGS="$OPT $OMP $HIPCC_FLAGS" +ENV HACC_MPI_CC="${HIPCC_BIN}/hipcc -x c -Xclang -std=c99 -Xclang -pthread" + +ENV HACC_MPI_CXXFLAGS="$OPT $OMP $HIPCC_FLAGS" +ENV HACC_MPI_CXX="${HIPCC_BIN}/hipcc -Xclang -pthread" +ENV HACC_MPI_LD="${HIPCC_BIN}/hipcc -Xclang -pthread" + +ENV HACC_MPI_LDFLAGS="-lm -lrt" diff --git a/util/dockerfiles/gem5-all-min-dependencies/Dockerfile b/util/dockerfiles/gem5-all-min-dependencies/Dockerfile index b28b674fa7..cb73ab0bce 100644 --- a/util/dockerfiles/gem5-all-min-dependencies/Dockerfile +++ b/util/dockerfiles/gem5-all-min-dependencies/Dockerfile @@ -24,13 +24,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -FROM gcr.io/gem5-test/ubuntu-22.04_min-dependencies:latest as source +FROM --platform=${BUILDPLATFORM} ghcr.io/gem5/ubuntu-22.04_min-dependencies:latest as source RUN apt -y update && apt -y install git RUN git clone -b develop https://github.com/gem5/gem5/ /gem5 WORKDIR /gem5 RUN scons -j`nproc` build/ALL/gem5.fast -FROM gcr.io/gem5-test/ubuntu-22.04_min-dependencies:latest +FROM ghcr.io/gem5/ubuntu-22.04_min-dependencies:latest COPY --from=source /gem5/build/ALL/gem5.fast /usr/local/bin/gem5 ENTRYPOINT [ "/usr/local/bin/gem5" ] diff --git a/util/dockerfiles/gpu-fs/Dockerfile b/util/dockerfiles/gpu-fs/Dockerfile index 63ae6b0783..55f4de4aff 100644 --- a/util/dockerfiles/gpu-fs/Dockerfile +++ b/util/dockerfiles/gpu-fs/Dockerfile @@ -27,7 +27,7 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -FROM ubuntu:20.04 +FROM --platform=${BUILDPLATFORM} ubuntu:20.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt -y update && apt -y upgrade && \ apt -y install build-essential git m4 scons zlib1g zlib1g-dev \ @@ -44,12 +44,12 @@ RUN apt -y install wget gnupg2 rpm # Get the radeon gpg key for apt repository RUN wget -q -O - https://repo.radeon.com/rocm/rocm.gpg.key | apt-key add - -# Modify apt sources to pull from ROCm 4.2 repository only -RUN echo 'deb [arch=amd64] https://repo.radeon.com/rocm/apt/4.2/ ubuntu main' | tee /etc/apt/sources.list.d/rocm.list +# Modify apt sources to pull from ROCm 5.4.2 repository only +RUN echo 'deb [arch=amd64] https://repo.radeon.com/rocm/apt/5.4.2/ ubuntu main' | tee /etc/apt/sources.list.d/rocm.list RUN apt-get update RUN apt -y install libnuma-dev # Install the ROCm-dkms source RUN apt -y install initramfs-tools -RUN apt -y install rocm-dkms +RUN apt -y install rocm-dev diff --git a/util/dockerfiles/llvm-gnu-cross-compiler-riscv64/Dockerfile b/util/dockerfiles/llvm-gnu-cross-compiler-riscv64/Dockerfile index 0f01e7931d..cc84f3d45a 100644 --- a/util/dockerfiles/llvm-gnu-cross-compiler-riscv64/Dockerfile +++ b/util/dockerfiles/llvm-gnu-cross-compiler-riscv64/Dockerfile @@ -26,7 +26,7 @@ # stage 1: download the dependencies -FROM ubuntu:20.04 AS stage1 +FROM --platform=${BUILDPLATFORM} ubuntu:20.04 AS stage1 ENV DEBIAN_FRONTEND=noninteractive RUN apt -y update && apt -y upgrade && apt -y install \ diff --git a/util/dockerfiles/sst-11.1.0/Dockerfile b/util/dockerfiles/sst/Dockerfile similarity index 88% rename from util/dockerfiles/sst-11.1.0/Dockerfile rename to util/dockerfiles/sst/Dockerfile index 970e6979b4..50027fe5b2 100644 --- a/util/dockerfiles/sst-11.1.0/Dockerfile +++ b/util/dockerfiles/sst/Dockerfile @@ -1,4 +1,4 @@ -# Copyright (c) 2021 The Regents of the University of California +# Copyright (c) 2021-2023 The Regents of the University of California # All Rights Reserved. # # Redistribution and use in source and binary forms, with or without @@ -24,7 +24,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -FROM ubuntu:20.04 +FROM --platform=${BUILDPLATFORM} ubuntu:20.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt -y update && apt -y upgrade && \ @@ -41,9 +41,9 @@ RUN mkdir /sst # Download and build SST-Core without MPI support WORKDIR /sst/ -RUN wget https://github.com/sstsimulator/sst-core/releases/download/v11.1.0_Final/sstcore-11.1.0.tar.gz; \ - tar xf sstcore-11.1.0.tar.gz; -WORKDIR /sst/sstcore-11.1.0/ +RUN wget https://github.com/sstsimulator/sst-core/releases/download/v13.0.0_Final/sstcore-13.0.0.tar.gz; \ + tar xf sstcore-13.0.0.tar.gz; +WORKDIR /sst/sstcore-13.0.0/ RUN ./configure --prefix=$SST_CORE_HOME --with-python=/usr/bin/python3-config \ --disable-mpi; \ make all -j $(nproc); \ @@ -51,9 +51,9 @@ RUN ./configure --prefix=$SST_CORE_HOME --with-python=/usr/bin/python3-config \ # Download and build SST-Elements WORKDIR /sst -RUN wget https://github.com/sstsimulator/sst-elements/releases/download/v11.1.0_Final/sstelements-11.1.0.tar.gz; \ - tar xf sstelements-11.1.0.tar.gz; -WORKDIR /sst/sst-elements-library-11.1.0/ +RUN wget https://github.com/sstsimulator/sst-elements/releases/download/v13.0.0_Final/sstelements-13.0.0.tar.gz; \ + tar xf sstelements-13.0.0.tar.gz; +WORKDIR /sst/sst-elements-library-13.0.0/ RUN ./configure --prefix=$SST_CORE_HOME --with-python=/usr/bin/python3-config \ --with-sst-core=$SST_CORE_HOME; \ make all -j $(nproc); \ diff --git a/util/dockerfiles/systemc-2.3.3/Dockerfile b/util/dockerfiles/systemc-2.3.3/Dockerfile index ac94666d6d..41d4fafb93 100644 --- a/util/dockerfiles/systemc-2.3.3/Dockerfile +++ b/util/dockerfiles/systemc-2.3.3/Dockerfile @@ -24,7 +24,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -FROM ubuntu:22.04 +FROM --platform=${BUILDPLATFORM} ubuntu:22.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt -y update && apt -y upgrade && \ diff --git a/util/dockerfiles/ubuntu-20.04_all-dependencies/Dockerfile b/util/dockerfiles/ubuntu-20.04_all-dependencies/Dockerfile index c838a06dda..055e3ea8e7 100644 --- a/util/dockerfiles/ubuntu-20.04_all-dependencies/Dockerfile +++ b/util/dockerfiles/ubuntu-20.04_all-dependencies/Dockerfile @@ -24,7 +24,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -FROM ubuntu:20.04 +FROM --platform=${BUILDPLATFORM} ubuntu:20.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt -y update && apt -y upgrade && \ @@ -32,6 +32,26 @@ RUN apt -y update && apt -y upgrade && \ libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev \ python3-dev python-is-python3 doxygen libboost-all-dev \ libhdf5-serial-dev python3-pydot libpng-dev libelf-dev pkg-config pip \ - python3-venv black + python3-venv black gcc-10 g++-10 cmake python3-tk wget libssl-dev RUN pip install mypy pre-commit + +RUN update-alternatives --install \ + /usr/bin/g++ g++ /usr/bin/g++-10 100 +RUN update-alternatives --install \ + /usr/bin/gcc gcc /usr/bin/gcc-10 100 +RUN update-alternatives --install \ + /usr/bin/c++ c++ /usr/bin/g++-10 100 +RUN update-alternatives --install \ + /usr/bin/cc cc /usr/bin/gcc-10 100 + + +# DRAMSys requires cmake >= 3.24.0. +RUN wget https://github.com/Kitware/CMake/releases/download/v3.24.0/cmake-3.24.0.tar.gz \ + && tar -xzf cmake-3.24.0.tar.gz \ + && cd cmake-3.24.0 \ + && ./bootstrap \ + && make -j`nproc` \ + && make install \ + && cd .. \ + && rm -rf cmake-3.24.0.tar.gz cmake-3.24.0 diff --git a/util/dockerfiles/ubuntu-20.04_clang-version/Dockerfile b/util/dockerfiles/ubuntu-20.04_clang-version/Dockerfile index 00f34c4d9e..2ea9413c7d 100644 --- a/util/dockerfiles/ubuntu-20.04_clang-version/Dockerfile +++ b/util/dockerfiles/ubuntu-20.04_clang-version/Dockerfile @@ -23,7 +23,7 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -FROM ubuntu:20.04 +FROM --platform=${BUILDPLATFORM} ubuntu:20.04 # Valid version values: # 6.0 @@ -40,7 +40,10 @@ RUN apt -y update && apt -y upgrade && \ apt -y install git m4 scons zlib1g zlib1g-dev libprotobuf-dev \ protobuf-compiler libprotoc-dev libgoogle-perftools-dev python3-dev \ python-is-python3 doxygen libboost-all-dev libhdf5-serial-dev \ - python3-pydot libpng-dev clang-${version} make + python3-pydot libpng-dev clang-${version} make \ + # This is needed as clang-8 does not have the libstdc++-10-dev package. + # It is necessary for compilation. + libstdc++-10-dev RUN apt-get --purge -y remove gcc diff --git a/util/dockerfiles/ubuntu-20.04_gcc-version/Dockerfile b/util/dockerfiles/ubuntu-20.04_gcc-version/Dockerfile index 0ec8083c53..7841635aef 100644 --- a/util/dockerfiles/ubuntu-20.04_gcc-version/Dockerfile +++ b/util/dockerfiles/ubuntu-20.04_gcc-version/Dockerfile @@ -23,12 +23,12 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -FROM ubuntu:20.04 +FROM --platform=${BUILDPLATFORM} ubuntu:20.04 # Valid version values: # 7 # 8 -# 9 +# 9 # Not supported. See: https://github.com/gem5/gem5/issues/555. # 10 ARG version diff --git a/util/dockerfiles/ubuntu-22.04_all-dependencies/Dockerfile b/util/dockerfiles/ubuntu-22.04_all-dependencies/Dockerfile index e5afc63be6..b6bd080ea7 100644 --- a/util/dockerfiles/ubuntu-22.04_all-dependencies/Dockerfile +++ b/util/dockerfiles/ubuntu-22.04_all-dependencies/Dockerfile @@ -24,13 +24,24 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -FROM ubuntu:22.04 +FROM --platform=${BUILDPLATFORM} ubuntu:22.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt -y update && apt -y upgrade && \ apt -y install build-essential git m4 scons zlib1g zlib1g-dev \ libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev \ python3-dev doxygen libboost-all-dev libhdf5-serial-dev python3-pydot \ - libpng-dev libelf-dev pkg-config pip python3-venv black + libpng-dev libelf-dev pkg-config pip python3-venv black python3-tk wget RUN pip install mypy pre-commit + +# DRAMSys requires cmake >= 3.24.0. APT only provides 3.22.1 for Ubuntu 22.04. +# Therefore, we install cmake from source. +RUN wget https://github.com/Kitware/CMake/releases/download/v3.24.0/cmake-3.24.0.tar.gz \ + && tar -xzf cmake-3.24.0.tar.gz \ + && cd cmake-3.24.0 \ + && ./bootstrap \ + && make -j`nproc` \ + && make install \ + && cd .. \ + && rm -rf cmake-3.24.0.tar.gz cmake-3.24.0 diff --git a/util/dockerfiles/ubuntu-18.04_clang-version/Dockerfile b/util/dockerfiles/ubuntu-22.04_clang-16/Dockerfile similarity index 71% rename from util/dockerfiles/ubuntu-18.04_clang-version/Dockerfile rename to util/dockerfiles/ubuntu-22.04_clang-16/Dockerfile index 3d9c3a7c12..045efbd13a 100644 --- a/util/dockerfiles/ubuntu-18.04_clang-version/Dockerfile +++ b/util/dockerfiles/ubuntu-22.04_clang-16/Dockerfile @@ -1,4 +1,4 @@ -# Copyright (c) 2020 The Regents of the University of California +# Copyright (c) 2023 The Regents of the University of California # All Rights Reserved. # # Redistribution and use in source and binary forms, with or without @@ -23,31 +23,27 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +FROM --platform=${BUILDPLATFORM} ubuntu:22.04 -FROM ubuntu:18.04 - -# Valid version values: -# 3.9 -# 4.0 -# 5.0 -# 6.0 -# 7 -# 8 -# 9 -ARG version +ENV DEBIAN_FRONTEND=noninteractive RUN apt -y update && apt -y upgrade && \ - apt -y install git m4 scons zlib1g zlib1g-dev clang-${version} \ - libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev \ - python3-dev python3 doxygen make + apt -y install git m4 scons zlib1g zlib1g-dev libprotobuf-dev \ + protobuf-compiler libprotoc-dev libgoogle-perftools-dev python3-dev \ + python-is-python3 doxygen libboost-all-dev libhdf5-serial-dev \ + python3-pydot libpng-dev make lsb-release wget \ + software-properties-common gnupg + +COPY llvm.sh /llvm.sh +RUN ./llvm.sh 16 RUN apt-get --purge -y remove gcc RUN update-alternatives --install \ - /usr/bin/clang++ clang++ /usr/bin/clang++-${version} 100 + /usr/bin/clang++ clang++ /usr/bin/clang++-16 100 RUN update-alternatives --install \ - /usr/bin/clang clang /usr/bin/clang-${version} 100 + /usr/bin/clang clang /usr/bin/clang-16 100 RUN update-alternatives --install \ - /usr/bin/c++ c++ /usr/bin/clang++-${version} 100 + /usr/bin/c++ c++ /usr/bin/clang++-16 100 RUN update-alternatives --install \ - /usr/bin/cc cc /usr/bin/clang-${version} 100 + /usr/bin/cc cc /usr/bin/clang-16 100 diff --git a/util/dockerfiles/ubuntu-22.04_clang-16/llvm.sh b/util/dockerfiles/ubuntu-22.04_clang-16/llvm.sh new file mode 100755 index 0000000000..200c0390fe --- /dev/null +++ b/util/dockerfiles/ubuntu-22.04_clang-16/llvm.sh @@ -0,0 +1,176 @@ +#!/bin/bash +################################################################################ +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +################################################################################ +# +# This script will install the llvm toolchain on the different +# Debian and Ubuntu versions + +set -eux + +usage() { + set +x + echo "Usage: $0 [llvm_major_version] [all] [OPTIONS]" 1>&2 + echo -e "all\t\t\tInstall all packages." 1>&2 + echo -e "-n=code_name\t\tSpecifies the distro codename, for example bionic" 1>&2 + echo -e "-h\t\t\tPrints this help." 1>&2 + echo -e "-m=repo_base_url\tSpecifies the base URL from which to download." 1>&2 + exit 1; +} + +CURRENT_LLVM_STABLE=17 +BASE_URL="http://apt.llvm.org" + +# Check for required tools +needed_binaries=(lsb_release wget add-apt-repository gpg) +missing_binaries=() +for binary in "${needed_binaries[@]}"; do + if ! which $binary &>/dev/null ; then + missing_binaries+=($binary) + fi +done +if [[ ${#missing_binaries[@]} -gt 0 ]] ; then + echo "You are missing some tools this script requires: ${missing_binaries[@]}" + echo "(hint: apt install lsb-release wget software-properties-common gnupg)" + exit 4 +fi + +# Set default values for commandline arguments +# We default to the current stable branch of LLVM +LLVM_VERSION=$CURRENT_LLVM_STABLE +ALL=0 +DISTRO=$(lsb_release -is) +VERSION=$(lsb_release -sr) +UBUNTU_CODENAME="" +CODENAME_FROM_ARGUMENTS="" +# Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) +source /etc/os-release +DISTRO=${DISTRO,,} +case ${DISTRO} in + debian) + # Debian Trixie has a workaround because of + # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1038383 + if [[ "${VERSION}" == "unstable" ]] || [[ "${VERSION}" == "testing" ]] || [[ "${VERSION_CODENAME}" == "trixie" ]]; then + CODENAME=unstable + LINKNAME= + else + # "stable" Debian release + CODENAME=${VERSION_CODENAME} + LINKNAME=-${CODENAME} + fi + ;; + *) + # ubuntu and its derivatives + if [[ -n "${UBUNTU_CODENAME}" ]]; then + CODENAME=${UBUNTU_CODENAME} + if [[ -n "${CODENAME}" ]]; then + LINKNAME=-${CODENAME} + fi + fi + ;; +esac + +# read optional command line arguments +if [ "$#" -ge 1 ] && [ "${1::1}" != "-" ]; then + if [ "$1" != "all" ]; then + LLVM_VERSION=$1 + else + # special case for ./llvm.sh all + ALL=1 + fi + OPTIND=2 + if [ "$#" -ge 2 ]; then + if [ "$2" == "all" ]; then + # Install all packages + ALL=1 + OPTIND=3 + fi + fi +fi + +while getopts ":hm:n:" arg; do + case $arg in + h) + usage + ;; + m) + BASE_URL=${OPTARG} + ;; + n) + CODENAME=${OPTARG} + if [[ "${CODENAME}" == "unstable" ]]; then + # link name does not apply to unstable repository + LINKNAME= + else + LINKNAME=-${CODENAME} + fi + CODENAME_FROM_ARGUMENTS="true" + ;; + esac +done + +if [[ $EUID -ne 0 ]]; then + echo "This script must be run as root!" + exit 1 +fi + +declare -A LLVM_VERSION_PATTERNS +LLVM_VERSION_PATTERNS[9]="-9" +LLVM_VERSION_PATTERNS[10]="-10" +LLVM_VERSION_PATTERNS[11]="-11" +LLVM_VERSION_PATTERNS[12]="-12" +LLVM_VERSION_PATTERNS[13]="-13" +LLVM_VERSION_PATTERNS[14]="-14" +LLVM_VERSION_PATTERNS[15]="-15" +LLVM_VERSION_PATTERNS[16]="-16" +LLVM_VERSION_PATTERNS[17]="-17" +LLVM_VERSION_PATTERNS[18]="" + +if [ ! ${LLVM_VERSION_PATTERNS[$LLVM_VERSION]+_} ]; then + echo "This script does not support LLVM version $LLVM_VERSION" + exit 3 +fi + +LLVM_VERSION_STRING=${LLVM_VERSION_PATTERNS[$LLVM_VERSION]} + +# join the repository name +if [[ -n "${CODENAME}" ]]; then + REPO_NAME="deb ${BASE_URL}/${CODENAME}/ llvm-toolchain${LINKNAME}${LLVM_VERSION_STRING} main" + + # check if the repository exists for the distro and version + if ! wget -q --method=HEAD ${BASE_URL}/${CODENAME} &> /dev/null; then + if [[ -n "${CODENAME_FROM_ARGUMENTS}" ]]; then + echo "Specified codename '${CODENAME}' is not supported by this script." + else + echo "Distribution '${DISTRO}' in version '${VERSION}' is not supported by this script." + fi + exit 2 + fi +fi + + +# install everything + +if [[ ! -f /etc/apt/trusted.gpg.d/apt.llvm.org.asc ]]; then + # download GPG key once + wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc +fi + +if [[ -z "`apt-key list 2> /dev/null | grep -i llvm`" ]]; then + # Delete the key in the old format + apt-key del AF4F7421 +fi +add-apt-repository "${REPO_NAME}" +apt-get update +PKG="clang-$LLVM_VERSION lldb-$LLVM_VERSION lld-$LLVM_VERSION clangd-$LLVM_VERSION" +if [[ $ALL -eq 1 ]]; then + # same as in test-install.sh + # No worries if we have dups + PKG="$PKG clang-tidy-$LLVM_VERSION clang-format-$LLVM_VERSION clang-tools-$LLVM_VERSION llvm-$LLVM_VERSION-dev lld-$LLVM_VERSION lldb-$LLVM_VERSION llvm-$LLVM_VERSION-tools libomp-$LLVM_VERSION-dev libc++-$LLVM_VERSION-dev libc++abi-$LLVM_VERSION-dev libclang-common-$LLVM_VERSION-dev libclang-$LLVM_VERSION-dev libclang-cpp$LLVM_VERSION-dev libunwind-$LLVM_VERSION-dev" + if test $LLVM_VERSION -gt 14; then + PKG="$PKG libclang-rt-$LLVM_VERSION-dev libpolly-$LLVM_VERSION-dev" + fi +fi +apt-get install -y $PKG diff --git a/util/dockerfiles/ubuntu-22.04_clang-version/Dockerfile b/util/dockerfiles/ubuntu-22.04_clang-version/Dockerfile index 148b71dea3..7ddf7fe825 100644 --- a/util/dockerfiles/ubuntu-22.04_clang-version/Dockerfile +++ b/util/dockerfiles/ubuntu-22.04_clang-version/Dockerfile @@ -23,10 +23,11 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -FROM ubuntu:22.04 +FROM --platform=${BUILDPLATFORM} ubuntu:22.04 # Valid version values: # 13 +# 15 ARG version ENV DEBIAN_FRONTEND=noninteractive diff --git a/util/dockerfiles/ubuntu-22.04_gcc-version/Dockerfile b/util/dockerfiles/ubuntu-22.04_gcc-version/Dockerfile index fcf909cec2..feac348780 100644 --- a/util/dockerfiles/ubuntu-22.04_gcc-version/Dockerfile +++ b/util/dockerfiles/ubuntu-22.04_gcc-version/Dockerfile @@ -23,7 +23,7 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -FROM ubuntu:22.04 +FROM --platform=${BUILDPLATFORM} ubuntu:22.04 # Valid version values: # 11 diff --git a/util/dockerfiles/ubuntu-22.04_min-dependencies/Dockerfile b/util/dockerfiles/ubuntu-22.04_min-dependencies/Dockerfile index 978e2c6af5..690959da91 100644 --- a/util/dockerfiles/ubuntu-22.04_min-dependencies/Dockerfile +++ b/util/dockerfiles/ubuntu-22.04_min-dependencies/Dockerfile @@ -24,7 +24,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -FROM ubuntu:22.04 +FROM --platform=${BUILDPLATFORM} ubuntu:22.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt -y update && apt -y upgrade && \ diff --git a/util/encode_inst_dep_trace.py b/util/encode_inst_dep_trace.py index 9ab95bd7ed..29aee2b473 100755 --- a/util/encode_inst_dep_trace.py +++ b/util/encode_inst_dep_trace.py @@ -86,9 +86,10 @@ # 8,35670,1,STORE,1748748,4,74,0:,6,3:,7 # 9,35670,1,COMP,500::,7 -import protolib import sys +import protolib + # Import the packet proto definitions. If they are not found, attempt # to generate them automatically. This assumes that the script is # executed from the gem5 root. @@ -127,8 +128,8 @@ def main(): # Open the file in read mode try: - ascii_in = open(sys.argv[1], "r") - except IOError: + ascii_in = open(sys.argv[1]) + except OSError: print("Failed to open ", sys.argv[1], " for reading") exit(-1) diff --git a/util/encode_packet_trace.py b/util/encode_packet_trace.py index bdf1c3db06..48d8b58a40 100755 --- a/util/encode_packet_trace.py +++ b/util/encode_packet_trace.py @@ -51,9 +51,10 @@ # This script can of course also be used as a template to convert # other trace formats into the gem5 protobuf format -import protolib import sys +import protolib + # Import the packet proto definitions. If they are not found, attempt # to generate them automatically. This assumes that the script is # executed from the gem5 root. @@ -92,14 +93,14 @@ def main(): exit(-1) try: - ascii_in = open(sys.argv[1], "r") - except IOError: + ascii_in = open(sys.argv[1]) + except OSError: print("Failed to open ", sys.argv[1], " for reading") exit(-1) try: proto_out = open(sys.argv[2], "wb") - except IOError: + except OSError: print("Failed to open ", sys.argv[2], " for writing") exit(-1) diff --git a/util/find_copyrights.py b/util/find_copyrights.py index 0bd0ef3a51..fa1d47dd45 100644 --- a/util/find_copyrights.py +++ b/util/find_copyrights.py @@ -4,9 +4,12 @@ import os import re import sys -from file_types import lang_type, find_files +from file_types import ( + find_files, + lang_type, +) -mode_line = re.compile("(-\*- *mode:.* *-\*-)") +mode_line = re.compile(r"(-\*- *mode:.* *-\*-)") shell_comment = re.compile(r"^\s*#") lisp_comment = re.compile(r";") cpp_comment = re.compile(r"//") @@ -116,7 +119,7 @@ def process_dates(dates): for date in dates: match = date_range_re.match(date) if match: - f, l = [int(d) for d in match.groups()] + f, l = (int(d) for d in match.groups()) for i in range(f, l + 1): output.add(i) else: diff --git a/util/gem5-resources-manager/.gitignore b/util/gem5-resources-manager/.gitignore new file mode 100644 index 0000000000..ce625cd446 --- /dev/null +++ b/util/gem5-resources-manager/.gitignore @@ -0,0 +1,12 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ + +# Unit test / coverage reports +.coverage +database/* +instance +instance/* + +# Environments +.env +.venv diff --git a/util/gem5-resources-manager/README.md b/util/gem5-resources-manager/README.md new file mode 100644 index 0000000000..efbbf97b16 --- /dev/null +++ b/util/gem5-resources-manager/README.md @@ -0,0 +1,216 @@ +# gem5 Resources Manager + +This directory contains the code to convert the JSON file to a MongoDB database. This also contains tools to manage the database as well as the JSON file. + +# Table of Contents +- [gem5 Resources Manager](#gem5-resources-manager) +- [Table of Contents](#table-of-contents) +- [Resources Manager](#resources-manager) + - [Setup](#setup) + - [Selecting a Database](#selecting-a-database) + - [MongoDB](#mongodb) + - [JSON File](#json-file) + - [Adding a Resource](#adding-a-resource) + - [Updating a Resource](#updating-a-resource) + - [Deleting a Resource](#deleting-a-resource) + - [Adding a New Version](#adding-a-new-version) + - [Validation](#validation) +- [CLI tool](#cli-tool) + - [create\_resources\_json](#create_resources_json) + - [restore\_backup](#restore_backup) + - [backup\_mongodb](#backup_mongodb) + - [get\_resource](#get_resource) +- [Changes to Structure of JSON](#changes-to-structure-of-json) +- [Testing](#testing) + +# Resources Manager + +This is a tool to manage the resources JSON file and the MongoDB database. This tool is used to add, delete, update, view, and search for resources. + +## Setup + +First, install the requirements: + +```bash +pip3 install -r requirements.txt +``` + +Then run the flask server: + +```bash +python3 server.py +``` + +Then, you can access the server at `http://localhost:5000`. + +## Selecting a Database + +The Resource Manager currently supports 2 database options: MongoDB and JSON file. + +Select the database you want to use by clicking on the button on home page. + +### MongoDB + +The MongoDB database is hosted on MongoDB Atlas. To use this database, you need to have the MongoDB URI, collection name, and database name. Once you have the information, enter it into the form and click "login" or "save and login" to login to the database. + +Another way to use the MongoDB database is to switch to the Generate URI tab and enter the information there. This would generate a URI that you can use to login to the database. + +### JSON File + +There are currently 3 ways to use the JSON file: + +1. Adding a URL to the JSON file +2. Uploading a JSON file +3. Using an existing JSON file + +## Adding a Resource + +Once you are logged in, you can use the search bar to search for resources. If the ID doesn't exist, it would be prefilled with the required fields. You can then edit the fields and click "add" to add the resource to the database. + +## Updating a Resource + +If the ID exists, the form would be prefilled with the existing data. You can then edit the fields and click "update" to update the resource in the database. + +## Deleting a Resource + +If the ID exists, the form would be prefilled with the existing data. You can then click "delete" to delete the resource from the database. + +## Adding a New Version + +If the ID exists, the form would be prefilled with the existing data. Change the `resource_version` field to the new version and click "add" to add the new version to the database. You will only be able to add a new version if the `resource_version` field is different from any of the existing versions. + +## Validation + +The Resource Manager validates the data before adding it to the database. If the data is invalid, it would show an error message and not add the data to the database. The validation is done using the [schema](schema/schema.json) file. The Monaco editor automatically validates the data as you type and displays the errors in the editor. + +To view the schema, click on the "Show Schema" button on the left side of the page. + +# CLI tool + +```bash +usage: gem5_resource_cli.py [-h] [-u URI] [-d DATABASE] [-c COLLECTION] {get_resource,backup_mongodb,restore_backup,create_resources_json} ... + +CLI for gem5-resources. + +positional arguments: + {get_resource,backup_mongodb,restore_backup,create_resources_json} + The command to run. + get_resource Retrieves a resource from the collection based on the given ID. if a resource version is provided, it will retrieve the resource + with the given ID and version. + backup_mongodb Backs up the MongoDB collection to a JSON file. + restore_backup Restores a backup of the MongoDB collection from a JSON file. + create_resources_json + Creates a JSON file of all the resources in the collection. + +optional arguments: + -h, --help show this help message and exit + -u URI, --uri URI The URI of the MongoDB database. (default: None) + -d DATABASE, --database DATABASE + The MongoDB database to use. (default: gem5-vision) + -c COLLECTION, --collection COLLECTION + The MongoDB collection to use. (default: versions_test) +``` + +By default, the cli uses environment variables to get the URI. You can create a .env file with the `MONGO_URI` variable set to your URI. If you want to use a different URI, you can use the `-u` flag to specify the URI. + +## create_resources_json + +This command is used to create a new JSON file from the old JSON file. This is used to make the JSON file "parseable" by removing the nested JSON and adding the new fields. + +```bash +usage: gem5_resource_cli.py create_resources_json [-h] [-v VERSION] [-o OUTPUT] [-s SOURCE] + +optional arguments: + -h, --help show this help message and exit + -v VERSION, --version VERSION + The version of the resources to create the JSON file for. (default: dev) + -o OUTPUT, --output OUTPUT + The JSON file to create. (default: resources.json) + -s SOURCE, --source SOURCE + The path to the gem5 source code. (default: ) +``` + +A sample command to run this is: + +```bash +python3 gem5_resource_cli.py create_resources_json -o resources_new.json -s ./gem5 +``` + +## restore_backup + +This command is used to update the MongoDB database with the new JSON file. This is used to update the database with the new JSON file. + +```bash +usage: gem5_resource_cli.py restore_backup [-h] [-f FILE] + +optional arguments: + -h, --help show this help message and exit + +required arguments: + -f FILE, --file FILE The JSON file to restore the MongoDB collection from. +``` + +A sample command to run this is: + +```bash +python3 gem5_resource_cli.py restore_backup -f resources.json +``` + +## backup_mongodb + +This command is used to backup the MongoDB database to a JSON file. This is used to create a backup of the database. + +```bash +usage: gem5_resource_cli.py backup_mongodb [-h] -f FILE + +optional arguments: + -h, --help show this help message and exit + +required arguments: + -f FILE, --file FILE The JSON file to back up the MongoDB collection to. +``` + +A sample command to run this is: + +```bash +python3 gem5_resource_cli.py backup_mongodb -f resources.json +``` + +## get_resource + +This command is used to get a resource from the MongoDB database. This is used to get a resource from the database. + +```bash +usage: gem5_resource_cli.py get_resource [-h] -i ID [-v VERSION] + +optional arguments: + -h, --help show this help message and exit + -v VERSION, --version VERSION + The version of the resource to retrieve. + +required arguments: + -i ID, --id ID The ID of the resource to retrieve. +``` + +A sample command to run this is: + +```bash +python3 gem5_resource_cli.py get_resource -i x86-ubuntu-18.04-img -v 1.0.0 +``` +# Changes to Structure of JSON + +To view the new schema, see [schema.json](https://resources.gem5.org/gem5-resources-schema.json). + +# Testing + +To run the tests, run the following command: + +```bash +coverage run -m unittest discover -s test -p '*_test.py' +``` + +To view the coverage report, run the following command: + +```bash +coverage report +``` diff --git a/util/gem5-resources-manager/api/client.py b/util/gem5-resources-manager/api/client.py new file mode 100644 index 0000000000..f8d5599e03 --- /dev/null +++ b/util/gem5-resources-manager/api/client.py @@ -0,0 +1,140 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from abc import ( + ABC, + abstractmethod, +) +from typing import ( + Dict, + List, +) + + +class Client(ABC): + def __init__(self): + self.__undo_stack = [] + self.__redo_stack = [] + self.__undo_limit = 10 + + @abstractmethod + def find_resource(self, query: Dict) -> Dict: + raise NotImplementedError + + @abstractmethod + def get_versions(self, query: Dict) -> List[Dict]: + raise NotImplementedError + + @abstractmethod + def update_resource(self, query: Dict) -> Dict: + raise NotImplementedError + + @abstractmethod + def check_resource_exists(self, query: Dict) -> Dict: + raise NotImplementedError + + @abstractmethod + def insert_resource(self, query: Dict) -> Dict: + raise NotImplementedError + + @abstractmethod + def delete_resource(self, query: Dict) -> Dict: + raise NotImplementedError + + @abstractmethod + def save_session(self) -> Dict: + raise NotImplementedError + + def undo_operation(self) -> Dict: + """ + This function undoes the last operation performed on the database. + """ + if len(self.__undo_stack) == 0: + return {"status": "Nothing to undo"} + operation = self.__undo_stack.pop() + print(operation) + if operation["operation"] == "insert": + self.delete_resource(operation["resource"]) + elif operation["operation"] == "delete": + self.insert_resource(operation["resource"]) + elif operation["operation"] == "update": + self.update_resource(operation["resource"]) + temp = operation["resource"]["resource"] + operation["resource"]["resource"] = operation["resource"][ + "original_resource" + ] + operation["resource"]["original_resource"] = temp + else: + raise Exception("Invalid Operation") + self.__redo_stack.append(operation) + return {"status": "Undone"} + + def redo_operation(self) -> Dict: + """ + This function redoes the last operation performed on the database. + """ + if len(self.__redo_stack) == 0: + return {"status": "No operations to redo"} + operation = self.__redo_stack.pop() + print(operation) + if operation["operation"] == "insert": + self.insert_resource(operation["resource"]) + elif operation["operation"] == "delete": + self.delete_resource(operation["resource"]) + elif operation["operation"] == "update": + self.update_resource(operation["resource"]) + temp = operation["resource"]["resource"] + operation["resource"]["resource"] = operation["resource"][ + "original_resource" + ] + operation["resource"]["original_resource"] = temp + else: + raise Exception("Invalid Operation") + self.__undo_stack.append(operation) + return {"status": "Redone"} + + def _add_to_stack(self, operation: Dict) -> Dict: + if len(self.__undo_stack) == self.__undo_limit: + self.__undo_stack.pop(0) + self.__undo_stack.append(operation) + self.__redo_stack.clear() + return {"status": "Added to stack"} + + def get_revision_status(self) -> Dict: + """ + This function saves the status of revision operations to a dictionary. + + The revision operations whose statuses are saved are undo and redo. + + If the stack of a given revision operation is empty, the status of + that operation is set to 1 else the status is set to 0. + + :return: A dictionary containing the status of revision operations. + """ + return { + "undo": 1 if len(self.__undo_stack) == 0 else 0, + "redo": 1 if len(self.__redo_stack) == 0 else 0, + } diff --git a/util/gem5-resources-manager/api/create_resources_json.py b/util/gem5-resources-manager/api/create_resources_json.py new file mode 100644 index 0000000000..02ec96af8d --- /dev/null +++ b/util/gem5-resources-manager/api/create_resources_json.py @@ -0,0 +1,332 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import base64 +import json +import os + +import requests +from jsonschema import validate + + +class ResourceJsonCreator: + """ + This class generates the JSON which is pushed onto MongoDB. + On a high-level, it does the following: + - Adds certain fields to the JSON. + - Populates those fields. + - Makes sure the JSON follows the schema. + """ + + # Global Variables + base_url = "https://github.com/gem5/gem5/tree/develop" # gem5 GitHub URL + resource_url_map = { + "dev": ( + "https://gem5.googlesource.com/public/gem5-resources/+/refs/heads/" + "develop/resources.json?format=TEXT" + ), + "22.1": ( + "https://gem5.googlesource.com/public/gem5-resources/+/refs/heads/" + "stable/resources.json?format=TEXT" + ), + "22.0": ( + "http://resources.gem5.org/prev-resources-json/" + "resources-21-2.json" + ), + "21.2": ( + "http://resources.gem5.org/prev-resources-json/" + "resources-22-0.json" + ), + } + + def __init__(self): + self.schema = {} + with open("schema/schema.json") as f: + self.schema = json.load(f) + + def _get_file_data(self, url): + json_data = None + try: + json_data = requests.get(url).text + json_data = base64.b64decode(json_data).decode("utf-8") + return json.loads(json_data) + except: + json_data = requests.get(url).json() + return json_data + + def _get_size(self, url): + """ + Helper function to return the size of a download through its URL. + Returns 0 if URL has an error. + + :param url: Download URL + """ + try: + response = requests.head(url) + size = int(response.headers.get("content-length", 0)) + return size + except Exception as e: + return 0 + + def _search_folder(self, folder_path, id): + """ + Helper function to find the instance of a string in a folder. + This is recursive, i.e., subfolders will also be searched. + + :param folder_path: Path to the folder to begin searching + :param id: Phrase to search in the folder + + :returns matching_files: List of file paths to the files containing id + """ + matching_files = [] + for filename in os.listdir(folder_path): + file_path = os.path.join(folder_path, filename) + if os.path.isfile(file_path): + with open(file_path, encoding="utf-8", errors="ignore") as f: + contents = f.read() + if id in contents: + file_path = file_path.replace("\\", "/") + matching_files.append(file_path) + elif os.path.isdir(file_path): + matching_files.extend(self._search_folder(file_path, id)) + return matching_files + + def _change_type(self, resource): + if resource["type"] == "workload": + # get the architecture from the name and remove 64 from it + resource["architecture"] = ( + resource["name"].split("-")[0].replace("64", "").upper() + ) + return resource + if "kernel" in resource["name"]: + resource["type"] = "kernel" + elif "bootloader" in resource["name"]: + resource["type"] = "bootloader" + elif "benchmark" in resource["documentation"]: + resource["type"] = "disk-image" + # if tags not in resource: + if "tags" not in resource: + resource["tags"] = [] + resource["tags"].append("benchmark") + if ( + "additional_metadata" in resource + and "root_partition" in resource["additional_metadata"] + and resource["additional_metadata"]["root_partition"] + is not None + ): + resource["root_partition"] = resource["additional_metadata"][ + "root_partition" + ] + else: + resource["root_partition"] = "" + elif resource["url"] is not None and ".img.gz" in resource["url"]: + resource["type"] = "disk-image" + if ( + "additional_metadata" in resource + and "root_partition" in resource["additional_metadata"] + and resource["additional_metadata"]["root_partition"] + is not None + ): + resource["root_partition"] = resource["additional_metadata"][ + "root_partition" + ] + else: + resource["root_partition"] = "" + elif "binary" in resource["documentation"]: + resource["type"] = "binary" + elif "checkpoint" in resource["documentation"]: + resource["type"] = "checkpoint" + elif "simpoint" in resource["documentation"]: + resource["type"] = "simpoint" + return resource + + def _extract_code_examples(self, resource, source): + """ + This function goes by IDs present in the resources DataFrame. + It finds which files use those IDs in gem5/configs. + It adds the GitHub URL of those files under "example". + It finds whether those files are used in gem5/tests/gem5. + If yes, it marks "tested" as True. If not, it marks "tested" as False. + "example" and "tested" are made into a JSON for every code example. + This list of JSONs is assigned to the 'code_examples' field of the + DataFrame. + + :param resources: A DataFrame containing the current state of + resources. + :param source: Path to gem5 + + :returns resources: DataFrame with ['code-examples'] populated. + """ + id = resource["id"] + # search for files in the folder tree that contain the 'id' value + matching_files = self._search_folder( + source + "/configs", '"' + id + '"' + ) + filenames = [os.path.basename(path) for path in matching_files] + tested_files = [] + for file in filenames: + tested_files.append( + True + if len(self._search_folder(source + "/tests/gem5", file)) > 0 + else False + ) + + matching_files = [ + file.replace(source, self.base_url) for file in matching_files + ] + + code_examples = [] + + for i in range(len(matching_files)): + json_obj = { + "example": matching_files[i], + "tested": tested_files[i], + } + code_examples.append(json_obj) + return code_examples + + def unwrap_resources(self, ver): + data = self._get_file_data(self.resource_url_map[ver]) + resources = data["resources"] + new_resources = [] + for resource in resources: + if resource["type"] == "group": + for group in resource["contents"]: + new_resources.append(group) + else: + new_resources.append(resource) + return new_resources + + def _get_example_usage(self, resource): + if resource["category"] == "workload": + return f"Workload(\"{resource['id']}\")" + else: + return f"obtain_resource(resource_id=\"{resource['id']}\")" + + def _parse_readme(self, url): + metadata = { + "tags": [], + "author": [], + "license": "", + } + try: + request = requests.get(url) + content = request.text + content = content.split("---")[1] + content = content.split("---")[0] + if "tags:" in content: + tags = content.split("tags:\n")[1] + tags = tags.split(":")[0] + tags = tags.split("\n")[:-1] + tags = [tag.strip().replace("- ", "") for tag in tags] + if tags == [""] or tags == None: + tags = [] + metadata["tags"] = tags + if "author:" in content: + author = content.split("author:")[1] + author = author.split("\n")[0] + author = ( + author.replace("[", "").replace("]", "").replace('"', "") + ) + author = author.split(",") + author = [a.strip() for a in author] + metadata["author"] = author + if "license:" in content: + license = content.split("license:")[1].split("\n")[0] + metadata["license"] = license + except: + pass + return metadata + + def _add_fields(self, resources, source): + new_resources = [] + for resource in resources: + res = self._change_type(resource) + res["gem5_versions"] = ["23.0"] + res["resource_version"] = "1.0.0" + res["category"] = res["type"] + del res["type"] + res["id"] = res["name"] + del res["name"] + res["description"] = res["documentation"] + del res["documentation"] + if "additional_metadata" in res: + for k, v in res["additional_metadata"].items(): + res[k] = v + del res["additional_metadata"] + res["example_usage"] = self._get_example_usage(res) + if "source" in res: + url = ( + "https://raw.githubusercontent.com/gem5/" + "gem5-resources/develop/" + + str(res["source"]) + + "/README.md" + ) + res["source_url"] = ( + "https://github.com/gem5/gem5-resources/tree/develop/" + + str(res["source"]) + ) + else: + url = "" + res["source_url"] = "" + metadata = self._parse_readme(url) + if "tags" in res: + res["tags"].extend(metadata["tags"]) + else: + res["tags"] = metadata["tags"] + res["author"] = metadata["author"] + res["license"] = metadata["license"] + + res["code_examples"] = self._extract_code_examples(res, source) + + if "url" in resource: + download_url = res["url"].replace( + "{url_base}", "http://dist.gem5.org/dist/develop" + ) + res["url"] = download_url + res["size"] = self._get_size(download_url) + else: + res["size"] = 0 + + res = {k: v for k, v in res.items() if v is not None} + + new_resources.append(res) + return new_resources + + def _validate_schema(self, resources): + for resource in resources: + try: + validate(resource, schema=self.schema) + except Exception as e: + print(resource) + raise e + + def create_json(self, version, source, output): + resources = self.unwrap_resources(version) + resources = self._add_fields(resources, source) + self._validate_schema(resources) + with open(output, "w") as f: + json.dump(resources, f, indent=4) diff --git a/util/gem5-resources-manager/api/json_client.py b/util/gem5-resources-manager/api/json_client.py new file mode 100644 index 0000000000..09c652d748 --- /dev/null +++ b/util/gem5-resources-manager/api/json_client.py @@ -0,0 +1,221 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import json +from pathlib import Path +from typing import ( + Dict, + List, +) + +from api.client import Client + + +class JSONClient(Client): + def __init__(self, file_path): + super().__init__() + self.file_path = Path("database/") / file_path + self.resources = self._get_resources(self.file_path) + + def _get_resources(self, path: Path) -> List[Dict]: + """ + Retrieves the resources from the JSON file. + :param path: The path to the JSON file. + :return: The resources as a JSON string. + """ + with open(path) as f: + return json.load(f) + + def find_resource(self, query: Dict) -> Dict: + """ + Finds a resource within a list of resources based on the + provided query. + :param query: The query object containing the search criteria. + :return: The resource that matches the query. + """ + found_resources = [] + for resource in self.resources: + if ( + "resource_version" not in query + or query["resource_version"] == "" + or query["resource_version"] == "Latest" + ): + if resource["id"] == query["id"]: + found_resources.append(resource) + else: + if ( + resource["id"] == query["id"] + and resource["resource_version"] + == query["resource_version"] + ): + return resource + if not found_resources: + return {"exists": False} + return max( + found_resources, + key=lambda resource: tuple( + map(int, resource["resource_version"].split(".")) + ), + ) + + def get_versions(self, query: Dict) -> List[Dict]: + """ + Retrieves all versions of a resource with the given ID from the + list of resources. + :param query: The query object containing the search criteria. + :return: A list of all versions of the resource. + """ + versions = [] + for resource in self.resources: + if resource["id"] == query["id"]: + versions.append( + {"resource_version": resource["resource_version"]} + ) + versions.sort( + key=lambda resource: tuple( + map(int, resource["resource_version"].split(".")) + ), + reverse=True, + ) + return versions + + def update_resource(self, query: Dict) -> Dict: + """ + Updates a resource within a list of resources based on the + provided query. + + The function iterates over the resources and checks if the "id" and + "resource_version" of a resource match the values in the query. + If there is a match, it removes the existing resource from the list + and appends the updated resource. + + After updating the resources, the function saves the updated list to + the specified file path. + + :param query: The query object containing the resource + identification criteria. + :return: A dictionary indicating that the resource was updated. + """ + original_resource = query["original_resource"] + modified_resource = query["resource"] + if ( + original_resource["id"] != modified_resource["id"] + and original_resource["resource_version"] + != modified_resource["resource_version"] + ): + return {"status": "Cannot change resource id"} + for resource in self.resources: + if ( + resource["id"] == original_resource["id"] + and resource["resource_version"] + == original_resource["resource_version"] + ): + self.resources.remove(resource) + self.resources.append(modified_resource) + + self.write_to_file() + return {"status": "Updated"} + + def check_resource_exists(self, query: Dict) -> Dict: + """ + Checks if a resource exists within a list of resources based on the + provided query. + + The function iterates over the resources and checks if the "id" and + "resource_version" of a resource match the values in the query. + If a matching resource is found, it returns a dictionary indicating + that the resource exists. + If no matching resource is found, it returns a dictionary indicating + that the resource does not exist. + + :param query: The query object containing the resource identification + criteria. + :return: A dictionary indicating whether the resource exists. + """ + for resource in self.resources: + if ( + resource["id"] == query["id"] + and resource["resource_version"] == query["resource_version"] + ): + return {"exists": True} + return {"exists": False} + + def insert_resource(self, query: Dict) -> Dict: + """ + Inserts a new resource into a list of resources. + + The function appends the query (new resource) to the resources list, + indicating the insertion. + It then writes the updated resources to the specified file path. + + :param query: The query object containing the resource identification + criteria. + :return: A dictionary indicating that the resource was inserted. + """ + if self.check_resource_exists(query)["exists"]: + return {"status": "Resource already exists"} + self.resources.append(query) + self.write_to_file() + return {"status": "Inserted"} + + def delete_resource(self, query: Dict) -> Dict: + """ + This function deletes a resource from the list of resources based on + the provided query. + + :param query: The query object containing the resource identification + criteria. + :return: A dictionary indicating that the resource was deleted. + """ + for resource in self.resources: + if ( + resource["id"] == query["id"] + and resource["resource_version"] == query["resource_version"] + ): + self.resources.remove(resource) + self.write_to_file() + return {"status": "Deleted"} + + def write_to_file(self) -> None: + """ + This function writes the list of resources to a file at the specified + file path. + + :return: None + """ + with Path(self.file_path).open("w") as outfile: + json.dump(self.resources, outfile, indent=4) + + def save_session(self) -> Dict: + """ + This function saves the client session to a dictionary. + :return: A dictionary containing the client session. + """ + session = { + "client": "json", + "filename": self.file_path.name, + } + return session diff --git a/util/gem5-resources-manager/api/mongo_client.py b/util/gem5-resources-manager/api/mongo_client.py new file mode 100644 index 0000000000..6a60549b9a --- /dev/null +++ b/util/gem5-resources-manager/api/mongo_client.py @@ -0,0 +1,244 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import json +from typing import ( + Dict, + List, +) + +import pymongo +from api.client import Client +from bson import json_util +from pymongo import MongoClient +from pymongo.errors import ( + ConfigurationError, + ConnectionFailure, +) + + +class DatabaseConnectionError(Exception): + "Raised for failure to connect to MongoDB client" + pass + + +class MongoDBClient(Client): + def __init__(self, mongo_uri, database_name, collection_name): + super().__init__() + self.mongo_uri = mongo_uri + self.collection_name = collection_name + self.database_name = database_name + self.collection = self._get_database( + mongo_uri, database_name, collection_name + ) + + def _get_database( + self, + mongo_uri: str, + database_name: str, + collection_name: str, + ) -> pymongo.collection.Collection: + """ + This function returns a MongoDB database object for the specified + collection. + It takes three arguments: 'mongo_uri', 'database_name', and + 'collection_name'. + + :param: mongo_uri: URI of the MongoDB instance + :param: database_name: Name of the database + :param: collection_name: Name of the collection + :return: database: MongoDB database object + """ + + try: + client = MongoClient(mongo_uri) + client.admin.command("ping") + except ConnectionFailure: + client.close() + raise DatabaseConnectionError( + "Could not connect to MongoClient with given URI!" + ) + except ConfigurationError as e: + raise DatabaseConnectionError(e) + + database = client[database_name] + if database.name not in client.list_database_names(): + raise DatabaseConnectionError("Database Does not Exist!") + + collection = database[collection_name] + if collection.name not in database.list_collection_names(): + raise DatabaseConnectionError("Collection Does not Exist!") + + return collection + + def find_resource(self, query: Dict) -> Dict: + """ + Find a resource in the database + + :param query: JSON object with id and resource_version + :return: json_resource: JSON object with request resource or + error message + """ + if "resource_version" not in query or query["resource_version"] == "": + resource = ( + self.collection.find({"id": query["id"]}, {"_id": 0}) + .sort("resource_version", -1) + .limit(1) + ) + else: + resource = ( + self.collection.find( + { + "id": query["id"], + "resource_version": query["resource_version"], + }, + {"_id": 0}, + ) + .sort("resource_version", -1) + .limit(1) + ) + json_resource = json_util.dumps(resource) + res = json.loads(json_resource) + if res == []: + return {"exists": False} + return res[0] + + def update_resource(self, query: Dict) -> Dict[str, str]: + """ + This function updates a resource in the database by first checking if + the resource version in the request matches the resource version + stored in the database. + If they match, the resource is updated in the database. If they do not + match, the update is rejected. + + :param: query: JSON object with original_resource and the + updated resource + :return: json_response: JSON object with status message + """ + original_resource = query["original_resource"] + modified_resource = query["resource"] + try: + self.collection.replace_one( + { + "id": original_resource["id"], + "resource_version": original_resource["resource_version"], + }, + modified_resource, + ) + except Exception as e: + print(e) + return {"status": "Resource does not exist"} + return {"status": "Updated"} + + def get_versions(self, query: Dict) -> List[Dict]: + """ + This function retrieves all versions of a resource with the given ID + from the database. + It takes two arguments, the database object and a JSON object + containing the 'id' key of the resource to be retrieved. + + :param: query: JSON object with id + :return: json_resource: JSON object with all resource versions + """ + versions = self.collection.find( + {"id": query["id"]}, {"resource_version": 1, "_id": 0} + ).sort("resource_version", -1) + # convert to json + res = json_util.dumps(versions) + return json_util.loads(res) + + def delete_resource(self, query: Dict) -> Dict[str, str]: + """ + This function deletes a resource from the database by first checking + if the resource version in the request matches the resource version + stored in the database. + If they match, the resource is deleted from the database. If they do + not match, the delete operation is rejected + + :param: query: JSON object with id and resource_version + :return: json_response: JSON object with status message + """ + self.collection.delete_one( + {"id": query["id"], "resource_version": query["resource_version"]} + ) + return {"status": "Deleted"} + + def insert_resource(self, query: Dict) -> Dict[str, str]: + """ + This function inserts a new resource into the database using the + 'insert_one' method of the MongoDB client. + The function takes two arguments, the database object and the JSON + object representing the new resource to be inserted. + + :param: json: JSON object representing the new resource to be inserted + :return: json_response: JSON object with status message + """ + try: + self.collection.insert_one(query) + except Exception as e: + return {"status": "Resource already exists"} + return {"status": "Inserted"} + + def check_resource_exists(self, query: Dict) -> Dict: + """ + This function checks if a resource exists in the database by searching + for a resource with a matching 'id' and 'resource_version' in + the database. + The function takes two arguments, the database object and a JSON object + containing the 'id' and 'resource_version' keys. + + :param: json: JSON object with id and resource_version + :return: json_response: JSON object with boolean 'exists' key + """ + resource = ( + self.collection.find( + { + "id": query["id"], + "resource_version": query["resource_version"], + }, + {"_id": 0}, + ) + .sort("resource_version", -1) + .limit(1) + ) + json_resource = json_util.dumps(resource) + res = json.loads(json_resource) + if res == []: + return {"exists": False} + return {"exists": True} + + def save_session(self) -> Dict: + """ + This function saves the client session to a dictionary. + :return: A dictionary containing the client session. + """ + session = { + "client": "mongodb", + "uri": self.mongo_uri, + "database": self.database_name, + "collection": self.collection_name, + } + return session diff --git a/util/gem5-resources-manager/gem5_resource_cli.py b/util/gem5-resources-manager/gem5_resource_cli.py new file mode 100644 index 0000000000..27f10aebe1 --- /dev/null +++ b/util/gem5-resources-manager/gem5_resource_cli.py @@ -0,0 +1,286 @@ +#!/usr/bin/env python3 +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import argparse +import json +import os +from itertools import cycle +from shutil import get_terminal_size +from threading import Thread +from time import sleep + +from api.create_resources_json import ResourceJsonCreator +from dotenv import load_dotenv +from pymongo import MongoClient + +load_dotenv() + +# read MONGO_URI from environment variable +MONGO_URI = os.getenv("MONGO_URI") + + +class Loader: + def __init__(self, desc="Loading...", end="Done!", timeout=0.1): + """ + A loader-like context manager + + Args: + desc (str, optional): The loader's description. + Defaults to "Loading...". + end (str, optional): Final print. Defaults to "Done!". + timeout (float, optional): Sleep time between prints. + Defaults to 0.1. + """ + self.desc = desc + self.end = end + self.timeout = timeout + + self._thread = Thread(target=self._animate, daemon=True) + self.steps = ["⢿", "⣻", "⣽", "⣾", "⣷", "⣯", "⣟", "⡿"] + self.done = False + + def start(self): + self._thread.start() + return self + + def _animate(self): + for c in cycle(self.steps): + if self.done: + break + print(f"\r{self.desc} {c}", flush=True, end="") + sleep(self.timeout) + + def __enter__(self): + self.start() + + def stop(self): + self.done = True + cols = get_terminal_size((80, 20)).columns + print("\r" + " " * cols, end="", flush=True) + print(f"\r{self.end}", flush=True) + + def __exit__(self, exc_type, exc_value, tb): + # handle exceptions with those variables ^ + self.stop() + + +def get_database(collection="versions_test", uri=MONGO_URI, db="gem5-vision"): + """ + Retrieves the MongoDB database for gem5-vision. + """ + CONNECTION_STRING = uri + try: + client = MongoClient(CONNECTION_STRING) + client.server_info() + except: + print("\nCould not connect to MongoDB") + exit(1) + return client[db][collection] + + +collection = None + + +def cli(): + parser = argparse.ArgumentParser( + description="CLI for gem5-resources.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "-u", + "--uri", + help="The URI of the MongoDB database.", + type=str, + default=MONGO_URI, + ) + parser.add_argument( + "-d", + "--database", + help="The MongoDB database to use.", + type=str, + default="gem5-vision", + ) + parser.add_argument( + "-c", + "--collection", + help="The MongoDB collection to use.", + type=str, + default="versions_test", + ) + + subparsers = parser.add_subparsers( + help="The command to run.", dest="command", required=True + ) + + parser_get_resource = subparsers.add_parser( + "get_resource", + help=( + "Retrieves a resource from the collection based on the given ID." + "\n if a resource version is provided, it will retrieve the " + "resource with the given ID and version." + ), + ) + req_group = parser_get_resource.add_argument_group( + title="required arguments" + ) + req_group.add_argument( + "-i", + "--id", + help="The ID of the resource to retrieve.", + type=str, + required=True, + ) + parser_get_resource.add_argument( + "-v", + "--version", + help="The version of the resource to retrieve.", + type=str, + required=False, + ) + parser_get_resource.set_defaults(func=get_resource) + + parser_backup_mongodb = subparsers.add_parser( + "backup_mongodb", + help="Backs up the MongoDB collection to a JSON file.", + ) + req_group = parser_backup_mongodb.add_argument_group( + title="required arguments" + ) + req_group.add_argument( + "-f", + "--file", + help="The JSON file to back up the MongoDB collection to.", + type=str, + required=True, + ) + parser_backup_mongodb.set_defaults(func=backup_mongodb) + + parser_update_mongodb = subparsers.add_parser( + "restore_backup", + help="Restores a backup of the MongoDB collection from a JSON file.", + ) + req_group = parser_update_mongodb.add_argument_group( + title="required arguments" + ) + req_group.add_argument( + "-f", + "--file", + help="The JSON file to restore the MongoDB collection from.", + type=str, + ) + parser_update_mongodb.set_defaults(func=restore_backup) + + parser_create_resources_json = subparsers.add_parser( + "create_resources_json", + help="Creates a JSON file of all the resources in the collection.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser_create_resources_json.add_argument( + "-v", + "--version", + help="The version of the resources to create the JSON file for.", + type=str, + default="dev", + ) + parser_create_resources_json.add_argument( + "-o", + "--output", + help="The JSON file to create.", + type=str, + default="resources.json", + ) + parser_create_resources_json.add_argument( + "-s", + "--source", + help="The path to the gem5 source code.", + type=str, + default="", + ) + parser_create_resources_json.set_defaults(func=create_resources_json) + + args = parser.parse_args() + if args.collection: + global collection + with Loader("Connecting to MongoDB...", end="Connected to MongoDB"): + collection = get_database(args.collection, args.uri, args.database) + args.func(args) + + +def get_resource(args): + # set the end after the loader is created + loader = Loader("Retrieving resource...").start() + resource = None + if args.version: + resource = collection.find_one( + {"id": args.id, "resource_version": args.version}, {"_id": 0} + ) + else: + resource = collection.find({"id": args.id}, {"_id": 0}) + resource = list(resource) + if resource: + loader.end = json.dumps(resource, indent=4) + else: + loader.end = "Resource not found" + + loader.stop() + + +def backup_mongodb(args): + """ + Backs up the MongoDB collection to a JSON file. + + :param file: The JSON file to back up the MongoDB collection to. + """ + with Loader( + "Backing up the database...", + end="Backed up the database to " + args.file, + ): + # get all the data from the collection + resources = collection.find({}, {"_id": 0}) + # write to resources.json + with open(args.file, "w") as f: + json.dump(list(resources), f, indent=4) + + +def restore_backup(args): + with Loader("Restoring backup...", end="Updated the database\n"): + with open(args.file) as f: + resources = json.load(f) + # clear the collection + collection.delete_many({}) + # push the new data + collection.insert_many(resources) + + +def create_resources_json(args): + with Loader("Creating resources JSON...", end="Created " + args.output): + creator = ResourceJsonCreator() + creator.create_json(args.version, args.source, args.output) + + +if __name__ == "__main__": + cli() diff --git a/util/gem5-resources-manager/requirements.txt b/util/gem5-resources-manager/requirements.txt new file mode 100644 index 0000000000..5ffd51deb2 --- /dev/null +++ b/util/gem5-resources-manager/requirements.txt @@ -0,0 +1,29 @@ +attrs==23.1.0 +blinker==1.6.2 +certifi==2023.5.7 +cffi==1.15.1 +charset-normalizer==3.1.0 +click==8.1.3 +colorama==0.4.6 +coverage==7.2.7 +cryptography==39.0.2 +dnspython==2.3.0 +Flask==2.3.2 +idna==3.4 +importlib-metadata==6.6.0 +itsdangerous==2.1.2 +Jinja2==3.1.2 +jsonschema==4.17.3 +Markdown==3.4.3 +MarkupSafe==2.1.3 +mongomock==4.1.2 +packaging==23.1 +pycparser==2.21 +pymongo==4.3.3 +pyrsistent==0.19.3 +python-dotenv==1.0.0 +requests==2.31.0 +sentinels==1.0.0 +urllib3==2.0.2 +Werkzeug==2.3.4 +zipp==3.15.0 diff --git a/util/gem5-resources-manager/server.py b/util/gem5-resources-manager/server.py new file mode 100644 index 0000000000..f0357a4a27 --- /dev/null +++ b/util/gem5-resources-manager/server.py @@ -0,0 +1,888 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import base64 +import json +import secrets +from pathlib import Path + +import jsonschema +import markdown +import requests +from api.json_client import JSONClient +from api.mongo_client import MongoDBClient +from bson import json_util +from cryptography.exceptions import InvalidSignature +from cryptography.fernet import ( + Fernet, + InvalidToken, +) +from cryptography.hazmat.primitives.kdf.scrypt import Scrypt +from flask import ( + Flask, + make_response, + redirect, + render_template, + request, + url_for, +) +from werkzeug.utils import secure_filename + +databases = {} + +response = requests.get( + "https://resources.gem5.org/gem5-resources-schema.json" +) +schema = json.loads(response.content) + + +UPLOAD_FOLDER = Path("database/") +TEMP_UPLOAD_FOLDER = Path("database/.tmp/") +CONFIG_FILE = Path("instance/config.py") +SESSIONS_COOKIE_KEY = "sessions" +ALLOWED_EXTENSIONS = {"json"} +CLIENT_TYPES = ["mongodb", "json"] + + +app = Flask(__name__, instance_relative_config=True) + + +if not CONFIG_FILE.exists(): + CONFIG_FILE.parent.mkdir() + with CONFIG_FILE.open("w+") as f: + f.write(f"SECRET_KEY = {secrets.token_bytes(32)}") + + +app.config.from_pyfile(CONFIG_FILE.name) + + +# Sorts keys in any serialized dict +# Default = True +# Set False to persevere JSON key order +app.json.sort_keys = False + + +def startup_config_validation(): + """ + Validates the startup configuration. + + Raises: + ValueError: If the 'SECRET_KEY' is not set or is not of type 'bytes'. + """ + if not app.secret_key: + raise ValueError("SECRET_KEY not set") + if not isinstance(app.secret_key, bytes): + raise ValueError("SECRET_KEY must be of type 'bytes'") + + +def startup_dir_file_validation(): + """ + Validates the startup directory and file configuration. + + Creates the required directories if they do not exist. + """ + for dir in [UPLOAD_FOLDER, TEMP_UPLOAD_FOLDER]: + if not dir.is_dir(): + dir.mkdir() + + +with app.app_context(): + startup_config_validation() + startup_dir_file_validation() + + +@app.route("/") +def index(): + """ + Renders the index HTML template. + + :return: The rendered index HTML template. + """ + return render_template("index.html") + + +@app.route("/login/mongodb") +def login_mongodb(): + """ + Renders the MongoDB login HTML template. + + :return: The rendered MongoDB login HTML template. + """ + return render_template("login/login_mongodb.html") + + +@app.route("/login/json") +def login_json(): + """ + Renders the JSON login HTML template. + + :return: The rendered JSON login HTML template. + """ + return render_template("login/login_json.html") + + +@app.route("/validateMongoDB", methods=["POST"]) +def validate_mongodb(): + """ + Validates the MongoDB connection parameters and redirects to the editor route if successful. + + This route expects a POST request with a JSON payload containing an alias for the session and the listed parameters in order to validate the MongoDB instance. + + This route expects the following JSON payload parameters: + - uri: The MongoDB connection URI. + - collection: The name of the collection in the MongoDB database. + - database: The name of the MongoDB database. + - alias: The value by which the session will be keyed in `databases`. + + If the 'uri' parameter is empty, a JSON response with an error message and status code 400 (Bad Request) is returned. + If the connection parameters are valid, the route redirects to the 'editor' route with the appropriate query parameters. + + :return: A redirect response to the 'editor' route or a JSON response with an error message and status code 400. + """ + global databases + try: + databases[request.json["alias"]] = MongoDBClient( + mongo_uri=request.json["uri"], + database_name=request.json["database"], + collection_name=request.json["collection"], + ) + except Exception as e: + return {"error": str(e)}, 400 + return redirect( + url_for("editor", alias=request.json["alias"]), + 302, + ) + + +@app.route("/validateJSON", methods=["GET"]) +def validate_json_get(): + """ + Validates the provided JSON URL and redirects to the editor route if successful. + + This route expects the following query parameters: + - q: The URL of the JSON file. + - filename: An optional filename for the uploaded JSON file. + + If the 'q' parameter is empty, a JSON response with an error message and status code 400 (Bad Request) is returned. + If the JSON URL is valid, the function retrieves the JSON content, saves it to a file, and redirects to the 'editor' + route with the appropriate query parameters. + + :return: A redirect response to the 'editor' route or a JSON response with an error message and status code 400. + """ + filename = request.args.get("filename") + url = request.args.get("q") + if not url: + return {"error": "empty"}, 400 + response = requests.get(url) + if response.status_code != 200: + return {"error": "invalid status"}, response.status_code + filename = secure_filename(request.args.get("filename")) + path = UPLOAD_FOLDER / filename + if (UPLOAD_FOLDER / filename).is_file(): + temp_path = TEMP_UPLOAD_FOLDER / filename + with temp_path.open("wb") as f: + f.write(response.content) + return {"conflict": "existing file in server"}, 409 + with path.open("wb") as f: + f.write(response.content) + global databases + if filename in databases: + return {"error": "alias already exists"}, 409 + try: + databases[filename] = JSONClient(filename) + except Exception as e: + return {"error": str(e)}, 400 + return redirect( + url_for("editor", alias=filename), + 302, + ) + + +@app.route("/validateJSON", methods=["POST"]) +def validate_json_post(): + """ + Validates and processes the uploaded JSON file. + + This route expects a file with the key 'file' in the request files. + If the file is not present, a JSON response with an error message + and status code 400 (Bad Request) is returned. + If the file already exists in the server, a JSON response with a + conflict error message and status code 409 (Conflict) is returned. + If the file's filename conflicts with an existing alias, a JSON + response with an error message and status code 409 (Conflict) is returned. + If there is an error while processing the JSON file, a JSON response + with the error message and status code 400 (Bad Request) is returned. + If the file is successfully processed, a redirect response to the + 'editor' route with the appropriate query parameters is returned. + + :return: A JSON response with an error message and + status code 400 or 409, or a redirect response to the 'editor' route. + """ + temp_path = None + if "file" not in request.files: + return {"error": "empty"}, 400 + file = request.files["file"] + filename = secure_filename(file.filename) + path = UPLOAD_FOLDER / filename + if path.is_file(): + temp_path = TEMP_UPLOAD_FOLDER / filename + file.save(temp_path) + return {"conflict": "existing file in server"}, 409 + file.save(path) + global databases + if filename in databases: + return {"error": "alias already exists"}, 409 + try: + databases[filename] = JSONClient(filename) + except Exception as e: + return {"error": str(e)}, 400 + return redirect( + url_for("editor", alias=filename), + 302, + ) + + +@app.route("/existingJSON", methods=["GET"]) +def existing_json(): + """ + Handles the request for an existing JSON file. + + This route expects a query parameter 'filename' + specifying the name of the JSON file. + If the file is not present in the 'databases', + it tries to create a 'JSONClient' instance for the file. + If there is an error while creating the 'JSONClient' + instance, a JSON response with the error message + and status code 400 (Bad Request) is returned. + If the file is present in the 'databases', a redirect + response to the 'editor' route with the appropriate + query parameters is returned. + + :return: A JSON response with an error message + and status code 400, or a redirect response to the 'editor' route. + """ + filename = request.args.get("filename") + global databases + if filename not in databases: + try: + databases[filename] = JSONClient(filename) + except Exception as e: + return {"error": str(e)}, 400 + return redirect( + url_for("editor", alias=filename), + 302, + ) + + +@app.route("/existingFiles", methods=["GET"]) +def get_existing_files(): + """ + Retrieves the list of existing files in the upload folder. + + This route returns a JSON response containing the names of the existing files in the upload folder configured in the + Flask application. + + :return: A JSON response with the list of existing files. + """ + files = [f.name for f in UPLOAD_FOLDER.iterdir() if f.is_file()] + return json.dumps(files) + + +@app.route("/resolveConflict", methods=["GET"]) +def resolve_conflict(): + """ + Resolves file conflict with JSON files. + + This route expects the following query parameters: + - filename: The name of the file that is conflicting or an updated name for it to resolve the name conflict + - resolution: A resolution option, defined as follows: + - clearInput: Deletes the conflicting file and does not proceed with login + - openExisting: Opens the existing file in `UPLOAD_FOLDER` + - overwrite: Overwrites the existing file with the conflicting file + - newFilename: Renames conflicting file, moving it to `UPLOAD_FOLDER` + + If the resolution parameter is not from the list given, an error is returned. + + The conflicting file in `TEMP_UPLOAD_FOLDER` is deleted. + + :return: A JSON response containing an error, or a success response, or a redirect to the editor. + """ + filename = secure_filename(request.args.get("filename")) + resolution = request.args.get("resolution") + resolution_options = [ + "clearInput", + "openExisting", + "overwrite", + "newFilename", + ] + temp_path = TEMP_UPLOAD_FOLDER / filename + if not resolution: + return {"error": "empty"}, 400 + if resolution not in resolution_options: + return {"error": "invalid resolution"}, 400 + if resolution == resolution_options[0]: + temp_path.unlink() + return {"success": "input cleared"}, 204 + if resolution in resolution_options[-2:]: + next(TEMP_UPLOAD_FOLDER.glob("*")).replace(UPLOAD_FOLDER / filename) + if temp_path.is_file(): + temp_path.unlink() + global databases + if filename in databases: + return {"error": "alias already exists"}, 409 + try: + databases[filename] = JSONClient(filename) + except Exception as e: + return {"error": str(e)}, 400 + return redirect( + url_for("editor", alias=filename), + 302, + ) + + +@app.route("/editor") +def editor(): + """ + Renders the editor page based on the specified database type. + + This route expects a GET request with specific query parameters: + - "alias": An optional alias for the MongoDB database. + + The function checks if the query parameters are present. If not, it returns a 404 error. + + The function determines the database type based on the instance of the client object stored in the databases['alias']. If the type is not in the + "CLIENT_TYPES" configuration, it returns a 404 error. + + :return: The rendered editor template based on the specified database type. + """ + global databases + if not request.args: + return render_template("404.html"), 404 + alias = request.args.get("alias") + if alias not in databases: + return render_template("404.html"), 404 + + client_type = "" + if isinstance(databases[alias], JSONClient): + client_type = CLIENT_TYPES[1] + elif isinstance(databases[alias], MongoDBClient): + client_type = CLIENT_TYPES[0] + else: + return render_template("404.html"), 404 + + response = make_response( + render_template("editor.html", client_type=client_type, alias=alias) + ) + + response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" + response.headers["Pragma"] = "no-cache" + response.headers["Expires"] = "0" + + return response + + +@app.route("/help") +def help(): + """ + Renders the help page. + + This route reads the contents of the "help.md" file located in the "static" folder and renders it as HTML using the + Markdown syntax. The rendered HTML is then passed to the "help.html" template for displaying the help page. + + :return: The rendered help page HTML. + """ + with Path("static/help.md").open("r") as f: + return render_template( + "help.html", rendered_html=markdown.markdown(f.read()) + ) + + +@app.route("/find", methods=["POST"]) +def find(): + """ + Finds a resource based on the provided search criteria. + + This route expects a POST request with a JSON payload containing the alias of the session which is to be searched for the + resource and the search criteria. + + The alias is used in retrieving the session from `databases`. If the session is not found, an error is returned. + + The Client API is used to find the resource by calling `find_resource()` on the session where the operation is + accomplished by the concrete client class. + + The result of the `find_resource` operation is returned as a JSON response. + + :return: A JSON response containing the result of the `find_resource` operation. + """ + alias = request.json["alias"] + if alias not in databases: + return {"error": "database not found"}, 400 + database = databases[alias] + return database.find_resource(request.json) + + +@app.route("/update", methods=["POST"]) +def update(): + """ + Updates a resource with provided changes. + + This route expects a POST request with a JSON payload containing the alias of the session which contains the resource + that is to be updated and the data for updating the resource. + + The alias is used in retrieving the session from `databases`. If the session is not found, an error is returned. + + The Client API is used to update the resource by calling `update_resource()` on the session where the operation is + accomplished by the concrete client class. + + The `_add_to_stack` function of the session is called to insert the operation, update, and necessary data onto the revision + operations stack. + + The result of the `update_resource` operation is returned as a JSON response. It contains the original and the modified resources. + + :return: A JSON response containing the result of the `update_resource` operation. + """ + alias = request.json["alias"] + if alias not in databases: + return {"error": "database not found"}, 400 + database = databases[alias] + original_resource = request.json["original_resource"] + modified_resource = request.json["resource"] + status = database.update_resource( + { + "original_resource": original_resource, + "resource": modified_resource, + } + ) + database._add_to_stack( + { + "operation": "update", + "resource": { + "original_resource": modified_resource, + "resource": original_resource, + }, + } + ) + return status + + +@app.route("/versions", methods=["POST"]) +def getVersions(): + """ + Retrieves the versions of a resource based on the provided search criteria. + + This route expects a POST request with a JSON payload containing the alias of the session which contains the resource + whose versions are to be retrieved and the search criteria. + + The alias is used in retrieving the session from `databases`. If the session is not found, an error is returned. + + The Client API is used to get the versions of a resource by calling `get_versions()` on the session where the operation is + accomplished by the concrete client class. + + The result of the `get_versions` operation is returned as a JSON response. + + :return: A JSON response containing the result of the `get_versions` operation. + """ + alias = request.json["alias"] + if alias not in databases: + return {"error": "database not found"}, 400 + database = databases[alias] + return database.get_versions(request.json) + + +@app.route("/categories", methods=["GET"]) +def getCategories(): + """ + Retrieves the categories of the resources. + + This route returns a JSON response containing the categories of the resources. The categories are obtained from the + "enum" property of the "category" field in the schema. + + :return: A JSON response with the categories of the resources. + """ + return json.dumps(schema["properties"]["category"]["enum"]) + + +@app.route("/schema", methods=["GET"]) +def getSchema(): + """ + Retrieves the schema definition of the resources. + + This route returns a JSON response containing the schema definition of the resources. The schema is obtained from the + `schema` variable. + + :return: A JSON response with the schema definition of the resources. + """ + return json_util.dumps(schema) + + +@app.route("/keys", methods=["POST"]) +def getFields(): + """ + Retrieves the required fields for a specific category based on the provided data. + + This route expects a POST request with a JSON payload containing the data for retrieving the required fields. + The function constructs an empty object `empty_object` with the "category" and "id" values from the request payload. + + The function then uses the JSONSchema validator to validate the `empty_object` against the `schema`. It iterates + through the validation errors and handles two types of errors: + + 1. "is a required property" error: If a required property is missing in the `empty_object`, the function retrieves + the default value for that property from the schema and sets it in the `empty_object`. + + 2. "is not valid under any of the given schemas" error: If a property is not valid under the current schema, the + function evolves the validator to use the schema corresponding to the requested category. It then iterates + through the validation errors again and handles any missing required properties as described in the previous + step. + + Finally, the `empty_object` with the required fields populated (including default values if applicable) is returned + as a JSON response. + + :return: A JSON response containing the `empty_object` with the required fields for the specified category. + """ + empty_object = { + "category": request.json["category"], + "id": request.json["id"], + } + validator = jsonschema.Draft7Validator(schema) + errors = list(validator.iter_errors(empty_object)) + for error in errors: + if "is a required property" in error.message: + required = error.message.split("'")[1] + empty_object[required] = error.schema["properties"][required][ + "default" + ] + if "is not valid under any of the given schemas" in error.message: + validator = validator.evolve( + schema=error.schema["definitions"][request.json["category"]] + ) + for e in validator.iter_errors(empty_object): + if "is a required property" in e.message: + required = e.message.split("'")[1] + if "default" in e.schema["properties"][required]: + empty_object[required] = e.schema["properties"][ + required + ]["default"] + else: + empty_object[required] = "" + return json.dumps(empty_object) + + +@app.route("/delete", methods=["POST"]) +def delete(): + """ + Deletes a resource. + + This route expects a POST request with a JSON payload containing the alias of the session from which a resource is to be + deleted and the data for deleting the resource. + + The alias is used in retrieving the session from `databases`. If the session is not found, an error is returned. + + The Client API is used to delete the resource by calling `delete_resource()` on the session where the operation is + accomplished by the concrete client class. + + The `_add_to_stack` function of the session is called to insert the operation, delete, and necessary data onto the revision + operations stack. + + The result of the `delete` operation is returned as a JSON response. + + :return: A JSON response containing the result of the `delete` operation. + """ + alias = request.json["alias"] + if alias not in databases: + return {"error": "database not found"}, 400 + database = databases[alias] + resource = request.json["resource"] + status = database.delete_resource(resource) + database._add_to_stack({"operation": "delete", "resource": resource}) + return status + + +@app.route("/insert", methods=["POST"]) +def insert(): + """ + Inserts a new resource. + + This route expects a POST request with a JSON payload containing the alias of the session to which the data + is to be inserted and the data for inserting the resource. + + The alias is used in retrieving the session from `databases`. If the session is not found, an error is returned. + + The Client API is used to insert the new resource by calling `insert_resource()` on the session where the operation is + accomplished by the concrete client class. + + The `_add_to_stack` function of the session is called to insert the operation, insert, and necessary data onto the revision + operations stack. + + The result of the `insert` operation is returned as a JSON response. + + :return: A JSON response containing the result of the `insert` operation. + """ + alias = request.json["alias"] + if alias not in databases: + return {"error": "database not found"}, 400 + database = databases[alias] + resource = request.json["resource"] + status = database.insert_resource(resource) + database._add_to_stack({"operation": "insert", "resource": resource}) + return status + + +@app.route("/undo", methods=["POST"]) +def undo(): + """ + Undoes last operation performed on the session. + + This route expects a POST request with a JSON payload containing the alias of the session whose last operation + is to be undone. + + The alias is used in retrieving the session from `databases`. If the session is not found, an error is returned. + + The Client API is used to undo the last operation performed on the session by calling `undo_operation()` on the + session where the operation is accomplished by the concrete client class. + + The result of the `undo_operation` operation is returned as a JSON response. + + :return: A JSON response containing the result of the `undo_operation` operation. + """ + alias = request.json["alias"] + if alias not in databases: + return {"error": "database not found"}, 400 + database = databases[alias] + return database.undo_operation() + + +@app.route("/redo", methods=["POST"]) +def redo(): + """ + Redoes last operation performed on the session. + + This route expects a POST request with a JSON payload containing the alias of the session whose last operation + is to be redone. + + The alias is used in retrieving the session from `databases`. If the session is not found, an error is returned. + + The Client API is used to redo the last operation performed on the session by calling `redo_operation()` on the + session where the operation is accomplished by the concrete client class. + + The result of the `redo_operation` operation is returned as a JSON response. + + :return: A JSON response containing the result of the `redo_operation` operation. + """ + alias = request.json["alias"] + if alias not in databases: + return {"error": "database not found"}, 400 + database = databases[alias] + return database.redo_operation() + + +@app.route("/getRevisionStatus", methods=["POST"]) +def get_revision_status(): + """ + Gets the status of revision operations. + + This route expects a POST request with a JSON payload containing the alias of the session whose revision operations + statuses is being requested. + + The alias is used in retrieving the session from `databases`. If the session is not found, an error is + returned. + + The Client API is used to get the status of the revision operations by calling `get_revision_status()` on the + session where the operation is accomplished by the concrete client class. + + The result of the `get_revision_status` is returned as a JSON response. + + :return: A JSON response contain the result of the `get_revision_status` operation. + """ + alias = request.json["alias"] + if alias not in databases: + return {"error": "database not found"}, 400 + database = databases[alias] + return database.get_revision_status() + + +def fernet_instance_generation(password): + """ + Generates Fernet instance for use in Saving and Loading Session. + + Utilizes Scrypt Key Derivation Function with `SECRET_KEY` as salt value and recommended + values for `length`, `n`, `r`, and `p` parameters. Derives key using `password`. Derived + key is then used to initialize Fernet instance. + + :param password: User provided password + :return: Fernet instance + """ + return Fernet( + base64.urlsafe_b64encode( + Scrypt(salt=app.secret_key, length=32, n=2**16, r=8, p=1).derive( + password.encode() + ) + ) + ) + + +@app.route("/saveSession", methods=["POST"]) +def save_session(): + """ + Generates ciphertext of session that is to be saved. + + This route expects a POST request with a JSON payload containing the alias of the session that is to be + saved and a password to be used in encrypting the session data. + + The alias is used in retrieving the session from `databases`. If the session is not found, an error is + returned. + + The `save_session()` method is called to get the necessary session data from the corresponding `Client` + as a dictionary. + + A Fernet instance, using the user provided password, is instantiated. The session data is encrypted using this + instance. If an Exception is raised, an error response is returned. + + The result of the save_session operation is returned as a JSON response. The ciphertext is returned or an error + message if an error occurred. + + :return: A JSON response containing the result of the save_session operation. + """ + alias = request.json["alias"] + if alias not in databases: + return {"error": "database not found"}, 400 + session = databases[alias].save_session() + try: + fernet_instance = fernet_instance_generation(request.json["password"]) + ciphertext = fernet_instance.encrypt(json.dumps(session).encode()) + except (TypeError, ValueError): + return {"error": "Failed to Encrypt Session!"}, 400 + return {"ciphertext": ciphertext.decode()}, 200 + + +@app.route("/loadSession", methods=["POST"]) +def load_session(): + """ + Loads session from data specified in user request. + + This route expects a POST request with a JSON payload containing the encrypted ciphertext containing the session + data, the alias of the session that is to be restored, and the password associated with it. + + A Fernet instance, using the user provided password, is instantiated. The session data is decrypted using this + instance. If an Exception is raised, an error response is returned. + + The `Client` type is retrieved from the session data and a redirect to the appropriate login with the stored + parameters from the session data is applied. + + The result of the load_session operation is returned either as a JSON response containing the error message + or a redirect. + + :return: A JSON response containing the error of the load_session operation or a redirect. + """ + alias = request.json["alias"] + session = request.json["session"] + try: + fernet_instance = fernet_instance_generation(request.json["password"]) + session_data = json.loads(fernet_instance.decrypt(session)) + except (InvalidSignature, InvalidToken): + return {"error": "Incorrect Password! Please Try Again!"}, 400 + client_type = session_data["client"] + if client_type == CLIENT_TYPES[0]: + try: + databases[alias] = MongoDBClient( + mongo_uri=session_data["uri"], + database_name=session_data["database"], + collection_name=session_data["collection"], + ) + except Exception as e: + return {"error": str(e)}, 400 + + return redirect( + url_for("editor", type=CLIENT_TYPES[0], alias=alias), + 302, + ) + elif client_type == CLIENT_TYPES[1]: + return redirect( + url_for("existing_json", filename=session_data["filename"]), + 302, + ) + else: + return {"error": "Invalid Client Type!"}, 409 + + +@app.errorhandler(404) +def handle404(error): + """ + Error handler for 404 (Not Found) errors. + + This function is called when a 404 error occurs. It renders the "404.html" template and returns it as a response with + a status code of 404. + + :param error: The error object representing the 404 error. + :return: A response containing the rendered "404.html" template with a status code of 404. + """ + return render_template("404.html"), 404 + + +@app.route("/checkExists", methods=["POST"]) +def checkExists(): + """ + Checks if a resource exists based on the provided data. + + This route expects a POST request with a JSON payload containing the alias of the session in which it is to be + determined whether a given resource exists and the necessary data for checking the existence of the resource. + + The alias is used in retrieving the session from `databases`. If the session is not found, an error is + returned. + + The Client API is used to check the existence of the resource by calling `check_resource_exists()` on the + session where the operation is accomplished by the concrete client class. + + The result of the `check_resource_exists` is returned as a JSON response. + + :return: A JSON response contain the result of the `check_resource_exists` operation. + """ + alias = request.json["alias"] + if alias not in databases: + return {"error": "database not found"}, 400 + database = databases[alias] + return database.check_resource_exists(request.json) + + +@app.route("/logout", methods=["POST"]) +def logout(): + """ + Logs the user out of the application. + + Deletes the alias from the `databases` dictionary. + + :param alias: The alias of the database to logout from. + + :return: A redirect to the index page. + """ + alias = request.json["alias"] + if alias not in databases: + return {"error": "database not found"}, 400 + databases.pop(alias) + return (redirect(url_for("index")), 302) + + +if __name__ == "__main__": + app.run(debug=True) diff --git a/util/gem5-resources-manager/static/help.md b/util/gem5-resources-manager/static/help.md new file mode 100644 index 0000000000..c79d26dea4 --- /dev/null +++ b/util/gem5-resources-manager/static/help.md @@ -0,0 +1,65 @@ +# Help + +## Load Previous Session +Retrieves list of saved sessions from browser localStorage. +If found, displays list, can select a session to restore, and if entered password is correct session is restored and redirects to editor. + +## MongoDB +Set up editor view for MongoDB Instance. + +### Login: Enter URI +Utilize if the MongoDB connection string is known. + +#### Fields: + - URI: [MongoDB](https://www.mongodb.com/docs/manual/reference/connection-string/) + +#### Additional Fields: + - Collection: Specify collection in MongoDB instance to retrieve + - Database: Specify database in MongoDB instance to retrieve + - Alias: Optional. Provide a display alias to show on editor view instead of URI + +### Login: Generate URI +Provides method to generate MongoDB URI connection string if it is not known or to supply with additional parameters. + +#### Fields: + + - Connection: Specify connection mode, Standard or DNS Seed List, as defined by [MongoDB](https://www.mongodb.com/docs/manual/reference/connection-string/) + - Username: Optional. + - Password: Optional. + - Host: Specify host/list of hosts for instance + - Retry Writes: Allow MongoDB to retry a write to database once if they fail the first time + - Write Concern: Determines level of acknowledgement required from database for write operations, specifies how many nodes must acknowledge the operation before it is considered successful. (Currently set to majority) + - Options: Optional. Additional parameters that can be set when connecting to the instance + +#### Additional Fields: + - Collection: Specify collection in MongoDB instance to retrieve + - Database: Specify database in MongoDB instance to retrieve + - Alias: Optional field to provide a display alias to show on editor view instead of URI + +## JSON +Set up editor view for JSON file. Can Specify a URL to a remote JSON file to be imported +or select a local JSON file. + + +## Editor +Page containing Monaco VSCode Diff Editor to allow editing of database entries. + +### Database Actions: +Actions that can be performed on database currently in use. + +- Search: Search for resource in database with exact Resource ID +- Version: Dropdown that allows for selection of a particular resource version of resource currently in view +- Category: Specify category of resource to viewed as defined by schema +- Undo: Undoes last edit to database +- Redo: Redoes last undone change to database +- Show Schema: Sets view for schema of current database (read only) +- Save Session: Save session in encrypted format to browser localStorage +- Logout: Removes sessions from list of active sessions + +### Editing Actions: +Actions that can be performed on resource currently in view. + +- Add New Resource: Add a new resource to database +- Add New Version: Insert a new version of current resource +- Delete: Permanently delete resource +- Update: Update resource with edits made diff --git a/util/gem5-resources-manager/static/images/favicon.png b/util/gem5-resources-manager/static/images/favicon.png new file mode 100644 index 0000000000..d0103efa4d Binary files /dev/null and b/util/gem5-resources-manager/static/images/favicon.png differ diff --git a/util/gem5-resources-manager/static/images/gem5ColorLong.gif b/util/gem5-resources-manager/static/images/gem5ColorLong.gif new file mode 100644 index 0000000000..552e4d12fc Binary files /dev/null and b/util/gem5-resources-manager/static/images/gem5ColorLong.gif differ diff --git a/util/gem5-resources-manager/static/images/gem5ResourcesManager.png b/util/gem5-resources-manager/static/images/gem5ResourcesManager.png new file mode 100644 index 0000000000..dac4cb595e Binary files /dev/null and b/util/gem5-resources-manager/static/images/gem5ResourcesManager.png differ diff --git a/util/gem5-resources-manager/static/js/app.js b/util/gem5-resources-manager/static/js/app.js new file mode 100644 index 0000000000..ed5025a2f2 --- /dev/null +++ b/util/gem5-resources-manager/static/js/app.js @@ -0,0 +1,135 @@ +const loadingContainer = document.getElementById("loading-container"); +const alertPlaceholder = document.getElementById('liveAlertPlaceholder'); +const interactiveElems = document.querySelectorAll('button, input, select'); + +const appendAlert = (errorHeader, id, message, type) => { + const alertDiv = document.createElement('div'); + alertDiv.classList.add("alert", `alert-${type}`, "alert-dismissible", "fade", "show", "d-flex", "flex-column", "shadow-sm"); + alertDiv.setAttribute("role", "alert"); + alertDiv.setAttribute("id", id); + alertDiv.style.maxWidth = "320px"; + + alertDiv.innerHTML = [ + `
`, + ` `, + ` `, + ` `, + ` ${errorHeader}`, + ` `, + `
`, + `
`, + `
${message}
`, + ].join(''); + + window.scrollTo(0, 0); + + alertPlaceholder.append(alertDiv); + + setTimeout(function () { + bootstrap.Alert.getOrCreateInstance(document.getElementById(`${id}`)).close(); + }, 5000); +} + +function toggleInteractables(isBlocking, excludedOnNotBlockingIds = [], otherBlockingUpdates = () => {}) { + if (isBlocking) { + loadingContainer.classList.add("d-flex"); + interactiveElems.forEach(elems => { + elems.disabled = true; + }); + window.scrollTo(0, 0); + otherBlockingUpdates(); + return; + } + + setTimeout(() => { + loadingContainer.classList.remove("d-flex"); + interactiveElems.forEach(elems => { + !excludedOnNotBlockingIds.includes(elems.id) ? elems.disabled = false : null; + }); + otherBlockingUpdates(); + }, 250); +} + +function showResetSavedSessionsModal() { + let sessions = localStorage.getItem("sessions"); + if (sessions === null) { + appendAlert('Error!', 'noSavedSessions', `No Saved Sessions Exist!`, 'danger'); + return; + } + sessions = JSON.parse(sessions); + + const resetSavedSessionsModal = new bootstrap.Modal(document.getElementById('resetSavedSessionsModal'), { + focus: true, keyboard: false + }); + + + let select = document.getElementById("delete-session-dropdown"); + select.innerHTML = ""; + Object.keys(sessions).forEach((alias) => { + let option = document.createElement("option"); + option.value = alias; + option.innerHTML = alias; + select.appendChild(option); + }); + + document.getElementById("selected-session").innerText = `"${document.getElementById("delete-session-dropdown").value}"`; + + resetSavedSessionsModal.show(); +} + +function resetSavedSessions() { + bootstrap.Modal.getInstance(document.getElementById("resetSavedSessionsModal")).hide(); + + const sessions = JSON.parse(localStorage.getItem("sessions")); + if (sessions === null) { + appendAlert('Error!', 'noSavedSessions', `No Saved Sessions Exist!`, 'danger'); + return; + } + + const activeTab = document.getElementById("reset-tabs").querySelector(".nav-link.active").getAttribute("id"); + if (activeTab === "delete-one-tab") { + const deleteOneConfirmation = document.getElementById("delete-one-confirmation").value; + if (deleteOneConfirmation !== document.getElementById("delete-session-dropdown").value) { + document.getElementById("resetSavedSessionsModal").querySelectorAll("form").forEach(form => { + form.reset(); + }) + appendAlert('Error!', 'noSavedSessions', `Invalid Confirmation Entry!`, 'danger'); + return; + } + + delete sessions[document.getElementById("delete-session-dropdown").value]; + Object.keys(sessions).length === 0 + ? localStorage.removeItem("sessions") + : localStorage.setItem("sessions", JSON.stringify(sessions)); + + } else { + const deleteAllConfirmation = document.getElementById("delete-all-confirmation").value; + if (deleteAllConfirmation !== "Delete All") { + document.getElementById("resetSavedSessionsModal").querySelectorAll("form").forEach(form => { + form.reset(); + }) + appendAlert('Error!', 'noSavedSessions', `Invalid Confirmation Entry!`, 'danger'); + return; + } + + localStorage.removeItem("sessions"); + } + + appendAlert('Success!', 'resetCookies', `Saved Session Reset Successful!`, 'success'); + setTimeout(() => { + location.reload(); + }, 750); +} + +document.getElementById("close-reset-modal").addEventListener("click", () => { + document.getElementById("resetSavedSessionsModal").querySelectorAll("form").forEach(form => { + form.reset(); + }) +}); + +document.getElementById("delete-session-dropdown").addEventListener("change", () => { + document.getElementById("selected-session").innerText = + `"${document.getElementById("delete-session-dropdown").value}"`; +}); diff --git a/util/gem5-resources-manager/static/js/editor.js b/util/gem5-resources-manager/static/js/editor.js new file mode 100644 index 0000000000..64786da0bd --- /dev/null +++ b/util/gem5-resources-manager/static/js/editor.js @@ -0,0 +1,589 @@ +const diffEditorContainer = document.getElementById("diff-editor"); +var diffEditor; +var originalModel; +var modifiedModel; + +const schemaEditorContainer = document.getElementById("schema-editor"); +var schemaEditor; +var schemaModel; + +const schemaButton = document.getElementById("schema-toggle"); +const editingActionsButtons = Array.from( + document.querySelectorAll("#editing-actions button") +); +var editingActionsState; + +const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); +tooltipTriggerList.forEach(tooltip => { + tooltip.setAttribute("data-bs-trigger", "hover"); +}); +const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); + +require.config({ + paths: { + vs: "https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs", + }, +}); +require(["vs/editor/editor.main"], () => { + originalModel = monaco.editor.createModel(`{\n}`, "json"); + modifiedModel = monaco.editor.createModel(`{\n}`, "json"); + diffEditor = monaco.editor.createDiffEditor(diffEditorContainer, { + theme: "vs-dark", + language: "json", + automaticLayout: true, + }); + diffEditor.setModel({ + original: originalModel, + modified: modifiedModel, + }); + fetch("/schema") + .then((res) => res.json()) + .then((data) => { + monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ + trailingCommas: "error", + comments: "error", + validate: true, + schemas: [ + { + uri: "http://json-schema.org/draft-07/schema", + fileMatch: ["*"], + schema: data, + }, + ], + }); + + schemaEditor = monaco.editor.create(schemaEditorContainer, { + theme: "vs-dark", + language: "json", + automaticLayout: true, + readOnly: true, + }); + + schemaModel = monaco.editor.createModel(`{\n}`, "json"); + schemaEditor.setModel(schemaModel); + schemaModel.setValue(JSON.stringify(data, null, 4)); + + schemaEditorContainer.style.display = "none"; + }); +}); + +let clientType = document.getElementById('client-type'); +clientType.textContent = clientType.textContent === "mongodb" ? "MongoDB" : clientType.textContent.toUpperCase(); + +const revisionButtons = [document.getElementById("undo-operation"), document.getElementById("redo-operation")]; +revisionButtons.forEach(btn => { + btn.disabled = true; +}); + +const editorGroupIds = []; +document.querySelectorAll(".editorButtonGroup button, .revisionButtonGroup button") + .forEach(btn => { + editorGroupIds.push(btn.id); + }); + +function checkErrors() { + let errors = monaco.editor.getModelMarkers({ resource: modifiedModel.uri }); + if (errors.length > 0) { + console.log(errors); + let str = ""; + errors.forEach((error) => { + str += error.message + "\n"; + }); + appendAlert('Error!', 'schemaError', { str }, 'danger'); + return true; + } + return false; +} +let didChange = false; + +function update(e) { + e.preventDefault(); + if (checkErrors()) { + return; + } + let json = JSON.parse(modifiedModel.getValue()); + let original_json = JSON.parse(originalModel.getValue()); + + console.log(json); + fetch("/update", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + resource: json, + original_resource: original_json, + alias: document.getElementById("alias").innerText, + }), + }) + .then((res) => res.json()) + .then(async (data) => { + console.log(data); + await addVersions(); + //Select last option + document.getElementById("version-dropdown").value = + json["resource_version"]; + console.log(document.getElementById("version-dropdown").value); + find(e); + }); +} + +function addNewResource(e) { + e.preventDefault(); + if (checkErrors()) { + return; + } + let json = JSON.parse(modifiedModel.getValue()); + console.log(json); + fetch("/insert", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + resource: json, + alias: document.getElementById("alias").innerText, + }), + }) + .then((res) => res.json()) + .then(async (data) => { + console.log(data); + await addVersions(); + //Select last option + document.getElementById("version-dropdown").value = + json["resource_version"]; + console.log(document.getElementById("version-dropdown").value); + find(e); + }); +} + +function addVersion(e) { + e.preventDefault(); + console.log("add version"); + if (checkErrors()) { + return; + } + let json = JSON.parse(modifiedModel.getValue()); + console.log(json["resource_version"]); + fetch("/checkExists", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + id: json["id"], + resource_version: json["resource_version"], + alias: document.getElementById("alias").innerText, + }), + }) + .then((res) => res.json()) + .then((data) => { + console.log(data["exists"]); + if (data["exists"] == true) { + appendAlert("Error!", "existingResourceVersion", "Resource version already exists!", "danger"); + return; + } else { + fetch("/insert", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + resource: json, + alias: document.getElementById("alias").innerText, + }), + }) + .then((res) => res.json()) + .then(async (data) => { + console.log("added version"); + console.log(data); + await addVersions(); + //Select last option + document.getElementById("version-dropdown").value = + json["resource_version"]; + console.log(document.getElementById("version-dropdown").value); + find(e); + }); + } + }); +} + +function deleteRes(e) { + e.preventDefault(); + console.log("delete"); + let id = document.getElementById("id").value; + let resource_version = JSON.parse(originalModel.getValue())[ + "resource_version" + ]; + let json = JSON.parse(originalModel.getValue()); + console.log(resource_version); + fetch("/delete", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + resource: json, + alias: document.getElementById("alias").innerText, + }), + }) + .then((res) => res.json()) + .then(async (data) => { + console.log(data); + await addVersions(); + //Select first option + document.getElementById("version-dropdown").value = + document.getElementById("version-dropdown").options[0].value; + console.log(document.getElementById("version-dropdown").value); + find(e); + }); +} + +document.getElementById("id").onchange = function () { + console.log("id changed"); + didChange = true; +}; + +async function addVersions() { + let select = document.getElementById("version-dropdown"); + select.innerHTML = "Latest"; + await fetch("/versions", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + id: document.getElementById("id").value, + alias: document.getElementById("alias").innerText, + }), + }) + .then((res) => res.json()) + .then((data) => { + let select = document.getElementById("version-dropdown"); + if (data.length == 0) { + data = [{ resource_version: "Latest" }]; + } + data.forEach((version) => { + let option = document.createElement("option"); + option.value = version["resource_version"]; + option.innerText = version["resource_version"]; + select.appendChild(option); + }); + }); +} + +function find(e) { + e.preventDefault(); + if (didChange) { + addVersions(); + didChange = false; + } + + closeSchema(); + + toggleInteractables(true, editorGroupIds, () => { + diffEditor.updateOptions({ readOnly: true }); + updateRevisionBtnsDisabledAttr(); + }); + + fetch("/find", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + id: document.getElementById("id").value, + resource_version: document.getElementById("version-dropdown").value, + alias: document.getElementById("alias").innerText, + }), + }) + .then((res) => res.json()) + .then((data) => { + console.log(data); + toggleInteractables(false, editorGroupIds, () => { + diffEditor.updateOptions({ readOnly: false }); + updateRevisionBtnsDisabledAttr(); + }); + + if (data["exists"] == false) { + fetch("/keys", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + category: document.getElementById("category").value, + id: document.getElementById("id").value, + }), + }) + .then((res) => res.json()) + .then((data) => { + console.log(data) + data["id"] = document.getElementById("id").value; + data["category"] = document.getElementById("category").value; + originalModel.setValue(JSON.stringify(data, null, 4)); + modifiedModel.setValue(JSON.stringify(data, null, 4)); + + document.getElementById("add_new_resource").disabled = false; + document.getElementById("add_version").disabled = true; + document.getElementById("delete").disabled = true; + document.getElementById("update").disabled = true; + }); + } else { + console.log(data); + originalModel.setValue(JSON.stringify(data, null, 4)); + modifiedModel.setValue(JSON.stringify(data, null, 4)); + + document.getElementById("version-dropdown").value = + data.resource_version; + document.getElementById("category").value = data.category; + + document.getElementById("add_new_resource").disabled = true; + document.getElementById("add_version").disabled = false; + document.getElementById("delete").disabled = false; + document.getElementById("update").disabled = false; + } + }); +} + +window.onload = () => { + let ver_dropdown = document.getElementById("version-dropdown"); + let option = document.createElement("option"); + option.value = "Latest"; + option.innerHTML = "Latest"; + ver_dropdown.appendChild(option); + fetch("/categories") + .then((res) => res.json()) + .then((data) => { + console.log(data); + let select = document.getElementById("category"); + data.forEach((category) => { + let option = document.createElement("option"); + option.value = category; + option.innerHTML = category; + select.appendChild(option); + }); + fetch("/keys", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + category: document.getElementById("category").value, + id: "", + }), + }) + .then((res) => res.json()) + .then((data) => { + data["id"] = ""; + data["category"] = document.getElementById("category").value; + originalModel.setValue(JSON.stringify(data, null, 4)); + modifiedModel.setValue(JSON.stringify(data, null, 4)); + document.getElementById("add_new_resource").disabled = false; + }); + }); + + checkExistingSavedSession(); +}; + +const myModal = new bootstrap.Modal("#ConfirmModal", { + keyboard: false, +}); + +let confirmButton = document.getElementById("confirm"); + +function showModal(event, callback) { + event.preventDefault(); + myModal.show(); + confirmButton.onclick = () => { + callback(event); + myModal.hide(); + }; +} + +let editorTitle = document.getElementById("editor-title"); + +function showSchema() { + if (diffEditorContainer.style.display !== "none") { + diffEditorContainer.style.display = "none"; + schemaEditorContainer.classList.add("editor-sizing"); + schemaEditor.setPosition({ column: 1, lineNumber: 1 }); + schemaEditor.revealPosition({ column: 1, lineNumber: 1 }); + schemaEditorContainer.style.display = "block"; + + editingActionsState = editingActionsButtons.map( + (button) => button.disabled + ); + + editingActionsButtons.forEach((btn) => { + btn.disabled = true; + }); + + editorTitle.children[0].style.display = "none"; + editorTitle.children[1].textContent = "Schema (Read Only)"; + + schemaButton.textContent = "Close Schema"; + schemaButton.onclick = closeSchema; + } +} + +function closeSchema() { + if (schemaEditorContainer.style.display !== "none") { + schemaEditorContainer.style.display = "none"; + diffEditorContainer.style.display = "block"; + + editingActionsButtons.forEach((btn, i) => { + btn.disabled = editingActionsState[i]; + }); + + editorTitle.children[0].style.display = "unset"; + editorTitle.children[1].textContent = "Edited"; + + schemaButton.textContent = "Show Schema"; + schemaButton.onclick = showSchema; + } +} + +const saveSessionBtn = document.getElementById("saveSession"); +saveSessionBtn.disabled = true; + +let password = document.getElementById("session-password"); +password.addEventListener("input", () => { + saveSessionBtn.disabled = password.value === ""; +}); + +function showSaveSessionModal() { + const saveSessionModal = new bootstrap.Modal(document.getElementById('saveSessionModal'), { + focus: true, keyboard: false + }); + saveSessionModal.show(); +} + +function saveSession() { + alias = document.getElementById("alias").innerText; + + bootstrap.Modal.getInstance(document.getElementById("saveSessionModal")).hide(); + + let preserveDisabled = []; + document.querySelectorAll(".editorButtonGroup button, .revisionButtonGroup button") + .forEach(btn => { + btn.disabled === true ? preserveDisabled.push(btn.id) : null; + }); + + toggleInteractables(true); + + fetch("/saveSession", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + alias: alias, + password: document.getElementById("session-password").value + }), + }) + .then((res) => { + document.getElementById("saveSessionForm").reset(); + + toggleInteractables(false, preserveDisabled); + + res.json() + .then((data) => { + if (res.status === 400) { + appendAlert('Error!', 'saveSessionError', `${data["error"]}`, 'danger'); + return; + } + + let sessions = JSON.parse(localStorage.getItem("sessions")) || {}; + sessions[alias] = data["ciphertext"]; + localStorage.setItem("sessions", JSON.stringify(sessions)); + + document.getElementById("showSaveSessionModal").innerText = "Session Saved"; + checkExistingSavedSession(); + }) + }) +} + +function executeRevision(event, operation) { + if (!["undo", "redo"].includes(operation)) { + appendAlert("Error!", "invalidRevOp", "Fatal! Invalid Revision Operation!", "danger"); + return; + } + + toggleInteractables(true, editorGroupIds, () => { + diffEditor.updateOptions({ readOnly: true }); + updateRevisionBtnsDisabledAttr(); + }); + fetch(`/${operation}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + alias: document.getElementById("alias").innerText, + }), + }) + .then(() => { + toggleInteractables(false, editorGroupIds, () => { + diffEditor.updateOptions({ readOnly: false }); + updateRevisionBtnsDisabledAttr(); + }); + find(event); + }) +} + +function updateRevisionBtnsDisabledAttr() { + fetch("/getRevisionStatus", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + alias: document.getElementById("alias").innerText, + }), + }) + .then((res) => res.json()) + .then((data) => { + revisionButtons[0].disabled = data.undo; + revisionButtons[1].disabled = data.redo; + }) +} + +function logout() { + toggleInteractables(true); + + fetch("/logout", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + alias: document.getElementById("alias").innerText, + }), + }) + .then((res) => { + toggleInteractables(false); + + if (res.status !== 302) { + res.json() + .then((data) => { + appendAlert('Error!', 'logoutError', `${data["error"]}`, 'danger'); + return; + }) + } + + window.location = res.url; + }) +} + +function checkExistingSavedSession() { + document.getElementById("existing-session-warning").style.display = + document.getElementById("alias").innerText in JSON.parse(localStorage.getItem("sessions") || "{}") + ? "flex" + : "none"; +} + +document.getElementById("close-save-session-modal").addEventListener("click", () => { + document.getElementById("saveSessionModal").querySelector("form").reset(); + saveSessionBtn.disabled = password.value === ""; +}); diff --git a/util/gem5-resources-manager/static/js/index.js b/util/gem5-resources-manager/static/js/index.js new file mode 100644 index 0000000000..1509d2d893 --- /dev/null +++ b/util/gem5-resources-manager/static/js/index.js @@ -0,0 +1,75 @@ +window.onload = () => { + let select = document.getElementById("sessions-dropdown"); + const sessions = JSON.parse(localStorage.getItem("sessions")); + + if (sessions === null) { + document.getElementById("showSavedSessionModal").disabled = true; + return; + } + + Object.keys(sessions).forEach((alias) => { + let option = document.createElement("option"); + option.value = alias; + option.innerHTML = alias; + select.appendChild(option); + }); +} + +const loadSessionBtn = document.getElementById("loadSession"); +loadSessionBtn.disabled = true; + +let password = document.getElementById("session-password"); +password.addEventListener("input", () => { + loadSessionBtn.disabled = password.value === ""; +}); + +document.getElementById("close-load-session-modal").addEventListener("click", () => { + document.getElementById("savedSessionModal").querySelector("form").reset(); +}) + +function showSavedSessionModal() { + const savedSessionModal = new bootstrap.Modal(document.getElementById('savedSessionModal'), { focus: true, keyboard: false }); + savedSessionModal.show(); +} + +function loadSession() { + bootstrap.Modal.getInstance(document.getElementById("savedSessionModal")).hide(); + + const alias = document.getElementById("sessions-dropdown").value; + const session = JSON.parse(localStorage.getItem("sessions"))[alias]; + + if (session === null) { + appendAlert("Error!", "sessionNotFound", "Saved Session Not Found!", "danger"); + return; + } + + toggleInteractables(true); + + fetch("/loadSession", { + method: "POST", + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + password: document.getElementById("session-password").value, + alias: alias, + session: session + }) + }) + .then((res) => { + toggleInteractables(false); + + if (res.status !== 200) { + res.json() + .then((error) => { + document.getElementById("savedSessionModal").querySelector("form").reset(); + appendAlert("Error!", "invalidStatus", `${error["error"]}`, "danger"); + return; + }) + } + + if (res.redirected) { + window.location = res.url; + } + }) +} diff --git a/util/gem5-resources-manager/static/js/login.js b/util/gem5-resources-manager/static/js/login.js new file mode 100644 index 0000000000..b21ffeb458 --- /dev/null +++ b/util/gem5-resources-manager/static/js/login.js @@ -0,0 +1,330 @@ +function handleMongoDBLogin(event) { + event.preventDefault(); + const activeTab = document.getElementById("mongodb-login-tabs").querySelector(".nav-link.active").getAttribute("id"); + + activeTab === "enter-uri-tab" ? handleEnteredURI() : handleGenerateURI(); + + return; +} + +function handleEnteredURI() { + const uri = document.getElementById('uri').value; + const collection = document.getElementById('collection').value; + const database = document.getElementById('database').value; + const alias = document.getElementById('alias').value; + const emptyInputs = [{ type: "Alias", value: alias }, { type: "Collection", value: collection }, { type: "Database", value: database }, { type: "URI", value: uri }]; + let error = false; + + for (let i = 0; i < emptyInputs.length; i++) { + if (emptyInputs[i].value === "") { + appendAlert("Error", `${emptyInputs[i].type}`, `Cannot Proceed Without ${emptyInputs[i].type} Value!`, 'danger'); + error = true; + } + } + + if (error) { + return; + } + + handleMongoURLFetch(uri, collection, database, alias); +} + +function handleGenerateURI() { + const connection = document.getElementById('connection').checked; + const username = document.getElementById('username').value; + const password = document.getElementById('password').value; + const collection = document.getElementById('collectionGenerate').value; + const database = document.getElementById('databaseGenerate').value; + const host = document.getElementById('host').value; + const alias = document.getElementById('aliasGenerate').value; + const options = document.getElementById('options').value.split(","); + let generatedURI = ""; + const emptyInputs = [{ type: "Alias", value: alias }, { type: "Host", value: host }, { type: "Collection", value: collection }, { type: "Database", value: database }]; + let error = false; + + for (let i = 0; i < emptyInputs.length; i++) { + if (emptyInputs[i].value === "") { + appendAlert("Error", `${emptyInputs[i].type}`, `Cannot Proceed Without ${emptyInputs[i].type} Value!`, 'danger'); + error = true; + } + } + + if (error) { + return; + } + + generatedURI = connection ? "mongodb+srv://" : "mongodb://"; + if (username && password) { + generatedURI += `${encodeURIComponent(username)}:${encodeURIComponent(password)}@`; + } + + generatedURI += host; + + if (options.length) { + generatedURI += `/?${options.join("&")}`; + } + + handleMongoURLFetch(generatedURI, collection, database, alias); +} + +function handleMongoURLFetch(uri, collection, database, alias) { + toggleInteractables(true); + + fetch("/validateMongoDB", + { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + uri: uri, + collection: collection, + database: database, + alias: alias + }) + }) + .then((res) => { + toggleInteractables(false); + + if (!res.ok) { + res.json() + .then(error => { + appendAlert('Error!', 'mongodbValidationError', `${error.error}`, 'danger'); + }); + return; + } + + res.redirected ? window.location = res.url : appendAlert('Error!', 'invalidRes', 'Invalid Server Response!', 'danger'); + }) +} + +function handleJSONLogin(event) { + event.preventDefault(); + const activeTab = document.getElementById("json-login-tabs").querySelector(".nav-link.active").getAttribute("id"); + if (activeTab === "remote-tab") { + handleRemoteJSON(); + } else if (activeTab === "existing-tab") { + const filename = document.getElementById("existing-dropdown").value; + if (filename !== "No Existing Files") { + toggleInteractables(true); + + fetch(`/existingJSON?filename=${filename}`, + { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }) + .then((res) => { + toggleInteractables(false); + + if (res.status !== 200) { + appendAlert('Error!', 'invalidURL', 'Invalid JSON File URL!', 'danger'); + } + if (res.redirected) { + window.location = res.url; + } + }) + } + } else { + handleUploadJSON(); + } + return; +} + +function handleRemoteJSON() { + const url = document.getElementById("jsonRemoteURL").value; + const filename = document.getElementById("remoteFilename").value; + const emptyInputs = [{ type: "URL", value: url }, { type: "Filename", value: filename }]; + let error = false; + + for (let i = 0; i < emptyInputs.length; i++) { + if (emptyInputs[i].value === "") { + appendAlert("Error", `${emptyInputs[i].type}`, `Cannot Proceed Without ${emptyInputs[i].type} Value!`, 'danger'); + error = true; + } + } + + if (error) { + return; + } + + const params = new URLSearchParams(); + params.append('filename', filename + ".json"); + params.append('q', url); + + const flask_url = `/validateJSON?${params.toString()}`; + + toggleInteractables(true); + + fetch(flask_url, { + method: 'GET', + }) + .then((res) => { + toggleInteractables(false); + + if (res.status === 400) { + appendAlert('Error!', 'invalidURL', 'Invalid JSON File URL!', 'danger'); + } + + if (res.status === 409) { + const myModal = new bootstrap.Modal(document.getElementById('conflictResolutionModal'), { focus: true, keyboard: false }); + document.getElementById("header-filename").textContent = `"${filename}"`; + myModal.show(); + } + + if (res.redirected) { + window.location = res.url; + } + }) +} + +var filename; + +function handleUploadJSON() { + const jsonFile = document.getElementById("jsonFile"); + const file = jsonFile.files[0]; + + if (jsonFile.value === "") { + appendAlert('Error!', 'emptyUpload', 'Cannot Proceed Without Uploading a File!', 'danger'); + return; + } + + filename = file.name; + + const form = new FormData(); + form.append("file", file); + + toggleInteractables(true); + + fetch("/validateJSON", { + method: 'POST', + body: form + }) + .then((res) => { + toggleInteractables(false); + + if (res.status === 400) { + appendAlert('Error!', 'invalidUpload', 'Invalid JSON File Upload!', 'danger'); + } + + if (res.status === 409) { + const myModal = new bootstrap.Modal(document.getElementById('conflictResolutionModal'), { focus: true, keyboard: false }); + document.getElementById("header-filename").textContent = `"${filename}"`; + myModal.show(); + } + + if (res.redirected) { + window.location = res.url; + } + }) +} + +function saveConflictResolution() { + const conflictResolutionModal = bootstrap.Modal.getInstance(document.getElementById("conflictResolutionModal")); + const selectedValue = document.querySelector('input[name="conflictRadio"]:checked').id; + const activeTab = document.getElementById("json-login-tabs").querySelector(".nav-link.active").getAttribute("id"); + + if (selectedValue === null) { + appendAlert('Error!', 'nullRadio', 'Fatal! Null Radio!', 'danger'); + return; + } + + if (selectedValue === "clearInput") { + if (activeTab === "upload-tab") { + document.getElementById("jsonFile").value = ''; + } + + if (activeTab === "remote-tab") { + document.getElementById('remoteFilename').value = ''; + document.getElementById('jsonRemoteURL').value = ''; + } + + conflictResolutionModal.hide(); + handleConflictResolution("clearInput", filename.split(".")[0]); + return; + } + + if (selectedValue === "openExisting") { + conflictResolutionModal.hide(); + handleConflictResolution("openExisting", filename.split(".")[0]); + return; + } + + if (selectedValue === "overwrite") { + conflictResolutionModal.hide(); + handleConflictResolution("overwrite", filename.split(".")[0]); + return; + } + + if (selectedValue === "newFilename") { + const updatedFilename = document.getElementById("updatedFilename").value; + if (updatedFilename === "") { + appendAlert('Error!', 'emptyFilename', 'Must Enter A New Name!', 'danger'); + return; + } + + if (`${updatedFilename}.json` === filename) { + appendAlert('Error!', 'sameFilenames', 'Cannot Have Same Name as Current!', 'danger'); + return; + } + + conflictResolutionModal.hide(); + handleConflictResolution("newFilename", updatedFilename); + return; + } +} + +function handleConflictResolution(resolution, filename) { + const params = new URLSearchParams(); + params.append('resolution', resolution); + params.append('filename', filename !== "" ? filename + ".json" : ""); + + const flask_url = `/resolveConflict?${params.toString()}`; + toggleInteractables(true); + + fetch(flask_url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }) + .then((res) => { + toggleInteractables(false); + + if (res.status === 204) { + console.log("Input Cleared, Cached File Deleted, Resources Unset"); + return; + } + + if (res.status !== 200) { + appendAlert('Error!', 'didNotRedirect', 'Server Did Not Redirect!', 'danger'); + return; + } + + if (res.redirected) { + window.location = res.url; + } + }) +} + +window.onload = () => { + if (window.location.pathname === "/login/json") { + fetch('/existingFiles', { + method: 'GET', + }) + .then((res) => res.json()) + .then((data) => { + let select = document.getElementById("existing-dropdown"); + if (data.length === 0) { + data = ["No Existing Files"]; + } + data.forEach((files) => { + let option = document.createElement("option"); + option.value = files; + option.innerHTML = files; + select.appendChild(option); + }); + }); + } +} diff --git a/util/gem5-resources-manager/static/styles/global.css b/util/gem5-resources-manager/static/styles/global.css new file mode 100644 index 0000000000..caa446a60b --- /dev/null +++ b/util/gem5-resources-manager/static/styles/global.css @@ -0,0 +1,231 @@ +@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600;700&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Mulish:wght@700&display=swap'); + +html, +body { + min-height: 100vh; + margin: 0; +} + +.btn-outline-primary { + --bs-btn-color: #0095AF; + --bs-btn-bg: #FFFFFF; + --bs-btn-border-color: #0095AF; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #0095AF; + --bs-btn-hover-border-color: #0095AF; + --bs-btn-focus-shadow-rgb: 13, 110, 253; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #0095AF; + --bs-btn-active-border-color: #0095AF; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: white; + --bs-btn-disabled-bg: grey; + --bs-btn-disabled-border-color: grey; + --bs-gradient: none; +} + +.btn-box-shadow { + box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; +} + +.calc-main-height { + height: calc(100vh - 81px); +} + +.main-text-semi { + font-family: 'Open Sans', sans-serif; + font-weight: 600; + font-size: 1rem; +} + +.main-text-regular, +.buttonGroup>button, +#markdown-body-styling p, +#markdown-body-styling li { + font-family: 'Open Sans', sans-serif; + font-weight: 400; + font-size: 1rem; +} + +.secondary-text-semi { + font-family: 'Open Sans', sans-serif; + font-weight: 600; + font-size: 1.25rem; +} + +.secondary-text-bold { + font-family: 'Open Sans', sans-serif; + font-weight: 600; + font-size: 1.25rem; +} + +.main-text-bold { + font-family: 'Open Sans', sans-serif; + font-weight: 700; + font-size: 1rem; +} + +.page-title, +#markdown-body-styling h1 { + color: #425469; + font-family: 'Mulish', sans-serif; + font-weight: 700; + font-size: 2.5rem; +} + +.main-panel-container { + max-width: 530px; + padding-top: 5rem; + padding-bottom: 5rem; +} + +.input-shadow, +.form-input-shadow>input { + box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; +} + +.panel-container { + background: rgba(0, 149, 175, 0.50); + border-radius: 1rem; + box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px, rgba(0, 0, 0, 0.35) 0px 5px 15px; + height: 555px; + width: 530px; +} + +.panel-text-styling, +#generate-uri-form>label { + text-shadow: 0px 0px 5px rgba(0, 0, 0, 0.50); + color: white; +} + +.editorContainer { + width: 80%; +} + +.monaco-editor { + position: absolute !important; +} + +.editor-sizing { + min-height: 650px; + height: 75%; + width: 100%; +} + +#liveAlertPlaceholder { + position: absolute; + margin-top: 1rem; + right: 2rem; + margin-left: 2rem; + z-index: 1040; +} + +.alert-dismissible { + padding-right: 1rem; +} + +.reset-nav, +.login-nav { + --bs-nav-link-color: #0095AF; + --bs-nav-link-hover-color: white; + --bs-nav-tabs-link-active-color: #0095AF; +} + +.login-nav-link { + color: white; + text-shadow: 0px 0px 5px rgba(0, 0, 0, 0.50); +} + +.login-nav-link.active { + text-shadow: none; +} + +.navbar-nav>.nav-link:hover { + text-decoration: underline; +} + +.reset-nav-link:hover, +.login-nav-link:hover { + background-color: #0095AF; +} + +.reset-nav-link { + color: black; +} + +.form-check-input:checked { + background-color: #6c6c6c; + border-color: #6c6c6c; +} + +#markdown-body-styling h1 { + color: #425469; +} + +code { + display: inline-table; + overflow-x: auto; + padding: 2px; + color: #333; + background: #f8f8f8; + border: 1px solid #ccc; + border-radius: 3px; +} + +.editor-tooltips { + --bs-tooltip-bg: #0095AF; + --bs-tooltip-opacity: 1; +} + +#loading-container { + display: none; + position: absolute; + right: 2rem; + margin-top: 1rem; +} + +.spinner { + --bs-spinner-width: 2.25rem; + --bs-spinner-height: 2.25rem; + --bs-spinner-border-width: 0.45em; + border-color: #0095AF; + border-right-color: transparent; +} + +#saved-confirmation { + opacity: 0; + transition: opacity 0.5s; +} + +@media (max-width: 991px) { + .editorContainer { + width: 95%; + } +} + +@media (max-width: 425px) { + + .main-text-regular, + .main-text-semi, + .main-text-bold, + .buttonGroup>button, + #markdown-body-styling p { + font-size: 0.875rem; + } + + .secondary-text-semi { + font-size: 1rem; + } + + .page-title, + #markdown-body-styling h1 { + font-size: 2.25rem; + } +} + +@media (min-width: 425px) { + #databaseActions { + max-width: 375px; + } +} diff --git a/util/gem5-resources-manager/templates/404.html b/util/gem5-resources-manager/templates/404.html new file mode 100644 index 0000000000..0a38326b2e --- /dev/null +++ b/util/gem5-resources-manager/templates/404.html @@ -0,0 +1,20 @@ +{% extends 'base.html' %} {% block head %} +Page Not Found +{% endblock %} {% block body %} +
+
+

404

+

+ The page you are looking for does not seem to exist. +

+ Home +
+
+{% endblock %} diff --git a/util/gem5-resources-manager/templates/base.html b/util/gem5-resources-manager/templates/base.html new file mode 100644 index 0000000000..3b89f8f4c1 --- /dev/null +++ b/util/gem5-resources-manager/templates/base.html @@ -0,0 +1,96 @@ + + + + + + + + + {% block head %}{% endblock %} + + + +
+
+ Processing... +
+ Processing... +
+
+ + {% block body %}{% endblock %} + + + diff --git a/util/gem5-resources-manager/templates/editor.html b/util/gem5-resources-manager/templates/editor.html new file mode 100644 index 0000000000..813a4d1a4b --- /dev/null +++ b/util/gem5-resources-manager/templates/editor.html @@ -0,0 +1,355 @@ +{% extends 'base.html' %} {% block head %} +Editor + +{% endblock %} {% block body %} + + +
+
+
+
+
+ Database Actions +
+ +
+
+ +
+ + +
+ + + +
+
+
+ Revision Actions +
+
+ + + + + + +
+
+
+
+ Other Actions +
+ + + + + + + +
+
+
+ +
+

{{ client_type }}

+

+ {{ alias }} +

+
+
+

Original

+

Modified

+
+
+
+
+ + + + + + + + + + + + +
+
+
+
+ +{% endblock %} diff --git a/util/gem5-resources-manager/templates/help.html b/util/gem5-resources-manager/templates/help.html new file mode 100644 index 0000000000..957a87b59e --- /dev/null +++ b/util/gem5-resources-manager/templates/help.html @@ -0,0 +1,20 @@ +{% extends 'base.html' %} {% block head %} +Help + +{% endblock %} {% block body %} +
+
+ {{ rendered_html|safe }} +
+
+{% endblock %} diff --git a/util/gem5-resources-manager/templates/index.html b/util/gem5-resources-manager/templates/index.html new file mode 100644 index 0000000000..6321a9e5b6 --- /dev/null +++ b/util/gem5-resources-manager/templates/index.html @@ -0,0 +1,116 @@ +{% extends 'base.html' %} {% block head %} +Resources Manager +{% endblock %} {% block body %} + +
+
+
+
+
+ gem5 +
+
+
+ + + + + + + +
+
+
+
+ +{% endblock %} diff --git a/util/gem5-resources-manager/templates/login/login_json.html b/util/gem5-resources-manager/templates/login/login_json.html new file mode 100644 index 0000000000..98663a304b --- /dev/null +++ b/util/gem5-resources-manager/templates/login/login_json.html @@ -0,0 +1,242 @@ +{% extends 'base.html' %} {% block head %} +JSON Login +{% endblock %} {% block body %} + +
+
+
+
+

JSON

+
+ +
+
+
+
+ +
+ + .json +
+
+ + +
+
+
+
+ +
+
+
+
+ + +
+
+
+
+
+
+ +
+
+
+
+ +{% endblock %} diff --git a/util/gem5-resources-manager/templates/login/login_mongodb.html b/util/gem5-resources-manager/templates/login/login_mongodb.html new file mode 100644 index 0000000000..83361b578e --- /dev/null +++ b/util/gem5-resources-manager/templates/login/login_mongodb.html @@ -0,0 +1,189 @@ +{% extends 'base.html' %} {% block head %} +MongoDB Login +{% endblock %} {% block body %} +
+
+
+
+

MongoDB

+
+ +
+
+
+ + + + + + + + +
+
+
+
+
+ Standard +
+ +
+ DNS Seed List +
+ + + + + + + + + + + + + + +
+
+
+
+
+
+ +
+
+
+
+ +{% endblock %} diff --git a/util/gem5-resources-manager/test/__init__.py b/util/gem5-resources-manager/test/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/util/gem5-resources-manager/test/api_test.py b/util/gem5-resources-manager/test/api_test.py new file mode 100644 index 0000000000..e6918831ea --- /dev/null +++ b/util/gem5-resources-manager/test/api_test.py @@ -0,0 +1,723 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import contextlib +import json +import unittest +from unittest.mock import patch + +import flask +import mongomock +import requests +import server +from api.mongo_client import MongoDBClient +from bson import json_util +from server import app + + +@contextlib.contextmanager +def captured_templates(app): + """ + This is a context manager that allows you to capture the templates + that are rendered during a test. + """ + recorded = [] + + def record(sender, template, context, **extra): + recorded.append((template, context)) + + flask.template_rendered.connect(record, app) + try: + yield recorded + finally: + flask.template_rendered.disconnect(record, app) + + +class TestAPI(unittest.TestCase): + @patch.object( + MongoDBClient, + "_get_database", + return_value=mongomock.MongoClient().db.collection, + ) + def setUp(self, mock_get_database): + """This method sets up the test environment.""" + self.ctx = app.app_context() + self.ctx.push() + self.app = app + self.test_client = app.test_client() + self.alias = "test" + objects = [] + with open("./test/refs/resources.json", "rb") as f: + objects = json.loads(f.read(), object_hook=json_util.object_hook) + self.collection = mock_get_database() + for obj in objects: + self.collection.insert_one(obj) + + self.test_client.post( + "/validateMongoDB", + json={ + "uri": "mongodb://localhost:27017", + "database": "test", + "collection": "test", + "alias": self.alias, + }, + ) + + def tearDown(self): + """ + This method tears down the test environment. + """ + self.collection.drop() + self.ctx.pop() + + def test_get_helppage(self): + """ + This method tests the call to the help page. + It checks if the call is GET, status code is 200 and if the template + rendered is help.html. + """ + with captured_templates(self.app) as templates: + response = self.test_client.get("/help") + self.assertEqual(response.status_code, 200) + self.assertTrue(templates[0][0].name == "help.html") + + def test_get_mongodb_loginpage(self): + """ + This method tests the call to the MongoDB login page. + It checks if the call is GET, status code is 200 and if the template + rendered is mongoDBLogin.html. + """ + with captured_templates(self.app) as templates: + response = self.test_client.get("/login/mongodb") + self.assertEqual(response.status_code, 200) + self.assertTrue(templates[0][0].name == "login/login_mongodb.html") + + def test_get_json_loginpage(self): + """ + This method tests the call to the JSON login page. + It checks if the call is GET, status code is 200 and if the template + rendered is jsonLogin.html. + """ + with captured_templates(self.app) as templates: + response = self.test_client.get("/login/json") + self.assertEqual(response.status_code, 200) + self.assertTrue(templates[0][0].name == "login/login_json.html") + + def test_get_editorpage(self): + """This method tests the call to the editor page. + It checks if the call is GET, status code is 200 and if the template + rendered is editor.html. + """ + with captured_templates(self.app) as templates: + response = self.test_client.get("/editor?alias=test") + self.assertEqual(response.status_code, 200) + self.assertTrue(templates[0][0].name == "editor.html") + + def test_get_editorpage_invalid(self): + """This method tests the call to the editor page without required + query parameters. + It checks if the call is GET, status code is 404 and if the template + rendered is 404.html. + """ + with captured_templates(self.app) as templates: + response = self.test_client.get("/editor") + self.assertEqual(response.status_code, 404) + self.assertTrue(templates[0][0].name == "404.html") + response = self.test_client.get("/editor?alias=invalid") + self.assertEqual(response.status_code, 404) + self.assertTrue(templates[0][0].name == "404.html") + + def test_default_call(self): + """This method tests the default call to the API.""" + with captured_templates(self.app) as templates: + response = self.test_client.get("/") + self.assertEqual(response.status_code, 200) + self.assertTrue(templates[0][0].name == "index.html") + + def test_default_call_is_not_post(self): + """This method tests that the default call is not a POST.""" + + response = self.test_client.post("/") + self.assertEqual(response.status_code, 405) + + def test_get_categories(self): + """ + The methods tests if the category call returns the same categories as + the schema. + """ + + response = self.test_client.get("/categories") + post_response = self.test_client.post("/categories") + categories = [ + "workload", + "disk-image", + "binary", + "kernel", + "checkpoint", + "git", + "bootloader", + "file", + "directory", + "simpoint", + "simpoint-directory", + "resource", + "looppoint-pinpoint-csv", + "looppoint-json", + ] + self.assertEqual(post_response.status_code, 405) + self.assertEqual(response.status_code, 200) + returnedData = json.loads(response.data) + self.assertTrue(returnedData == categories) + + def test_get_schema(self): + """ + The methods tests if the schema call returns the same schema as the + schema file. + """ + + response = self.test_client.get("/schema") + post_response = self.test_client.post("/schema") + self.assertEqual(post_response.status_code, 405) + self.assertEqual(response.status_code, 200) + returnedData = json.loads(response.data) + schema = {} + schema = requests.get( + "https://resources.gem5.org/gem5-resources-schema.json" + ).json() + self.assertTrue(returnedData == schema) + + def test_insert(self): + """This method tests the insert method of the API.""" + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources/" + "tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + response = self.test_client.post( + "/insert", json={"resource": test_resource, "alias": self.alias} + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Inserted"}) + resource = self.collection.find({"id": "test-resource"}, {"_id": 0}) + + json_resource = json.loads(json_util.dumps(resource[0])) + self.assertTrue(json_resource == test_resource) + + def test_find_no_version(self): + """This method tests the find method of the API.""" + test_id = "test-resource" + test_resource_version = "1.0.0" + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources/" + "tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + self.collection.insert_one(test_resource.copy()) + response = self.test_client.post( + "/find", + json={"id": test_id, "resource_version": "", "alias": self.alias}, + ) + self.assertEqual(response.status_code, 200) + self.assertTrue(response.json == test_resource) + + def test_find_not_exist(self): + """This method tests the find method of the API.""" + test_id = "test-resource" + response = self.test_client.post( + "/find", + json={"id": test_id, "resource_version": "", "alias": self.alias}, + ) + self.assertEqual(response.status_code, 200) + self.assertTrue(response.json == {"exists": False}) + + def test_find_with_version(self): + """This method tests the find method of the API.""" + test_id = "test-resource" + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources/" + "tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + self.collection.insert_one(test_resource.copy()) + test_resource["resource_version"] = "1.0.1" + test_resource["description"] = "test-description2" + self.collection.insert_one(test_resource.copy()) + response = self.test_client.post( + "/find", + json={ + "id": test_id, + "resource_version": "1.0.1", + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + return_json = response.json + self.assertTrue(return_json["description"] == "test-description2") + self.assertTrue(return_json["resource_version"] == "1.0.1") + self.assertTrue(return_json == test_resource) + + def test_delete(self): + """This method tests the delete method of the API.""" + test_id = "test-resource" + test_version = "1.0.0" + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources/" + "tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + self.collection.insert_one(test_resource.copy()) + response = self.test_client.post( + "/delete", json={"resource": test_resource, "alias": self.alias} + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Deleted"}) + resource = self.collection.find({"id": "test-resource"}, {"_id": 0}) + json_resource = json.loads(json_util.dumps(resource)) + self.assertTrue(json_resource == []) + + def test_if_resource_exists_true(self): + """This method tests the checkExists method of the API.""" + test_id = "test-resource" + test_version = "1.0.0" + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources/" + "tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + self.collection.insert_one(test_resource.copy()) + response = self.test_client.post( + "/checkExists", + json={ + "id": test_id, + "resource_version": test_version, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"exists": True}) + + def test_if_resource_exists_false(self): + """This method tests the checkExists method of the API.""" + test_id = "test-resource" + test_version = "1.0.0" + response = self.test_client.post( + "/checkExists", + json={ + "id": test_id, + "resource_version": test_version, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"exists": False}) + + def test_get_resource_versions(self): + """This method tests the getResourceVersions method of the API.""" + test_id = "test-resource" + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources/" + "tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + self.collection.insert_one(test_resource.copy()) + test_resource["resource_version"] = "1.0.1" + test_resource["description"] = "test-description2" + self.collection.insert_one(test_resource.copy()) + response = self.test_client.post( + "/versions", json={"id": test_id, "alias": self.alias} + ) + return_json = json.loads(response.data) + self.assertEqual(response.status_code, 200) + self.assertEqual( + return_json, + [{"resource_version": "1.0.1"}, {"resource_version": "1.0.0"}], + ) + + def test_update_resource(self): + """This method tests the updateResource method of the API.""" + test_id = "test-resource" + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources/" + "tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + original_resource = test_resource.copy() + self.collection.insert_one(test_resource.copy()) + test_resource["description"] = "test-description2" + test_resource["example_usage"] = "test-usage2" + response = self.test_client.post( + "/update", + json={ + "original_resource": original_resource, + "resource": test_resource, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Updated"}) + resource = self.collection.find({"id": test_id}, {"_id": 0}) + json_resource = json.loads(json_util.dumps(resource)) + self.assertTrue(json_resource == [test_resource]) + + def test_keys_1(self): + """This method tests the keys method of the API.""" + response = self.test_client.post( + "/keys", json={"category": "simpoint", "id": "test-resource"} + ) + test_response = { + "category": "simpoint", + "id": "test-resource", + "author": [], + "description": "", + "license": "", + "source_url": "", + "tags": [], + "example_usage": "", + "gem5_versions": [], + "resource_version": "1.0.0", + "simpoint_interval": 0, + "warmup_interval": 0, + } + self.assertEqual(response.status_code, 200) + self.assertEqual(json.loads(response.data), test_response) + + def test_keys_2(self): + """This method tests the keys method of the API.""" + response = self.test_client.post( + "/keys", json={"category": "disk-image", "id": "test-resource"} + ) + test_response = { + "category": "disk-image", + "id": "test-resource", + "author": [], + "description": "", + "license": "", + "source_url": "", + "tags": [], + "example_usage": "", + "gem5_versions": [], + "resource_version": "1.0.0", + } + self.assertEqual(response.status_code, 200) + self.assertEqual(json.loads(response.data), test_response) + + def test_undo(self): + """This method tests the undo method of the API.""" + test_id = "test-resource" + test_resource = { + "category": "disk-image", + "id": "test-resource", + "author": [], + "description": "", + "license": "", + "source_url": "", + "tags": [], + "example_usage": "", + "gem5_versions": [], + "resource_version": "1.0.0", + } + original_resource = test_resource.copy() + # insert resource + response = self.test_client.post( + "/insert", json={"resource": test_resource, "alias": self.alias} + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Inserted"}) + # update resource + test_resource["description"] = "test-description2" + test_resource["example_usage"] = "test-usage2" + response = self.test_client.post( + "/update", + json={ + "original_resource": original_resource, + "resource": test_resource, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Updated"}) + # check if resource is updated + resource = self.collection.find({"id": test_id}, {"_id": 0}) + json_resource = json.loads(json_util.dumps(resource)) + self.assertTrue(json_resource == [test_resource]) + # undo update + response = self.test_client.post("/undo", json={"alias": self.alias}) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Undone"}) + # check if resource is back to original + resource = self.collection.find({"id": test_id}, {"_id": 0}) + json_resource = json.loads(json_util.dumps(resource)) + self.assertTrue(json_resource == [original_resource]) + + def test_redo(self): + """This method tests the undo method of the API.""" + test_id = "test-resource" + test_resource = { + "category": "disk-image", + "id": "test-resource", + "author": [], + "description": "", + "license": "", + "source_url": "", + "tags": [], + "example_usage": "", + "gem5_versions": [], + "resource_version": "1.0.0", + } + original_resource = test_resource.copy() + # insert resource + response = self.test_client.post( + "/insert", json={"resource": test_resource, "alias": self.alias} + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Inserted"}) + # update resource + test_resource["description"] = "test-description2" + test_resource["example_usage"] = "test-usage2" + response = self.test_client.post( + "/update", + json={ + "original_resource": original_resource, + "resource": test_resource, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Updated"}) + # check if resource is updated + resource = self.collection.find({"id": test_id}, {"_id": 0}) + json_resource = json.loads(json_util.dumps(resource)) + self.assertTrue(json_resource == [test_resource]) + # undo update + response = self.test_client.post("/undo", json={"alias": self.alias}) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Undone"}) + # check if resource is back to original + resource = self.collection.find({"id": test_id}, {"_id": 0}) + json_resource = json.loads(json_util.dumps(resource)) + self.assertTrue(json_resource == [original_resource]) + # redo update + response = self.test_client.post("/redo", json={"alias": self.alias}) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Redone"}) + # check if resource is updated again + resource = self.collection.find({"id": test_id}, {"_id": 0}) + json_resource = json.loads(json_util.dumps(resource)) + self.assertTrue(json_resource == [test_resource]) + + def test_invalid_alias(self): + test_id = "test-resource" + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources/" + "tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + alias = "invalid" + response = self.test_client.post( + "/insert", json={"resource": test_resource, "alias": alias} + ) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json, {"error": "database not found"}) + response = self.test_client.post( + "/find", + json={"id": test_id, "resource_version": "", "alias": alias}, + ) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json, {"error": "database not found"}) + response = self.test_client.post( + "/delete", json={"resource": test_resource, "alias": alias} + ) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json, {"error": "database not found"}) + response = self.test_client.post( + "/checkExists", + json={"id": test_id, "resource_version": "", "alias": alias}, + ) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json, {"error": "database not found"}) + response = self.test_client.post( + "/versions", json={"id": test_id, "alias": alias} + ) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json, {"error": "database not found"}) + response = self.test_client.post( + "/update", + json={ + "original_resource": test_resource, + "resource": test_resource, + "alias": alias, + }, + ) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json, {"error": "database not found"}) + response = self.test_client.post("/undo", json={"alias": alias}) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json, {"error": "database not found"}) + response = self.test_client.post("/redo", json={"alias": alias}) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json, {"error": "database not found"}) + response = self.test_client.post( + "/getRevisionStatus", json={"alias": alias} + ) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json, {"error": "database not found"}) + response = self.test_client.post("/saveSession", json={"alias": alias}) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json, {"error": "database not found"}) + + def test_get_revision_status_valid(self): + response = self.test_client.post( + "/getRevisionStatus", json={"alias": self.alias} + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"undo": 1, "redo": 1}) + + @patch.object( + MongoDBClient, + "_get_database", + return_value=mongomock.MongoClient().db.collection, + ) + def test_save_session_load_session(self, mock_get_database): + password = "test" + expected_session = server.databases["test"].save_session() + response = self.test_client.post( + "/saveSession", json={"alias": self.alias, "password": password} + ) + self.assertEqual(response.status_code, 200) + + response = self.test_client.post( + "/loadSession", + json={ + "alias": self.alias, + "session": response.json["ciphertext"], + "password": password, + }, + ) + self.assertEqual(response.status_code, 302) + self.assertEqual( + expected_session, server.databases[self.alias].save_session() + ) + + def test_logout(self): + response = self.test_client.post("/logout", json={"alias": self.alias}) + self.assertEqual(response.status_code, 302) + self.assertNotIn(self.alias, server.databases) diff --git a/util/gem5-resources-manager/test/comprehensive_test.py b/util/gem5-resources-manager/test/comprehensive_test.py new file mode 100644 index 0000000000..f878b0c347 --- /dev/null +++ b/util/gem5-resources-manager/test/comprehensive_test.py @@ -0,0 +1,408 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import copy +import json +import unittest +from unittest.mock import patch + +import mongomock +from api.mongo_client import MongoDBClient +from bson import json_util +from server import app + + +class TestComprehensive(unittest.TestCase): + @patch.object( + MongoDBClient, + "_get_database", + return_value=mongomock.MongoClient().db.collection, + ) + def setUp(self, mock_get_database): + """This method sets up the test environment.""" + self.ctx = app.app_context() + self.ctx.push() + self.app = app + self.test_client = app.test_client() + self.alias = "test" + objects = [] + with open("./test/refs/resources.json", "rb") as f: + objects = json.loads(f.read(), object_hook=json_util.object_hook) + self.collection = mock_get_database() + for obj in objects: + self.collection.insert_one(obj) + + self.test_client.post( + "/validateMongoDB", + json={ + "uri": "mongodb://localhost:27017", + "database": "test", + "collection": "test", + "alias": self.alias, + }, + ) + + def tearDown(self): + """This method tears down the test environment.""" + self.collection.drop() + self.ctx.pop() + + def test_insert_find_update_find(self): + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources/" + "tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + original_resource = test_resource.copy() + test_id = test_resource["id"] + test_resource_version = test_resource["resource_version"] + # insert resource + response = self.test_client.post( + "/insert", json={"resource": test_resource, "alias": self.alias} + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Inserted"}) + # find resource + response = self.test_client.post( + "/find", + json={ + "id": test_id, + "resource_version": test_resource_version, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertTrue(response.json == test_resource) + + # update resource + test_resource["description"] = "test-description-2" + test_resource["author"].append("test-author-2") + response = self.test_client.post( + "/update", + json={ + "original_resource": original_resource, + "resource": test_resource, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Updated"}) + # find resource + response = self.test_client.post( + "/find", + json={ + "id": test_id, + "resource_version": test_resource_version, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertTrue(response.json == test_resource) + + def test_find_new_insert(self): + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources/" + "tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + test_id = test_resource["id"] + test_resource_version = test_resource["resource_version"] + # find resource + response = self.test_client.post( + "/find", + json={ + "id": test_id, + "resource_version": test_resource_version, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"exists": False}) + # insert resource + response = self.test_client.post( + "/insert", json={"resource": test_resource, "alias": self.alias} + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Inserted"}) + # find resource + response = self.test_client.post( + "/find", + json={ + "id": test_id, + "resource_version": test_resource_version, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertTrue(response.json == test_resource) + + def test_insert_find_new_version_find_older(self): + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources/" + "tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + test_id = test_resource["id"] + test_resource_version = test_resource["resource_version"] + # insert resource + response = self.test_client.post( + "/insert", json={"resource": test_resource, "alias": self.alias} + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Inserted"}) + # find resource + response = self.test_client.post( + "/find", + json={ + "id": test_id, + "resource_version": test_resource_version, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertTrue(response.json == test_resource) + + # add new version + test_resource_new_version = copy.deepcopy(test_resource) + test_resource_new_version["description"] = "test-description-2" + test_resource_new_version["author"].append("test-author-2") + test_resource_new_version["resource_version"] = "1.0.1" + + response = self.test_client.post( + "/insert", + json={"resource": test_resource_new_version, "alias": self.alias}, + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Inserted"}) + + # get resource versions + response = self.test_client.post( + "/versions", json={"id": test_id, "alias": self.alias} + ) + return_json = json.loads(response.data) + self.assertEqual(response.status_code, 200) + self.assertEqual( + return_json, + [{"resource_version": "1.0.1"}, {"resource_version": "1.0.0"}], + ) + + resource_version = return_json[1]["resource_version"] + # find older version + response = self.test_client.post( + "/find", + json={ + "id": test_id, + "resource_version": resource_version, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertTrue(response.json == test_resource) + + def test_find_add_new_version_delete_older(self): + test_resource = { + "category": "binary", + "id": "binary-example", + "description": "binary-example documentation.", + "architecture": "ARM", + "is_zipped": False, + "md5sum": "71b2cb004fe2cda4556f0b1a38638af6", + "url": ( + "http://dist.gem5.org/dist/develop/" + "test-progs/hello/bin/arm/linux/hello64-static" + ), + "source": "src/simple", + "resource_version": "1.0.0", + "gem5_versions": ["23.0"], + } + test_id = test_resource["id"] + test_resource_version = test_resource["resource_version"] + # find resource + response = self.test_client.post( + "/find", + json={ + "id": test_id, + "resource_version": test_resource_version, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertTrue(response.json == test_resource) + + # add new version + test_resource_new_version = copy.deepcopy(test_resource) + test_resource_new_version["description"] = "test-description-2" + test_resource_new_version["resource_version"] = "1.0.1" + + response = self.test_client.post( + "/insert", + json={"resource": test_resource_new_version, "alias": self.alias}, + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Inserted"}) + + # get resource versions + response = self.test_client.post( + "/versions", json={"id": test_id, "alias": self.alias} + ) + return_json = json.loads(response.data) + self.assertEqual(response.status_code, 200) + self.assertEqual( + return_json, + [{"resource_version": "1.0.1"}, {"resource_version": "1.0.0"}], + ) + # delete older version + response = self.test_client.post( + "/delete", json={"resource": test_resource, "alias": self.alias} + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Deleted"}) + + # get resource versions + response = self.test_client.post( + "/versions", json={"id": test_id, "alias": self.alias} + ) + return_json = json.loads(response.data) + self.assertEqual(response.status_code, 200) + self.assertEqual(return_json, [{"resource_version": "1.0.1"}]) + + def test_find_add_new_version_update_older(self): + test_resource = { + "category": "binary", + "id": "binary-example", + "description": "binary-example documentation.", + "architecture": "ARM", + "is_zipped": False, + "md5sum": "71b2cb004fe2cda4556f0b1a38638af6", + "url": ( + "http://dist.gem5.org/dist/develop/" + "test-progs/hello/bin/arm/linux/hello64-static" + ), + "source": "src/simple", + "resource_version": "1.0.0", + "gem5_versions": ["23.0"], + } + original_resource = test_resource.copy() + test_id = test_resource["id"] + test_resource_version = test_resource["resource_version"] + # find resource + response = self.test_client.post( + "/find", + json={ + "id": test_id, + "resource_version": test_resource_version, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertTrue(response.json == test_resource) + + # add new version + test_resource_new_version = copy.deepcopy(test_resource) + test_resource_new_version["description"] = "test-description-2" + test_resource_new_version["resource_version"] = "1.0.1" + + response = self.test_client.post( + "/insert", + json={"resource": test_resource_new_version, "alias": self.alias}, + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Inserted"}) + + # get resource versions + response = self.test_client.post( + "/versions", json={"id": test_id, "alias": self.alias} + ) + return_json = json.loads(response.data) + self.assertEqual(response.status_code, 200) + self.assertEqual( + return_json, + [{"resource_version": "1.0.1"}, {"resource_version": "1.0.0"}], + ) + + resource_version = return_json[1]["resource_version"] + + # update older version + test_resource["description"] = "test-description-3" + + response = self.test_client.post( + "/update", + json={ + "original_resource": original_resource, + "resource": test_resource, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json, {"status": "Updated"}) + + # find resource + response = self.test_client.post( + "/find", + json={ + "id": test_id, + "resource_version": resource_version, + "alias": self.alias, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertTrue(response.json == test_resource) diff --git a/util/gem5-resources-manager/test/json_client_test.py b/util/gem5-resources-manager/test/json_client_test.py new file mode 100644 index 0000000000..963743c4ac --- /dev/null +++ b/util/gem5-resources-manager/test/json_client_test.py @@ -0,0 +1,262 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import json +import unittest +from pathlib import Path +from unittest.mock import patch + +from api.json_client import JSONClient +from bson import json_util +from server import app + + +def get_json(): + with open("test/refs/test_json.json") as f: + jsonFile = f.read() + return json.loads(jsonFile) + + +def mockinit(self, file_path): + self.file_path = Path("test/refs/") / file_path + with open(self.file_path) as f: + self.resources = json.load(f) + + +class TestJson(unittest.TestCase): + @classmethod + def setUpClass(cls): + with open("./test/refs/resources.json", "rb") as f: + jsonFile = f.read() + with open("./test/refs/test_json.json", "wb") as f: + f.write(jsonFile) + + @classmethod + def tearDownClass(cls): + Path("./test/refs/test_json.json").unlink() + + @patch.object(JSONClient, "__init__", mockinit) + def setUp(self): + """This method sets up the test environment.""" + with open("./test/refs/test_json.json", "rb") as f: + jsonFile = f.read() + self.original_json = json.loads(jsonFile) + self.json_client = JSONClient("test_json.json") + + def tearDown(self): + """This method tears down the test environment.""" + with open("./test/refs/test_json.json", "w") as f: + json.dump(self.original_json, f, indent=4) + + def test_insertResource(self): + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources/" + "tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + response = self.json_client.insert_resource(test_resource) + self.assertEqual(response, {"status": "Inserted"}) + json_data = get_json() + self.assertNotEqual(json_data, self.original_json) + self.assertIn(test_resource, json_data) + + def test_insertResource_duplicate(self): + test_resource = { + "category": "diskimage", + "id": "disk-image-example", + "description": "disk-image documentation.", + "architecture": "X86", + "is_zipped": True, + "md5sum": "90e363abf0ddf22eefa2c7c5c9391c49", + "url": ( + "http://dist.gem5.org/dist/develop/images" + "/x86/ubuntu-18-04/x86-ubuntu.img.gz" + ), + "source": "src/x86-ubuntu", + "root_partition": "1", + "resource_version": "1.0.0", + "gem5_versions": ["23.0"], + } + response = self.json_client.insert_resource(test_resource) + self.assertEqual(response, {"status": "Resource already exists"}) + + def test_find_no_version(self): + expected_response = { + "category": "diskimage", + "id": "disk-image-example", + "description": "disk-image documentation.", + "architecture": "X86", + "is_zipped": True, + "md5sum": "90e363abf0ddf22eefa2c7c5c9391c49", + "url": ( + "http://dist.gem5.org/dist/develop/images" + "/x86/ubuntu-18-04/x86-ubuntu.img.gz" + ), + "source": "src/x86-ubuntu", + "root_partition": "1", + "resource_version": "1.0.0", + "gem5_versions": ["23.0"], + } + response = self.json_client.find_resource( + {"id": expected_response["id"]} + ) + self.assertEqual(response, expected_response) + + def test_find_with_version(self): + expected_response = { + "category": "kernel", + "id": "kernel-example", + "description": "kernel-example documentation.", + "architecture": "RISCV", + "is_zipped": False, + "md5sum": "60a53c7d47d7057436bf4b9df707a841", + "url": ( + "http://dist.gem5.org/dist/develop" + "/kernels/x86/static/vmlinux-5.4.49" + ), + "source": "src/linux-kernel", + "resource_version": "1.0.0", + "gem5_versions": ["23.0"], + } + response = self.json_client.find_resource( + { + "id": expected_response["id"], + "resource_version": expected_response["resource_version"], + } + ) + self.assertEqual(response, expected_response) + + def test_find_not_found(self): + response = self.json_client.find_resource({"id": "not-found"}) + self.assertEqual(response, {"exists": False}) + + def test_deleteResource(self): + deleted_resource = { + "category": "diskimage", + "id": "disk-image-example", + "description": "disk-image documentation.", + "architecture": "X86", + "is_zipped": True, + "md5sum": "90e363abf0ddf22eefa2c7c5c9391c49", + "url": ( + "http://dist.gem5.org/dist/develop/" + "images/x86/ubuntu-18-04/x86-ubuntu.img.gz" + ), + "source": "src/x86-ubuntu", + "root_partition": "1", + "resource_version": "1.0.0", + "gem5_versions": ["23.0"], + } + response = self.json_client.delete_resource( + { + "id": deleted_resource["id"], + "resource_version": deleted_resource["resource_version"], + } + ) + self.assertEqual(response, {"status": "Deleted"}) + json_data = get_json() + self.assertNotEqual(json_data, self.original_json) + self.assertNotIn(deleted_resource, json_data) + + def test_updateResource(self): + updated_resource = { + "category": "diskimage", + "id": "disk-image-example", + "description": "disk-image documentation.", + "architecture": "X86", + "is_zipped": True, + "md5sum": "90e363abf0ddf22eefa2c7c5c9391c49", + "url": ( + "http://dist.gem5.org/dist/develop/images" + "/x86/ubuntu-18-04/x86-ubuntu.img.gz" + ), + "source": "src/x86-ubuntu", + "root_partition": "1", + "resource_version": "1.0.0", + "gem5_versions": ["23.0"], + } + original_resource = { + "category": "diskimage", + "id": "disk-image-example", + "description": "disk-image documentation.", + "architecture": "X86", + "is_zipped": True, + "md5sum": "90e363abf0ddf22eefa2c7c5c9391c49", + "url": ( + "http://dist.gem5.org/dist/develop/" + "images/x86/ubuntu-18-04/x86-ubuntu.img.gz" + ), + "source": "src/x86-ubuntu", + "root_partition": "1", + "resource_version": "1.0.0", + "gem5_versions": ["23.0"], + } + response = self.json_client.update_resource( + { + "original_resource": original_resource, + "resource": updated_resource, + } + ) + self.assertEqual(response, {"status": "Updated"}) + json_data = get_json() + self.assertNotEqual(json_data, self.original_json) + self.assertIn(updated_resource, json_data) + + def test_getVersions(self): + resource_id = "kernel-example" + response = self.json_client.get_versions({"id": resource_id}) + self.assertEqual( + response, + [{"resource_version": "2.0.0"}, {"resource_version": "1.0.0"}], + ) + + def test_checkResourceExists_True(self): + resource_id = "kernel-example" + resource_version = "1.0.0" + response = self.json_client.check_resource_exists( + {"id": resource_id, "resource_version": resource_version} + ) + self.assertEqual(response, {"exists": True}) + + def test_checkResourceExists_False(self): + resource_id = "kernel-example" + resource_version = "3.0.0" + response = self.json_client.check_resource_exists( + {"id": resource_id, "resource_version": resource_version} + ) + self.assertEqual(response, {"exists": False}) diff --git a/util/gem5-resources-manager/test/mongo_client_test.py b/util/gem5-resources-manager/test/mongo_client_test.py new file mode 100644 index 0000000000..8322fe7507 --- /dev/null +++ b/util/gem5-resources-manager/test/mongo_client_test.py @@ -0,0 +1,285 @@ +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import json +import unittest +from unittest.mock import patch + +import mongomock +from api.mongo_client import MongoDBClient +from bson import json_util +from server import ( + app, + databases, +) + + +class TestApi(unittest.TestCase): + """This is a test class that tests the API.""" + + API_URL = "http://127.0.0.1:5000" + + @patch.object( + MongoDBClient, + "_get_database", + return_value=mongomock.MongoClient().db.collection, + ) + def setUp(self, mock_get_database): + """This method sets up the test environment.""" + objects = [] + with open("./test/refs/resources.json", "rb") as f: + objects = json.loads(f.read(), object_hook=json_util.object_hook) + self.collection = mock_get_database() + for obj in objects: + self.collection.insert_one(obj) + self.mongo_client = MongoDBClient( + "mongodb://localhost:27017", "test", "test" + ) + + def tearDown(self): + """This method tears down the test environment.""" + self.collection.drop() + + def test_insertResource(self): + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources/" + "tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + ret_value = self.mongo_client.insert_resource(test_resource) + self.assertEqual(ret_value, {"status": "Inserted"}) + self.assertEqual( + self.collection.find({"id": "test-resource"})[0], test_resource + ) + self.collection.delete_one({"id": "test-resource"}) + + def test_insertResource_duplicate(self): + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources/" + "tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + self.collection.insert_one(test_resource) + ret_value = self.mongo_client.insert_resource(test_resource) + self.assertEqual(ret_value, {"status": "Resource already exists"}) + + def test_findResource_no_version(self): + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources" + "/tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + self.collection.insert_one(test_resource.copy()) + ret_value = self.mongo_client.find_resource({"id": "test-resource"}) + self.assertEqual(ret_value, test_resource) + self.collection.delete_one({"id": "test-resource"}) + + def test_findResource_with_version(self): + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources" + "/tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + self.collection.insert_one(test_resource.copy()) + test_resource["resource_version"] = "2.0.0" + test_resource["description"] = "test-description2" + self.collection.insert_one(test_resource.copy()) + ret_value = self.mongo_client.find_resource( + {"id": "test-resource", "resource_version": "2.0.0"} + ) + self.assertEqual(ret_value, test_resource) + + def test_findResource_not_found(self): + ret_value = self.mongo_client.find_resource({"id": "test-resource"}) + self.assertEqual(ret_value, {"exists": False}) + + def test_deleteResource(self): + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources" + "/tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + self.collection.insert_one(test_resource.copy()) + ret_value = self.mongo_client.delete_resource( + {"id": "test-resource", "resource_version": "1.0.0"} + ) + self.assertEqual(ret_value, {"status": "Deleted"}) + + self.assertEqual( + json.loads( + json_util.dumps(self.collection.find({"id": "test-resource"})) + ), + [], + ) + + def test_updateResource(self): + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources" + "/tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + original_resource = test_resource.copy() + self.collection.insert_one(test_resource.copy()) + test_resource["author"].append("test-author2") + test_resource["description"] = "test-description2" + ret_value = self.mongo_client.update_resource( + {"original_resource": original_resource, "resource": test_resource} + ) + self.assertEqual(ret_value, {"status": "Updated"}) + self.assertEqual( + self.collection.find({"id": "test-resource"}, {"_id": 0})[0], + test_resource, + ) + + def test_checkResourceExists(self): + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources" + "/tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + self.collection.insert_one(test_resource.copy()) + ret_value = self.mongo_client.check_resource_exists( + {"id": "test-resource", "resource_version": "1.0.0"} + ) + self.assertEqual(ret_value, {"exists": True}) + + def test_checkResourceExists_not_found(self): + ret_value = self.mongo_client.check_resource_exists( + {"id": "test-resource", "resource_version": "1.0.0"} + ) + self.assertEqual(ret_value, {"exists": False}) + + def test_getVersion(self): + test_resource = { + "category": "diskimage", + "id": "test-resource", + "author": ["test-author"], + "description": "test-description", + "license": "test-license", + "source_url": ( + "https://github.com/gem5/gem5-resources" + "/tree/develop/src/x86-ubuntu" + ), + "tags": ["test-tag", "test-tag2"], + "example_usage": " test-usage", + "gem5_versions": [ + "22.1", + ], + "resource_version": "1.0.0", + } + self.collection.insert_one(test_resource.copy()) + test_resource["resource_version"] = "2.0.0" + test_resource["description"] = "test-description2" + self.collection.insert_one(test_resource.copy()) + ret_value = self.mongo_client.get_versions({"id": "test-resource"}) + self.assertEqual( + ret_value, + [{"resource_version": "2.0.0"}, {"resource_version": "1.0.0"}], + ) diff --git a/util/gem5-resources-manager/test/refs/resources.json b/util/gem5-resources-manager/test/refs/resources.json new file mode 100644 index 0000000000..614f8dc764 --- /dev/null +++ b/util/gem5-resources-manager/test/refs/resources.json @@ -0,0 +1,196 @@ +[ + { + "category": "kernel", + "id": "kernel-example", + "description": "kernel-example documentation.", + "architecture": "RISCV", + "is_zipped": false, + "md5sum": "60a53c7d47d7057436bf4b9df707a841", + "url": "http://dist.gem5.org/dist/develop/kernels/x86/static/vmlinux-5.4.49", + "source": "src/linux-kernel", + "resource_version": "1.0.0", + "gem5_versions": [ + "23.0" + ] + }, + { + "category": "kernel", + "id": "kernel-example", + "description": "kernel-example documentation 2.", + "architecture": "RISCV", + "is_zipped": false, + "md5sum": "60a53c7d47d7057436bf4b9df707a841", + "url": "http://dist.gem5.org/dist/develop/kernels/x86/static/vmlinux-5.4.49", + "source": "src/linux-kernel", + "resource_version": "2.0.0", + "gem5_versions": [ + "23.0" + ] + }, + { + "category": "diskimage", + "id": "disk-image-example", + "description": "disk-image documentation.", + "architecture": "X86", + "is_zipped": true, + "md5sum": "90e363abf0ddf22eefa2c7c5c9391c49", + "url": "http://dist.gem5.org/dist/develop/images/x86/ubuntu-18-04/x86-ubuntu.img.gz", + "source": "src/x86-ubuntu", + "root_partition": "1", + "resource_version": "1.0.0", + "gem5_versions": [ + "23.0" + ] + }, + { + "category": "binary", + "id": "binary-example", + "description": "binary-example documentation.", + "architecture": "ARM", + "is_zipped": false, + "md5sum": "71b2cb004fe2cda4556f0b1a38638af6", + "url": "http://dist.gem5.org/dist/develop/test-progs/hello/bin/arm/linux/hello64-static", + "source": "src/simple", + "resource_version": "1.0.0", + "gem5_versions": [ + "23.0" + ] + + }, + { + "category": "bootloader", + "id": "bootloader-example", + "description": "bootloader documentation.", + "is_zipped": false, + "md5sum": "71b2cb004fe2cda4556f0b1a38638af6", + "url": "http://dist.gem5.org/dist/develop/test-progs/hello/bin/arm/linux/hello64-static", + "resource_version": "1.0.0", + "gem5_versions": [ + "23.0" + ] + }, + { + "category": "checkpoint", + "id": "checkpoint-example", + "description": "checkpoint-example documentation.", + "architecture": "RISCV", + "is_zipped": false, + "md5sum": "3a57c1bb1077176c4587b8a3bf4f8ace", + "source": null, + "is_tar_archive": true, + "url": "http://dist.gem5.org/dist/develop/checkpoints/riscv-hello-example-checkpoint.tar", + "resource_version": "1.0.0", + "gem5_versions": [ + "23.0" + ] + }, + { + "category": "git", + "id": "git-example", + "description": null, + "is_zipped": false, + "is_tar_archive": true, + "md5sum": "71b2cb004fe2cda4556f0b1a38638af6", + "url": "http://dist.gem5.org/dist/develop/checkpoints/riscv-hello-example-checkpoint.tar", + "resource_version": "1.0.0", + "gem5_versions": [ + "23.0" + ] + }, + { + "category": "file", + "id": "file-example", + "description": null, + "is_zipped": false, + "md5sum": "71b2cb004fe2cda4556f0b1a38638af6", + "url": "http://dist.gem5.org/dist/develop/checkpoints/riscv-hello-example-checkpoint.tar", + "source": null, + "resource_version": "1.0.0", + "gem5_versions": [ + "23.0" + ] + }, + { + "category": "directory", + "id": "directory-example", + "description": "directory-example documentation.", + "is_zipped": false, + "md5sum": "3a57c1bb1077176c4587b8a3bf4f8ace", + "source": null, + "is_tar_archive": true, + "url": "http://dist.gem5.org/dist/develop/checkpoints/riscv-hello-example-checkpoint.tar", + "resource_version": "1.0.0", + "gem5_versions": [ + "23.0" + ] + }, + { + "category": "simpoint-directory", + "id": "simpoint-directory-example", + "description": "simpoint directory documentation.", + "is_zipped": false, + "md5sum": "3fcffe3956c8a95e3fb82e232e2b41fb", + "source": null, + "is_tar_archive": true, + "url": "http://dist.gem5.org/dist/develop/simpoints/x86-print-this-15000-simpoints-20221013.tar", + "simpoint_interval": 1000000, + "warmup_interval": 1000000, + "simpoint_file": "simpoint.simpt", + "weight_file": "simpoint.weight", + "workload_name": "Example Workload", + "resource_version": "1.0.0", + "gem5_versions": [ + "23.0" + ] + }, + { + "category": "simpoint", + "id": "simpoint-example", + "description": "simpoint documentation.", + "simpoint_interval": 1000000, + "warmup_interval": 23445, + "simpoint_list": [ + 2, + 3, + 4, + 15 + ], + "weight_list": [ + 0.1, + 0.2, + 0.4, + 0.3 + ], + "resource_version": "1.0.0", + "gem5_versions": [ + "23.0" + ] + }, + { + "category": "looppoint-pinpoint-csv", + "id": "looppoint-pinpoint-csv-resource", + "description": "A looppoint pinpoints csv file.", + "is_zipped": false, + "md5sum": "199ab22dd463dc70ee2d034bfe045082", + "url": "http://dist.gem5.org/dist/develop/pinpoints/x86-matrix-multiply-omp-100-8-global-pinpoints-20230127", + "source": null, + "resource_version": "1.0.0", + "gem5_versions": [ + "23.0" + ] + }, + { + "category": "looppoint-json", + "id": "looppoint-json-restore-resource-region-1", + "description": "A looppoint json file resource.", + "is_zipped": false, + "region_id": "1", + "md5sum": "a71ed64908b082ea619b26b940a643c1", + "url": "http://dist.gem5.org/dist/develop/looppoints/x86-matrix-multiply-omp-100-8-looppoint-json-20230128", + "source": null, + "resource_version": "1.0.0", + "gem5_versions": [ + "23.0" + ] + } +] diff --git a/tests/configs/simple-atomic-mp-ruby.py b/util/gem5-stubgen.py similarity index 58% rename from tests/configs/simple-atomic-mp-ruby.py rename to util/gem5-stubgen.py index e3ac279022..70d7c3ac64 100644 --- a/tests/configs/simple-atomic-mp-ruby.py +++ b/util/gem5-stubgen.py @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2007 The Regents of The University of Michigan +# Copyright (c) 2023 The Regents of the University of California # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -24,43 +24,43 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import m5 -from m5.objects import * - -nb_cores = 4 -cpus = [AtomicSimpleCPU(cpu_id=i) for i in range(nb_cores)] - -import ruby_config - -ruby_memory = ruby_config.generate("TwoLevel_SplitL1UnifiedL2.rb", nb_cores) - -# system simulated -system = System( - cpu=cpus, - physmem=ruby_memory, - membus=SystemXBar(), - clk_domain=SrcClockDomain(clock="1GHz"), +from mypy.stubgen import ( + generate_stubs, + parse_options, ) -# Create a seperate clock domain for components that should run at -# CPUs frequency -system.cpu.clk_domain = SrcClockDomain(clock="2GHz") +""" +This allows us to generate stubs for the modules in gem5. The output will be +a "typings" directory which can be used by Pylance (Python IntelliSense) to +infer typings in Visual Studio Code. -# add L1 caches -for cpu in cpus: - cpu.connectBus(system.membus) - # All cpus are associated with cpu_clk_domain - cpu.clk_domain = system.cpu_clk_domain +Note: A "typings" directory in the root of the workspace is the default +location for Pylance to look for typings. This can be changed via +`python.analysis.stubPath` in "settings.json". -# connect memory to membus -system.physmem.port = system.membus.mem_side_ports +Usage +===== -# Connect the system port for loading of binaries etc -system.system_port = system.membus.cpu_side_ports +```sh +pip3 install -r requirements.txt +scons build/ALL/gem5.opt -j$(nproc) +./build/ALL/gem5.opt util/gem5-stubgen.py +``` -# ----------------------- -# run simulation -# ----------------------- +""" -root = Root(full_system=False, system=system) -root.system.mem_mode = "atomic" +if __name__ == "__m5_main__": + import m5 + + # get a list of all modules exported by gem5 + modules = m5.__spec__.loader_state + + options = parse_options( + ("--module " + " --module ".join(modules)).split(" ") + + ["--output", "typings"] + ) + generate_stubs(options) + +if __name__ == "__main__": + print("Error: This script is meant to be run with the gem5 binary") + exit(1) diff --git a/util/gem5art/artifact/gem5art/artifact/__init__.py b/util/gem5art/artifact/gem5art/artifact/__init__.py index 7e026d5761..c15c4008e8 100644 --- a/util/gem5art/artifact/gem5art/artifact/__init__.py +++ b/util/gem5art/artifact/gem5art/artifact/__init__.py @@ -26,14 +26,14 @@ """This is the gem5 artifact package""" +from ._artifactdb import getDBConnection from .artifact import Artifact from .common_queries import ( getByName, getDiskImages, - getLinuxBinaries, getgem5Binaries, + getLinuxBinaries, ) -from ._artifactdb import getDBConnection __all__ = [ "Artifact", diff --git a/util/gem5art/artifact/gem5art/artifact/_artifactdb.py b/util/gem5art/artifact/gem5art/artifact/_artifactdb.py index 16d35e86e8..428936075c 100644 --- a/util/gem5art/artifact/gem5art/artifact/_artifactdb.py +++ b/util/gem5art/artifact/gem5art/artifact/_artifactdb.py @@ -34,14 +34,24 @@ artifacts stored in the database. Some common queries can be found in common_queries.py """ -from abc import ABC, abstractmethod - import copy import json import os -from pathlib import Path import shutil -from typing import Any, Dict, Iterable, Union, Type, List, Tuple +from abc import ( + ABC, + abstractmethod, +) +from pathlib import Path +from typing import ( + Any, + Dict, + Iterable, + List, + Tuple, + Type, + Union, +) from urllib.parse import urlparse from uuid import UUID @@ -185,22 +195,21 @@ class ArtifactMongoDB(ArtifactDB): def searchByName(self, name: str, limit: int) -> Iterable[Dict[str, Any]]: """Returns an iterable of all artifacts in the database that match some name.""" - for d in self.artifacts.find({"name": name}, limit=limit): - yield d + yield from self.artifacts.find({"name": name}, limit=limit) def searchByType(self, typ: str, limit: int) -> Iterable[Dict[str, Any]]: """Returns an iterable of all artifacts in the database that match some type.""" - for d in self.artifacts.find({"type": typ}, limit=limit): - yield d + yield from self.artifacts.find({"type": typ}, limit=limit) def searchByNameType( self, name: str, typ: str, limit: int ) -> Iterable[Dict[str, Any]]: """Returns an iterable of all artifacts in the database that match some name and type.""" - for d in self.artifacts.find({"type": typ, "name": name}, limit=limit): - yield d + yield from self.artifacts.find( + {"type": typ, "name": name}, limit=limit + ) def searchByLikeNameType( self, name: str, typ: str, limit: int @@ -211,8 +220,7 @@ class ArtifactMongoDB(ArtifactDB): data = self.artifacts.find( {"type": typ, "name": {"$regex": f"{name}"}}, limit=limit ) - for d in data: - yield d + yield from data class ArtifactFileDB(ArtifactDB): @@ -318,7 +326,7 @@ class ArtifactFileDB(ArtifactDB): uuid_mapping: Dict[str, Dict[str, str]] = {} hash_mapping: Dict[str, List[str]] = {} if json_file.exists(): - with open(json_file, "r") as f: + with open(json_file) as f: j = json.load(f) for an_artifact in j: the_uuid = an_artifact["_id"] diff --git a/util/gem5art/artifact/gem5art/artifact/artifact.py b/util/gem5art/artifact/gem5art/artifact/artifact.py index b71369c689..177842f591 100644 --- a/util/gem5art/artifact/gem5art/artifact/artifact.py +++ b/util/gem5art/artifact/gem5art/artifact/artifact.py @@ -28,14 +28,22 @@ """ import hashlib -from inspect import cleandoc import json -from pathlib import Path import subprocess import time -from typing import Any, Dict, List, Union, Optional -from uuid import UUID, uuid4 -import json +from inspect import cleandoc +from pathlib import Path +from typing import ( + Any, + Dict, + List, + Optional, + Union, +) +from uuid import ( + UUID, + uuid4, +) from ._artifactdb import getDBConnection @@ -166,7 +174,6 @@ class Artifact: version: str = "", **kwargs: str, ) -> "Artifact": - """Constructs a new artifact without using the database. Different from registerArtifact(), this method won't use database. diff --git a/util/gem5art/artifact/setup.py b/util/gem5art/artifact/setup.py index 78247eb16d..7c1a1d8851 100755 --- a/util/gem5art/artifact/setup.py +++ b/util/gem5art/artifact/setup.py @@ -28,8 +28,11 @@ from os.path import join from pathlib import Path -from setuptools import setup, find_namespace_packages +from setuptools import ( + find_namespace_packages, + setup, +) with open(Path(__file__).parent / "README.md", encoding="utf-8") as f: long_description = f.read() diff --git a/util/gem5art/artifact/tests/test_artifact.py b/util/gem5art/artifact/tests/test_artifact.py index af6f8ae75b..c1578bda68 100644 --- a/util/gem5art/artifact/tests/test_artifact.py +++ b/util/gem5art/artifact/tests/test_artifact.py @@ -27,14 +27,20 @@ """Tests for the Artifact object and associated functions""" import hashlib -from pathlib import Path -import unittest -from uuid import uuid4, UUID -import sys import io +import sys +import unittest +from pathlib import Path +from uuid import ( + UUID, + uuid4, +) from gem5art import artifact -from gem5art.artifact._artifactdb import ArtifactDB, getDBConnection +from gem5art.artifact._artifactdb import ( + ArtifactDB, + getDBConnection, +) class MockDB(ArtifactDB): @@ -85,7 +91,7 @@ class TestGit(unittest.TestCase): def test_keys(self): git = artifact.artifact.getGit(Path(".")) self.assertSetEqual( - set(git.keys()), set(["origin", "hash", "name"]), "git keys wrong" + set(git.keys()), {"origin", "hash", "name"}, "git keys wrong" ) def test_origin(self): @@ -205,7 +211,6 @@ class TestArtifactSimilarity(unittest.TestCase): class TestRegisterArtifact(unittest.TestCase): def setUp(self): - # Create and register an artifact self.testArtifactA = artifact.Artifact.registerArtifact( name="artifact-A", diff --git a/util/gem5art/artifact/tests/test_filedb.py b/util/gem5art/artifact/tests/test_filedb.py index 9b5cd02d52..bd87e59dbd 100644 --- a/util/gem5art/artifact/tests/test_filedb.py +++ b/util/gem5art/artifact/tests/test_filedb.py @@ -29,8 +29,8 @@ import json import os -from pathlib import Path import unittest +from pathlib import Path from uuid import UUID from gem5art.artifact import Artifact @@ -62,7 +62,7 @@ class TestArtifactFileDB(unittest.TestCase): self.assertTrue(Path("test.json").exists()) def test_json_content(self): - with open("test.json", "r") as f: + with open("test.json") as f: artifacts = json.load(f) self.assertTrue(len(artifacts) == 1) artifact = artifacts[0] diff --git a/util/gem5art/run/bin/gem5art-getruns b/util/gem5art/run/bin/gem5art-getruns index 5474dd68d7..fe6046c1c0 100755 --- a/util/gem5art/run/bin/gem5art-getruns +++ b/util/gem5art/run/bin/gem5art-getruns @@ -35,7 +35,10 @@ from json import dump import gem5art.artifact from gem5art.artifact import getDBConnection -from gem5art.run import getRunsByNameLike, getRuns +from gem5art.run import ( + getRuns, + getRunsByNameLike, +) def parseArgs(): @@ -71,7 +74,6 @@ def parseArgs(): if __name__ == "__main__": - args = parseArgs() db = getDBConnection(args.db_uri) diff --git a/util/gem5art/run/gem5art/run.py b/util/gem5art/run/gem5art/run.py index 12e4b3e208..4918fa870c 100644 --- a/util/gem5art/run/gem5art/run.py +++ b/util/gem5art/run/gem5art/run.py @@ -35,13 +35,25 @@ experiment is reproducible and the output is saved to the database. import hashlib import json import os -from pathlib import Path import signal import subprocess import time -from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union -from uuid import UUID, uuid4 import zipfile +from pathlib import Path +from typing import ( + Any, + Callable, + Dict, + Iterable, + List, + Optional, + Tuple, + Union, +) +from uuid import ( + UUID, + uuid4, +) from gem5art import artifact from gem5art.artifact import Artifact @@ -672,7 +684,6 @@ def getRunsByNameLike( def getRerunnableRunsByNameLike( db: ArtifactDB, name: str, fs_only: bool = False, limit: int = 0 ) -> Iterable[gem5Run]: - """Returns a generator of gem5Run objects having rerunnable as true and the object "name" containing the name parameter as a substring. The parameter is case sensitive. diff --git a/util/gem5art/run/setup.py b/util/gem5art/run/setup.py index 1ab51b5c2d..70d62117d2 100755 --- a/util/gem5art/run/setup.py +++ b/util/gem5art/run/setup.py @@ -28,8 +28,11 @@ from os.path import join from pathlib import Path -from setuptools import setup, find_namespace_packages +from setuptools import ( + find_namespace_packages, + setup, +) with open(Path(__file__).parent / "README.md", encoding="utf-8") as f: long_description = f.read() diff --git a/util/gem5art/run/tests/test_run.py b/util/gem5art/run/tests/test_run.py index 0bdd561220..58fe8571b5 100644 --- a/util/gem5art/run/tests/test_run.py +++ b/util/gem5art/run/tests/test_run.py @@ -27,9 +27,9 @@ """Tests for gem5Run object""" import hashlib -from pathlib import Path import os import unittest +from pathlib import Path from uuid import uuid4 from gem5art.artifact import artifact diff --git a/util/gem5art/tasks/gem5art/tasks/tasks.py b/util/gem5art/tasks/gem5art/tasks/tasks.py index 3d92956341..6e1a6030f7 100755 --- a/util/gem5art/tasks/gem5art/tasks/tasks.py +++ b/util/gem5art/tasks/gem5art/tasks/tasks.py @@ -24,10 +24,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from .celery import gem5app import multiprocessing as mp import time +from .celery import gem5app + @gem5app.task(bind=True, serializer="pickle") def run_gem5_instance(self, gem5_run, cwd="."): diff --git a/util/gem5art/tasks/setup.py b/util/gem5art/tasks/setup.py index 290c68a17a..1cfe4c770d 100755 --- a/util/gem5art/tasks/setup.py +++ b/util/gem5art/tasks/setup.py @@ -28,8 +28,11 @@ from os.path import join from pathlib import Path -from setuptools import setup, find_namespace_packages +from setuptools import ( + find_namespace_packages, + setup, +) with open(Path(__file__).parent / "README.md", encoding="utf-8") as f: long_description = f.read() diff --git a/util/gem5img.py b/util/gem5img.py index 8eb0965c9e..4a8c0916fe 100755 --- a/util/gem5img.py +++ b/util/gem5img.py @@ -42,13 +42,21 @@ # Script for managing a gem5 disk image. # -from argparse import ArgumentParser import os -from os import environ as env -import string -from subprocess import CalledProcessError, Popen, PIPE, STDOUT -from sys import exit, argv import re +import string +from argparse import ArgumentParser +from os import environ as env +from subprocess import ( + PIPE, + STDOUT, + CalledProcessError, + Popen, +) +from sys import ( + argv, + exit, +) # Some constants. MaxLBACylinders = 16383 @@ -65,6 +73,7 @@ env["PATH"] += ":/sbin:/usr/sbin" # Whether to print debug output. debug = False + # Figure out cylinders, heads and sectors from a size in blocks. def chsFromSize(sizeInBlocks): if sizeInBlocks >= MaxLBABlocks: @@ -139,7 +148,7 @@ def findProg(program, cleanupDev=None): return out.strip() -class LoopbackDevice(object): +class LoopbackDevice: def __init__(self, devFile=None): self.devFile = devFile @@ -227,7 +236,7 @@ commands = {} commandOrder = [] -class Command(object): +class Command: def addArgument(self, *args, **kargs): self.parser.add_argument(*args, **kargs) diff --git a/util/gen_arm_fs_files.py b/util/gen_arm_fs_files.py index 6446d79bfb..3a6605be6f 100755 --- a/util/gen_arm_fs_files.py +++ b/util/gen_arm_fs_files.py @@ -38,14 +38,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter -from subprocess import call -from platform import machine +import os +import sys +from argparse import ( + ArgumentDefaultsHelpFormatter, + ArgumentParser, +) from distutils import spawn from glob import glob - -import sys -import os +from platform import machine +from subprocess import call def run_cmd(explanation, working_dir, cmd, stdout=None): diff --git a/util/gerrit-bot/bot.py b/util/gerrit-bot/bot.py index f6b9469d9b..5f6afdaf99 100755 --- a/util/gerrit-bot/bot.py +++ b/util/gerrit-bot/bot.py @@ -26,14 +26,17 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from gerrit import GerritResponseParser as Parser -from gerrit import GerritRestAPI -from util import add_maintainers_to_change, convert_time_in_seconds - import json +import sys import time -import sys +from gerrit import GerritResponseParser as Parser +from gerrit import GerritRestAPI + +from util import ( + add_maintainers_to_change, + convert_time_in_seconds, +) sys.path.append("..") import maint.lib.maintainers @@ -97,7 +100,7 @@ class GerritBot: def __read_auth_file(self, auth_file_path): username = "" password = "" - with open(auth_file_path, "r") as f: + with open(auth_file_path) as f: lines = f.readlines() username = lines[0].strip() password = lines[1].strip() @@ -107,7 +110,7 @@ class GerritBot: prev_query_time = 0 try: - with open(file_path, "r") as f: + with open(file_path) as f: lines = f.readlines() prev_query_time = int(float(lines[0].strip())) except FileNotFoundError: @@ -134,7 +137,7 @@ class GerritBot: def __read_maintainer_account_id_file(self, maintainers, file_path): account_ids = {} try: - with open(file_path, "r") as f: + with open(file_path) as f: account_ids = json.load(f) except (FileNotFoundError, json.decoder.JSONDecodeError): # create a placeholder file @@ -147,7 +150,7 @@ class GerritBot: def __update_maintainer_account_id_file(self, file_path, maintainers): # get the current map - with open(file_path, "r") as f: + with open(file_path) as f: account_ids = json.load(f) # get maintainer email addresses email_addresses = set() diff --git a/util/gerrit-bot/extract_gitcookies.py b/util/gerrit-bot/extract_gitcookies.py index ef17be10de..4487513dee 100755 --- a/util/gerrit-bot/extract_gitcookies.py +++ b/util/gerrit-bot/extract_gitcookies.py @@ -44,7 +44,7 @@ def parse_gitcookies_line(raw): def parse_gitcookies(input_path): username_password_dict = {} - with open(input_path, "r") as input_stream: + with open(input_path) as input_stream: for line in input_stream: username, password = parse_gitcookies_line(line) if not username: diff --git a/util/gerrit-bot/gerrit.py b/util/gerrit-bot/gerrit.py index 2e68a70645..b75434b4b3 100644 --- a/util/gerrit-bot/gerrit.py +++ b/util/gerrit-bot/gerrit.py @@ -26,10 +26,11 @@ import copy import json -import requests from types import SimpleNamespace from urllib.parse import urljoin +import requests + class GerritResponseParser: @staticmethod diff --git a/util/gerrit-bot/util.py b/util/gerrit-bot/util.py index b410858e14..16a3b49216 100644 --- a/util/gerrit-bot/util.py +++ b/util/gerrit-bot/util.py @@ -78,10 +78,8 @@ def add_maintainers_to_change( maintainer_emails.add(email) except KeyError: print( - ( - f"warning: `change-{change_id}` has an unknown tag: " - f"`{tag}`" - ) + f"warning: `change-{change_id}` has an unknown tag: " + f"`{tag}`" ) for email in maintainer_emails: if email in avoid_emails: diff --git a/util/git-commit-msg.py b/util/git-commit-msg.py index 12baad8c19..d40aa68e1c 100755 --- a/util/git-commit-msg.py +++ b/util/git-commit-msg.py @@ -32,8 +32,8 @@ import os import re import sys -from maint.lib import maintainers +from maint.lib import maintainers from style.repo import GitRepo @@ -57,7 +57,7 @@ def _printErrorQuit(error_message): -------------------------------------------------------------------------- """ ) - print(open(sys.argv[1], "r").read()) + print(open(sys.argv[1]).read()) print( """ -------------------------------------------------------------------------- @@ -100,13 +100,12 @@ def _validateTags(commit_header): maintainer_dict = maintainers.Maintainers.from_file() valid_tags = [tag for tag, _ in maintainer_dict] - # Remove non-tag 'pmc' and add special tags not in MAINTAINERS.yaml - valid_tags.remove("pmc") + # Add special tags not in MAINTAINERS.yaml valid_tags.extend(["RFC", "WIP"]) tags = "".join(commit_header.split(":")[0].split()).split(",") if any(tag not in valid_tags for tag in tags): - invalid_tag = next((tag for tag in tags if tag not in valid_tags)) + invalid_tag = next(tag for tag in tags if tag not in valid_tags) _printErrorQuit("Invalid Gem5 tag: " + invalid_tag) @@ -121,7 +120,7 @@ commit_message = open(sys.argv[1]).read() commit_message_lines = commit_message.splitlines() commit_header = commit_message_lines[0] commit_header_match = re.search( - "^(fixup! )?(\S[\w\-][,\s*[\w\-]+]*:.+\S$)", commit_header + r"^(fixup! )?(\S[\w\-][,\s*[\w\-]+]*:.+\S$)", commit_header ) if commit_header_match is None: _printErrorQuit("Invalid commit header") diff --git a/util/git-pre-commit.py b/util/git-pre-commit.py index c6f3b3c033..0a51ba0478 100755 --- a/util/git-pre-commit.py +++ b/util/git-pre-commit.py @@ -36,16 +36,21 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from tempfile import TemporaryFile +import argparse import os import subprocess import sys +from tempfile import TemporaryFile from style.repo import GitRepo -from style.verifiers import all_verifiers, all_regions -from style.style import StdioUI, check_ignores - -import argparse +from style.style import ( + StdioUI, + check_ignores, +) +from style.verifiers import ( + all_regions, + all_verifiers, +) parser = argparse.ArgumentParser(description="gem5 git style checker hook") diff --git a/util/github-runners-vagrant/README.md b/util/github-runners-vagrant/README.md index 7d0f116260..ca504e3c63 100644 --- a/util/github-runners-vagrant/README.md +++ b/util/github-runners-vagrant/README.md @@ -1,6 +1,6 @@ # Setting up a Github Actions Runner with Vagrant -This directory provides a way to setup a Github Actions runner using Vagrant to host the runner in a Virtual machine. +This directory provides a way to setup Github Actions runners using Vagrant to host them in Virtual machines. This tutorial has been written with the assumption of running on a machine with Ubuntu 22.04. Setting up a runner on a different OS may require some changes. @@ -21,14 +21,6 @@ sudo apt-get install qemu libvirt-daemon-system libvirt-clients ebtables dnsmasq sudo apt purge vagrant-libvirt ``` -## Set up the Vagrantfiles for the GitHub repository - -First, generate a Personal Access Token, which you can create [here](https://github.com/settings/tokens) -Make sure to set admin permissions on this token, then replace instances of `` in the Vagrantfiles ("Vagrantfile-builder" and "Vagrant-runner") with your token. - -Next, replace instances of `` with your GitHub account name and the repository name, separated by a forward slash. -For example, if your GitHub account name is `example` and your repository name is `example-repo`, you would replace `` with `example/example-repo`. - ## Install Vagrant Plugins Once everything is set properly, set the `VAGRANT_HOME` environment variable to the directory in which the Vagrant files and other scripts are stored (i.e., the CWD). @@ -46,71 +38,35 @@ vagrant plugin install vagrant-libvirt vagrant plugin install vagrant-reload ``` -## The "builder" and "runner" VMs +## Creating the virtual machines -The number of CPUs and the memory size differs between the "Vagrantfile-builder" and "Vagrantfile-runner". +The Vagrantfile in this directory defines the VMs that can be built and used to create a GitHub Actions runner. +This standard VM has 4-cores, 16GB of RAM, and 60GB of disk space. +This is sufficient to both compile gem5 and run most simulations. -In our work we have two types of machines "runners" and "builders". -Runners are single core machines with 8GB of memory, and builders are 4 core machines with 16GB of memory. -The latter is used for building gem5 binaries while the former is used for running instances of gem5. -You can expect each machine to take up approximately 60GB of disk space though VMs will consume the disk space they require. +At the top of the Vagrantfile, there are a few variables that must be set prior to creating the VMs. -The "Vagrantfile-builder" file is set to create a runner machine and the "Vagrantfile-builder" file is set to create a builder machine. +* `NUM_RUNNERS`: The number of runners to create. +* `PERSONAL_ACCESS_TOKEN`: The GitHub personal access token to use. +You can generate a Personal Access Token [here](https://github.com/settings/tokens) +Make sure to set admin permissions on this token. +* `GITHUB_ORG`: The GitHub organization to add the runners to. +E.g., if the URL to your organization is https://github.com/orgs/gem5, then the variable should be set to "gem5". +* `HOSTNAME` : The hostname of the VM to be created (note, this will be appended with a number to create a unique hostname for each VM). +E.g., if set to `my-machine` and the number of runners set to `2`, two VMs will be created. +One called `my-machine-1` and the other `my-machine-2`. -Specifying which Vagrantfile to use is done by setting the `VAGRANT_VAGRANTFILE` environment variable. - -## Creating the virtual machine - -Each VM on your host system must have a unique name. -Give the VM to be created a unique name by setting the `` variables in the Vagrantfile you wish to utilize. - -Then run: +When set simply run: ```sh -VAGRANT_VAGRANTFILE= vagrant up --provider=libvirt +vagrant up --provider=libvirt ``` -This should automatically create your machine, as well as configure and start up a Github Actions runner. -You can check the status of the runner here: https://github.com///settings/actions/runners +This should automatically create your machines then configure and start up a Github Actions runner in each. +You can check the status of the runner here: https://github.com/organizations/{GITHUB_ORG}/settings/actions/runners If the runner ever shows as offline, you can rerun the `vagrant up --provider=libvirt` command to make sure everything is working properly. -If you wish to create more than one runner you must edit the `` in the Vagrant file. - -## Helper scripts - -The "vm_manager" script can be used to set up multiple builder and runner VMs. -To use this script simply modify the `NUM_RUNNERS`, `NUM_BUILDERS`, `RUNNER_PREFIX`, and `BUILDER_PREFIX` variables to the desired values. -Then run the script with: - -```sh -./vm_manager.sh -``` - -This script will create any VMs that don't already exist and ensure those that do exists are running. - -If you wish to destroy all the VMs you can run: - -```sh -./vm_manager.sh destroy -``` - -**Note:** This script assumes "VAGRANT_HOME" is set to the CWD. - -## Improving stability - -Occasionally GitHub runner services, or VMs, go down. This is often silent and -usually only noticable from going to the GitHub repo page "settings" -> "actions" -> "runners" and observing the status. -When the VMs or the service stop working they need restarted. -To do so you can sun `./vm_manager.sh`. This will cycle through the VMs and execute a `vagrant up` command. -This does one of three things depending on the state of the VM: - -1. If the VM is down this will bring the VM back online and start the GitHub runner service. -2. If the VM is up but the GitHub runner service is down, this will start the GitHub runner service. -3. If the VM is up and the GitHub runner service is running (i.e., everything is fine) then this does nothing. - -Given there is no harm in running this command frequently, we recommend setting up a cron job to automatically execute `./vm_manager.sh` every few hours. - ## Troubleshooting ### The default libvirt disk image storage pool is on the wrong drive @@ -130,3 +86,15 @@ virsh pool-define default-pool.xml # From here we re-add the default. virsh pool-start default virsh pool-autostart default ``` + +### Error: "Vagrant failed to initialize at a very early stage" + +W set the `VAGRANT_HOME` environment variable to the CWD. +It's likely this has become unset The solution is simple. +Within the directory containing "Vagrantfile": + +```sh +VAGRANT_HOME=`pwd` vagrant +``` + +You may want to set `VAGRANT_HOME` in your .bashrc or .zshrc. diff --git a/util/github-runners-vagrant/Vagrantfile b/util/github-runners-vagrant/Vagrantfile new file mode 100644 index 0000000000..0e505ba38b --- /dev/null +++ b/util/github-runners-vagrant/Vagrantfile @@ -0,0 +1,89 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +NUM_RUNNERS=0 # Set this to the desired number of runners. +PERSONAL_ACCESS_TOKEN="" +GITHUB_ORG="" +HOSTNAME="" + +Vagrant.configure("2") do |config| + config.ssh.username = "vagrant" + config.ssh.password = "vagrant" + + (1..NUM_RUNNERS).each do |i| + + config.vm.define "#{HOSTNAME}-#{i}" do |runner| + runner.vm.hostname = "#{HOSTNAME}-#{i}" + runner.vm.box = "generic/ubuntu2204" + runner.vm.box_check_update = true + + runner.vm.provider "libvirt" do |vb| + # Customize the amount of cpus, memory, and storage on the VM: + vb.cpus = "4".to_i + vb.memory = "16384".to_i + vb.machine_virtual_size = 128 # 128G is the minimum. + end + + # sets up vm + runner.vm.provision :shell, path: "provision_root.sh" + runner.vm.provision :shell, privileged: false, path: "provision_nonroot.sh" + # The provision_root.sh adds the vagrant user to the docker group, so we need to reload the VM. + runner.vm.provision :reload + # Copy the "action-run.sh" script from the host to the VM. + runner.vm.provision "file", source: "./action-run.sh", destination: "/tmp/action-run.sh" + runner.vm.provision :shell, privileged: false, inline: "cp /tmp/action-run.sh ." + + # The following attempts to see if KVM can be used inside the docker + # container. + # + # Almost all github action jobs run within a docker container. Therefore + # to be compatible with KVM, KVM must be enabled inside the docker. + # + # We used existence of "kvm-works" in the VM home directory is how we + # indicate that KVM is working. It is created if the 'kvm-ok' command is + # successful. This is then passed to the action-run.sh script to indicate + # that the runner can be used for KVM via the `kvm` label. + runner.vm.provision :shell, privileged: false, run: 'always', inline: <<-SHELL + rm -f kvm-works + docker run --device /dev/kvm -v$(pwd):/work -w /work --rm ubuntu:22.04 bash -c "apt update -y && apt install -y cpu-checker && kvm-ok" + status=$? + if [[ ${status} == 0 ]]; then + echo >&1 "Success. KVM enabled." + echo "success" > kvm-works + else + echo >&2 "Failure. KVM not enabled." + fi + exit 0 + SHELL + # Execute the actions-run.sh script on every boot. This configures and starts the runner. + # Note the 'kvm' label is applied to this runner if the "kvm-works" file eixsts. See above. + runner.vm.provision :shell, privileged: false, run: 'always', inline: "./action-run.sh #{PERSONAL_ACCESS_TOKEN} #{GITHUB_ORG} $(if [ -f 'kvm-works' ]; then echo 'kvm'; fi) >> action-run.log 2>&1 &" + end + end +end diff --git a/util/github-runners-vagrant/action-run.sh b/util/github-runners-vagrant/action-run.sh new file mode 100755 index 0000000000..67cc6f3cf8 --- /dev/null +++ b/util/github-runners-vagrant/action-run.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set -x + +# No argument checking here, this is run directly in the Vagrantfile. +PERSONAL_ACCESS_TOKEN="$1" +GITHUB_ORG="$2" +LABELS="$3" +WORK_DIR="_work" + +# This checks there isn't another instance of this script running. +# If this script is run twice then more than one runner can be active in the +# VM and this causes problems. +if [[ `pgrep -f $0` != "$$" ]]; then + echo "Another instance of shell already exist! Exiting" + exit +fi + +# If the tarball isn't here then download it and extract it. +# Note: we don't delete the tarball, we use it to check if we've already +# downloaded it and extracted it. +if [ ! -f "actions-runner-linux-x64-2.304.0.tar.gz" ]; then + wget https://github.com/actions/runner/releases/download/v2.304.0/actions-runner-linux-x64-2.304.0.tar.gz + tar xzf ./actions-runner-linux-x64-2.304.0.tar.gz +fi + +# An infinite loop to re-configure and re-run the runner after each job. +while true; do + # 1. Obtain the registration token. + token_curl=$(curl -L \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${PERSONAL_ACCESS_TOKEN}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/orgs/${GITHUB_ORG}/actions/runners/registration-token) + + token=$(echo ${token_curl} | jq -r '.token') + + if [[ "${token}" == "null" ]]; + then + # If "null" is returned, this can be because the GitHub API rate limit + # has been exceeded. To be safe we wait for 15 mintues before + # continuing. + sleep 900 # 15 minutes. + continue + fi + + # 2. Configure the runner. + ./config.sh --unattended \ + --url https://github.com/${GITHUB_ORG} \ + --ephemeral \ + --replace \ + --work "${WORK_DIR}" \ + --name "$(hostname)" \ + --labels "${LABELS}" \ + --token ${token} + + # 3. Run the runner. + ./run.sh # This will complete with the runner being destroyed + + # 4. Cleanup the machine + sudo rm -rf "${WORK_DIR}" + docker system prune --force --volumes --all + + # 5. Sleep for a few minutes + # GitHub has a api rate limit. This sleep ensures we dont ping GitHub + # too frequently. + sleep 180 # 3 minutes. +done diff --git a/util/github-runners-vagrant/halt-helper.sh b/util/github-runners-vagrant/halt-helper.sh new file mode 100755 index 0000000000..9b4caf7e28 --- /dev/null +++ b/util/github-runners-vagrant/halt-helper.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# This script will try to safely halt each VM specified in the Vagrantfile. +# A VM is skipped if it is currently running a job and returned to after +# attempted shutdowns on the other VMs. This cycle continues indefinitely until +# all the runners are shutdown. +# +# This script is usefull as the VMs occasionally need to be halted to apply +# patches and do maintenance. This script allows us to do this without +# interrupting any jobs that may be running. + +while true; do + # This will list all the VMs still running. If there are no VM's running, + # we infer all have been shutdown and we exit the script. Otherwise, we + # iterate over he VMs in an attempt to shut them down. + active=$(vagrant status | grep running | tr -s ' ' | cut -d ' ' -f1) + if [ "$active" == "" ]; then + echo "All VMs have been shutdown. Exiting." + exit 0 + fi + echo "The following VMs are still running:" + echo "${active}" + + for virtm in $active + do + # This script will first list the contents of the "_diag" directory. + # This directory hosts the github action runner job logs. Each job + # is logged to a seperate file in the directpry. This script then + # sort these files by name. The last file in this sorted list is the + # most recent file and therefore for the most recent job. We can sort + # them in this was because their filenames are appended with UTC + # timestamps. + # + # One one job ever runs at a time on a GitHub runner so if there is any + # job running, it be being logged in the most recent file in the + # "_diag" directory. + # + # If the job has completed the last line in the file will contain the + # string "Job completed.". This script checks for this and, if found, + # we assume there are no jobs running safely run `vagrant halt` to + # shutdown the VM. If the job is still running we print a message + # saying the job is still running and will return to it on the next + # iteration of the loop. + echo "Inspecting \"${virtm}\"..." + vagrant ssh $virtm -c 'ls _diag | sort | tail -1 | xargs -I % cat "_diag/%" | tail -1 | grep -q "Job completed"' + status=$? + if [[ ${status} == 0 ]]; then + echo "${virtm} is Idle. Attempting shutdown" + vagrant halt ${virtm} && echo "${virtm} successfully halted" || echo "${virtm} experience a failure halting" + else + echo "${virtm} is Busy. Skipping for now." + fi + done + # Sleep here for 20 seconds just to ensure all the VMs have time + # to shutdown. + sleep 20 +done diff --git a/util/github-runners-vagrant/provision_nonroot.sh b/util/github-runners-vagrant/provision_nonroot.sh index 4465b5a192..d817959285 100644 --- a/util/github-runners-vagrant/provision_nonroot.sh +++ b/util/github-runners-vagrant/provision_nonroot.sh @@ -1,5 +1,31 @@ #!/usr/bin/env bash +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + # fail on unset variables and command errors set -eu -o pipefail # -x: is for debugging diff --git a/util/github-runners-vagrant/provision_root.sh b/util/github-runners-vagrant/provision_root.sh index 660064f8ca..d3e6bb574c 100644 --- a/util/github-runners-vagrant/provision_root.sh +++ b/util/github-runners-vagrant/provision_root.sh @@ -1,13 +1,39 @@ #!/usr/bin/env bash +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + # fail on unset variables and command errors set -eu -o pipefail # -x: is for debugging apt-get update apt-get upgrade -y -apt-get install -y software-properties-common add-apt-repository --yes --update ppa:git-core/ppa apt-get install -y \ + software-properties-common \ bash \ build-essential \ clang-format \ @@ -25,8 +51,13 @@ apt-get install -y \ tree \ wget \ yamllint \ - zstd -snap install jq + zstd \ + jq \ + apt-transport-https ca-certificates \ + curl \ + gnupg \ + lsb-release \ + cpu-checker # Install docker apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release @@ -34,10 +65,31 @@ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/ echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null apt-get update -y apt-get install -y docker-ce docker-ce-cli containerd.io -groupadd docker || true -gpasswd -a vagrant docker -newgrp docker -systemctl restart docker + +# Add the Vagrant user to the docker group. +# Note: The VM needs rebooted for this to take effect. `newgrp docker` doesn't +# work. +usermod -aG docker vagrant + +kvm-ok +kvm_ok_status=$? + +# `kvm-ok` will return a exit zero if the machine supports KVM, and non-zero +# otherwise. If the machine support KVM, let's enable it. +if [[ ${kvm_ok_status} == 0 ]]; then + apt install -y qemu-kvm \ + virt-manager \ + libvirt-daemon-system virtinst \ + libvirt-clients bridge-utils && \ + sudo systemctl enable --now libvirtd && \ + sudo systemctl start libvirtd && \ + usermod -aG kvm vagrant && \ + usermod -aG libvirt vagrant +fi # Cleanup apt-get autoremove -y + +# Resize the root partition to fill up all the free size on the disk +lvextend -l +100%FREE $(df / --output=source | sed 1d) +resize2fs $(df / --output=source | sed 1d) diff --git a/util/logroll.py b/util/logroll.py index 02ca309e87..04f7d5c69d 100755 --- a/util/logroll.py +++ b/util/logroll.py @@ -105,7 +105,7 @@ class CopyingMock(unittest.mock.MagicMock): def __call__(self, *args, **kwargs): args = copy.deepcopy(args) kwargs = copy.deepcopy(kwargs) - return super(CopyingMock, self).__call__(*args, **kwargs) + return super().__call__(*args, **kwargs) class TestLogroll(unittest.TestCase): @@ -125,14 +125,12 @@ class TestLogroll(unittest.TestCase): # Generator which returns lines like a file object would. def line_gen(self, lines): - for line in lines: - yield line + yield from lines # Generator like above, but which simulates a signal midway through. def signal_line_gen(self, lines, pos, sig_dict, signal): # Return the first few lines. - for line in lines[:pos]: - yield line + yield from lines[:pos] # Simulate receiving the signal. self.assertIn(signal, sig_dict) @@ -141,8 +139,7 @@ class TestLogroll(unittest.TestCase): sig_dict[signal](None, None) # Return the remaining lines. - for line in lines[pos:]: - yield line + yield from lines[pos:] # Set up a mock of signal.signal to record handlers in a dict. def mock_signal_dict(self, mock): @@ -214,7 +211,6 @@ class TestLogroll(unittest.TestCase): ) as mock_signal, unittest.mock.patch( __name__ + ".dump_lines", new_callable=CopyingMock ) as mock_dump_lines: - signal_dict = self.mock_signal_dict(mock_signal) main( @@ -237,7 +233,6 @@ class TestLogroll(unittest.TestCase): ) as mock_signal, unittest.mock.patch( __name__ + ".dump_lines", new_callable=CopyingMock ) as mock_dump_lines: - signal_dict = self.mock_signal_dict(mock_signal) with self.assertRaises(SystemExit): @@ -258,7 +253,6 @@ class TestLogroll(unittest.TestCase): ) as mock_signal, unittest.mock.patch( __name__ + ".dump_lines", new_callable=CopyingMock ) as mock_dump_lines: - signal_dict = self.mock_signal_dict(mock_signal) main( @@ -283,7 +277,6 @@ class TestLogroll(unittest.TestCase): ) as mock_signal, unittest.mock.patch( __name__ + ".dump_lines", new_callable=CopyingMock ) as mock_dump_lines: - signal_dict = self.mock_signal_dict(mock_signal) with self.assertRaises(SystemExit): diff --git a/util/maint/lib/maintainers.py b/util/maint/lib/maintainers.py index 93ea1a17bf..4cc713aada 100644 --- a/util/maint/lib/maintainers.py +++ b/util/maint/lib/maintainers.py @@ -83,7 +83,7 @@ class Status(enum.Enum): ] -class Subsystem(object): +class Subsystem: tag: str status: Status maintainers: List[Tuple[str, str]] # Name, email @@ -102,7 +102,7 @@ class Subsystem(object): self.description = description if description is not None else "" -class Maintainers(object): +class Maintainers: DEFAULT_MAINTAINERS = os.path.join( os.path.dirname(__file__), "../../../MAINTAINERS.yaml" ) @@ -118,7 +118,6 @@ class Maintainers(object): def from_file( cls, path_or_file: Optional[PathOrFile] = None ) -> "Maintainers": - return cls(Maintainers._load_maintainers_file(path_or_file)) @classmethod @@ -133,7 +132,7 @@ class Maintainers(object): path_or_file = cls.DEFAULT_MAINTAINERS if isinstance(path_or_file, str): - with open(path_or_file, "r") as fin: + with open(path_or_file) as fin: return yaml.load(fin, Loader=yaml.SafeLoader) else: return yaml.load(path_or_file, Loader=yaml.SafeLoader) diff --git a/util/maint/list_changes.py b/util/maint/list_changes.py index 0d61e39fde..805d7676b6 100755 --- a/util/maint/list_changes.py +++ b/util/maint/list_changes.py @@ -36,12 +36,12 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import subprocess import re +import subprocess from functools import wraps -class Commit(object): +class Commit: _re_tag = re.compile(r"^((?:\w|-)+): (.*)$") def __init__(self, rev): @@ -137,12 +137,12 @@ def list_changes(upstream, feature, paths=[]): feature_revs = tuple(list_revs(upstream, feature, paths=paths)) upstream_revs = tuple(list_revs(feature, upstream, paths=paths)) - feature_cids = dict( - [(c.change_id, c) for c in feature_revs if c.change_id is not None] - ) - upstream_cids = dict( - [(c.change_id, c) for c in upstream_revs if c.change_id is not None] - ) + feature_cids = { + c.change_id: c for c in feature_revs if c.change_id is not None + } + upstream_cids = { + c.change_id: c for c in upstream_revs if c.change_id is not None + } incoming = [ r @@ -251,13 +251,11 @@ def _main(): if args.deep_search: print("Incorrectly rebased changes:") all_upstream_revs = list_revs(args.upstream, paths=args.paths) - all_upstream_cids = dict( - [ - (c.change_id, c) - for c in all_upstream_revs - if c.change_id is not None - ] - ) + all_upstream_cids = { + c.change_id: c + for c in all_upstream_revs + if c.change_id is not None + } incorrect_outgoing = [ r for r in outgoing if r.change_id in all_upstream_cids ] diff --git a/util/maint/show_changes_by_file.py b/util/maint/show_changes_by_file.py index 75b7e7edd9..600f1e8f1a 100755 --- a/util/maint/show_changes_by_file.py +++ b/util/maint/show_changes_by_file.py @@ -33,12 +33,15 @@ import subprocess -from collections import OrderedDict, defaultdict +from collections import ( + OrderedDict, + defaultdict, +) class OrderedDefaultDict(OrderedDict, defaultdict): def __init__(self, default_factory=None, *args, **kwargs): - super(OrderedDefaultDict, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.default_factory = default_factory diff --git a/util/md5.py b/util/md5.py index 333f714109..0c55f92e0d 100644 --- a/util/md5.py +++ b/util/md5.py @@ -26,7 +26,11 @@ import argparse from pathlib import Path -from gem5.resources.md5_utils import md5_file, md5_dir + +from gem5.resources.md5_utils import ( + md5_dir, + md5_file, +) parser = argparse.ArgumentParser( description="A utility to determine the md5 hash of files and " diff --git a/util/minorview.py b/util/minorview.py index f7a53b1d26..eb4e0bd617 100755 --- a/util/minorview.py +++ b/util/minorview.py @@ -38,18 +38,23 @@ # minorview.py: Minorview visuliser for MinorCPU model MinorTrace output # -import gtk +import argparse import os import sys -import argparse + +import gtk # Find MinorView modules even if not called from minorview directory minorviewDir = os.path.dirname(os.path.realpath(__file__)) sys.path.append(minorviewDir) from minorview.model import BlobModel -from minorview.view import BlobView, BlobController, BlobWindow from minorview.point import Point +from minorview.view import ( + BlobController, + BlobView, + BlobWindow, +) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Minor visualiser") diff --git a/util/minorview/blobs.py b/util/minorview/blobs.py index 51b28d0d1e..1f6753e76a 100644 --- a/util/minorview/blobs.py +++ b/util/minorview/blobs.py @@ -41,17 +41,23 @@ import pygtk pygtk.require("2.0") -import gtk -import gobject -import cairo -import re import math +import re +import cairo +import gobject +import gtk + +from . import ( + colours, + model, + parse, +) +from .colours import ( + backgroundColour, + black, +) from .point import Point -from . import parse -from . import colours -from .colours import backgroundColour, black -from . import model def centre_size_to_sides(centre, size): @@ -181,7 +187,7 @@ def cross(cr, centre, size): cr.line_to(x, bottom) -class Blob(object): +class Blob: """Blob super class""" def __init__(self, picChar, unit, topLeft, colour, size=Point(1, 1)): @@ -217,7 +223,7 @@ class Block(Blob): colour=colours.black, size=Point(1, 1), ): - super(Block, self).__init__(picChar, unit, topLeft, colour, size=size) + super().__init__(picChar, unit, topLeft, colour, size=size) # {horiz, vert} self.stripDir = "horiz" # {LR, RL}: LR means the first strip will be on the left/top, @@ -388,7 +394,7 @@ class Key(Blob): def __init__( self, picChar, unit, topLeft, colour=colours.black, size=Point(1, 1) ): - super(Key, self).__init__(picChar, unit, topLeft, colour, size=size) + super().__init__(picChar, unit, topLeft, colour, size=size) self.colours = "BBBB" self.displayName = unit @@ -464,7 +470,7 @@ class Arrow(Blob): size=Point(1.0, 1.0), direc="right", ): - super(Arrow, self).__init__(unit, unit, topLeft, colour, size=size) + super().__init__(unit, unit, topLeft, colour, size=size) self.direc = direc def render(self, cr, view, event, select, time): diff --git a/util/minorview/model.py b/util/minorview/model.py index 126b730bea..91979825c3 100644 --- a/util/minorview/model.py +++ b/util/minorview/model.py @@ -33,22 +33,25 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from . import parse -from . import colours +import os +import re +from time import time as wall_time + +from . import ( + blobs, + colours, + parse, +) from .colours import unknownColour from .point import Point -import re -from . import blobs -from time import time as wall_time -import os id_parts = "TSPLFE" all_ids = set(id_parts) -no_ids = set([]) +no_ids = set() -class BlobDataSelect(object): +class BlobDataSelect: """Represents which data is displayed for Ided object""" def __init__(self): @@ -62,7 +65,7 @@ class BlobDataSelect(object): return ret -class BlobVisualData(object): +class BlobVisualData: """Super class for block data colouring""" def to_striped_block(self, select): @@ -113,7 +116,7 @@ class Id(BlobVisualData): def from_string(self, string): m = re.match( - "^(F;)?(\d+)/(\d+)\.(\d+)/(\d+)(/(\d+)(\.(\d+))?)?", string + r"^(F;)?(\d+)/(\d+)\.(\d+)/(\d+)(/(\d+)(\.(\d+))?)?", string ) def seqnum_from_string(string): @@ -205,7 +208,7 @@ class Branch(BlobVisualData): self.id = Id() def from_string(self, string): - m = re.match("^(\w+);(\d+)\.(\d+);([0-9a-fA-Fx]+);(.*)$", string) + m = re.match(r"^(\w+);(\d+)\.(\d+);([0-9a-fA-Fx]+);(.*)$", string) if m is not None: ( @@ -283,7 +286,7 @@ class DcacheAccess(BlobVisualData): return [direc_colour] + self.id.to_striped_block(select) -class ColourPattern(object): +class ColourPattern: """Super class for decoders that make 2D grids rather than just single striped blocks""" @@ -493,7 +496,7 @@ def find_colour_decoder(stripSpace, decoderName, dataName, picPairs): return None -class IdedObj(object): +class IdedObj: """An object identified by an Id carrying paired data. The super class for Inst and Line""" @@ -518,7 +521,7 @@ class Inst(IdedObj): """A non-fault instruction""" def __init__(self, id, disassembly, addr, pairs={}): - super(Inst, self).__init__(id, pairs) + super().__init__(id, pairs) if "nextAddr" in pairs: self.nextAddr = int(pairs["nextAddr"], 0) del pairs["nextAddr"] @@ -542,7 +545,7 @@ class InstFault(IdedObj): """A fault instruction""" def __init__(self, id, fault, addr, pairs={}): - super(InstFault, self).__init__(id, pairs) + super().__init__(id, pairs) self.fault = fault self.addr = addr @@ -557,7 +560,7 @@ class Line(IdedObj): """A fetched line""" def __init__(self, id, vaddr, paddr, size, pairs={}): - super(Line, self).__init__(id, pairs) + super().__init__(id, pairs) self.vaddr = vaddr self.paddr = paddr self.size = size @@ -573,7 +576,7 @@ class LineFault(IdedObj): """A faulting line""" def __init__(self, id, fault, vaddr, pairs={}): - super(LineFault, self).__init__(id, pairs) + super().__init__(id, pairs) self.vaddr = vaddr self.fault = fault @@ -584,7 +587,7 @@ class LineFault(IdedObj): return ret -class BlobEvent(object): +class BlobEvent: """Time event for a single blob""" def __init__(self, unit, time, pairs={}): @@ -624,7 +627,7 @@ class BlobEvent(object): return sorted(ret) -class BlobModel(object): +class BlobModel: """Model bringing together blob definitions and parsed events""" def __init__(self, unitNamePrefix=""): @@ -856,7 +859,7 @@ class BlobModel(object): still_skipping = True l = f.readline() while l and still_skipping: - match = re.match("^\s*(\d+):", l) + match = re.match(r"^\s*(\d+):", l) if match is not None: event_time = match.groups() if int(event_time[0]) >= startTime: @@ -867,7 +870,7 @@ class BlobModel(object): l = f.readline() match_line_re = re.compile( - "^\s*(\d+):\s*([\w\.]+):\s*(Minor\w+:)?\s*(.*)$" + r"^\s*(\d+):\s*([\w\.]+):\s*(Minor\w+:)?\s*(.*)$" ) # Parse each line of the events file, accumulating comments to be @@ -880,13 +883,13 @@ class BlobModel(object): event_time = int(event_time) unit = re.sub( - "^" + self.unitNamePrefix + "\.?(.*)$", "\\1", unit + "^" + self.unitNamePrefix + r"\.?(.*)$", "\\1", unit ) # When the time changes, resolve comments if event_time != time: if self.numEvents > next_progress_print_event_count: - print(("Parsed to time: %d" % event_time)) + print("Parsed to time: %d" % event_time) next_progress_print_event_count = self.numEvents + 1000 update_comments(comments, time) comments = [] @@ -1137,7 +1140,7 @@ class BlobModel(object): def line_is_comment(line): """Returns true if a line starts with #, returns False for lines which are None""" - return line is not None and re.match("^\s*#", line) is not None + return line is not None and re.match(r"^\s*#", line) is not None def get_line(f): """Get a line from file f extending that line if it ends in @@ -1186,19 +1189,19 @@ class BlobModel(object): l = parse.remove_trailing_ws(l) l = re.sub("#.*", "", l) - if re.match("^\s*$", l) is not None: + if re.match(r"^\s*$", l) is not None: pass elif l == "<<<": in_picture = True elif l == ">>>": in_picture = False elif in_picture: - picture.append(re.sub("\s*$", "", l)) + picture.append(re.sub(r"\s*$", "", l)) else: line_match = re.match( - "^([a-zA-Z0-9][a-zA-Z0-9]):\s+([\w.]+)\s*(.*)", l + r"^([a-zA-Z0-9][a-zA-Z0-9]):\s+([\w.]+)\s*(.*)", l ) - macro_match = re.match("macro\s+(\w+):(.*)", l) + macro_match = re.match(r"macro\s+(\w+):(.*)", l) if macro_match is not None: name, defn = macro_match.groups() diff --git a/util/minorview/parse.py b/util/minorview/parse.py index 5b6bea0c79..bc12536980 100644 --- a/util/minorview/parse.py +++ b/util/minorview/parse.py @@ -44,14 +44,14 @@ def list_parser(names): ret = [] accum = [] for elem in elems: - if re.search("^\((.*)\)$", elem): - accum.append(re.sub("^\((.*)\)", "\\1", elem)) + if re.search(r"^\((.*)\)$", elem): + accum.append(re.sub(r"^\((.*)\)", "\\1", elem)) ret.append(accum) accum = [] - elif re.search("^\(", elem): - accum.append(re.sub("^\(", "", elem)) - elif re.search("\)$", elem): - accum.append(re.sub("\)$", "", elem)) + elif re.search(r"^\(", elem): + accum.append(re.sub(r"^\(", "", elem)) + elif re.search(r"\)$", elem): + accum.append(re.sub(r"\)$", "", elem)) ret.append(accum) accum = [] elif len(accum) != 0: @@ -72,18 +72,18 @@ def map2(f, ls): def remove_trailing_ws(line): - return re.sub("\s*$", "", line) + return re.sub(r"\s*$", "", line) def remove_leading_and_trailing_ws(line): - return re.sub("\s*$", "", re.sub("^\s*", "", line)) + return re.sub(r"\s*$", "", re.sub(r"^\s*", "", line)) def parse_pairs_list(pairString): """parse a string like 'name=value name2=value2' into a list of pairs of ('name', 'value') ...""" ret = [] - pairs = re.finditer('(\w+)(=("[^"]*"|[^\s]*))?', pairString) + pairs = re.finditer(r'(\w+)(=("[^"]*"|[^\s]*))?', pairString) for pair in pairs: name, rest, value = pair.groups() if value is not None: diff --git a/util/minorview/point.py b/util/minorview/point.py index 17190e1ca7..636704002d 100644 --- a/util/minorview/point.py +++ b/util/minorview/point.py @@ -34,7 +34,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -class Point(object): +class Point: """2D point coordinates/size type""" def __init__(self, x, y): diff --git a/util/minorview/view.py b/util/minorview/view.py index 7c1aef873f..671a80869c 100644 --- a/util/minorview/view.py +++ b/util/minorview/view.py @@ -36,20 +36,28 @@ import pygtk pygtk.require("2.0") -import gtk -import gobject -import cairo import re +import cairo +import gobject +import gtk + +from . import ( + blobs, + colours, + model, + parse, +) +from .model import ( + BlobDataSelect, + BlobModel, + Id, + special_state_chars, +) from .point import Point -from . import parse -from . import colours -from . import model -from .model import Id, BlobModel, BlobDataSelect, special_state_chars -from . import blobs -class BlobView(object): +class BlobView: """The canvas view of the pipeline""" def __init__(self, model): @@ -189,7 +197,7 @@ class BlobView(object): self.da.set_size_request(10, int(self.initialHeight)) -class BlobController(object): +class BlobController: """The controller bar for the viewer""" def __init__( @@ -361,7 +369,7 @@ class BlobController(object): self.view.redraw() -class Overlay(object): +class Overlay: """An Overlay is a speech bubble explaining the data in a blob""" def __init__(self, model, view, point, blob): @@ -456,7 +464,7 @@ class Overlay(object): text_point += text_step -class BlobWindow(object): +class BlobWindow: """The top-level window and its mouse control""" def __init__(self, model, view, controller): diff --git a/util/o3-pipeview.py b/util/o3-pipeview.py index fe49706dad..a821a13b4f 100755 --- a/util/o3-pipeview.py +++ b/util/o3-pipeview.py @@ -38,9 +38,9 @@ # Pipeline activity viewer for the O3 CPU model. import argparse +import copy import os import sys -import copy # Temporary storage for instructions. The queue is filled in out-of-order # until it reaches 'max_threshold' number of instructions. It is then @@ -511,7 +511,7 @@ def main(): sys.exit(1) # Process trace print("Processing trace... ", end=" ") - with open(args.tracefile, "r") as trace: + with open(args.tracefile) as trace: with open(args.outfile, "w") as out: process_trace( trace, diff --git a/util/obtain-resource.py b/util/obtain-resource.py new file mode 100644 index 0000000000..b11ee328de --- /dev/null +++ b/util/obtain-resource.py @@ -0,0 +1,88 @@ +# Copyright (c) 2023 The Regents of the University of California +# All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +Obtain a resource from gem5 resource. + +Usage +----- + +```sh +scons build/ALL/gem5.opt -j$(nproc) +build/ALL/gem5.opt util/obtain-resource.py [-p ] [-q] +# Example: +# `build/ALL/gem5.opt util/obtain-resource.py arm-hello64-static -p arm-hello` +# This will download the resource with id `arm-hello64-static` to the +# "arm-hello" in the CWD. +``` +""" + +if __name__ == "__m5_main__": + import argparse + + from gem5.resources.resource import obtain_resource + + parser = argparse.ArgumentParser() + + parser.add_argument( + "id", + type=str, + help="The resource id to download.", + ) + + parser.add_argument( + "-p", + "--path", + type=str, + required=False, + help="The path the resource is to be downloaded to. If not specified, " + "the resource will be downloaded to the default location in the " + "gem5 local cache of resources", + ) + + parser.add_argument( + "-q", + "--quiet", + action="store_true", + default=False, + help="Suppress output.", + ) + + args = parser.parse_args() + + resource = obtain_resource( + resource_id=args.id, + quiet=args.quiet, + to_path=args.path, + ) + + if not args.quiet: + print(f"Resource at: '" + str(resource.get_local_path()) + "'") + + exit(0) + +print("Error: This script is meant to be run with the gem5 binary") +exit(1) diff --git a/util/offline_db/README.md b/util/offline_db/README.md new file mode 100644 index 0000000000..869f79c7ab --- /dev/null +++ b/util/offline_db/README.md @@ -0,0 +1,45 @@ + +# Offline Database Utility + +This Python script makes local copies of all the gem5 Resource's external files and data. +This is useful for cases where an internet connection is not permitted. +With this script users can cache a local copy which can be used by gem5. + +Two interconnected parts of the gem5 infrastructure are cached using this script. +They are: + +1. **The gem5 Resources database**: When the gem5 Standard Library attempts to construct a gem5 Resource it will reference that resource's entry in the gem5 MongoDB database. +It then uses this data to construct the resource. +With this script we download this database as a JSON file for gem5 to use in-place of the database. + +2. **gem5 Resource Remote Files**: Resources can (and often do) require files to be downloaded for them to function correctly in gem5. +Once the gem5 Resource is constructed from the database entry the gem5 Resource may download the remote files it requires. +As a simple example, a `DiskImageResource` will contain disk image partitioning information necessary for a gem5 simulation to be setup correctly. +The disk image file is a remote file which is downloaded as this information cannot be stored directly in the database. + +The location of these file is stored within the resource's database entry as a URL. + +To create a local cache of these remote files we download all the remote files and, when creating the JSON file from the database, update the URLs to remote files to File URIs to their local equivalents. + +Once this script is finished running the user will have a directory of all the remote gem5 Resources files and a JSON file representation of the database, with the remote files' URLs updated to local path URIs. + +## Running the Script + +### Arguments +- `--config-file-path`: Filepath to the gem5 config file. +- `--output-dir`: Output directory path (default: current working directory). + + +```bash +python3 ./get-resources-from-db.py [--config-file-path CONFIG_FILE_PATH] [--output_dir OUTPUT_DIR] +``` + +## Functionality + +### What is the config file? + +The resources config file represents all the sources. + +The gem5_default_config adds the gem5 resources MongoDB Atlas database as a datasource. + +Documentation on setting up your own data sources can be found here: https://www.gem5.org/documentation/gem5-stdlib/using-local-resources diff --git a/util/offline_db/gem5_default_config.json b/util/offline_db/gem5_default_config.json new file mode 100644 index 0000000000..73c4eeead0 --- /dev/null +++ b/util/offline_db/gem5_default_config.json @@ -0,0 +1,13 @@ +{ + "sources": { + "gem5-resources": { + "dataSource": "gem5-vision", + "database": "gem5-vision", + "collection": "resources", + "url": "https://data.mongodb-api.com/app/data-ejhjf/endpoint/data/v1", + "authUrl": "https://realm.mongodb.com/api/client/v2.0/app/data-ejhjf/auth/providers/api-key/login", + "apiKey": "OIi5bAP7xxIGK782t8ZoiD2BkBGEzMdX3upChf9zdCxHSnMoiTnjI22Yw5kOSgy9", + "isMongo": true + } + } +} diff --git a/util/offline_db/get-resources-from-db.py b/util/offline_db/get-resources-from-db.py new file mode 100644 index 0000000000..4eeca46079 --- /dev/null +++ b/util/offline_db/get-resources-from-db.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python3 +# Copyright (c) 2023 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" +This is a script to get all resources from the database and save them to +a json file and download all resources to a directory. The resources in +the resources.json file that is generated will be updated to point to the +local resources instead of the database resources. This script is used to +create the offline database. To use the resources.json with gem5 you need +to update the gem5 config to point to the resources.json file. +""" + +import argparse +import hashlib +import json +from pathlib import Path +from typing import ( + Dict, + List, + TextIO, +) +from urllib import ( + parse, + request, +) + + +def get_token(auth_url: str, api_key: str) -> str: + """ + This function gets the token from the database using the api key + :param auth_url: Authentication url to use Atlas data API + :param api_key: API key to access the database + :return: Token to access the database + """ + + data = {"key": api_key} + data = json.dumps(data).encode("utf-8") + + req = request.Request( + auth_url, data=data, headers={"content-type": "application/json"} + ) + + try: + response = request.urlopen(req) + except Exception as e: + raise Exception( + "Failed to obtain token via url request. " + f"Exception thrown:\n{e}" + ) + + json_response = json.loads(response.read().decode("utf-8")) + assert "access_token" in json_response, ( + "'access_token' not key in" + "json dictionary. JSON: \n" + f"{json_response}" + ) + token = json_response["access_token"] + + return token + + +def get_all_resources( + url: str, data_source: str, collection: str, database: str, token: str +) -> List: + """ + This function gets all the JSON objects for resources from the database + :param url: Database url to use Atlas data API + :param data_source: Data source name for the mongoDB database + :param collection: Collection name for the mongoDB database + :param database: Database name for the mongoDB database + :param token: Token to access the database + + :return: List of JSON objects for resources + """ + + url = f"{url}/action/find" + data = { + "dataSource": data_source, + "collection": collection, + "database": database, + } + data = json.dumps(data).encode("utf-8") + headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + } + + req = request.Request(url, data=data, headers=headers) + + try: + response = request.urlopen(req) + except Exception as e: + raise Exception( + "Failed to obtain resources via url request. " + f"Exception thrown:\n{e}" + ) + + json_response = json.loads(response.read().decode("utf-8")) + assert "documents" in json_response, ( + "'documents' not a key in " + "json dictionary. JSON: \n" + f"{json_response}" + ) + resources = json_response["documents"] + + return resources + + +def save_resources_to_file(resources: List[Dict], output: TextIO): + """ + This function saves all the JSON objects for resources to a file. + :param resources: List of JSON objects for resources + :param output: Output directory absolute path + """ + + path = output.joinpath("resources.json") + + with open(path, "w") as f: + json.dump(resources, f, indent=4) + + +def get_resource_local_filepath( + resource_id: str, resource_version: str, base_output_path: TextIO +) -> Path: + """ + This function returns the local filepath for a resource + :param resource_url: URL field of the resource + :param base_output_path: Base output directory absolute path + :return: Local filepath for the resource + """ + filename = f"{resource_id}-{resource_version}" + + filepath = Path(base_output_path).joinpath(filename) + + return filepath + + +def download_resources(resources: List[Dict], output: TextIO): + """ + This function downloads the resources which have a url field and + updates the url field to point to the local download of the resource. + :param output: Output directory absolute path + """ + + path = output.joinpath("offline_resources") + if not path.exists(): + path.mkdir() + + for resource in resources: + if "url" in resource.keys(): + url = resource["url"] + filepath = get_resource_local_filepath( + resource["id"], resource["resource_version"], path + ) + + if not filepath.exists() or ( # If the file does not exist + "url-md5sum" + in resource.keys() # If it exist but the md5sum is wrong. + and hashlib.md5(filepath.read_bytes()).hexdigest() + != resource["url-md5sum"] + ): + print(f"Downloading {url} to {filepath}") + request.urlretrieve(url, filepath) + resource["url"] = filepath.absolute().as_uri() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Download all the remote gem5 Resources data and files " + "to be cached and used locally." + ) + + parser.add_argument( + "--config-file-path", + type=str, + default=Path(__file__) + .resolve() + .parent.joinpath("gem5_default_config.json"), + help="Filepath to the gem5 config file", + ) + + parser.add_argument( + "--output-dir", + type=str, + default=Path.cwd(), + help="Output directory path, default is the cwd." + "The resources.json and all resources will be saved in this directory", + ) + args = parser.parse_args() + + output_path = Path(args.output_dir) + + # Get the gem5 config file from the file + + with open(args.config_file_path) as f: + gem5_config_json = json.load(f) + gem5_config_gem5_resources = gem5_config_json["sources"]["gem5-resources"] + + # Parse the gem5 config + db_url = gem5_config_gem5_resources["url"] + data_source = gem5_config_gem5_resources["dataSource"] + collection_name = gem5_config_gem5_resources["collection"] + db_name = gem5_config_gem5_resources["database"] + auth_url = gem5_config_gem5_resources["authUrl"] + api_key = gem5_config_gem5_resources["apiKey"] + + if not output_path.exists(): + output_path = output_path.mkdir(parents=True, exist_ok=True) + + token = get_token(auth_url, api_key) + + resources = get_all_resources( + db_url, + data_source, + collection_name, + db_name, + token, + ) + + download_resources(resources, output_path) + + save_resources_to_file( + resources, + output_path, + ) diff --git a/util/on-chip-network-power-area.py b/util/on-chip-network-power-area.py index 61a316a99c..f5e463b3b9 100644 --- a/util/on-chip-network-power-area.py +++ b/util/on-chip-network-power-area.py @@ -25,8 +25,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import os +import string +import subprocess +import sys from configparser import ConfigParser -import string, sys, subprocess, os # Compile DSENT to generate the Python module and then import it. # This script assumes it is executed from the gem5 root. @@ -55,6 +58,7 @@ os.chdir("../../../") sys.path.append("build/ext/dsent") import dsent + # Parse gem5 config.ini file for the configuration parameters related to # the on-chip network. def parseConfig(config_file): @@ -182,13 +186,12 @@ def parseStats( buffers_per_control_vc, ni_flit_size_bits, ): - # Open the stats.txt file and parse it to for the required numbers # and the number of routers. try: - stats_handle = open(stats_file, "r") + stats_handle = open(stats_file) stats_handle.close() - except IOError: + except OSError: print("Failed to open ", stats_file, " for reading") exit(-1) diff --git a/util/oprofile-top.py b/util/oprofile-top.py index 4d5a693451..a694894473 100755 --- a/util/oprofile-top.py +++ b/util/oprofile-top.py @@ -28,9 +28,10 @@ # Parse sampled function profile output (quick hack). -import sys -import re import getopt +import re +import sys + from categories import * @@ -66,7 +67,7 @@ total = 0 prof = {} linenum = 0 for line in f.readlines(): - line = re.sub("\(no symbols\)", "nosym", line) + line = re.sub(r"\(no symbols\)", "nosym", line) line = re.sub("anonymous.*", "nosym", line) linenum += 1 if linenum < 4: diff --git a/util/plot_dram/PlotPowerStates.py b/util/plot_dram/PlotPowerStates.py index b476a24da1..d22249aa25 100755 --- a/util/plot_dram/PlotPowerStates.py +++ b/util/plot_dram/PlotPowerStates.py @@ -36,11 +36,12 @@ import matplotlib matplotlib.use("Agg") -import matplotlib.pyplot as plt -from matplotlib.font_manager import FontProperties -import numpy as np import os +import matplotlib.pyplot as plt +import numpy as np +from matplotlib.font_manager import FontProperties + # global results dict results = {} idleResults = {} @@ -127,7 +128,7 @@ def plotLowPStates( @param delay_list: list of itt max multipliers (e.g. [1, 20, 200]) """ - stats_file = open(stats_fname, "r") + stats_file = open(stats_fname) global bankUtilValues bankUtilValues = bank_util_list @@ -150,7 +151,6 @@ def plotLowPStates( for delay in delayValues: for bank_util in bankUtilValues: for seq_bytes in seqBytesValues: - for line in stats_file: if "Begin" in line: break @@ -261,7 +261,6 @@ def plotStackedStates(delay, states_list, bottom_state, plot_name, ylabel_str): ind = np.arange(N) for sub_idx, bank_util in enumerate(bankUtilValues): - l_states = {} p_states = {} diff --git a/util/plot_dram/dram_lat_mem_rd_plot.py b/util/plot_dram/dram_lat_mem_rd_plot.py index 0d0e8d052b..a8ef288028 100755 --- a/util/plot_dram/dram_lat_mem_rd_plot.py +++ b/util/plot_dram/dram_lat_mem_rd_plot.py @@ -36,35 +36,35 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. try: - import matplotlib.pyplot as plt import matplotlib as mpl + import matplotlib.pyplot as plt import numpy as np except ImportError: print("Failed to import matplotlib and numpy") exit(-1) -import sys import re +import sys + # This script is intended to post process and plot the output from -# running configs/dram/lat_mem_rd.py, as such it parses the simout and +# running configs/dram/lat_mem_rd.py, as such it parses the simout.txt and # stats.txt to get the relevant data points. def main(): - if len(sys.argv) != 2: print("Usage: ", sys.argv[0], "") exit(-1) try: - stats = open(sys.argv[1] + "/stats.txt", "r") - except IOError: + stats = open(sys.argv[1] + "/stats.txt") + except OSError: print("Failed to open ", sys.argv[1] + "/stats.txt", " for reading") exit(-1) try: - simout = open(sys.argv[1] + "/simout", "r") - except IOError: - print("Failed to open ", sys.argv[1] + "/simout", " for reading") + simout = open(sys.argv[1] + "/simout.txt") + except OSError: + print("Failed to open ", sys.argv[1] + "/simout.txt", " for reading") exit(-1) # Get the address ranges @@ -77,7 +77,7 @@ def main(): if got_ranges: ranges.append(int(line) / 1024) - match = re.match("lat_mem_rd with (\d+) iterations, ranges:.*", line) + match = re.match(r"lat_mem_rd with (\d+) iterations, ranges:.*", line) if match: got_ranges = True iterations = int(match.groups(0)[0]) @@ -85,14 +85,14 @@ def main(): simout.close() if not got_ranges: - print("Failed to get address ranges, ensure simout is up-to-date") + print("Failed to get address ranges, ensure simout.txt is up-to-date") exit(-1) # Now parse the stats raw_rd_lat = [] for line in stats: - match = re.match(".*readLatencyHist::mean\s+(.+)\s+#.*", line) + match = re.match(r".*readLatencyHist::mean\s+(.+)\s+#.*", line) if match: raw_rd_lat.append(float(match.groups(0)[0]) / 1000) stats.close() @@ -122,7 +122,7 @@ def main(): ) exit(-1) - for (r, l) in zip(ranges, final_rd_lat): + for r, l in zip(ranges, final_rd_lat): print(r, round(l, 2)) # lazy version to check if an integer is a power of two diff --git a/util/plot_dram/dram_sweep_plot.py b/util/plot_dram/dram_sweep_plot.py index 1350f7af77..c789ab32a4 100755 --- a/util/plot_dram/dram_sweep_plot.py +++ b/util/plot_dram/dram_sweep_plot.py @@ -36,23 +36,23 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. try: - from mpl_toolkits.mplot3d import Axes3D - from matplotlib import cm import matplotlib.pyplot as plt import numpy as np + from matplotlib import cm + from mpl_toolkits.mplot3d import Axes3D except ImportError: print("Failed to import matplotlib and numpy") exit(-1) -import sys import re +import sys + # Determine the parameters of the sweep from the simout output, and # then parse the stats and plot the 3D surface corresponding to the # different combinations of parallel banks, and stride size, as # generated by the config/dram/sweep.py script def main(): - if len(sys.argv) != 3: print("Usage: ", sys.argv[0], "-u|p|e ") exit(-1) @@ -73,15 +73,15 @@ def main(): mode = sys.argv[1][1] try: - stats = open(sys.argv[2] + "/stats.txt", "r") - except IOError: + stats = open(sys.argv[2] + "/stats.txt") + except OSError: print("Failed to open ", sys.argv[2] + "/stats.txt", " for reading") exit(-1) try: - simout = open(sys.argv[2] + "/simout", "r") - except IOError: - print("Failed to open ", sys.argv[2] + "/simout", " for reading") + simout = open(sys.argv[2] + "/simout.txt") + except OSError: + print("Failed to open ", sys.argv[2] + "/simout.txt", " for reading") exit(-1) # Get the burst size, number of banks and the maximum stride from @@ -90,7 +90,7 @@ def main(): for line in simout: match = re.match( - "DRAM sweep with burst: (\d+), banks: (\d+), max stride: (\d+)", + r"DRAM sweep with burst: (\d+), banks: (\d+), max stride: (\d+)", line, ) if match: @@ -102,7 +102,9 @@ def main(): simout.close() if not got_sweep: - print("Failed to establish sweep details, ensure simout is up-to-date") + print( + "Failed to establish sweep details, ensure simout.txt is up-to-date" + ) exit(-1) # Now parse the stats @@ -111,15 +113,15 @@ def main(): avg_pwr = [] for line in stats: - match = re.match(".*busUtil\s+(\d+\.\d+)\s+#.*", line) + match = re.match(r".*busUtil\s+(\d+\.\d+)\s+#.*", line) if match: bus_util.append(float(match.groups(0)[0])) - match = re.match(".*peakBW\s+(\d+\.\d+)\s+#.*", line) + match = re.match(r".*peakBW\s+(\d+\.\d+)\s+#.*", line) if match: peak_bw.append(float(match.groups(0)[0])) - match = re.match(".*averagePower\s+(\d+\.?\d*)\s+#.*", line) + match = re.match(r".*averagePower\s+(\d+\.?\d*)\s+#.*", line) if match: avg_pwr.append(float(match.groups(0)[0])) stats.close() diff --git a/util/plot_dram/lowp_dram_sweep_plot.py b/util/plot_dram/lowp_dram_sweep_plot.py index dedd1e0c0d..89b69b5116 100755 --- a/util/plot_dram/lowp_dram_sweep_plot.py +++ b/util/plot_dram/lowp_dram_sweep_plot.py @@ -35,11 +35,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import PlotPowerStates as plotter import argparse import os from subprocess import call +import PlotPowerStates as plotter + parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter ) @@ -106,7 +107,7 @@ def main(): filename = plotter.stateTimePlotName(str(delay) + "-") outfile.write(wrapForGraphic(filename, textwidth)) outfile.write(getCaption(delay)) - outfile.write("\end{figure}\n") + outfile.write("\\end{figure}\n") # Energy plots for all delay values outfile.write("\\begin{figure} \n\\centering\n") @@ -140,7 +141,6 @@ def wrapForGraphic(filename, width="1.0"): def startDocText(outfile): - start_stuff = """ \\documentclass[a4paper,landscape,twocolumn]{article} @@ -152,7 +152,6 @@ def startDocText(outfile): def endDocText(outfile): - end_stuff = """ \\end{document} diff --git a/util/protolib.py b/util/protolib.py index dcfb7aabb5..e795625c39 100644 --- a/util/protolib.py +++ b/util/protolib.py @@ -89,9 +89,9 @@ def openFileRd(in_file): # reading the first message. proto_in.seek(1) proto_in.seek(0) - except IOError: + except OSError: proto_in = open(in_file, "rb") - except IOError: + except OSError: print("Failed to open ", in_file, " for reading") exit(-1) return proto_in @@ -125,7 +125,7 @@ def _DecodeVarint32(in_file): return (result, pos) shift += 7 if shift >= 64: - raise IOError("Too many bytes when decoding varint.") + raise OSError("Too many bytes when decoding varint.") def decodeMessage(in_file, message): @@ -140,7 +140,7 @@ def decodeMessage(in_file, message): buf = in_file.read(size) message.ParseFromString(buf) return True - except IOError: + except OSError: return False diff --git a/util/slicc b/util/slicc index d660510b58..14f4c09c54 100755 --- a/util/slicc +++ b/util/slicc @@ -27,7 +27,10 @@ if __name__ == "__main__": import sys - from os.path import dirname, join + from os.path import ( + dirname, + join, + ) base = dirname(__file__) sys.path.insert(1, join(base, "../src/mem")) diff --git a/util/streamline/m5stats2streamline.py b/util/streamline/m5stats2streamline.py index 8dc72bf0f9..ec2dc0e834 100755 --- a/util/streamline/m5stats2streamline.py +++ b/util/streamline/m5stats2streamline.py @@ -58,15 +58,16 @@ # APC project generation based on Gator v17 (DS-5 v5.17) # Subsequent versions should be backward compatible -import re, sys, os -from configparser import ConfigParser -import gzip -import xml.etree.ElementTree as ET -import xml.dom.minidom as minidom -import shutil -import zlib - import argparse +import gzip +import os +import re +import shutil +import sys +import xml.dom.minidom as minidom +import xml.etree.ElementTree as ET +import zlib +from configparser import ConfigParser parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, @@ -126,7 +127,7 @@ parser.add_argument( args = parser.parse_args() -if not re.match("(.*)\.apc", args.output_path): +if not re.match(r"(.*)\.apc", args.output_path): print("ERROR: should end with '.apc'!") sys.exit(1) @@ -143,6 +144,7 @@ num_events = args.num_events start_tick = -1 end_tick = -1 + # Parse gem5 config.ini file to determine some system configurations. # Number of CPUs, L2s, etc. def parseConfig(config_file): @@ -187,7 +189,7 @@ idle_uid = -1 kernel_uid = -1 -class Task(object): +class Task: def __init__(self, uid, pid, tgid, task_name, is_process, tick): if pid == 0: # Idle self.uid = 0 @@ -203,7 +205,7 @@ class Task(object): self.tick = tick # time this task first appeared -class Event(object): +class Event: def __init__(self, tick, task): self.tick = tick self.task = task @@ -221,6 +223,7 @@ def packed32(x): ret = [] more = True while more: + x = int(x) b = x & 0x7F x = x >> 7 if ((x == 0) and ((b & 0x40) == 0)) or ( @@ -383,7 +386,13 @@ def timestampList(x): def writeBinary(outfile, binary_list): for i in binary_list: - outfile.write(f"{i:c}") + if isinstance(i, str): + byteVal = bytes(i, "utf-8") + elif isinstance(i, int): + byteVal = bytes([i]) + else: + byteVal = i + outfile.write(byteVal) ############################################################ @@ -654,14 +663,14 @@ def parseProcessInfo(task_file): sys.exit(1) process_re = re.compile( - "tick=(\d+)\s+(\d+)\s+cpu_id=(\d+)\s+" - + "next_pid=([-\d]+)\s+next_tgid=([-\d]+)\s+next_task=(.*)" + r"tick=(\d+)\s+(\d+)\s+cpu_id=(\d+)\s+" + + r"next_pid=([-\d]+)\s+next_tgid=([-\d]+)\s+next_task=(.*)" ) task_name_failure_warned = False for line in process_file: - match = re.match(process_re, line) + match = re.match(process_re, line.decode()) if match: tick = int(match.group(1)) if start_tick < 0: @@ -805,9 +814,8 @@ def writeXmlFile(xml, filename): # StatsEntry that contains individual statistics -class StatsEntry(object): +class StatsEntry: def __init__(self, name, group, group_index, per_cpu, key): - # Full name of statistics self.name = name @@ -819,12 +827,14 @@ class StatsEntry(object): # Shorter name with "system" stripped off # and symbols converted to alphanumerics - self.short_name = re.sub("system\.", "", name) + self.short_name = re.sub(r"system\.", "", name) self.short_name = re.sub(":", "_", name) # Regex for this stat (string version used to construct union regex) - self.regex_string = "^" + name + "\s+([\d\.]+)" - self.regex = re.compile("^" + name + "\s+([\d\.e\-]+)\s+# (.*)$", re.M) + self.regex_string = "^" + name + r"\s+([\d\.]+)" + self.regex = re.compile( + "^" + name + r"\s+([\d\.e\-]+)\s+# (.*)$", re.M + ) self.description = "" # Whether this stat is use per CPU or not @@ -862,11 +872,11 @@ class StatsEntry(object): print("\t", per_cpu_name) self.per_cpu_regex_string.append( - "^" + per_cpu_name + "\s+[\d\.]+" + "^" + per_cpu_name + r"\s+[\d\.]+" ) self.per_cpu_regex.append( re.compile( - "^" + per_cpu_name + "\s+([\d\.e\-]+)\s+# (.*)$", re.M + "^" + per_cpu_name + r"\s+([\d\.e\-]+)\s+# (.*)$", re.M ) ) self.values.append([]) @@ -881,7 +891,7 @@ class StatsEntry(object): # Global stats object that contains the list of stats entries # and other utility functions -class Stats(object): +class Stats: def __init__(self): self.stats_list = [] self.tick_list = [] @@ -975,17 +985,17 @@ def readGem5Stats(stats, gem5_stats_file): window_end_regex = re.compile( "^---------- End Simulation Statistics ----------" ) - final_tick_regex = re.compile("^final_tick\s+(\d+)") + final_tick_regex = re.compile(r"^final_tick\s+(\d+)") global ticks_in_ns - sim_freq_regex = re.compile("^sim_freq\s+(\d+)") + sim_freq_regex = re.compile(r"^sim_freq\s+(\d+)") sim_freq = -1 try: if ext == ".gz": f = gzip.open(gem5_stats_file, "r") else: - f = open(gem5_stats_file, "r") + f = open(gem5_stats_file) except: print("ERROR opening stats file", gem5_stats_file, "!") sys.exit(1) @@ -997,7 +1007,7 @@ def readGem5Stats(stats, gem5_stats_file): error = False try: line = f.readline() - except IOError: + except OSError: print("") print("WARNING: IO error in stats file") print("(gzip stream not closed properly?)...continuing for now") @@ -1139,7 +1149,7 @@ def doCapturedXML(output_path, stats): counters = ET.SubElement(xml, "counters") for stat in stats.stats_list: s = ET.SubElement(counters, "counter") - stat_name = re.sub("\.", "_", stat.short_name) + stat_name = re.sub(r"\.", "_", stat.short_name) stat_name = re.sub("#", "", stat_name) s.set("title", stat.group) s.set("name", stat_name) @@ -1266,7 +1276,7 @@ def writeVisualAnnotations(blob, input_path, output_path): frame_count = 0 file_list = os.listdir(frame_path) file_list.sort() - re_fb = re.compile("fb\.(\d+)\.(\d+)\.bmp.gz") + re_fb = re.compile(r"fb\.(\d+)\.(\d+)\.bmp.gz") # Use first non-negative pid to tag visual annotations annotate_pid = -1 diff --git a/util/style.py b/util/style.py index 27d6568ec3..fd1882a4d5 100755 --- a/util/style.py +++ b/util/style.py @@ -38,14 +38,13 @@ import os import sys -from style.file_types import lang_type import style.verifiers -from style.region import all_regions - -from style.style import StdioUI from style import repo +from style.file_types import lang_type +from style.region import all_regions +from style.style import StdioUI -verifier_names = dict([(c.__name__, c) for c in style.verifiers.all_verifiers]) +verifier_names = {c.__name__: c for c in style.verifiers.all_verifiers} def verify( diff --git a/util/style/file_types.py b/util/style/file_types.py index 3a6b93098b..1ce8363a1e 100644 --- a/util/style/file_types.py +++ b/util/style/file_types.py @@ -101,7 +101,7 @@ def lang_type(filename, firstline=None, openok=True): # if a first line was not provided but the file is ok to open, # grab the first line of the file. if firstline is None and openok: - handle = open(filename, "r") + handle = open(filename) firstline = handle.readline() handle.close() diff --git a/util/style/region.py b/util/style/region.py index bd2fc89251..05b0073e50 100644 --- a/util/style/region.py +++ b/util/style/region.py @@ -25,7 +25,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -class _neg_inf(object): +class _neg_inf: """This object always compares less than any other object""" def __repr__(self): @@ -53,7 +53,7 @@ class _neg_inf(object): neg_inf = _neg_inf() -class _pos_inf(object): +class _pos_inf: """This object always compares greater than any other object""" def __repr__(self): @@ -176,7 +176,7 @@ class Region(tuple): return self[1] > other -class Regions(object): +class Regions: """A set of regions (ranges). Basically a region with holes. Includes utility functions to merge regions and figure out if something is in one of the regions.""" diff --git a/util/style/repo.py b/util/style/repo.py index 18079cea6a..758aaa1180 100644 --- a/util/style/repo.py +++ b/util/style/repo.py @@ -35,15 +35,15 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from abc import * import os import subprocess +from abc import * from .region import * from .style import modified_regions -class AbstractRepo(object, metaclass=ABCMeta): +class AbstractRepo(metaclass=ABCMeta): def file_path(self, fname): """Get the absolute path to a file relative within the repository. The input file name must be a valid path within the repository. @@ -76,7 +76,7 @@ class AbstractRepo(object, metaclass=ABCMeta): to the repository root. """ - with open(self.file_path(name), "r") as f: + with open(self.file_path(name)) as f: return f.read() @abstractmethod diff --git a/util/style/sort_includes.py b/util/style/sort_includes.py index 9c532b5669..86d886f213 100644 --- a/util/style/sort_includes.py +++ b/util/style/sort_includes.py @@ -94,7 +94,7 @@ def _include_matcher(keyword="#include", delim="<>"): """Match an include statement and return a (keyword, file, extra) duple, or a touple of None values if there isn't a match.""" - rex = re.compile(r"^(%s)\s*%s(.*)%s(.*)$" % (keyword, delim[0], delim[1])) + rex = re.compile(rf"^({keyword})\s*{delim[0]}(.*){delim[1]}(.*)$") def matcher(context, line): m = rex.match(line) @@ -146,7 +146,7 @@ def _include_matcher_main(): return matcher -class SortIncludes(object): +class SortIncludes: # different types of includes for different sorting of headers # - Python header needs to be first if it exists # <*.h> - system headers (directories before files) @@ -155,17 +155,21 @@ class SortIncludes(object): # "*" - M5 headers (directories before files) includes_re = ( ("main", '""', _include_matcher_main()), - ("python", "<>", _include_matcher_fname("^Python\.h$")), + ("python", "<>", _include_matcher_fname(r"^Python\.h$")), ( "pybind", '""', - _include_matcher_fname("^pybind11/.*\.h$", delim='""'), + _include_matcher_fname(r"^pybind11/.*\.h$", delim='""'), ), ("m5shared", "<>", _include_matcher_fname("^gem5/")), - ("c", "<>", _include_matcher_fname("^.*\.h$")), - ("stl", "<>", _include_matcher_fname("^\w+$")), - ("cc", "<>", _include_matcher_fname("^.*\.(hh|hxx|hpp|H)$")), - ("m5header", '""', _include_matcher_fname("^.*\.h{1,2}$", delim='""')), + ("c", "<>", _include_matcher_fname(r"^.*\.h$")), + ("stl", "<>", _include_matcher_fname(r"^\w+$")), + ("cc", "<>", _include_matcher_fname(r"^.*\.(hh|hxx|hpp|H)$")), + ( + "m5header", + '""', + _include_matcher_fname(r"^.*\.h{1,2}$", delim='""'), + ), ("swig0", "<>", _include_matcher(keyword="%import")), ("swig1", "<>", _include_matcher(keyword="%include")), ("swig2", '""', _include_matcher(keyword="%import", delim='""')), @@ -266,8 +270,7 @@ class SortIncludes(object): # Output pending includes, a new line between, and the # current l. - for include in self.dump_includes(): - yield include + yield from self.dump_includes() yield "" yield line else: @@ -276,8 +279,7 @@ class SortIncludes(object): # We've reached EOF, so dump any pending includes if processing_includes: - for include in self.dump_includes(): - yield include + yield from self.dump_includes() # default language types to try to apply our sorting rules to diff --git a/util/style/style.py b/util/style/style.py index 1c6ed1cf96..add8069bdf 100644 --- a/util/style/style.py +++ b/util/style/style.py @@ -39,10 +39,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from abc import ABCMeta, abstractmethod import difflib import re import sys +from abc import ( + ABCMeta, + abstractmethod, +) from .region import * @@ -52,7 +55,7 @@ trail = re.compile(r"([ \t]+)$") any_control = re.compile(r"\b(if|while|for)([ \t]*)\(") -class UserInterface(object, metaclass=ABCMeta): +class UserInterface(metaclass=ABCMeta): def __init__(self, verbose=False): self.verbose = verbose @@ -118,8 +121,8 @@ style_ignores = [ # Only include Scons files and those with extensions that suggest source # code _re_only( - "^((.*\/)?(SConscript|SConstruct)|" - ".*\.(c|h|cc|hh|cpp|hpp|isa|proto))$" + r"^((.*\/)?(SConscript|SConstruct)|" + r".*\.(c|h|cc|hh|cpp|hpp|isa|proto))$" ), ] diff --git a/util/style/verifiers.py b/util/style/verifiers.py index dbcce1c764..654ce7a720 100644 --- a/util/style/verifiers.py +++ b/util/style/verifiers.py @@ -40,17 +40,22 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from abc import ABCMeta, abstractmethod -from difflib import SequenceMatcher import inspect import os import re import sys +from abc import ( + ABCMeta, + abstractmethod, +) +from difflib import SequenceMatcher -from . import style -from . import sort_includes -from .region import * +from . import ( + sort_includes, + style, +) from .file_types import lang_type +from .region import * def safefix(fix_func): @@ -103,7 +108,7 @@ def _modified_regions(old, new): return regions -class Verifier(object, metaclass=ABCMeta): +class Verifier(metaclass=ABCMeta): """Base class for style verifiers Verifiers check for style violations and optionally fix such @@ -292,10 +297,18 @@ class Whitespace(LineVerifier): - No trailing whitespace """ - languages = set( - ("C", "C++", "swig", "python", "asm", "isa", "scons", "make", "dts") - ) - trail_only = set(("make", "dts")) + languages = { + "C", + "C++", + "swig", + "python", + "asm", + "isa", + "scons", + "make", + "dts", + } + trail_only = {"make", "dts"} test_name = "whitespace" opt_name = "white" @@ -345,7 +358,7 @@ class SortedIncludes(Verifier): opt_name = "include" def __init__(self, *args, **kwargs): - super(SortedIncludes, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.sort_includes = sort_includes.SortIncludes() def check(self, filename, regions=all_regions, fobj=None, silent=False): @@ -404,7 +417,7 @@ class SortedIncludes(Verifier): class ControlSpace(LineVerifier): """Check for exactly one space after if/while/for""" - languages = set(("C", "C++")) + languages = {"C", "C++"} test_name = "spacing after if/while/for" opt_name = "control" @@ -420,11 +433,15 @@ class ControlSpace(LineVerifier): class LineLength(LineVerifier): - languages = set(("C", "C++", "swig", "python", "asm", "isa", "scons")) + languages = {"C", "C++", "swig", "python", "asm", "isa", "scons"} test_name = "line length" opt_name = "length" - def check_line(self, line, **kwargs): + def check_line(self, line, language, **kwargs): + # Ignore line length check for include pragmas of C/C++. + if language in {"C", "C++"}: + if line.startswith("#include"): + return True return style.normalized_len(line) <= 79 def fix(self, filename, regions=all_regions, **kwargs): @@ -435,7 +452,7 @@ class LineLength(LineVerifier): class ControlCharacters(LineVerifier): - languages = set(("C", "C++", "swig", "python", "asm", "isa", "scons")) + languages = {"C", "C++", "swig", "python", "asm", "isa", "scons"} test_name = "control character" opt_name = "ascii" @@ -451,7 +468,7 @@ class ControlCharacters(LineVerifier): class BoolCompare(LineVerifier): - languages = set(("C", "C++", "python")) + languages = {"C", "C++", "python"} test_name = "boolean comparison" opt_name = "boolcomp" @@ -499,22 +516,22 @@ class StructureBraces(LineVerifier): : public BaseClass { """ - languages = set(("C", "C++")) + languages = {"C", "C++"} test_name = "structure opening brace position" opt_name = "structurebrace" # Matches the indentation of the line - regex_indentation = "(?P\s*)" + regex_indentation = r"(?P\s*)" # Matches an optional "typedef" before the keyword - regex_typedef = "(?P(typedef\s+)?)" + regex_typedef = r"(?P(typedef\s+)?)" # Matches the structure's keyword regex_keyword = "(?Pclass|struct|enum|union)" # A negative lookahead to avoid incorrect matches with variable's names # e.g., "classifications = {" should not be fixed here. - regex_avoid = "(?![^\{\s])" + regex_avoid = r"(?![^\{\s])" # Matches anything after the keyword and before the opening brace. # e.g., structure name, base type, type of inheritance, etc - regex_name = "(?P[^\{]*)" + regex_name = r"(?P[^\{]*)" # Matches anything after the opening brace, which should be # parsed recursively regex_extra = "(?P.*)$" @@ -525,7 +542,7 @@ class StructureBraces(LineVerifier): + regex_keyword + regex_avoid + regex_name - + "\{" + + r"\{" + regex_extra ) diff --git a/util/systemc/gem5_within_systemc/README b/util/systemc/gem5_within_systemc/README index bcaaceeb77..8d396c15ad 100644 --- a/util/systemc/gem5_within_systemc/README +++ b/util/systemc/gem5_within_systemc/README @@ -36,12 +36,13 @@ present in the "build" directory. > cd ../../.. > scons build/ARM/gem5.opt -> scons --with-cxx-config --without-python --without-tcmalloc USE_SYSTEMC=0 \ +> scons setconfig build/ARM USE_SYSTEMC=n +> scons --with-cxx-config --without-python --without-tcmalloc \ > --duplicate-source build/ARM/libgem5_opt.so > cd util/systemc Note: For MAC / OSX this command should be used: -> scons --with-cxx-config --without-python --without-tcmalloc USE_SYSTEMC=0 \ +> scons --with-cxx-config --without-python --without-tcmalloc \ > --duplicate-sources build/ARM/libgem5_opt.dylib Set a proper LD_LIBRARY_PATH e.g. for bash: @@ -67,7 +68,7 @@ Then run make: Make a config file for the C++-configured gem5 using normal gem5 -> ../../../build/ARM/gem5.opt ../../../configs/example/se.py -c \ +> ../../../build/ARM/gem5.opt ../../../configs/deprecated/example/se.py -c \ > ../../../tests/test-progs/hello/bin/arm/linux/hello The binary 'gem5.opt.cxx' can now be used to load in the generated config diff --git a/util/systemc/systemc_within_gem5/systemc_gem5_tlm/SystemC_Example.py b/util/systemc/systemc_within_gem5/systemc_gem5_tlm/SystemC_Example.py index f2bee1653a..1e8269c92a 100644 --- a/util/systemc/systemc_within_gem5/systemc_gem5_tlm/SystemC_Example.py +++ b/util/systemc/systemc_within_gem5/systemc_gem5_tlm/SystemC_Example.py @@ -23,13 +23,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from m5.params import * -from m5.SimObject import SimObject -from m5.params import * -from m5.proxy import * - from m5.objects.SystemC import SystemC_ScModule from m5.objects.Tlm import TlmTargetSocket +from m5.params import * +from m5.proxy import * +from m5.SimObject import SimObject + # This class is a subclass of sc_module, and all the special magic which makes # that work is handled in the base classes. diff --git a/util/systemc/systemc_within_gem5/systemc_gem5_tlm/config.py b/util/systemc/systemc_within_gem5/systemc_gem5_tlm/config.py index 71529ba879..36686bf89b 100755 --- a/util/systemc/systemc_within_gem5/systemc_gem5_tlm/config.py +++ b/util/systemc/systemc_within_gem5/systemc_gem5_tlm/config.py @@ -28,8 +28,8 @@ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import os -import m5 +import m5 from m5.objects import * # Create a config to be used by the traffic generator diff --git a/util/systemc/systemc_within_gem5/systemc_sc_main/config.py b/util/systemc/systemc_within_gem5/systemc_sc_main/config.py index 454c3b5813..81c9e15f56 100755 --- a/util/systemc/systemc_within_gem5/systemc_sc_main/config.py +++ b/util/systemc/systemc_within_gem5/systemc_sc_main/config.py @@ -24,10 +24,13 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import argparse -import m5 import sys -from m5.objects import SystemC_Kernel, Root +import m5 +from m5.objects import ( + Root, + SystemC_Kernel, +) # pylint:disable=unused-variable diff --git a/util/systemc/systemc_within_gem5/systemc_simple_object/SystemC_Example.py b/util/systemc/systemc_within_gem5/systemc_simple_object/SystemC_Example.py index 66b05bf79f..7832328c0d 100644 --- a/util/systemc/systemc_within_gem5/systemc_simple_object/SystemC_Example.py +++ b/util/systemc/systemc_within_gem5/systemc_simple_object/SystemC_Example.py @@ -23,10 +23,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from m5.objects.SystemC import SystemC_ScModule from m5.params import * from m5.SimObject import SimObject -from m5.objects.SystemC import SystemC_ScModule # This class is a subclass of sc_module, and all the special magic which makes # that work is handled in the base classes. diff --git a/util/systemc/systemc_within_gem5/systemc_simple_object/config.py b/util/systemc/systemc_within_gem5/systemc_simple_object/config.py index 8a86e1fb01..c71c5ac3db 100755 --- a/util/systemc/systemc_within_gem5/systemc_simple_object/config.py +++ b/util/systemc/systemc_within_gem5/systemc_simple_object/config.py @@ -25,10 +25,15 @@ import argparse -import m5 import sys -from m5.objects import SystemC_Kernel, Root, SystemC_Printer, Gem5_Feeder +import m5 +from m5.objects import ( + Gem5_Feeder, + Root, + SystemC_Kernel, + SystemC_Printer, +) # pylint:disable=unused-variable diff --git a/util/systemc/systemc_within_gem5/systemc_tlm/config.py b/util/systemc/systemc_within_gem5/systemc_tlm/config.py index 454c3b5813..81c9e15f56 100755 --- a/util/systemc/systemc_within_gem5/systemc_tlm/config.py +++ b/util/systemc/systemc_within_gem5/systemc_tlm/config.py @@ -24,10 +24,13 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import argparse -import m5 import sys -from m5.objects import SystemC_Kernel, Root +import m5 +from m5.objects import ( + Root, + SystemC_Kernel, +) # pylint:disable=unused-variable diff --git a/util/tlm/README b/util/tlm/README index 3ae43c5909..1dbfe41ae7 100644 --- a/util/tlm/README +++ b/util/tlm/README @@ -93,13 +93,14 @@ without python. > cd ../.. > scons build/ARM/gem5.opt -> scons --with-cxx-config --without-python --without-tcmalloc USE_SYSTEMC=0 \ -> build/ARM/libgem5_opt.so +> scons setconfig build/ARM USE_SYSTEMC=n +> scons --with-cxx-config --without-python --without-tcmalloc \ +> --duplicate-sources build/ARM/libgem5_opt.so > cd util/tlm Note: For MAC / OSX this command should be used: -> scons --with-cxx-config --without-python --without-tcmalloc USE_SYSTEMC=0 \ -> build/ARM/libgem5_opt.dylib +> scons --with-cxx-config --without-python --without-tcmalloc \ +> --duplicate-sources build/ARM/libgem5_opt.dylib To build all sources of the SystemC binding and the examples simply run scons: diff --git a/util/tlm/conf/tlm_elastic_slave.py b/util/tlm/conf/tlm_elastic_slave.py index 30a412b2f8..8614eae26b 100644 --- a/util/tlm/conf/tlm_elastic_slave.py +++ b/util/tlm/conf/tlm_elastic_slave.py @@ -29,9 +29,11 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import m5 - from m5.objects import * -from m5.util import addToPath, fatal +from m5.util import ( + addToPath, + fatal, +) addToPath("../../../configs/common/") @@ -65,7 +67,7 @@ from Caches import * # Setup System: system = System( - cpu=TraceCPU(cpu_id=0), + cpu=TraceCPU(), mem_mode="timing", mem_ranges=[AddrRange("512MB")], cache_line_size=64, @@ -89,8 +91,7 @@ system.cpu_clk_domain = SrcClockDomain( clock="1GHz", voltage_domain=system.cpu_voltage_domain ) -# Setup CPU and its L1 caches: -system.cpu.createInterruptController() +# Setup CPU's L1 caches: system.cpu.icache = L1_ICache(size="32kB") system.cpu.dcache = L1_DCache(size="32kB") system.cpu.icache.cpu_side = system.cpu.icache_port @@ -114,10 +115,10 @@ system.tlm.port_data = "transactor" # Connect everything: system.membus = SystemXBar() -system.system_port = system.membus.slave -system.cpu.icache.mem_side = system.membus.slave -system.cpu.dcache.mem_side = system.membus.slave -system.membus.master = system.tlm.port +system.system_port = system.membus.cpu_side_ports +system.cpu.icache.mem_side = system.membus.cpu_side_ports +system.cpu.dcache.mem_side = system.membus.cpu_side_ports +system.membus.mem_side_ports = system.tlm.port # Start the simulation: root = Root(full_system=False, system=system) diff --git a/util/tlm/conf/tlm_master.py b/util/tlm/conf/tlm_master.py index a3782a72cc..a7e5d8b99e 100644 --- a/util/tlm/conf/tlm_master.py +++ b/util/tlm/conf/tlm_master.py @@ -29,11 +29,11 @@ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import os + import m5 from m5.objects import * -import os - # Base System Architecture: # +-----+ ^ # | TLM | | TLM World diff --git a/util/tlm/examples/tlm_elastic_slave_with_l2.py b/util/tlm/examples/tlm_elastic_slave_with_l2.py index c72bc8976c..5bb15b520d 100644 --- a/util/tlm/examples/tlm_elastic_slave_with_l2.py +++ b/util/tlm/examples/tlm_elastic_slave_with_l2.py @@ -29,9 +29,11 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import m5 - from m5.objects import * -from m5.util import addToPath, fatal +from m5.util import ( + addToPath, + fatal, +) addToPath("../../../configs/common/") @@ -72,7 +74,7 @@ from Caches import * # Setup System: system = System( - cpu=TraceCPU(cpu_id=0), + cpu=TraceCPU(), mem_mode="timing", mem_ranges=[AddrRange("1024MB")], cache_line_size=64, @@ -96,8 +98,7 @@ system.cpu_clk_domain = SrcClockDomain( clock="1GHz", voltage_domain=system.cpu_voltage_domain ) -# Setup CPU and its L1 caches: -system.cpu.createInterruptController() +# Setup CPU's L1 caches: system.cpu.icache = L1_ICache(size="32kB") system.cpu.dcache = L1_DCache(size="32kB") system.cpu.icache.cpu_side = system.cpu.icache_port @@ -122,12 +123,12 @@ system.tlm.port_data = "transactor1" # Connect everything: system.membus = SystemXBar() -system.system_port = system.membus.slave -system.cpu.icache.mem_side = system.tol2bus.slave -system.cpu.dcache.mem_side = system.tol2bus.slave -system.tol2bus.master = system.l2cache.cpu_side -system.l2cache.mem_side = system.membus.slave -system.membus.master = system.tlm.port +system.system_port = system.membus.cpu_side_ports +system.cpu.icache.mem_side = system.tol2bus.cpu_side_ports +system.cpu.dcache.mem_side = system.tol2bus.cpu_side_ports +system.tol2bus.mem_side_ports = system.l2cache.cpu_side +system.l2cache.mem_side = system.membus.cpu_side_ports +system.membus.mem_side_ports = system.tlm.port # Start the simulation: root = Root(full_system=False, system=system) diff --git a/util/update-copyright.py b/util/update-copyright.py index 1c3fd611ac..3ad0c92466 100755 --- a/util/update-copyright.py +++ b/util/update-copyright.py @@ -41,7 +41,6 @@ import subprocess import sys import git_filter_repo - import update_copyright parser = argparse.ArgumentParser( diff --git a/util/update_copyright/__init__.py b/util/update_copyright/__init__.py index 3b5a534696..a8bf92494b 100644 --- a/util/update_copyright/__init__.py +++ b/util/update_copyright/__init__.py @@ -79,7 +79,7 @@ def _update_copyright_years(m, cur_year, org_bytes): def update_copyright(data, cur_year, org_bytes): update_copyright_regexp = re.compile( - b" Copyright \\(c\\) ([0-9,\- ]+) " + org_bytes + b"\n", re.IGNORECASE + b" Copyright \\(c\\) ([0-9,\\- ]+) " + org_bytes + b"\n", re.IGNORECASE ) return update_copyright_regexp.sub( lambda m: _update_copyright_years(m, cur_year, org_bytes),