Repository: tukl-msd/DRAMPower Branch: master Commit: 8b0dedaf45f9 Files: 255 Total size: 1.5 MB Directory structure: gitextract_nfddae32/ ├── .clang-format ├── .clang-tidy ├── .github/ │ └── workflows/ │ └── CI.yml ├── .gitignore ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE.txt ├── README.md ├── benches/ │ ├── CMakeLists.txt │ ├── main.cpp │ └── simulation.cpp ├── cmake/ │ ├── build_source_group.cmake │ ├── diagnostics_print.cmake │ ├── enable_clang_format.cmake │ ├── enable_clang_tidy.cmake │ └── enable_cppcheck.cmake ├── lib/ │ ├── cli11/ │ │ └── CMakeLists.txt │ └── spdlog/ │ └── CMakeLists.txt ├── src/ │ ├── DRAMPower/ │ │ ├── CMakeLists.txt │ │ └── DRAMPower/ │ │ ├── Exceptions.h │ │ ├── Types.h │ │ ├── command/ │ │ │ ├── CmdType.h │ │ │ ├── Command.cpp │ │ │ ├── Command.h │ │ │ ├── Pattern.cpp │ │ │ └── Pattern.h │ │ ├── data/ │ │ │ ├── energy.cpp │ │ │ ├── energy.h │ │ │ └── stats.h │ │ ├── dram/ │ │ │ ├── Bank.h │ │ │ ├── Interface.cpp │ │ │ ├── Interface.h │ │ │ ├── Rank.cpp │ │ │ ├── Rank.h │ │ │ └── dram_base.h │ │ ├── memspec/ │ │ │ ├── MemSpec.h │ │ │ ├── MemSpecDDR4.cpp │ │ │ ├── MemSpecDDR4.h │ │ │ ├── MemSpecDDR5.cpp │ │ │ ├── MemSpecDDR5.h │ │ │ ├── MemSpecLPDDR4.cpp │ │ │ ├── MemSpecLPDDR4.h │ │ │ ├── MemSpecLPDDR5.cpp │ │ │ └── MemSpecLPDDR5.h │ │ ├── simconfig/ │ │ │ └── simconfig.h │ │ ├── standards/ │ │ │ ├── ddr4/ │ │ │ │ ├── DDR4.cpp │ │ │ │ ├── DDR4.h │ │ │ │ ├── DDR4Core.cpp │ │ │ │ ├── DDR4Core.h │ │ │ │ ├── DDR4Interface.cpp │ │ │ │ ├── DDR4Interface.h │ │ │ │ ├── core_calculation_DDR4.cpp │ │ │ │ ├── core_calculation_DDR4.h │ │ │ │ ├── interface_calculation_DDR4.cpp │ │ │ │ ├── interface_calculation_DDR4.h │ │ │ │ └── types.h │ │ │ ├── ddr5/ │ │ │ │ ├── DDR5.cpp │ │ │ │ ├── DDR5.h │ │ │ │ ├── DDR5Core.cpp │ │ │ │ ├── DDR5Core.h │ │ │ │ ├── DDR5Interface.cpp │ │ │ │ ├── DDR5Interface.h │ │ │ │ ├── core_calculation_DDR5.cpp │ │ │ │ ├── core_calculation_DDR5.h │ │ │ │ ├── interface_calculation_DDR5.cpp │ │ │ │ ├── interface_calculation_DDR5.h │ │ │ │ └── types.h │ │ │ ├── lpddr4/ │ │ │ │ ├── LPDDR4.cpp │ │ │ │ ├── LPDDR4.h │ │ │ │ ├── LPDDR4Core.cpp │ │ │ │ ├── LPDDR4Core.h │ │ │ │ ├── LPDDR4Interface.cpp │ │ │ │ ├── LPDDR4Interface.h │ │ │ │ ├── core_calculation_LPDDR4.cpp │ │ │ │ ├── core_calculation_LPDDR4.h │ │ │ │ ├── interface_calculation_LPDDR4.cpp │ │ │ │ ├── interface_calculation_LPDDR4.h │ │ │ │ └── types.h │ │ │ ├── lpddr5/ │ │ │ │ ├── LPDDR5.cpp │ │ │ │ ├── LPDDR5.h │ │ │ │ ├── LPDDR5Core.cpp │ │ │ │ ├── LPDDR5Core.h │ │ │ │ ├── LPDDR5Interface.cpp │ │ │ │ ├── LPDDR5Interface.h │ │ │ │ ├── core_calculation_LPDDR5.cpp │ │ │ │ ├── core_calculation_LPDDR5.h │ │ │ │ ├── interface_calculation_LPDDR5.cpp │ │ │ │ ├── interface_calculation_LPDDR5.h │ │ │ │ └── types.h │ │ │ └── test_accessor.h │ │ └── util/ │ │ ├── Deserialize.h │ │ ├── ImplicitCommandHandler.h │ │ ├── PatternHandler.h │ │ ├── RegisterHelper.h │ │ ├── Router.h │ │ ├── Serialize.h │ │ ├── binary_ops.h │ │ ├── burst_storage.h │ │ ├── bus.cpp │ │ ├── bus.h │ │ ├── bus_types.h │ │ ├── cli_architecture_config.h │ │ ├── clock.h │ │ ├── command_counter.h │ │ ├── cycle_stats.h │ │ ├── databus.h │ │ ├── databus_presets.h │ │ ├── databus_types.h │ │ ├── dbi.h │ │ ├── dbialgos.h │ │ ├── dbihelpers.h │ │ ├── dbitypes.h │ │ ├── dynamic_bitset.h │ │ ├── extension_base.h │ │ ├── extension_manager.h │ │ ├── extension_manager_static.h │ │ ├── extensions.cpp │ │ ├── extensions.h │ │ ├── pending_stats.h │ │ ├── pin.h │ │ ├── pin_types.h │ │ └── sub_bitset.h │ └── cli/ │ ├── CMakeLists.txt │ ├── lib/ │ │ ├── CMakeLists.txt │ │ └── DRAMPower/ │ │ └── cli/ │ │ ├── config.h │ │ ├── csv.hpp │ │ ├── run.cpp │ │ ├── run.hpp │ │ ├── util.cpp │ │ └── util.hpp │ └── main/ │ ├── CMakeLists.txt │ ├── main.cpp │ ├── validators.cpp │ └── validators.h └── tests/ ├── CMakeLists.txt ├── tests_drampower/ │ ├── CMakeLists.txt │ ├── base/ │ │ ├── test_ddr_base.cpp │ │ ├── test_ddr_data.cpp │ │ ├── test_ddr_serialize.cpp │ │ └── test_pattern_pre_cycles.cpp │ ├── core/ │ │ ├── DDR4/ │ │ │ ├── ddr4_multidevice_tests.cpp │ │ │ ├── ddr4_multirank_tests.cpp │ │ │ ├── ddr4_test_pattern_0.cpp │ │ │ ├── ddr4_test_pattern_1.cpp │ │ │ ├── ddr4_test_pattern_10.cpp │ │ │ ├── ddr4_test_pattern_11.cpp │ │ │ ├── ddr4_test_pattern_12.cpp │ │ │ ├── ddr4_test_pattern_13.cpp │ │ │ ├── ddr4_test_pattern_14.cpp │ │ │ ├── ddr4_test_pattern_15.cpp │ │ │ ├── ddr4_test_pattern_2.cpp │ │ │ ├── ddr4_test_pattern_3.cpp │ │ │ ├── ddr4_test_pattern_4.cpp │ │ │ ├── ddr4_test_pattern_5.cpp │ │ │ ├── ddr4_test_pattern_6.cpp │ │ │ ├── ddr4_test_pattern_7.cpp │ │ │ ├── ddr4_test_pattern_8.cpp │ │ │ └── ddr4_test_pattern_9.cpp │ │ ├── DDR5/ │ │ │ ├── ddr5_multidevice_tests.cpp │ │ │ ├── ddr5_multirank_tests.cpp │ │ │ ├── ddr5_test_pattern_0.cpp │ │ │ ├── ddr5_test_pattern_1.cpp │ │ │ ├── ddr5_test_pattern_10.cpp │ │ │ ├── ddr5_test_pattern_11.cpp │ │ │ ├── ddr5_test_pattern_12.cpp │ │ │ ├── ddr5_test_pattern_13.cpp │ │ │ ├── ddr5_test_pattern_14.cpp │ │ │ ├── ddr5_test_pattern_15.cpp │ │ │ ├── ddr5_test_pattern_16.cpp │ │ │ ├── ddr5_test_pattern_17.cpp │ │ │ ├── ddr5_test_pattern_18.cpp │ │ │ ├── ddr5_test_pattern_2.cpp │ │ │ ├── ddr5_test_pattern_3.cpp │ │ │ ├── ddr5_test_pattern_4.cpp │ │ │ ├── ddr5_test_pattern_5.cpp │ │ │ ├── ddr5_test_pattern_6.cpp │ │ │ ├── ddr5_test_pattern_7.cpp │ │ │ ├── ddr5_test_pattern_8.cpp │ │ │ └── ddr5_test_pattern_9.cpp │ │ ├── LPDDR4/ │ │ │ ├── lpddr4_multidevice_tests.cpp │ │ │ ├── lpddr4_multirank_tests.cpp │ │ │ ├── lpddr4_test_pattern_0.cpp │ │ │ ├── lpddr4_test_pattern_1.cpp │ │ │ ├── lpddr4_test_pattern_10.cpp │ │ │ ├── lpddr4_test_pattern_11.cpp │ │ │ ├── lpddr4_test_pattern_12.cpp │ │ │ ├── lpddr4_test_pattern_13.cpp │ │ │ ├── lpddr4_test_pattern_14.cpp │ │ │ ├── lpddr4_test_pattern_15.cpp │ │ │ ├── lpddr4_test_pattern_16.cpp │ │ │ ├── lpddr4_test_pattern_17.cpp │ │ │ ├── lpddr4_test_pattern_18.cpp │ │ │ ├── lpddr4_test_pattern_2.cpp │ │ │ ├── lpddr4_test_pattern_3.cpp │ │ │ ├── lpddr4_test_pattern_4.cpp │ │ │ ├── lpddr4_test_pattern_5.cpp │ │ │ ├── lpddr4_test_pattern_6.cpp │ │ │ ├── lpddr4_test_pattern_7.cpp │ │ │ ├── lpddr4_test_pattern_8.cpp │ │ │ └── lpddr4_test_pattern_9.cpp │ │ └── LPDDR5/ │ │ ├── lpddr5_multidevice_tests.cpp │ │ ├── lpddr5_multirank_tests.cpp │ │ ├── lpddr5_test_pattern_0.cpp │ │ ├── lpddr5_test_pattern_1.cpp │ │ ├── lpddr5_test_pattern_10.cpp │ │ ├── lpddr5_test_pattern_11.cpp │ │ ├── lpddr5_test_pattern_12.cpp │ │ ├── lpddr5_test_pattern_13.cpp │ │ ├── lpddr5_test_pattern_14.cpp │ │ ├── lpddr5_test_pattern_15.cpp │ │ ├── lpddr5_test_pattern_16.cpp │ │ ├── lpddr5_test_pattern_17.cpp │ │ ├── lpddr5_test_pattern_18.cpp │ │ ├── lpddr5_test_pattern_19.cpp │ │ ├── lpddr5_test_pattern_2.cpp │ │ ├── lpddr5_test_pattern_20.cpp │ │ ├── lpddr5_test_pattern_21.cpp │ │ ├── lpddr5_test_pattern_3.cpp │ │ ├── lpddr5_test_pattern_4.cpp │ │ ├── lpddr5_test_pattern_5.cpp │ │ ├── lpddr5_test_pattern_6.cpp │ │ ├── lpddr5_test_pattern_7.cpp │ │ ├── lpddr5_test_pattern_8.cpp │ │ └── lpddr5_test_pattern_9.cpp │ ├── interface/ │ │ ├── test_dbi_ddr4.cpp │ │ ├── test_dbi_lpddr4.cpp │ │ ├── test_dbi_lpddr5.cpp │ │ ├── test_interface_ddr4.cpp │ │ ├── test_interface_ddr5.cpp │ │ ├── test_interface_lpddr4.cpp │ │ ├── test_interface_lpddr5.cpp │ │ ├── test_togglingrate_ddr4.cpp │ │ ├── test_togglingrate_ddr5.cpp │ │ ├── test_togglingrate_lpddr4.cpp │ │ └── test_togglingrate_lpddr5.cpp │ └── resources/ │ ├── cliconfig.json │ ├── ddr4.csv │ ├── ddr4.json │ ├── ddr5.csv │ ├── ddr5.json │ ├── lpddr4.csv │ ├── lpddr4.json │ ├── lpddr5.csv │ └── lpddr5.json └── tests_misc/ ├── CMakeLists.txt ├── test_bus.cpp ├── test_bus_extended.cpp ├── test_clock.cpp ├── test_dynamic_bitset.cpp ├── test_dynamic_extension_manager.cpp ├── test_interval.cpp ├── test_misc.cpp ├── test_pattern.cpp ├── test_pin.cpp └── test_static_extension_manager.cpp ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-format ================================================ AccessModifierOffset: '-4' AllowShortFunctionsOnASingleLine: InlineOnly AllowShortIfStatementsOnASingleLine: Never BinPackArguments: 'false' BinPackParameters: 'false' BreakBeforeBraces: Allman BreakConstructorInitializers: AfterColon ColumnLimit: '100' IndentWidth: '4' PackConstructorInitializers: CurrentLine PointerAlignment: Left ================================================ FILE: .clang-tidy ================================================ Checks: > clang-diagnostic-*, clang-analyzer-*, modernize-*, bugprone-*, concurrency-*, cppcoreguidelines-*, performance-*, portability-*, readability-*, -modernize-use-trailing-return-type, -readability-braces-around-statements, -readability-identifier-length, -readability-function-cognitive-complexity WarningsAsErrors: '' HeaderFilterRegex: '' AnalyzeTemporaryDtors: false FormatStyle: file ================================================ FILE: .github/workflows/CI.yml ================================================ name: Continuous Integration on: push: branches: [ "master" ] pull_request: branches: [ "master" ] env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release jobs: # Compile and test project on Linux based builds build-gcc: # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. # You can convert this to a matrix build if you need cross-platform coverage. # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type run: cmake --preset ci-unix - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j4 - name: Test working-directory: ${{github.workspace}}/build # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -C ${{env.BUILD_TYPE}} # Compile and test project on Linux based builds using clang build-clang: # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. # You can convert this to a matrix build if you need cross-platform coverage. # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix runs-on: ubuntu-latest env: CC: clang CXX: clang++ steps: - uses: actions/checkout@v4 - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type run: cmake --preset ci-unix - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j4 - name: Test working-directory: ${{github.workspace}}/build # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -C ${{env.BUILD_TYPE}} # Compile and test project on Windows based builds build-msvc: runs-on: windows-latest steps: - uses: actions/checkout@v4 - name: Configure CMake # Configure CMake in a 'build' subdirectory. run: cmake --preset ci-msvc - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j4 - name: Test working-directory: ${{github.workspace}}/build # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -C ${{env.BUILD_TYPE}} ================================================ FILE: .gitignore ================================================ # Prerequisites *.d # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod *.smod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.app # Common project files .DS_Store .project .cproject compile_commands.json .clangd/ .vscode .idea build*/ bin/ *.swp .~* cmake-build-debug cmake-build-release .cache ================================================ FILE: CMakeLists.txt ================================================ # Copyright (c) 2022, 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: # # 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. # # Author: # Thomas Psota # Marco Mörz ######################################### ### DRAMPower ### ######################################### cmake_minimum_required(VERSION 3.22.0) # TODO change for release set(DRAMPOWER_VERSION_MAJOR 6) set(DRAMPOWER_VERSION_MINOR 0) set(DRAMPOWER_VERSION_PATCH 0) set(DRAMPOWER_VERSION_STRING "${DRAMPOWER_VERSION_MAJOR}.${DRAMPOWER_VERSION_MINOR}.${DRAMPOWER_VERSION_PATCH}") add_compile_definitions(DRAMPOWER_VERSION_STRING="${DRAMPOWER_VERSION_STRING}") set(PROJECT_NAME "DRAMPower ${DRAMPOWER_VERSION_STRING}") set(PROJECT_SHORTNAME "DRAMPower") project(${PROJECT_NAME} VERSION ${DRAMPOWER_VERSION_MAJOR}.${DRAMPOWER_VERSION_MINOR}.${DRAMPOWER_VERSION_PATCH} LANGUAGES CXX) if(POLICY CMP0135) cmake_policy(SET CMP0135 NEW) endif() ### CMake settings ### list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") include(build_source_group) include(diagnostics_print) include(enable_clang_format) include(enable_clang_tidy) include(enable_cppcheck) if (PROJECT_IS_TOP_LEVEL) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) endif() # set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) ### Project settings ### message(STATUS "CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}") message(STATUS "CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}") message(STATUS "" ) ### Build options ### option(DRAMPOWER_BUILD_CLI "Build DRAMPower Command Line Tool" ${PROJECT_IS_TOP_LEVEL}) option(DRAMPOWER_BUILD_BENCHMARKS "Build DRAMPower Command Line Tool" OFF) option(DRAMPOWER_BUILD_TESTS "Build DRAMPower unit tests" OFF) ### Compiler optimization settings ### if(PROJECT_IS_TOP_LEVEL) option(OPTIMIZE_FOR_NATIVE "Build with -march=native (overrides CPU_TYPE if enabled)" ON) set(CPU_TYPE "" CACHE STRING "CPU type for -march=CPU_TYPE and -mtune=CPU_TYPE compile options") # Set CPU_TYPE to native if OPTIMIZE_FOR_NATIVE is enabled if(OPTIMIZE_FOR_NATIVE) if(CPU_TYPE) message(NOTICE "OPTIMIZE_FOR_NATIVE is enabled. Overriding CPU_TYPE from \"${CPU_TYPE}\" to \"native\".") endif() set(CPU_TYPE "native") endif() # add CPU_TYPE to cmake cxx flags if(CPU_TYPE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${CPU_TYPE} -mtune=${CPU_TYPE}") endif() endif() # Use sane defaults for FetchContent: # In case we are the top-level project, get everything by default # In case we are included in another project, the user might want to provide their own system dependencies option(DRAMPOWER_USE_FETCH_CONTENT "Enable the FetchContent module" ${PROJECT_IS_TOP_LEVEL}) option(DRAMPOWER_USE_FETCH_CONTENT_INTERNAL "Enable FetchContent to provide internal dependencies" ${DRAMPOWER_USE_FETCH_CONTENT}) option(DRAMPOWER_USE_FETCH_CONTENT_CLI11 "Enable FetchContent to provide CLI11" ${DRAMPOWER_USE_FETCH_CONTENT}) option(DRAMPOWER_USE_FETCH_CONTENT_SPDLOG "Enable FetchContent to provide spdlog" ${DRAMPOWER_USE_FETCH_CONTENT}) option(DRAMPOWER_USE_FETCH_CONTENT_NLOHMANN_JSON "Enable FetchContent to provide nlohmann json" ${DRAMPOWER_USE_FETCH_CONTENT}) ### DRAMPower directories ### set(DRAMPOWER_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src") set(DRAMPOWER_LIBRARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/lib") set(DRAMPOWER_TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests") ############################################### ### Library Settings ### ############################################### ### Detect OS threading library ### find_package(Threads) if (DRAMPOWER_USE_FETCH_CONTENT) include(FetchContent) # nlohmann_json for DRAMUtils if (DRAMPOWER_USE_FETCH_CONTENT_NLOHMANN_JSON) FetchContent_Declare(nlohmann_json URL "https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz" OVERRIDE_FIND_PACKAGE ) FetchContent_MakeAvailable(nlohmann_json) endif() # DRAMUtils if (DRAMPOWER_USE_FETCH_CONTENT_INTERNAL) FetchContent_Declare( DRAMUtils URL "https://github.com/tukl-msd/DRAMUtils/archive/refs/tags/v1.12.1.tar.gz" OVERRIDE_FIND_PACKAGE ) FetchContent_MakeAvailable(DRAMUtils) endif() # cli11 if (DRAMPOWER_USE_FETCH_CONTENT_CLI11 AND DRAMPOWER_BUILD_CLI) add_subdirectory(${DRAMPOWER_LIBRARY_DIR}/cli11) endif() # spdlog if (DRAMPOWER_USE_FETCH_CONTENT_SPDLOG AND (DRAMPOWER_BUILD_CLI OR DRAMPOWER_BUILD_BENCHMARKS)) add_subdirectory(${DRAMPOWER_LIBRARY_DIR}/spdlog) endif() endif() ############################################### ### Source Directory ### ############################################### add_subdirectory(src/DRAMPower) if(DRAMPOWER_BUILD_CLI OR DRAMPOWER_BUILD_BENCHMARKS) add_subdirectory(src/cli) endif() ############################################### ### Test Directory ### ############################################### if(DRAMPOWER_BUILD_TESTS) enable_testing() add_subdirectory(tests) endif() ############################################### ### Benchmark Directory ### ############################################### if(DRAMPOWER_BUILD_BENCHMARKS) add_subdirectory(benches) endif() ############################################### ### Utility Projects ### ############################################### if(${DRAMPOWER_UTILITY_PROJECTS}) enable_clang_format() enable_clang_tidy() enable_cppcheck() endif() ================================================ FILE: CMakePresets.json ================================================ { "version": 3, "cmakeMinimumRequired": { "major": 3, "minor": 14, "patch": 0 }, "configurePresets": [ { "name": "cmake-pedantic", "hidden": true, "warnings": { "dev": true, "deprecated": true, "unusedCli": true, "systemVars": false } }, { "name": "dev-mode", "hidden": true, "binaryDir": "${sourceDir}/build", "inherits": "cmake-pedantic", "cacheVariables": { "DRAMPOWER_BUILD_TESTS": "ON", "CMAKE_BUILD_PARALLEL_LEVEL": "", "DRAMPOWER_BUILD_CLI": "ON", "DRAMPOWER_BUILD_BENCHMARKS": "ON" } }, { "name": "ci-common", "inherits": [ "dev-mode" ], "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } }, { "name": "ci-unix", "generator": "Unix Makefiles", "inherits": [ "ci-common" ] }, { "name": "ci-msvc", "generator": "Visual Studio 17 2022", "inherits": [ "ci-common" ], "cacheVariables": { "CMAKE_CXX_FLAGS": "/DWIN32 /D_WINDOWS /W3 /GR /EHsc /MP" } }, { "name": "dev-unix", "generator": "Ninja", "binaryDir": "${sourceDir}/build", "inherits": [ "dev-mode" ], "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "CMAKE_CXX_FLAGS": "-Wall -Wextra -Wpedantic" } }, { "name": "dev-msvc", "generator": "Visual Studio 17 2022", "inherits": [ "dev-mode" ], "cacheVariables": { "CMAKE_CXX_FLAGS": "/DWIN32 /D_WINDOWS /W3 /GR /EHsc /MP" } } ] } ================================================ FILE: LICENSE.txt ================================================ Copyright (c) 2022, Technische Universität Kaiserslautern, Fraunhofer IESE All rights reserved. This software is released under the BSD 3-Clause License. By using this software, the user implicitly agrees to the licensing terms. 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. ================================================ FILE: README.md ================================================ DRAMPower 5.0 # DRAM Power Model (DRAMPower 6.0.0) - [Releases](#releases) - [Installation of the DRAMPower library](#installation-of-the-drampower-library) - [Installation of the DRAMPower Command Line application](#installation-of-the-drampower-command-line-application) - [Project structure](#project-structure) - [Dependencies](#dependencies) - [Usage of the DRAMPower library](#usage-of-the-drampower-library) - [Usage of the DRAMPower Command Line application](#usage-of-the-drampower-command-line-application) - [Memory Specifications](#memory-specifications) - [Variation-aware Power And Energy Estimation](#variation-aware-power-and-energy-estimation) - [Authors & Acknowledgment](#authors--acknowledgment) - [Contact Information](#contact-information) ## Releases The last official release can be found here: https://github.com/tukl-msd/DRAMPower/releases/tag/v6.0.0 The master branch of the repository should be regarded as the bleeding-edge version, which has all the latest features, but also all the latest bugs. Use at your own discretion. ## Installation of the DRAMPower library CMake is required for the building of DRAMPower. If DRAMPower is the top level project DRAMPower fetches the required dependencies. If DRAMPower is not the top level project, the dependencies have to be fetched manually, provided by the top level project or the DRAMPower library can fetch the dependencies. Fetching content is enabled by the following cmake flag: ```console $ -D DRAMPOWER_USE_FETCH_CONTENT=Y ``` By default DRAMPower fetches all dependencies if `DRAMPOWER_USE_FETCH_CONTENT` is enabled. If you want to disable the fetching of a specific dependency, you can set the respective flag to N. The following flags are available: - `DRAMPOWER_USE_FETCH_CONTENT_INTERNAL` Fetches the internal dependencies of DRAMPower (DRAMUtils) - `DRAMPOWER_USE_FETCH_CONTENT_SPDLOG` Fetches the spdlog library used by the command line application - `DRAMPOWER_USE_FETCH_CONTENT_CLI11` Fetches the CLI11 library used by the command line application - `DRAMPOWER_USE_FETCH_CONTENT_NLOHMANN_JSON` Fetches the nlohmann_json library used by DRAMUtils The following cmake flags show an example of how to disable the fetching of the spdlog and CLI11 libraries: ```console $ -D DRAMPOWER_USE_FETCH_CONTENT=Y -D DRAMPOWER_USE_FETCH_CONTENT_SPDLOG=N -D DRAMPOWER_USE_FETCH_CONTENT_CLI11=N ``` For building DRAMPower clone the repository, or download the zip file of the release you would like to use and use CMake to generate the build files, e.g. ```console $ cd DRAMPower $ cmake -S . -B build $ cmake --build build --parallel ``` Optionally, test cases can be built by toggling the `DRAMPOWER_BUILD_TESTS` flag with CMake. ## Installation of the DRAMPower Command Line application The command line application is automatically build if DRAMPower is the top level project (see [Installation of the DRAMPower library](#installation-of-the-drampower-library)). Alternatively, the `DRAMPOWER_BUILD_CLI` flag can be set to Y to force the build of the command line tool. Clone the repository, or download the zip file of the release you would like to use and use CMake to generate the build files. The following commands force the build of the command line application: ```console $ cd DRAMPower $ cmake -S . -B build -D DRAMPOWER_BUILD_CLI=Y $ cmake --build build ``` ## Project structure The project is structured in a library part, a (optional) test part and an (optional) Command Line application. Integration of DRAMPower in other projects can be easily achieved by including it as a git submodule or by using the CMake FetchContent directive. This repository contains the following sub-directoires DRAMPower # top directory └── cmake # cmake scripts used by configuration step ├── lib # contains bundled dependencies of the project ├── src # top level directory containing the actual sources ├── DRAMPower # source code of the actual DRAMPower library └── cli # the optional Command Line tool └── tests # test cases used by the project ## Dependencies DRAMPower comes bundled with all necessary libraries and no installation of further system packages is required. DRAMPower uses the following libraries: - [DRAMUtils](https://github.com/tukl-msd/DRAMUtils) The DRAMPower cli tool uses the following libraries: - DRAMPower - [DRAMUtils](https://github.com/tukl-msd/DRAMUtils) - [spdlog](https://github.com/gabime/spdlog/releases/tag/v1.9.2) - [CLI11 (CLI11 2.2 Copyright (c) 2017-2024 University of Cincinnati, developed by Henry Schreiner under NSF AWARD 1414736. All rights reserved.)](https://github.com/CLIUtils/CLI11/releases/tag/v2.4.2) ## Usage of the DRAMPower library The project is [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) ready and can be easily included in any other CMake based project. ```cmake include(FetchContent) FetchContent_Declare( drampower GIT_REPOSITORY https://github.com/tukl-msd/DRAMPower GIT_TAG master) FetchContent_MakeAvailable(drampower) ``` The library target DRAMPower is then available to the rest of the project and can be consumed by any other target, e.g. ```cmake add_executable(drampower_app ${SOURCE_FILES}) target_link_libraries(drampower_app PRIVATE DRAMPower::DRAMPower) ``` All constructs inside DRAMPower are exposed through the DRAMPower namespace. Therefore all the following examples will refer to them with the implied usage of their namespace. ```cpp using namespace DRAMPower; ``` To use the actual DRAM calculations, first a memspec has to be supplied. Some example memspecs are supplied in the [tests directory](https://github.com/tukl-msd/DRAMPower/tree/master/tests/tests_drampower/resources). The values in the memspecs are in SI units. An example snippet to initialize a DDR4 based DRAM spec can look like this (only the DRAMPower related includes are shown): ```cpp #include #include #include // optional for simulation with toggling rates #include // Use DRAMUtils library to parse the memspec auto memspec = DRAMUtils::parse_memspec_from_file( std::filesystem::path("tests/tests_drampower/resources/ddr4.json") ); if (!memspec) { throw std::runtime_error("Could not parse memspec"); } // Make the DRAMPower specific memspec from the parsed memspec and initialize the DRAM // The next line can throw std::bad_variant_access if the memspec is not a DDR4 memspec // or an exception derived from std::exception if the json object is not valid DDR4 dram(MemSpecDDR4::from_memspec(*memspec)); // Optionally use toggle rates if no data is available for the traces // dram.setToggleRate(DRAMUtils::Config::ToggleRateDefinition{ // 0.5, // togglingRateRead 0.0 to 1.0 // 0.5, // togglingRateWrite 0.0 to 1.0 // 0.5, // dutyCycleRead 0.0 to 1.0 // 0.5, // dutyCycleWrite 0.0 to 1.0 // TogglingRateIdlePattern::H, // idlePatternRead H, L, Z // TogglingRateIdlePattern::H // idlePatternWrite H, L, Z // }); ``` The created DRAM simulator then has to be fed with commands, e.g. ```cpp #include #define SZ_BITS(x) sizeof(x)*8 uint8_t rd_data[] = { 0, 0, 0, 0, 0, 0, 0, 1, }; std::vector testPattern = { // {timestamp, commandtype, {bank_id, bank_group_id, rank_id, row_id} { 0, CmdType::ACT, {0, 0, 0, 0}}, // {timestamp, commandtype, {bank_id, bank_group_id, rank_id, row_id, column_id}, data, data_size} { 15, CmdType::RD , {0, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}, // optional for simulation with toggling rates (dram.setToggleRate() was called) // { 15, CmdType::RD , {0, 0, 0, 0, 16}, nullptr, 64}, { 35, CmdType::PRE, {0, 0, 0, 0}}, { 45, CmdType::END_OF_SIMULATION }, }; for (const auto& command : testPattern) { dram.doCoreInterfaceCommand(command); }; ``` The bank stats and energy calculations can then be accessed through their respective methods. In the example energy_core holds the individual bank energy measures. total_energy_core holds the accumulated energy measures accessable through the energy type (e.g E_act, E_pre, E_RD, E_bg_act, E_bg_pre). total_core, total_interface and total_all hold the total energy measures for the core, interface and the sum of both as double values. All values are returned in their respective SI units. ```cpp auto stats = dram.getStats(); stats.bank[0].counter.act; // 1; stats.bank[0].counter.reads; // 1; stats.bank[0].counter.pre; // 1; stats.rank_total[0].cycles.act; // 35; stats.rank_total[0].cycles.pre; // 10; stats.bank[0].cycles.act; // 35; stats.bank[0].cycles.pre; // 10; // Core energy auto energy_core = dram.calcCoreEnergy(dram.getLastCommandTime()); // Interface energy auto energy_interface = dram.calcInterfaceEnergy(dram.getLastCommandTime()); // Total energy with core and interface energy // Accummulated bank energy // Note: total_core_detailed.E_bg_act holds holds the accumulated backgound activation energy plus the shared background activation energy auto total_energy_core = energy_core.total_energy(); energy_core.bank_energy[0].E_act; // 1.7949519230769228e-10 J total_energy_core.E_act; // 1.7949519230769228e-10 J energy_core.E_bg_act_shared; // 1.1832692307692309e-09 J energy_core.bank_energy[0].E_bg_act; // 5.8052884615384527e-12 J total_energy_core.E_bg_act; // 1.1890745192307694e-09 J (5.8052884615384527e-12 J + 1.1832692307692309e-09 J) total_energy_core.E_pre; // 2.076923076923077e-10 J energy_core.bank_energy[0].E_pre; // 2.076923076923077e-10 J total_energy_core.E_bg_pre; // 3.1153846153846144e-10 J energy_core.bank_energy[0].E_bg_pre; // 1.9471153846153847e-11 J total_energy_core.E_RD; // 4.3569230769230762e-10 J energy_core.bank_energy[0].E_RD; // 4.3569230769230762e-10 J double total_core = core_energy.total(); // 2.3234927884615384e-09 J double total_interface = interface_energy.total(); // 1.1102233846153846e-10 J double total_all = dram.getTotalEnergy(dram.getLastCommandTime()); // 2.4345151269230767e-09 J ``` ## Usage of the DRAMPower Command Line application The Command Line application can be built directly by setting the DRAMPOWER_BUILD_CLI flag with CMake (see [Installation Command Line application](#installation-command-line-application)). The Command Line application can be used to calculate energy consumption of a DRAM memory using a command trace. The output can be printed to the console or written as a JSON file. The application needs the following arguments: - -m, --memspec (required): The path to the memory specification file (JSON format) - -c, --config (required): Configuration file for the Command Line application (JSON format) - -t, --trace (required): The path to the command trace file (CSV format) (see [tests directory](https://github.com/tukl-msd/DRAMPower/tree/master/tests/tests_drampower/resources)) - -j, --json (optional): The path to the output JSON file (default: print to console) (the output JSON file must exist and will be overwritten) The configuration file has the following format: ```json { "useToggleRate": false, // true or false "toggleRateConfig": { "togglingRateRead": 0.5, // 0.0 to 1.0 "togglingRateWrite": 0.5, // 0.0 to 1.0 "dutyCycleRead": 0.5, // 0.0 to 1.0 "dutyCycleWrite": 0.5, // 0.0 to 1.0 "idlePatternRead": "H", // "H", "L" or "Z" "idlePatternWrite": "H" // "H", "L" or "Z" } } ``` The Command Line application can be used as follows (in the following example the executable drampower_cli is located in the DRAMPower/build/bin directory): ```console $ cd build/bin $ touch output.json $ touch config.json $ # The configuration file config.json must be filled with a valid configuration (see above) $ ./drampower_cli -c config.json -m ../../tests/tests_drampower/resources/ddr4.json -t ../../tests/tests_drampower/resources/ddr4.csv $ ./drampower_cli -c config.json -m ../../tests/tests_drampower/resources/ddr4.json -t ../../tests/tests_drampower/resources/ddr4.csv -j output.json ``` ## Memory Specifications Note: The timing specifications in the JSONs are in clock cycles (cc). The current specifications for Reading and Writing do not include the I/O consumption. They are computed and included seperately. The IDD measures associated with different power supply sources of equal measure (VDD2, VDDCA and VDDQ). The current measures for dual-rank DIMMs reflect only the measures for the active rank. The default state of the idle rank is assumed to be the same as the complete memory state, for background power estimation. Accordingly, in all dual-rank memory specifications, IDD2P0 has been subtracted from the active currents and all background currents have been halved. They are also accounted for seperately by the power model. Stacking multiple Wide IO DRAM dies can also be captured by the nbrOfRanks parameter. ## Variation-aware Power And Energy Estimation 15 of the included datasheets reflect the impact of process-variations on DRAM currents for a selection of DDR3 memories manufactured at 50nm process technology. These memories include: (1) MICRON_128MB_DDR3-1066_8bit - revision G (2) MICRON_128MB_DDR3-1066_16bit - revision G (3) MICRON_128MB_DDR3-1600_8bit - revision G (4) MICRON_256MB_DDR3-1066_8bit - revision D (5) MICRON_256MB_DDR3-1600_16bit - revision D The original vendor-provided datasheet current specifications are given in XMLs without suffixes such as _mu, _2s and _3s. XMLs including suffixes indicate that the current measures are either: (1) typical (mu), or (2) include +2 sigma variation (2s), or (3) include +3 sigma variation (3s). These measures are derived based on the Monte-Carlo analysis performed on our SPICE-based DRAM cross-section. To include these XMLs in your simulations, simply use them as the target memory. ## Authors & Acknowledgment The tool is based on the DRAM power model developed jointly by the Computer Engineering Research Group at TU Delft and the Electronic Systems Group at TU Eindhoven and verified by the Microelectronic System Design Research Group at TU Kaiserslautern with equivalent circuit-level simulations. This tool has been developed by Karthik Chandrasekar with Yonghui Li under the supervision of Dr. Benny Akesson and Prof. Kees Goossens. The IO and Termination Power measures have been employed from Micron's DRAM Power Calculator. If you decide to use DRAMPower in your research, please cite one of the following references: **To cite the DRAMPower Tool:** ``` [1] DRAMPower: Open-source DRAM Power & Energy Estimation Tool Karthik Chandrasekar, Christian Weis, Yonghui Li, Sven Goossens, Matthias Jung, Omar Naji, Benny Akesson, Norbert Wehn, and Kees Goossens URL: http://www.drampower.info ``` **To cite the DRAM power model:** ``` [2] "Improved Power Modeling of DDR SDRAMs" Karthik Chandrasekar, Benny Akesson, and Kees Goossens In Proc. 14th Euromicro Conference on Digital System Design (DSD), 2011 ``` **To cite the 3D-DRAM power model:** ``` [3] "System and Circuit Level Power Modeling of Energy-Efficient 3D-Stacked Wide I/O DRAMs" Karthik Chandrasekar, Christian Weis, Benny Akesson, Norbert Wehn, and Kees Goossens In Proc. Design, Automation and Test in Europe (DATE), 2013 ``` **To cite variation-aware DRAM power estimation:** ``` [4] "Towards Variation-Aware System-Level Power Estimation of DRAMs: An Empirical Approach" Karthik Chandrasekar, Christian Weis, Benny Akesson, Norbert Wehn, and Kees Goossens In Proc. Design Automation Conference (DAC), 2013 ``` ## Contact Information Further questions about the tool and the power model can be directed to: Matthias Jung (matthias.jung@iese.fraunhofer.de) Feel free to ask for updates to the tool's features and please do report any bugs and errors you encounter. This will encourage us to continuously improve the tool. ## Disclaimer The tool does not check the timing accuracy of the user's memory command trace and the use of commands and memory modes. It is expected that the user employs a valid trace generated using a DRAM memory controller or simulator, which satisfies all memory timing constraints and other requirements. The user DOES NOT get ANY WARRANTIES when using this tool. This software is released under the BSD 3-Clause License. By using this software, the user implicitly agrees to the licensing terms. ================================================ FILE: benches/CMakeLists.txt ================================================ # Copyright (c) 2024, RPTU Kaiserslautern-Landau # 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. # # Authors: # Derek Christ # Marco Mörz ############################################### ### benches_drampower ### ############################################### option(DRAMPOWER_USE_FETCH_CONTENT_GOOGLE_BENCHMARK "Enable FetchContent to provide Google Benchmark" ${DRAMPOWER_USE_FETCH_CONTENT}) if(DRAMPOWER_USE_FETCH_CONTENT) if(DRAMPOWER_USE_FETCH_CONTENT_GOOGLE_BENCHMARK AND DRAMPOWER_BUILD_BENCHMARKS) set(BENCHMARK_ENABLE_TESTING OFF) FetchContent_Declare( benchmark URL "https://github.com/google/benchmark/archive/refs/tags/v1.8.3.tar.gz" OVERRIDE_FIND_PACKAGE ) FetchContent_MakeAvailable(benchmark) endif() endif() find_package(benchmark REQUIRED) add_executable(benches_drampower main.cpp simulation.cpp ) target_link_libraries(benches_drampower PRIVATE DRAMPower::DRAMPower DRAMPower::cli_lib DRAMUtils::DRAMUtils benchmark::benchmark ) target_compile_definitions(benches_drampower PUBLIC DRAMPOWER_BENCHMARK_CONFIGS_DIR="${CMAKE_SOURCE_DIR}/tests/tests_drampower/resources") ================================================ FILE: benches/main.cpp ================================================ /* * Copyright (c) 2023, RPTU Kaiserslautern-Landau * 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. * * Authors: * Derek Christ * Marco Mörz */ #include int main(int argc, char** argv) { ::benchmark::Initialize(&argc, argv); ::benchmark::RunSpecifiedBenchmarks(); return 0; } ================================================ FILE: benches/simulation.cpp ================================================ /* * Copyright (c) 2023, RPTU Kaiserslautern-Landau * 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. * * Authors: * Derek Christ * Marco Mörz */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class LPDDR4_Bench : public benchmark::Fixture { public: using Command = DRAMPower::Command; using CmdType = DRAMPower::CmdType; using CommandList_t = std::vector>>; using BaseDDR_t = DRAMPower::dram_base; void SetUp(::benchmark::State&) { std::string memspecFile{DRAMPOWER_BENCHMARK_CONFIGS_DIR"/lpddr4.json"}; std::string commandFile{DRAMPOWER_BENCHMARK_CONFIGS_DIR"/lpddr4.csv"}; // Get dram // auto ddr = DRAMPower::CLI::getMemory(memspecFile, std::nullopt); auto memspeccontainer = DRAMUtils::parse_memspec_from_file(memspecFile); if(!memspeccontainer) { throw std::runtime_error("Failed to parse memspec from file"); } memspec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*memspeccontainer)); // Parse command list if (!DRAMPower::DRAMPowerCLI::parse_command_list(commandFile, commandlist)) { throw std::runtime_error("Failed to parse command list"); } } void TearDown(::benchmark::State&) { commandlist.clear(); } std::unique_ptr memspec; CommandList_t commandlist; }; BENCHMARK_DEFINE_F(LPDDR4_Bench, lpddr4PowerSimulation)(benchmark::State& state) { auto rdbuf = std::cout.rdbuf(nullptr); for (auto _ : state) { std::unique_ptr ddr = std::make_unique(*memspec); DRAMPower::DRAMPowerCLI::runCommands(ddr, commandlist); } std::cout.rdbuf(rdbuf); } BENCHMARK_REGISTER_F(LPDDR4_Bench, lpddr4PowerSimulation)->Unit(benchmark::kMicrosecond)->Iterations(10); BENCHMARK_DEFINE_F(LPDDR4_Bench, lpddr4PowerSimulationToggling)(benchmark::State& state) { auto rdbuf = std::cout.rdbuf(nullptr); for (auto _ : state) { auto trd = DRAMUtils::Config::ToggleRateDefinition{ 0.5, // togglingRateRead 0.5, // togglingRateWrite 0.5, // dutyCycleRead 0.5, // dutyCycleWrite DRAMUtils::Config::TogglingRateIdlePattern::L, // idlePatternRead DRAMUtils::Config::TogglingRateIdlePattern::L // idlePatternWrite }; std::unique_ptr ddr = std::make_unique(*memspec, DRAMPower::config::SimConfig{trd}); DRAMPower::DRAMPowerCLI::runCommands(ddr, commandlist); } std::cout.rdbuf(rdbuf); } BENCHMARK_REGISTER_F(LPDDR4_Bench, lpddr4PowerSimulationToggling)->Unit(benchmark::kMicrosecond)->Iterations(10); ================================================ FILE: cmake/build_source_group.cmake ================================================ ############################################### ### build_source_group ### ############################################### ### ### Builds a source group from a set of files ### for nicer display in IDEs ### function( build_source_group ) file(GLOB_RECURSE files ${CMAKE_CURRENT_SOURCE_DIR}/*.* ) list(REMOVE_ITEM files "CMakeLists.txt") source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "[src]" FILES ${files}) endfunction() ================================================ FILE: cmake/diagnostics_print.cmake ================================================ ############################################### ### diagnostics_print ### ############################################### ### ### Prints different diagnostics and infos ### about the specified target ### function( diagnostics_print target_name ) message(STATUS "${target_name} settings:") message(STATUS "==============\n") message(STATUS "Source Files:") get_target_property(SOURCE_FILES ${target_name} SOURCES) if(SOURCE_FILES) message(STATUS "${SOURCE_FILES}") endif() message(STATUS "\nInclude Directories:") get_target_property(HEADER_DIR ${target_name} INCLUDE_DIRECTORIES) if(HEADER_DIR) message(STATUS "${HEADER_DIR}") endif() message(STATUS "\nLink Libraries:") get_target_property(LINKED_LIBS ${target_name} LINK_LIBRARIES) if(LINKED_LIBS) message(STATUS "${LINKED_LIBS}") endif() message(STATUS "\n") endfunction() ================================================ FILE: cmake/enable_clang_format.cmake ================================================ ############################################### ### enable_clang_format ### ############################################### ### ### Enables a clang-format target, if ### clang-format is installed, automatically ### formatting all sources in the tree ### function( enable_clang_format ) endfunction() ================================================ FILE: cmake/enable_clang_tidy.cmake ================================================ ############################################### ### enable_clang_tidy ### ############################################### ### ### Enables a clang-tidy target, if ### clang-tidy is installed, to run static ### code analysis on all sources ### function( enable_clang_tidy ) file(GLOB_RECURSE SOURCE_FILES src/*.c src/*.cpp src/*.cxx src/*.cc ) file(GLOB_RECURSE HEADER_FILES src/*.h src/*.hpp src/*.hxx src/*.hh ) find_program(UTIL_TIDY_PATH clang-tidy) if(UTIL_TIDY_PATH) message(STATUS "Using clang-tidy static-analysis: yes") add_custom_target(clang-tidy COMMAND ${UTIL_TIDY_PATH} ${SOURCE_FILES} ${HEADER_FILES} -p=./ ) else() message(STATUS "Using clang-tidy static-analysis: no") endif() endfunction() ================================================ FILE: cmake/enable_cppcheck.cmake ================================================ ############################################### ### enable_cppcheck ### ############################################### ### ### Enables a cppcheck target, if ### cppcheck is installed, to run static ### analysis on all sources ### function( enable_cppcheck ) file(GLOB_RECURSE SOURCE_FILES src/*.c src/*.cpp src/*.cxx src/*.cc ) file(GLOB_RECURSE HEADER_FILES src/*.h src/*.hpp src/*.hxx src/*.hh ) find_program(UTIL_CPPCHECK_PATH cppcheck) if(UTIL_CPPCHECK_PATH) message(STATUS "Using cppcheck static-analysis: yes ") add_custom_target( cppcheck COMMAND ${UTIL_CPPCHECK_PATH} --enable=warning,performance,portability,information,missingInclude --language=c++ --std=c++11 --template=gcc --verbose --quiet ${SOURCE_FILES} ${HEADER_FILES} ) else() message(STATUS "Using cppcheck static-analysis: no ") endif() endfunction() ================================================ FILE: lib/cli11/CMakeLists.txt ================================================ ######################################## ### CLI11 ### ######################################## FetchContent_Declare(cli11 URL "https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.4.2.tar.gz" OVERRIDE_FIND_PACKAGE ) FetchContent_MakeAvailable(cli11) ================================================ FILE: lib/spdlog/CMakeLists.txt ================================================ ######################################## ### spdlog ### ######################################## FetchContent_Declare(spdlog URL "https://github.com/gabime/spdlog/archive/refs/tags/v1.9.2.tar.gz" OVERRIDE_FIND_PACKAGE ) FetchContent_MakeAvailable(spdlog) ================================================ FILE: src/DRAMPower/CMakeLists.txt ================================================ ######################################## ### DRAMPower ### ######################################## find_package(DRAMUtils REQUIRED) add_library(DRAMPower DRAMPower/command/Command.cpp DRAMPower/command/Pattern.cpp DRAMPower/data/energy.cpp DRAMPower/dram/Interface.cpp DRAMPower/dram/Rank.cpp DRAMPower/memspec/MemSpecDDR4.cpp DRAMPower/memspec/MemSpecDDR5.cpp DRAMPower/memspec/MemSpecLPDDR4.cpp DRAMPower/memspec/MemSpecLPDDR5.cpp DRAMPower/standards/ddr4/core_calculation_DDR4.cpp DRAMPower/standards/ddr4/DDR4.cpp DRAMPower/standards/ddr4/DDR4Core.cpp DRAMPower/standards/ddr4/DDR4Interface.cpp DRAMPower/standards/ddr4/interface_calculation_DDR4.cpp DRAMPower/standards/ddr5/core_calculation_DDR5.cpp DRAMPower/standards/ddr5/DDR5.cpp DRAMPower/standards/ddr5/DDR5Core.cpp DRAMPower/standards/ddr5/DDR5Interface.cpp DRAMPower/standards/ddr5/interface_calculation_DDR5.cpp DRAMPower/standards/lpddr4/core_calculation_LPDDR4.cpp DRAMPower/standards/lpddr4/LPDDR4.cpp DRAMPower/standards/lpddr4/LPDDR4Core.cpp DRAMPower/standards/lpddr4/LPDDR4Interface.cpp DRAMPower/standards/lpddr4/interface_calculation_LPDDR4.cpp DRAMPower/standards/lpddr5/core_calculation_LPDDR5.cpp DRAMPower/standards/lpddr5/LPDDR5.cpp DRAMPower/standards/lpddr5/LPDDR5Core.cpp DRAMPower/standards/lpddr5/LPDDR5Interface.cpp DRAMPower/standards/lpddr5/interface_calculation_LPDDR5.cpp DRAMPower/util/extensions.cpp ) target_sources(DRAMPower PUBLIC FILE_SET api TYPE HEADERS FILES DRAMPower/Types.h DRAMPower/Exceptions.h DRAMPower/dram/dram_base.h DRAMPower/simconfig/simconfig.h DRAMPower/command/Command.h DRAMPower/data/energy.h DRAMPower/data/stats.h DRAMPower/util/extension_manager.h DRAMPower/util/extensions.h DRAMPower/memspec/MemSpec.h DRAMPower/memspec/MemSpecDDR4.h DRAMPower/standards/ddr4/DDR4.h DRAMPower/standards/ddr4/DDR4Core.h DRAMPower/standards/ddr4/DDR4Interface.h DRAMPower/standards/ddr4/core_calculation_DDR4.h DRAMPower/standards/ddr4/interface_calculation_DDR4.h DRAMPower/standards/ddr4/types.h DRAMPower/memspec/MemSpecDDR5.h DRAMPower/standards/ddr5/DDR5.h DRAMPower/standards/ddr5/DDR5Core.h DRAMPower/standards/ddr5/DDR5Interface.h DRAMPower/standards/ddr5/core_calculation_DDR5.h DRAMPower/standards/ddr5/interface_calculation_DDR5.h DRAMPower/standards/ddr5/types.h DRAMPower/memspec/MemSpecLPDDR4.h DRAMPower/standards/lpddr4/LPDDR4.h DRAMPower/standards/lpddr4/LPDDR4Core.h DRAMPower/standards/lpddr4/LPDDR4Interface.h DRAMPower/standards/lpddr4/core_calculation_LPDDR4.h DRAMPower/standards/lpddr4/interface_calculation_LPDDR4.h DRAMPower/standards/lpddr4/types.h DRAMPower/memspec/MemSpecLPDDR5.h DRAMPower/standards/lpddr5/LPDDR5.h DRAMPower/standards/lpddr5/LPDDR5Core.h DRAMPower/standards/lpddr5/LPDDR5Interface.h DRAMPower/standards/lpddr5/core_calculation_LPDDR5.h DRAMPower/standards/lpddr5/interface_calculation_LPDDR5.h DRAMPower/standards/lpddr5/types.h DRAMPower/util/Serialize.h DRAMPower/util/Deserialize.h ) target_include_directories(DRAMPower PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_features(DRAMPower PUBLIC cxx_std_17) set_target_properties(DRAMPower PROPERTIES CXX_EXTENSIONS OFF) set_target_properties(DRAMPower PROPERTIES CXX_STANDARD_REQUIRED ON) target_link_libraries(DRAMPower PUBLIC DRAMUtils::DRAMUtils ) # Add test functions if(DRAMPOWER_BUILD_TESTS) target_compile_definitions(DRAMPower PUBLIC DRAMPOWER_TESTING) endif() add_library(DRAMPower::DRAMPower ALIAS DRAMPower) install(TARGETS DRAMPower FILE_SET api) ================================================ FILE: src/DRAMPower/DRAMPower/Exceptions.h ================================================ #ifndef DRAMPOWER_EXCEPTIONS_H #define DRAMPOWER_EXCEPTIONS_H #include #include namespace DRAMPower { class Exception : public std::exception { // protected constructors protected: // public constructors public: // Constructors with message explicit Exception(const std::string &message) : m_message(message) {} explicit Exception(std::string &&message) : m_message(std::move(message)) {} // Exception without message should not be allowed Exception() = delete; // Copy and move constructors and assignment operators Exception(const Exception &other) = default; Exception &operator=(const Exception &other) = default; Exception(Exception &&other) = default; Exception &operator=(Exception &&other) = default; // Destructor virtual ~Exception() noexcept = default; // Public member functions public: const char *what() const noexcept override { return m_message.c_str(); } // Private member variables private: std::string m_message; }; } // namespace DRAMPower #endif /* DRAMPOWER_EXCEPTIONS_H */ ================================================ FILE: src/DRAMPower/DRAMPower/Types.h ================================================ #ifndef DRAMPOWER_TYPES_H #define DRAMPOWER_TYPES_H #include #include namespace DRAMPower { using timestamp_t = uint64_t; using interval_t = util::interval_counter; } #endif /* DRAMPOWER_TYPES_H */ ================================================ FILE: src/DRAMPower/DRAMPower/command/CmdType.h ================================================ #ifndef DRAMPOWER_COMMAND_CMDTYPE_H #define DRAMPOWER_COMMAND_CMDTYPE_H #include #include namespace DRAMPower { // Taken from DRAM 4.0 // DRAMSys // \ \__> DRAMPower // \ \_v // \_______> DRAMCommon enum class CmdType { NOP = 0, // 0 RD, // 1 WR, // 2 RDA, // 3 WRA, // 4 ACT, // 5 PRE, // 6 REFB, // 7 REFP2B, // 8 PRESB, // 9 REFSB, // 10 PREA, // 11 REFA, // 12 PDEA, // 13 PDEP, // 14 PDXA, // 15 PDXP, // 16 SREFEN, // 17 SREFEX, // 18 DSMEN, // 19 DSMEX, // 20 END_OF_SIMULATION, // 21 COUNT, // 22 }; namespace CmdTypeUtil { constexpr bool needs_data(CmdType cmd) { switch (cmd) { case CmdType::RD: case CmdType::RDA: case CmdType::WR: case CmdType::WRA: return true; default: return false; }; }; constexpr CmdType from_string(const std::string_view& str) { if (str == "NOP") return CmdType::NOP; if (str == "ACT") return CmdType::ACT; if (str == "PRE") return CmdType::PRE; if (str == "PREA") return CmdType::PREA; if (str == "PRESB") return CmdType::PRESB; if (str == "REFA") return CmdType::REFA; if (str == "REFB") return CmdType::REFB; if (str == "REFSB") return CmdType::REFSB; if (str == "REFP2B") return CmdType::REFP2B; if (str == "RD") return CmdType::RD; if (str == "RDA") return CmdType::RDA; if (str == "WR") return CmdType::WR; if (str == "WRA") return CmdType::WRA; if (str == "PDEA") return CmdType::PDEA; if (str == "PDEP") return CmdType::PDEP; if (str == "PDXA") return CmdType::PDXA; if (str == "PDXP") return CmdType::PDXP; if (str == "SREFEN") return CmdType::SREFEN; if (str == "SREFEX") return CmdType::SREFEX; if (str == "DSMEN") return CmdType::DSMEN; if (str == "DSMEX") return CmdType::DSMEX; if (str == "END") return CmdType::END_OF_SIMULATION; return CmdType::NOP; }; constexpr const char * to_string(CmdType cmd) { switch (cmd) { case CmdType::NOP: return "NOP"; case CmdType::ACT: return "ACT"; case CmdType::PRE: return "PRE"; case CmdType::PREA: return "PREA"; case CmdType::REFA: return "REFA"; case CmdType::REFB: return "REFB"; case CmdType::RD: return "RD"; case CmdType::RDA: return "RDA"; case CmdType::WR: return "WR"; case CmdType::WRA: return "WRA"; case CmdType::REFSB: return "REFSB"; case CmdType::REFP2B: return "REFP2B"; case CmdType::PRESB: return "PRESB"; case CmdType::PDEA: return "PDEA"; case CmdType::PDEP: return "PDEP"; case CmdType::PDXA: return "PDXA"; case CmdType::PDXP: return "PDXP"; case CmdType::SREFEN: return "SREFEN"; case CmdType::SREFEX: return "SREFEX"; case CmdType::DSMEN: return "DSMEN"; case CmdType::DSMEX: return "DSMEX"; case CmdType::END_OF_SIMULATION: return "END_OF_SIMULATION"; default: return "to_string()"; } } inline std::ostream& operator<<(std::ostream& os, CmdType cmd) { return os << to_string(cmd); } } } #endif /* DRAMPOWER_COMMAND_CMDTYPE_H */ ================================================ FILE: src/DRAMPower/DRAMPower/command/Command.cpp ================================================ #include "Command.h" namespace DRAMPower { TargetCoordinate::TargetCoordinate(std::size_t bank_id, std::size_t bank_group_id, std::size_t rank_id) : bank(bank_id), bankGroup(bank_group_id), rank(rank_id) {}; TargetCoordinate::TargetCoordinate(std::size_t bank_id, std::size_t bank_group_id, std::size_t rank_id, std::size_t row_id) : bank(bank_id), bankGroup(bank_group_id), rank(rank_id), row(row_id) {}; TargetCoordinate::TargetCoordinate(std::size_t bank_id, std::size_t bank_group_id, std::size_t rank_id, std::size_t row_id, std::size_t column_id) : bank(bank_id), bankGroup(bank_group_id), rank(rank_id), row(row_id), column(column_id) {}; Command::Command(timestamp_t timestamp, CmdType type, TargetCoordinate targetCoord, const uint8_t * data, std::size_t sz_bits) : timestamp(timestamp) , type(type) , targetCoordinate(targetCoord) , data(data) , sz_bits(sz_bits) {}; } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/command/Command.h ================================================ #ifndef DRAMPOWER_COMMAND_COMMAND_H #define DRAMPOWER_COMMAND_COMMAND_H #include #include #include namespace DRAMPower { struct TargetCoordinate { std::size_t bank = 0; std::size_t bankGroup = 0; std::size_t rank = 0; std::size_t row = 0; std::size_t column = 0; TargetCoordinate() = default; TargetCoordinate(std::size_t bank_id, std::size_t bank_group_id, std::size_t rank_id); TargetCoordinate(std::size_t bank_id, std::size_t bank_group_id, std::size_t rank_id, std::size_t row_id); TargetCoordinate(std::size_t bank_id, std::size_t bank_group_id, std::size_t rank_id, std::size_t row_id, std::size_t column_id); }; class Command { public: Command() = default; Command(timestamp_t timestamp, CmdType type, TargetCoordinate targetCoord = {}, const uint8_t * data = nullptr, std::size_t sz_bits = 0); public: timestamp_t timestamp = 0; CmdType type; TargetCoordinate targetCoordinate; const uint8_t * data = 0x00; std::size_t sz_bits; //uint64_t burstLength; public: }; } #endif /* DRAMPOWER_COMMAND_COMMAND_H */ ================================================ FILE: src/DRAMPower/DRAMPower/command/Pattern.cpp ================================================ #include "Pattern.h" #include #include #include namespace DRAMPower { PatternEncoderOverrides::PatternEncoderOverrides(std::initializer_list _settings) { for (const auto &setting : _settings) { this->settings.emplace(setting.descriptor, setting.bitSpec); } } void PatternEncoderOverrides::updateSettings(std::initializer_list _settings) { // Update settings if descriptor is already present for (const auto &setting : _settings) { this->settings[setting.descriptor] = setting.bitSpec; } } std::unordered_map PatternEncoderOverrides::getSettings() { return this->settings; } const std::unordered_map& PatternEncoderOverrides::getSettings() const { return this->settings; } PatternEncoderBitSpec PatternEncoderOverrides::getSetting(pattern_descriptor::t descriptor) { if (this->settings.find(descriptor) != this->settings.end()) { return this->settings.at(descriptor); } return PatternEncoderBitSpec::INVALID; } bool PatternEncoderOverrides::hasSetting(pattern_descriptor::t descriptor) { return this->settings.find(descriptor) != this->settings.end(); } bool PatternEncoderOverrides::removeSetting(pattern_descriptor::t descriptor) { return this->settings.erase(descriptor) > 0; } PatternEncoder::PatternEncoder(PatternEncoderOverrides settings) : settings(settings) {} inline bool PatternEncoder::applyBitSpec( PatternEncoderOverrides &spec, pattern_descriptor::t descriptor, bool LAST_BIT, bool default_bit ) { auto setting = spec.getSetting(descriptor); switch (setting) { case PatternEncoderBitSpec::L: return false; case PatternEncoderBitSpec::H: return true; case PatternEncoderBitSpec::LAST_BIT: return LAST_BIT; case PatternEncoderBitSpec::INVALID: return default_bit; default: assert(false); break; } return false; } uint64_t PatternEncoder::encode(const Command& cmd, const std::vector& pattern, const uint64_t lastpattern) { return encode(cmd.targetCoordinate, pattern, lastpattern); } uint64_t PatternEncoder::encode(const TargetCoordinate& targetCoordinate, const std::vector& pattern, const uint64_t lastpattern) { using namespace pattern_descriptor; std::bitset<64> bitset(0); std::bitset<32> bank_bits(targetCoordinate.bank); std::bitset<32> row_bits(targetCoordinate.row); std::bitset<32> column_bits(targetCoordinate.column); std::bitset<32> bank_group_bits(targetCoordinate.bankGroup); std::size_t n = pattern.size() - 1; uint64_t opcodeshifter = 1; uint16_t opcodecount = 0; assert(n < 64); for (const auto descriptor : pattern) { // assert(n >= 0); // std::size_t is unsigned switch (descriptor) { case H: bitset[n] = true; break; case L: bitset[n] = false; break; // Command bits case BL: case CID0: case CID1: case CID2: case CID3: bitset[n] = applyBitSpec(settings, descriptor, ((lastpattern >> n) & 1) == 1, true); break; case V: case X: case AP: bitset[n] = applyBitSpec(settings, descriptor, ((lastpattern >> n) & 1) == 1, false); break; // Target Coordinate bits // Bank bits case BA0: bitset[n] = bank_bits[0]; break; case BA1: bitset[n] = bank_bits[1]; break; case BA2: bitset[n] = bank_bits[2]; break; case BA3: bitset[n] = bank_bits[3]; break; case BA4: bitset[n] = bank_bits[4]; break; case BA5: bitset[n] = bank_bits[5]; break; case BA6: bitset[n] = bank_bits[6]; break; case BA7: bitset[n] = bank_bits[7]; break; case BA8: bitset[n] = bank_bits[8]; break; // BG bits case BG0: bitset[n] = bank_group_bits[0]; break; case BG1: bitset[n] = bank_group_bits[1]; break; case BG2: bitset[n] = bank_group_bits[2]; break; case C0: bitset[n] = applyBitSpec(settings, descriptor, ((lastpattern >> n) & 1) == 1, column_bits[0]); break; case C1: bitset[n] = applyBitSpec(settings, descriptor, ((lastpattern >> n) & 1) == 1, column_bits[1]); break; case C2: bitset[n] = applyBitSpec(settings, descriptor, ((lastpattern >> n) & 1) == 1, column_bits[2]); break; case C3: bitset[n] = applyBitSpec(settings, descriptor, ((lastpattern >> n) & 1) == 1, column_bits[3]); break; case C4: bitset[n] = applyBitSpec(settings, descriptor, ((lastpattern >> n) & 1) == 1, column_bits[4]); break; case C5: bitset[n] = column_bits[5]; break; case C6: bitset[n] = column_bits[6]; break; case C7: bitset[n] = column_bits[7]; break; case C8: bitset[n] = column_bits[8]; break; case C9: bitset[n] = column_bits[9]; break; case C10: bitset[n] = applyBitSpec(settings, descriptor, ((lastpattern >> n) & 1) == 1, column_bits[10]); break; case C11: bitset[n] = column_bits[C11]; break; case C12: bitset[n] = column_bits[C12]; break; case C13: bitset[n] = column_bits[C13]; break; case C14: bitset[n] = column_bits[C14]; break; case C15: bitset[n] = column_bits[C15]; break; case C16: bitset[n] = column_bits[C16]; break; // Row bits case R0: bitset[n] = row_bits[0]; break; case R1: bitset[n] = row_bits[1]; break; case R2: bitset[n] = row_bits[2]; break; case R3: bitset[n] = row_bits[3]; break; case R4: bitset[n] = row_bits[4]; break; case R5: bitset[n] = row_bits[5]; break; case R6: bitset[n] = row_bits[6]; break; case R7: bitset[n] = row_bits[7]; break; case R8: bitset[n] = row_bits[8]; break; case R9: bitset[n] = row_bits[9]; break; case R10: bitset[n] = row_bits[10]; break; case R11: bitset[n] = row_bits[11]; break; case R12: bitset[n] = row_bits[12]; break; case R13: bitset[n] = row_bits[13]; break; case R14: bitset[n] = row_bits[14]; break; case R15: bitset[n] = row_bits[15]; break; case R16: bitset[n] = row_bits[16]; break; case R17: bitset[n] = row_bits[17]; break; case OPCODE: // Example: opcode 0x31 results in pattern 0b0011'0001 assert(m_opcodeLength > opcodecount); bitset[n] = m_opcode & opcodeshifter; opcodeshifter <<= 1; ++opcodecount; break; default: break; } --n; } return bitset.to_ullong(); } void PatternEncoder::setOpcode(uint64_t opcode, uint16_t opcodeLength) { m_opcode = opcode; m_opcodeLength = opcodeLength; } uint64_t PatternEncoder::getOpcode() const { return m_opcode; } uint16_t PatternEncoder::getOpcodeLength() const { return m_opcodeLength; } void PatternEncoder::serialize(std::ostream& stream) const { stream.write(reinterpret_cast(&m_opcode), sizeof(m_opcode)); stream.write(reinterpret_cast(&m_opcodeLength), sizeof(m_opcodeLength)); // settings const std::size_t settingsSize = settings.getSettings().size(); stream.write(reinterpret_cast(&settingsSize), sizeof(settingsSize)); for (const auto& [descriptor, bitSpec] : settings.getSettings()) { stream.write(reinterpret_cast(&descriptor), sizeof(descriptor)); stream.write(reinterpret_cast(&bitSpec), sizeof(bitSpec)); } } void PatternEncoder::deserialize(std::istream& stream) { stream.read(reinterpret_cast(&m_opcode), sizeof(m_opcode)); stream.read(reinterpret_cast(&m_opcodeLength), sizeof(m_opcodeLength)); // settings std::size_t settingsSize = 0; stream.read(reinterpret_cast(&settingsSize), sizeof(settingsSize)); settings = PatternEncoderOverrides{}; for (std::size_t i = 0; i < settingsSize; ++i) { pattern_descriptor::t descriptor; PatternEncoderBitSpec bitSpec; stream.read(reinterpret_cast(&descriptor), sizeof(descriptor)); stream.read(reinterpret_cast(&bitSpec), sizeof(bitSpec)); settings.updateSettings({{descriptor, bitSpec}}); } } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/command/Pattern.h ================================================ #ifndef DRAMPOWER_COMMAND_PATTERN_H #define DRAMPOWER_COMMAND_PATTERN_H #include #include #include #include #include namespace DRAMPower { namespace pattern_descriptor { enum t { H, L, V, X, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, BG0, BG1, BG2, BA0, BA1, BA2, BA3, BA4, BA5, BA6, BA7, BA8, C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15, C16, R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, R16, R17, R18, R19, R20, R21, R22, R23, CID0, CID1, CID2, CID3, AP, BL, OPCODE, // Example: opcode 0x03 pattern: {H, L, OPCODE, OPCODE, OPCODE, OPCODE} result in 0b100011 }; } enum class PatternEncoderBitSpec { L = 0, H = 1, LAST_BIT = 2, INVALID = -1 }; // struct for initializer list in PatternEncoderSettings struct PatternEncoderSettingsEntry { pattern_descriptor::t descriptor; PatternEncoderBitSpec bitSpec; }; // class to store pattern descriptor overrides class PatternEncoderOverrides { private: std::unordered_map settings; public: // Constructor with initializer list for settings PatternEncoderOverrides() = default; PatternEncoderOverrides(std::initializer_list _settings); public: void updateSettings(std::initializer_list _settings); std::unordered_map getSettings(); PatternEncoderBitSpec getSetting(pattern_descriptor::t descriptor); const std::unordered_map& getSettings() const; bool hasSetting(pattern_descriptor::t descriptor); bool removeSetting(pattern_descriptor::t descriptor); }; class PatternEncoder : public util::Serialize, public util::Deserialize // Currently LPDDR4 { public: PatternEncoderOverrides settings; PatternEncoder(PatternEncoderOverrides settings); private: inline bool applyBitSpec( PatternEncoderOverrides &spec, pattern_descriptor::t descriptor, bool LAST_BIT, bool default_bit ); public: void setOpcode(uint64_t opcode, uint16_t opcodeLength); uint64_t getOpcode() const; uint16_t getOpcodeLength() const; // Overrides public: void serialize(std::ostream& stream) const override; void deserialize(std::istream& stream) override; public: uint64_t encode(const Command& cmd, const std::vector& pattern, const uint64_t lastpattern); uint64_t encode(const TargetCoordinate& coordinate, const std::vector& pattern, const uint64_t lastpattern); // Private member variables private: uint64_t m_opcode = 0; uint16_t m_opcodeLength = 0; }; } // namespace DRAMPower #endif /* DRAMPOWER_COMMAND_PATTERN_H */ ================================================ FILE: src/DRAMPower/DRAMPower/data/energy.cpp ================================================ #include "energy.h" using namespace DRAMPower; double energy_info_t::total() const { auto total = E_act + E_pre + E_bg_act + E_bg_pre + E_RD + E_WR + E_RDA + E_WRA //+ E_pre_RDA //+ E_pre_WRA + E_ref_AB + E_ref_PB + E_ref_SB + E_ref_2B; return total; }; void energy_info_t::to_json(json_t &j) const { j = nlohmann::json{ {"ACT", E_act}, {"PRE", E_pre}, {"BG_ACT", E_bg_act}, {"BG_PRE", E_bg_pre}, {"RD", E_RD}, {"WR", E_WR}, {"RDA", E_RDA}, {"WRA", E_WRA}, {"PRE_RDA", E_pre_RDA}, {"PRE_WRA", E_pre_WRA}, {"REF_AB", E_ref_AB}, {"REF_PB", E_ref_PB}, {"REF_SB", E_ref_SB}, {"REF_2B", E_ref_2B} }; } energy_info_t& energy_info_t::operator+=(const DRAMPower::energy_info_t& other) { this->E_act += other.E_act; this->E_pre += other.E_pre; this->E_bg_act += other.E_bg_act; this->E_bg_pre += other.E_bg_pre; this->E_RD += other.E_RD; this->E_WR += other.E_WR; this->E_RDA += other.E_RDA; this->E_WRA += other.E_WRA; this->E_pre_RDA += other.E_pre_RDA; this->E_pre_WRA += other.E_pre_WRA; this->E_ref_AB += other.E_ref_AB; this->E_ref_PB += other.E_ref_PB; this->E_ref_SB += other.E_ref_SB; this->E_ref_2B += other.E_ref_2B; return *this; } double DRAMPower::energy_t::total() const { double total = 0.0; energy_info_t bank_energy_total; for (const auto& bank_e : this->bank_energy) bank_energy_total += bank_e; total += bank_energy_total.total() + E_bg_act_shared + E_PDNA + E_PDNP + E_sref + E_dsm + E_refab; return total; } void DRAMPower::interface_energy_info_t::to_json(json_t &j) const { j = nlohmann::json{}; j["controller"]["dynamicEnergy"] = controller.dynamicEnergy; j["controller"]["staticEnergy"] = controller.staticEnergy; j["dram"]["dynamicEnergy"] = dram.dynamicEnergy; j["dram"]["staticEnergy"] = dram.staticEnergy; } void DRAMPower::energy_t::to_json(json_t &j) const { j = nlohmann::json{ {"E_bg_act_shared", E_bg_act_shared}, {"E_PDNA", E_PDNA}, {"E_PDNP", E_PDNP}, {"E_sref", E_sref}, {"E_dsm", E_dsm}, {"E_refab", E_refab} }; // Bank energy auto energy_arr = nlohmann::json::array(); for (const energy_info_t& energy : bank_energy) { json_t bank_energy_json; energy.to_json(bank_energy_json); energy_arr.push_back(bank_energy_json); } j[this->get_Bank_energy_keyword()] = energy_arr; } energy_info_t DRAMPower::energy_t::aggregated_bank_energy() const { energy_info_t total; for (const auto& bank_e : this->bank_energy) total += bank_e; total.E_bg_act += this->E_bg_act_shared; return total; } ================================================ FILE: src/DRAMPower/DRAMPower/data/energy.h ================================================ #ifndef DRAMPOWER_CALCULATION_ENERGY_H #define DRAMPOWER_CALCULATION_ENERGY_H #pragma once #include #include #include namespace DRAMPower { struct energy_info_t { double E_act = 0.0; double E_pre = 0.0; double E_bg_act = 0.0; double E_bg_pre = 0.0; double E_RD = 0.0; double E_WR = 0.0; double E_RDA = 0.0; double E_WRA = 0.0; double E_pre_RDA = 0.0; double E_pre_WRA = 0.0; double E_ref_AB = 0.0; double E_ref_PB = 0.0; double E_ref_SB = 0.0; double E_ref_2B = 0.0; double total() const; void to_json(json_t &j) const; energy_info_t& operator+=(const energy_info_t & other); // Operator << for std::cout friend std::ostream & operator<<(std::ostream & os, const energy_info_t & ei) { os << "ACT: " << ei.E_act << " "; os << "PRE: " << ei.E_pre << " "; os << "BG_ACT: " << ei.E_bg_act << " "; os << "BG_PRE: " << ei.E_bg_pre << " "; os << "RD: " << ei.E_RD << " "; os << "WR: " << ei.E_WR << " "; os << "RDA: " << ei.E_RDA << " "; os << "WRA: " << ei.E_WRA << " "; os << "PRE_RDA: " << ei.E_pre_RDA << " "; os << "PRE_WRA: " << ei.E_pre_WRA << " "; os << "REF_AB: " << ei.E_ref_AB << " "; os << "REF_PB: " << ei.E_ref_PB << " "; os << "REF_SB: " << ei.E_ref_SB << " "; os << "REF_2B: " << ei.E_ref_2B << " "; return os; } }; struct energy_t { std::vector bank_energy; energy_info_t aggregated_bank_energy() const; void to_json(json_t &j) const; constexpr inline const char * get_Bank_energy_keyword() const { return "BankEnergy"; } double E_bg_act_shared = 0.0; double E_PDNA = 0.0; double E_PDNP = 0.0; double E_sref = 0.0; double E_dsm = 0.0; double E_refab = 0.0; energy_t(std::size_t num_banks) : bank_energy(num_banks) {}; // get fields of os stream output friend std::ostream & operator<<(std::ostream & os, const energy_t & e) { os << "E_bg_act_shared: " << e.E_bg_act_shared << " "; os << "E_PDNA: " << e.E_PDNA << " "; os << "E_PDNP: " << e.E_PDNP << " "; os << "E_sref: " << e.E_sref << " "; os << "E_dsm: " << e.E_dsm << " "; os << "E_refab: " << e.E_refab << " "; return os; } double total() const; }; struct interface_energy_t { double dynamicEnergy = 0.0; double staticEnergy = 0.0; interface_energy_t &operator+=(const interface_energy_t &rhs) { dynamicEnergy += rhs.dynamicEnergy; staticEnergy += rhs.staticEnergy; return *this; } friend interface_energy_t operator+(interface_energy_t lhs, const interface_energy_t &rhs) { lhs += rhs; return lhs; } }; struct interface_energy_info_t { interface_energy_t controller; interface_energy_t dram; double total() const { return controller.dynamicEnergy + controller.staticEnergy + dram.dynamicEnergy + dram.staticEnergy; } interface_energy_info_t &operator+=(const interface_energy_info_t &rhs) { controller += rhs.controller; dram += rhs.dram; return *this; } friend interface_energy_info_t operator+(interface_energy_info_t lhs, const interface_energy_info_t &rhs) { lhs += rhs; return lhs; } friend std::ostream & operator<<(std::ostream & os, const interface_energy_info_t & e) { os << "Controller: dynamicEnergy: " << e.controller.dynamicEnergy << " "; os << "staticEnergy: " << e.controller.staticEnergy << std::endl; os << "DRAM: dynamicEnergy: " << e.dram.dynamicEnergy << " "; os << "staticEnergy: " << e.dram.staticEnergy << std::endl; os << "Total: " << e.total(); return os; } void to_json(json_t &j) const; }; }; #endif /* DRAMPOWER_CALCULATION_ENERGY_H */ ================================================ FILE: src/DRAMPower/DRAMPower/data/stats.h ================================================ #ifndef DRAMPOWER_DATA_STATS_H #define DRAMPOWER_DATA_STATS_H #include #include #include #include #include namespace DRAMPower { struct CycleStats : public util::Serialize, public util::Deserialize { struct command_stats_t : public util::Serialize, public util::Deserialize { uint64_t act = 0; uint64_t pre = 0; uint64_t preSameBank = 0; uint64_t reads = 0; uint64_t writes = 0; uint64_t refAllBank = 0; uint64_t refPerBank = 0; uint64_t refPerTwoBanks = 0; uint64_t refSameBank = 0; uint64_t readAuto = 0; uint64_t writeAuto = 0; void serialize(std::ostream& stream) const override { stream.write(reinterpret_cast(&act), sizeof(act)); stream.write(reinterpret_cast(&pre), sizeof(pre)); stream.write(reinterpret_cast(&preSameBank), sizeof(preSameBank)); stream.write(reinterpret_cast(&reads), sizeof(reads)); stream.write(reinterpret_cast(&writes), sizeof(writes)); stream.write(reinterpret_cast(&refAllBank), sizeof(refAllBank)); stream.write(reinterpret_cast(&refPerBank), sizeof(refPerBank)); stream.write(reinterpret_cast(&refPerTwoBanks), sizeof(refPerTwoBanks)); stream.write(reinterpret_cast(&refSameBank), sizeof(refSameBank)); stream.write(reinterpret_cast(&readAuto), sizeof(readAuto)); stream.write(reinterpret_cast(&writeAuto), sizeof(writeAuto)); } void deserialize(std::istream& stream) override { stream.read(reinterpret_cast(&act), sizeof(act)); stream.read(reinterpret_cast(&pre), sizeof(pre)); stream.read(reinterpret_cast(&preSameBank), sizeof(preSameBank)); stream.read(reinterpret_cast(&reads), sizeof(reads)); stream.read(reinterpret_cast(&writes), sizeof(writes)); stream.read(reinterpret_cast(&refAllBank), sizeof(refAllBank)); stream.read(reinterpret_cast(&refPerBank), sizeof(refPerBank)); stream.read(reinterpret_cast(&refPerTwoBanks), sizeof(refPerTwoBanks)); stream.read(reinterpret_cast(&refSameBank), sizeof(refSameBank)); stream.read(reinterpret_cast(&readAuto), sizeof(readAuto)); stream.read(reinterpret_cast(&writeAuto), sizeof(writeAuto)); } // operator == bool operator==(const command_stats_t& rhs) const { return act == rhs.act && pre == rhs.pre && preSameBank == rhs.preSameBank && reads == rhs.reads && writes == rhs.writes && refAllBank == rhs.refAllBank && refPerBank == rhs.refPerBank && refPerTwoBanks == rhs.refPerTwoBanks && refSameBank == rhs.refSameBank && readAuto == rhs.readAuto && writeAuto == rhs.writeAuto; } } counter; struct cycles_t : public util::Serialize, public util::Deserialize { uint64_t act = 0; uint64_t pre = 0; uint64_t powerDownAct = 0; uint64_t powerDownPre = 0; uint64_t selfRefresh = 0; uint64_t deepSleepMode = 0; uint64_t activeTime() const { return act; }; //uint64_t prechargeTime; void serialize(std::ostream& stream) const override { stream.write(reinterpret_cast(&act), sizeof(act)); stream.write(reinterpret_cast(&pre), sizeof(pre)); stream.write(reinterpret_cast(&powerDownAct), sizeof(powerDownAct)); stream.write(reinterpret_cast(&powerDownPre), sizeof(powerDownPre)); stream.write(reinterpret_cast(&selfRefresh), sizeof(selfRefresh)); stream.write(reinterpret_cast(&deepSleepMode), sizeof(deepSleepMode)); } void deserialize(std::istream& stream) override { stream.read(reinterpret_cast(&act), sizeof(act)); stream.read(reinterpret_cast(&pre), sizeof(pre)); stream.read(reinterpret_cast(&powerDownAct), sizeof(powerDownAct)); stream.read(reinterpret_cast(&powerDownPre), sizeof(powerDownPre)); stream.read(reinterpret_cast(&selfRefresh), sizeof(selfRefresh)); stream.read(reinterpret_cast(&deepSleepMode), sizeof(deepSleepMode)); } // operator == bool operator==(const cycles_t& rhs) const { return act == rhs.act && pre == rhs.pre && powerDownAct == rhs.powerDownAct && powerDownPre == rhs.powerDownPre && selfRefresh == rhs.selfRefresh && deepSleepMode == rhs.deepSleepMode; } } cycles; struct prepos_t : public util::Serialize, public util::Deserialize { uint64_t readMerged = 0; uint64_t readMergedTime = 0; uint64_t writeMerged = 0; uint64_t writeMergedTime = 0; uint64_t readSeamless = 0; uint64_t writeSeamless = 0; void serialize(std::ostream& stream) const override { stream.write(reinterpret_cast(&readMerged), sizeof(readMerged)); stream.write(reinterpret_cast(&readMergedTime), sizeof(readMergedTime)); stream.write(reinterpret_cast(&writeMerged), sizeof(writeMerged)); stream.write(reinterpret_cast(&writeMergedTime), sizeof(writeMergedTime)); stream.write(reinterpret_cast(&readSeamless), sizeof(readSeamless)); stream.write(reinterpret_cast(&writeSeamless), sizeof(writeSeamless)); } void deserialize(std::istream& stream) override { stream.read(reinterpret_cast(&readMerged), sizeof(readMerged)); stream.read(reinterpret_cast(&readMergedTime), sizeof(readMergedTime)); stream.read(reinterpret_cast(&writeMerged), sizeof(writeMerged)); stream.read(reinterpret_cast(&writeMergedTime), sizeof(writeMergedTime)); stream.read(reinterpret_cast(&readSeamless), sizeof(readSeamless)); stream.read(reinterpret_cast(&writeSeamless), sizeof(writeSeamless)); } // operator == bool operator==(const prepos_t& rhs) const { return readMerged == rhs.readMerged && readMergedTime == rhs.readMergedTime && writeMerged == rhs.writeMerged && writeMergedTime == rhs.writeMergedTime && readSeamless == rhs.readSeamless && writeSeamless == rhs.writeSeamless; } } prepos; void serialize(std::ostream& stream) const override { counter.serialize(stream); cycles.serialize(stream); prepos.serialize(stream); } void deserialize(std::istream& stream) override { counter.deserialize(stream); cycles.deserialize(stream); prepos.deserialize(stream); } // operator == bool operator==(const CycleStats& rhs) const { return counter == rhs.counter && cycles == rhs.cycles && prepos == rhs.prepos; } }; struct TogglingStats { util::bus_stats_t read; util::bus_stats_t write; }; struct SimulationStats { util::bus_stats_t commandBus; util::bus_stats_t readBus; util::bus_stats_t writeBus; util::bus_stats_t clockStats; util::bus_stats_t wClockStats; util::bus_stats_t readDBI; util::bus_stats_t writeDBI; TogglingStats togglingStats; util::bus_stats_t readDQSStats; util::bus_stats_t writeDQSStats; std::vector bank; std::vector rank_total; // Operator == bool operator==(const SimulationStats& other) const { // Compare banks bool banksEqual = true; if (bank.size() != other.bank.size()) { banksEqual = false; } else { for (size_t i = 0; i < bank.size(); ++i) { if (!(bank[i] == other.bank[i])) { banksEqual = false; break; } } } bool rank_totalEqual = true; if (rank_total.size() != other.rank_total.size()) { rank_totalEqual = false; } else { for (size_t i = 0; i < rank_total.size(); ++i) { if (!(rank_total[i] == other.rank_total[i])) { rank_totalEqual = false; break; } } } return commandBus == other.commandBus && readBus == other.readBus && writeBus == other.writeBus && clockStats == other.clockStats && wClockStats == other.wClockStats && readDBI == other.readDBI && writeDBI == other.writeDBI && togglingStats.read == other.togglingStats.read && togglingStats.write == other.togglingStats.write && readDQSStats == other.readDQSStats && writeDQSStats == other.writeDQSStats && banksEqual && rank_totalEqual; } }; }; #endif /* DRAMPOWER_DATA_STATS_H */ ================================================ FILE: src/DRAMPower/DRAMPower/dram/Bank.h ================================================ #ifndef DRAMPOWER_DDR_BANK_H #define DRAMPOWER_DDR_BANK_H #include #include #include #include #include namespace DRAMPower { struct Bank : public util::Serialize, public util::Deserialize { public: enum class BankState { BANK_PRECHARGED = 0, BANK_ACTIVE = 1, }; public: CycleStats::command_stats_t counter; struct { interval_t act; interval_t powerDownAct; interval_t powerDownPre; } cycles; BankState bankState; public: timestamp_t latestPre = 0; timestamp_t refreshEndTime = 0; void serialize(std::ostream& stream) const override { stream.write(reinterpret_cast(&bankState), sizeof(bankState)); stream.write(reinterpret_cast(&latestPre), sizeof(latestPre)); stream.write(reinterpret_cast(&refreshEndTime), sizeof(refreshEndTime)); cycles.act.serialize(stream); cycles.powerDownAct.serialize(stream); cycles.powerDownPre.serialize(stream); counter.serialize(stream); } void deserialize(std::istream& stream) override { stream.read(reinterpret_cast(&bankState), sizeof(bankState)); stream.read(reinterpret_cast(&latestPre), sizeof(latestPre)); stream.read(reinterpret_cast(&refreshEndTime), sizeof(refreshEndTime)); cycles.act.deserialize(stream); cycles.powerDownAct.deserialize(stream); cycles.powerDownPre.deserialize(stream); counter.deserialize(stream); } }; } #endif /* DRAMPOWER_DDR_BANK_H */ ================================================ FILE: src/DRAMPower/DRAMPower/dram/Interface.cpp ================================================ #include "Interface.h" namespace DRAMPower { using namespace DRAMUtils::Config; void TogglingHandle::TogglingHandleLastBurst::serialize(std::ostream &stream) const { stream.write(reinterpret_cast(&last_length), sizeof(last_length)); stream.write(reinterpret_cast(&last_load), sizeof(last_load)); stream.write(reinterpret_cast(&handled), sizeof(handled)); } void TogglingHandle::TogglingHandleLastBurst::deserialize(std::istream &stream) { stream.read(reinterpret_cast(&last_length), sizeof(last_length)); stream.read(reinterpret_cast(&last_load), sizeof(last_load)); stream.read(reinterpret_cast(&handled), sizeof(handled)); } TogglingHandle::TogglingHandle(const uint64_t width, const uint64_t datarate, const double toggling_rate, const double duty_cycle, const bool enabled) : width(width) , datarate(datarate) , toggling_rate(toggling_rate) , duty_cycle(duty_cycle) , enableflag(enabled) { assert(width > 0); // Check bounds assert(datarate > 0); // Check bounds assert(duty_cycle >= 0 && duty_cycle <= 1); // Check bounds assert(toggling_rate >= 0 && toggling_rate <= 1); // Check bounds } TogglingHandle::TogglingHandle(const uint64_t width, const uint64_t datarate, const double toggling_rate, const double duty_cycle, DRAMUtils::Config::TogglingRateIdlePattern idlepattern, const bool enabled) : width(width) , datarate(datarate) , toggling_rate(toggling_rate) , duty_cycle(duty_cycle) , enableflag(enabled) , idlepattern(idlepattern) { assert(width > 0); // Check bounds assert(datarate > 0); // Check bounds assert(duty_cycle >= 0 && duty_cycle <= 1); // Check bounds assert(toggling_rate >= 0 && toggling_rate <= 1); // Check bounds } void TogglingHandle::disable(timestamp_t timestamp) { timestamp_t virtualtimestamp = timestamp * this->datarate; if(!this->enableflag) { return; } if (this->last_burst) { if(virtualtimestamp >= this->last_burst.last_length + this->last_burst.last_load) { this->count += this->last_burst.last_length; } else { // Partial burst is lost this->count += virtualtimestamp - this->last_burst.last_load; } this->last_burst.handled = true; } this->disable_timestamp = virtualtimestamp; this->enableflag = false; } void TogglingHandle::enable(timestamp_t timestamp) { if (this->enableflag) { return; } this->disable_time += timestamp * this->datarate - this->disable_timestamp; this->enableflag = true; } bool TogglingHandle::isEnabled() const { return this->enableflag; } double TogglingHandle::getTogglingRate() const { return this->toggling_rate; } double TogglingHandle::getDutyCycle() const { return this->duty_cycle; } uint64_t TogglingHandle::getWidth() const { return this->width; } void TogglingHandle::setWidth(const uint64_t width) { this->width = width; } uint64_t TogglingHandle::getDatarate() const { return this->datarate; } void TogglingHandle::setDataRate(const uint64_t datarate) { this->datarate = datarate; } void TogglingHandle::setTogglingRateAndDutyCycle(const double toggling_rate, const double duty_cycle, const TogglingRateIdlePattern idlepattern) { this->toggling_rate = toggling_rate; this->duty_cycle = duty_cycle; this->idlepattern = idlepattern; } uint64_t TogglingHandle::getCount() const { return this->count; } // Returns timestamp of last burst timestamp_t TogglingHandle::get_lastburst_timestamp(bool relative_to_clock) const { timestamp_t lastburst = this->last_burst.last_load + this->last_burst.last_length; if (relative_to_clock) { auto remainder = lastburst % this->datarate; if (remainder != 0) { lastburst += this->datarate - remainder; } return lastburst / this->datarate; } return lastburst; } void TogglingHandle::incCountBurstLength(timestamp_t timestamp, uint64_t burstlength) { // Convert to bus timings timestamp_t virtual_timestamp = timestamp * this->datarate; assert(virtual_timestamp / this->datarate == timestamp); // No overflow assert( (this->last_burst && (virtual_timestamp >= (this->last_burst.last_length + this->last_burst.last_load))) || !this->last_burst ); // Add last burst if (!this->last_burst.handled) { // Add last burst to count this->count += this->last_burst.last_length; } // Store burst in last_length and last_load if enabled if (this->enableflag) { // Set last_length and last_load to new burst_length this->last_burst = TogglingHandleLastBurst { burstlength, // last_length virtual_timestamp, // last_load false // handled }; } else { // Clear last_burst this->last_burst.handled = true; } } void TogglingHandle::incCountBitLength(timestamp_t timestamp, uint64_t bitlength) { assert(bitlength % this->width == 0); this->incCountBurstLength(timestamp, bitlength / this->width); } util::bus_stats_t TogglingHandle::get_stats(timestamp_t timestamp) const { // Convert to bus timings timestamp_t virtual_timestamp = timestamp * this->datarate; assert(virtual_timestamp / this->datarate == timestamp); // No overflow util::bus_stats_t stats; uint64_t count = this->count; timestamp_t disable_time = this->disable_time; if(this->enableflag) { // Check if last burst is finished if ((this->last_burst) && (virtual_timestamp < this->last_burst.last_length + this->last_burst.last_load) ) { // last burst not finished count += virtual_timestamp - this->last_burst.last_load; } else if (this->last_burst) { // last burst finished count += this->last_burst.last_length; } } else { disable_time += virtual_timestamp - this->disable_timestamp; } // Compute toggles stats.ones = count * this->duty_cycle; stats.zeroes = count * (1 - this->duty_cycle); stats.ones_to_zeroes = count * this->toggling_rate / 2; // Round down // Compute idle switch (this->idlepattern) { case TogglingRateIdlePattern::L: stats.zeroes += virtual_timestamp - disable_time - count; break; case TogglingRateIdlePattern::H: stats.ones += virtual_timestamp - disable_time - count; break; case TogglingRateIdlePattern::Invalid: assert(false); // Fallback to Z case TogglingRateIdlePattern::Z: // Nothing to do in high impedance mode break; } // Scale by bus width stats.ones *= this->width; stats.zeroes *= this->width; stats.ones_to_zeroes *= this->width; // Compute zeroes_to_ones and bit changes stats.zeroes_to_ones = stats.ones_to_zeroes; stats.bit_changes = stats.ones_to_zeroes + stats.zeroes_to_ones; return stats; } void TogglingHandle::serialize(std::ostream &stream) const { stream.write(reinterpret_cast(&width), sizeof(width)); stream.write(reinterpret_cast(&datarate), sizeof(datarate)); stream.write(reinterpret_cast(&toggling_rate), sizeof(toggling_rate)); stream.write(reinterpret_cast(&duty_cycle), sizeof(duty_cycle)); last_burst.serialize(stream); stream.write(reinterpret_cast(&enableflag), sizeof(enableflag)); stream.write(reinterpret_cast(&count), sizeof(count)); stream.write(reinterpret_cast(&disable_timestamp), sizeof(disable_timestamp)); stream.write(reinterpret_cast(&disable_time), sizeof(disable_time)); stream.write(reinterpret_cast(&idlepattern), sizeof(idlepattern)); } void TogglingHandle::deserialize(std::istream &stream) { stream.read(reinterpret_cast(&width), sizeof(width)); stream.read(reinterpret_cast(&datarate), sizeof(datarate)); stream.read(reinterpret_cast(&toggling_rate), sizeof(toggling_rate)); stream.read(reinterpret_cast(&duty_cycle), sizeof(duty_cycle)); last_burst.deserialize(stream); stream.read(reinterpret_cast(&enableflag), sizeof(enableflag)); stream.read(reinterpret_cast(&count), sizeof(count)); stream.read(reinterpret_cast(&disable_timestamp), sizeof(disable_timestamp)); stream.read(reinterpret_cast(&disable_time), sizeof(disable_time)); stream.read(reinterpret_cast(&idlepattern), sizeof(idlepattern)); } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/dram/Interface.h ================================================ #ifndef DRAMPOWER_DDR_INTERFACE_H #define DRAMPOWER_DDR_INTERFACE_H #pragma once #include #include #include #include #include #include #include namespace DRAMPower { class TogglingHandle : public util::Serialize, public util::Deserialize { struct TogglingHandleLastBurst : public util::Serialize, public util::Deserialize { uint64_t last_length = 0; timestamp_t last_load = 0; bool handled = true; TogglingHandleLastBurst() = default; TogglingHandleLastBurst(uint64_t last_length, timestamp_t last_load, bool handled) : last_length(last_length), last_load(last_load), handled(handled) {} operator bool() const { return !this->handled; } void serialize(std::ostream &stream) const override; void deserialize(std::istream &stream) override; }; private: uint64_t width = 0; uint64_t datarate = 1; double toggling_rate = 0; // [0, 1] allowed double duty_cycle = 0.0; // [0, 1] allowed TogglingHandleLastBurst last_burst; bool enableflag = false; // default disabled if default constructor is used uint64_t count = 0; timestamp_t disable_timestamp = 0; timestamp_t disable_time = 0; DRAMUtils::Config::TogglingRateIdlePattern idlepattern = DRAMUtils::Config::TogglingRateIdlePattern::Z; public: TogglingHandle(const uint64_t width, const uint64_t datarate, const double toggling_rate, const double duty_cycle, const bool enabled = true); TogglingHandle(const uint64_t width, const uint64_t datarate, const double toggling_rate, const double duty_cycle, DRAMUtils::Config::TogglingRateIdlePattern idlepattern, const bool enabled = true); TogglingHandle() = default; public: // Getters and Setters double getTogglingRate() const; double getDutyCycle() const; bool isEnabled() const; uint64_t getWidth() const; uint64_t getDatarate() const; timestamp_t get_lastburst_timestamp(bool relative_to_clock = true) const; void setTogglingRateAndDutyCycle(const double toggling_rate, const double duty_cycle, const DRAMUtils::Config::TogglingRateIdlePattern idlepattern); void disable(timestamp_t timestamp); void enable(timestamp_t timestamp); void setWidth(const uint64_t width); void setDataRate(const uint64_t datarate); uint64_t getCount() const; public: void incCountBurstLength(timestamp_t timestamp, uint64_t burstlength); void incCountBitLength(timestamp_t timestamp, uint64_t bitlength); util::bus_stats_t get_stats(timestamp_t timestamp) const; // Overrides void serialize(std::ostream &stream) const override; void deserialize(std::istream &stream) override; }; struct interface_stats_t { util::bus_stats_t command_bus; util::bus_stats_t read_bus; util::bus_stats_t write_bus; }; } // namespace DRAMPower #endif /* DRAMPOWER_DDR_INTERFACE_H */ ================================================ FILE: src/DRAMPower/DRAMPower/dram/Rank.cpp ================================================ #include "Rank.h" #include namespace DRAMPower { Rank::Rank(std::size_t numBanks) : banks(numBanks) {}; bool Rank::isActive(timestamp_t timestamp) { if ( timestamp < this->endRefreshTime ) { std::cout << "[WARN] Rank::isActive() -> timestamp (" << timestamp <<") < " << "endRefreshTime (" << this->endRefreshTime << ")" << std::endl; } return countActiveBanks() > 0 || timestamp < this->endRefreshTime; } std::size_t Rank::countActiveBanks() const { return (unsigned)std::count_if(banks.begin(), banks.end(), [](const auto& bank) { return (bank.bankState == Bank::BankState::BANK_ACTIVE); }); } void Rank::serialize(std::ostream& stream) const { stream.write(reinterpret_cast(&memState), sizeof(memState)); stream.write(reinterpret_cast(&endRefreshTime), sizeof(endRefreshTime)); commandCounter.serialize(stream); stream.write(reinterpret_cast(&counter.selfRefresh), sizeof(counter.selfRefresh)); stream.write(reinterpret_cast(&counter.deepSleepMode), sizeof(counter.deepSleepMode)); stream.write(reinterpret_cast(&endRefreshTime), sizeof(endRefreshTime)); cycles.pre.serialize(stream); cycles.act.serialize(stream); cycles.ref.serialize(stream); cycles.sref.serialize(stream); cycles.powerDownAct.serialize(stream); cycles.powerDownPre.serialize(stream); cycles.deepSleepMode.serialize(stream); for (const auto& bank : banks) { bank.serialize(stream); } }; void Rank::deserialize(std::istream& stream) { stream.read(reinterpret_cast(&memState), sizeof(memState)); stream.read(reinterpret_cast(&endRefreshTime), sizeof(endRefreshTime)); commandCounter.deserialize(stream); stream.read(reinterpret_cast(&counter.selfRefresh), sizeof(counter.selfRefresh)); stream.read(reinterpret_cast(&counter.deepSleepMode), sizeof(counter.deepSleepMode)); stream.read(reinterpret_cast(&endRefreshTime), sizeof(endRefreshTime)); cycles.pre.deserialize(stream); cycles.act.deserialize(stream); cycles.ref.deserialize(stream); cycles.sref.deserialize(stream); cycles.powerDownAct.deserialize(stream); cycles.powerDownPre.deserialize(stream); cycles.deepSleepMode.deserialize(stream); for (auto & bank : banks) { bank.deserialize(stream); } }; void RankInterface::serialize(std::ostream& stream) const { stream.write(reinterpret_cast(&seamlessPrePostambleCounter_read), sizeof(seamlessPrePostambleCounter_read)); stream.write(reinterpret_cast(&seamlessPrePostambleCounter_write), sizeof(seamlessPrePostambleCounter_write)); stream.write(reinterpret_cast(&mergedPrePostambleCounter_read), sizeof(mergedPrePostambleCounter_read)); stream.write(reinterpret_cast(&mergedPrePostambleCounter_write), sizeof(mergedPrePostambleCounter_write)); stream.write(reinterpret_cast(&mergedPrePostambleTime_read), sizeof(mergedPrePostambleTime_read)); stream.write(reinterpret_cast(&mergedPrePostambleTime_write), sizeof(mergedPrePostambleTime_write)); stream.write(reinterpret_cast(&lastReadEnd), sizeof(lastReadEnd)); stream.write(reinterpret_cast(&lastWriteEnd), sizeof(lastWriteEnd)); } void RankInterface::deserialize(std::istream& stream) { stream.read(reinterpret_cast(&seamlessPrePostambleCounter_read), sizeof(seamlessPrePostambleCounter_read)); stream.read(reinterpret_cast(&seamlessPrePostambleCounter_write), sizeof(seamlessPrePostambleCounter_write)); stream.read(reinterpret_cast(&mergedPrePostambleCounter_read), sizeof(mergedPrePostambleCounter_read)); stream.read(reinterpret_cast(&mergedPrePostambleCounter_write), sizeof(mergedPrePostambleCounter_write)); stream.read(reinterpret_cast(&mergedPrePostambleTime_read), sizeof(mergedPrePostambleTime_read)); stream.read(reinterpret_cast(&mergedPrePostambleTime_write), sizeof(mergedPrePostambleTime_write)); stream.read(reinterpret_cast(&lastReadEnd), sizeof(lastReadEnd)); stream.read(reinterpret_cast(&lastWriteEnd), sizeof(lastWriteEnd)); } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/dram/Rank.h ================================================ #ifndef DRAMPOWER_DDR_RANK_H #define DRAMPOWER_DDR_RANK_H #include #include #include #include #include #include #include namespace DRAMPower { // Power-Down and Self-refresh related memory states enum class MemState { NOT_IN_PD = 0, PDN_ACT, PDN_PRE, SREF, DSM, }; struct Rank : public util::Serialize, public util::Deserialize { // Type aliases using commandCounter_t = util::CommandCounter; public: // Variables MemState memState = MemState::NOT_IN_PD; commandCounter_t commandCounter; struct { interval_t pre; // useful ??? interval_t act; interval_t ref; interval_t sref; interval_t powerDownAct; interval_t powerDownPre; interval_t deepSleepMode; } cycles; struct { uint64_t selfRefresh = 0; uint64_t deepSleepMode = 0; } counter = { 0 }; timestamp_t endRefreshTime = 0; std::vector banks; public: // Constructors Rank(std::size_t numBanks); // Functions public: bool isActive(timestamp_t timestamp); std::size_t countActiveBanks() const; // Overrides void serialize(std::ostream& stream) const override; void deserialize(std::istream& stream) override; }; struct RankInterface : public util::Serialize, public util::Deserialize { uint64_t seamlessPrePostambleCounter_read = 0; uint64_t seamlessPrePostambleCounter_write = 0; uint64_t mergedPrePostambleCounter_read = 0; uint64_t mergedPrePostambleCounter_write = 0; timestamp_t mergedPrePostambleTime_read = 0; timestamp_t mergedPrePostambleTime_write = 0; timestamp_t lastReadEnd = 0; timestamp_t lastWriteEnd = 0; // Overrides void serialize(std::ostream& stream) const override; void deserialize(std::istream& stream) override; }; } // namespace DRAMPower #endif /* DRAMPOWER_DDR_RANK_H */ ================================================ FILE: src/DRAMPower/DRAMPower/dram/dram_base.h ================================================ #ifndef DRAMPOWER_DRAM_DRAM_BASE_H #define DRAMPOWER_DRAM_DRAM_BASE_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DRAMPower { using namespace DRAMUtils::Config; template class dram_base : public util::Serialize, public util::Deserialize { // Public type definitions public: using commandEnum_t = CommandEnum; using commandCount_t = std::vector; // ExtensionManager using extension_manager_t = util::extension_manager::ExtensionManager< extensions::Base >; // Public constructors and assignment operators public: dram_base(const dram_base&) = default; // copy constructor dram_base& operator=(const dram_base&) = default; // copy assignment operator dram_base(dram_base&&) = default; // Move constructor dram_base& operator=(dram_base&&) = default; // Move assignment operator virtual ~dram_base() = 0; // Protected constructors protected: dram_base() = default; // Public member functions public: // ExtensionManager extension_manager_t& getExtensionManager() { return m_extensionManager; } const extension_manager_t& getExtensionManager() const { return m_extensionManager; } void doCoreCommand(const Command& command) { doCoreCommandImpl(command); } void doInterfaceCommand(const Command& command) { doInterfaceCommandImpl(command); } void doCommand(const Command& command) { doCoreCommandImpl(command); doInterfaceCommandImpl(command); } // deprecated void doCoreInterfaceCommand(const Command& command) { doCommand(command); } timestamp_t getLastCommandTime() const { return getLastCommandTime_impl(); } double getTotalEnergy(timestamp_t timestamp) { return calcCoreEnergy(timestamp).total() + calcInterfaceEnergy(timestamp).total(); }; SimulationStats getStats() { return getWindowStats(getLastCommandTime()); } void serialize(std::ostream& stream) const override { // Serialize the extension manager m_extensionManager.serialize(stream); serialize_impl(stream); } void deserialize(std::istream& stream) override { // Deserialize the extension manager m_extensionManager.deserialize(stream); deserialize_impl(stream); } // Public virtual methods public: virtual energy_t calcCoreEnergyStats(const SimulationStats& stats) const = 0; energy_t calcCoreEnergy(timestamp_t timestamp) { return calcCoreEnergyStats(getWindowStats(timestamp)); } virtual interface_energy_info_t calcInterfaceEnergyStats(const SimulationStats& stats) const = 0; interface_energy_info_t calcInterfaceEnergy(timestamp_t timestamp) { return calcInterfaceEnergyStats(getWindowStats(timestamp)); } virtual SimulationStats getWindowStats(timestamp_t timestamp) = 0; virtual util::CLIArchitectureConfig getCLIArchitectureConfig() = 0; virtual bool isSerializable() const = 0; // Private virtual methods private: virtual void doCoreCommandImpl(const Command& command) = 0; virtual void doInterfaceCommandImpl(const Command& command) = 0; virtual timestamp_t getLastCommandTime_impl() const = 0; virtual void serialize_impl(std::ostream& stream) const = 0; virtual void deserialize_impl(std::istream& stream) = 0; // Private member variables private: extension_manager_t m_extensionManager; }; template dram_base::~dram_base() = default; } #endif /* DRAMPOWER_DDR_DRAM_BASE_H */ ================================================ FILE: src/DRAMPower/DRAMPower/memspec/MemSpec.h ================================================ /* * Copyright (c) 2015-2020, University of Kaiserslautern * Copyright (c) 2012-2021, 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: * * 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. * * Authors: * Janik Schlemminger * Matthias Jung * Lukas Steiner * Luiza Correa */ #ifndef DRAMPOWER_MEMSPEC_MEMSPEC_H #define DRAMPOWER_MEMSPEC_MEMSPEC_H #include #include #include #include #include #include namespace DRAMPower { template class MemSpec { T rawmemspec; public: uint64_t numberOfBanks; uint64_t numberOfRows; uint64_t numberOfColumns; uint64_t numberOfDevices; // Number of devices per rank uint64_t burstLength; uint64_t dataRate; uint64_t bitWidth; /* MEMSPEC specific parameters unsigned numberOfBankGroups; unsigned numberOfDevicesOnDIMM; unsigned numberOfRanks; unsigned banksPerRank; unsigned groupsPerRank; unsigned banksPerGroup; unsigned numberOfChannels; */ std::string memoryId; std::string memoryType; uint64_t prechargeOffsetRD; uint64_t prechargeOffsetWR; MemSpec() = delete; virtual ~MemSpec() = default; // MemSpec() = default; MemSpec(const T &memspec) : rawmemspec(memspec) { numberOfBanks = memspec.memarchitecturespec.nbrOfBanks; numberOfRows = memspec.memarchitecturespec.nbrOfRows; numberOfColumns = memspec.memarchitecturespec.nbrOfColumns; numberOfDevices = memspec.memarchitecturespec.nbrOfDevices; if (numberOfDevices < 1) { numberOfDevices = 1; } burstLength = memspec.memarchitecturespec.burstLength; dataRate = memspec.memarchitecturespec.dataRate; bitWidth = memspec.memarchitecturespec.width; memoryId = memspec.memoryId; memoryType = memspec.id; } }; } // namespace DRAMPower #endif /* DRAMPOWER_MEMSPEC_MEMSPEC_H */ ================================================ FILE: src/DRAMPower/DRAMPower/memspec/MemSpecDDR4.cpp ================================================ /* * Copyright (c) 2019, University of Kaiserslautern * Copyright (c) 2012-2021, 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: * * 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. * * Authors: * Lukas Steiner * Luiza Correa */ #include "MemSpecDDR4.h" using namespace DRAMPower; MemSpecDDR4::MemSpecDDR4(const DRAMUtils::MemSpec::MemSpecDDR4 &memspec) : MemSpec(memspec) { numberOfBankGroups = memspec.memarchitecturespec.nbrOfBankGroups; numberOfRanks = memspec.memarchitecturespec.nbrOfRanks; memTimingSpec.tCK = memspec.memtimingspec.tCK; memTimingSpec.tRAS = memspec.memtimingspec.RAS; memTimingSpec.tRCD = memspec.memtimingspec.RCD; memTimingSpec.tRTP = memspec.memtimingspec.RTP; memTimingSpec.tWL = memspec.memtimingspec.WL; memTimingSpec.tWR = memspec.memtimingspec.WR; memTimingSpec.tRP = memspec.memtimingspec.RP; memTimingSpec.tAL = memspec.memtimingspec.AL; auto VDD = VoltageDomain::VDD; auto VPP = VoltageDomain::VPP; memPowerSpec.push_back(MemPowerSpec()); memPowerSpec[VDD].vXX = memspec.mempowerspec.vdd; memPowerSpec[VDD].iXX0 = memspec.mempowerspec.idd0; memPowerSpec[VDD].iXX2N = memspec.mempowerspec.idd2n; memPowerSpec[VDD].iXX3N = memspec.mempowerspec.idd3n; memPowerSpec[VDD].iXX4R = memspec.mempowerspec.idd4r; memPowerSpec[VDD].iXX4W = memspec.mempowerspec.idd4w; memPowerSpec[VDD].iXX6N = memspec.mempowerspec.idd6n; memPowerSpec[VDD].iXX2P = memspec.mempowerspec.idd2p; memPowerSpec[VDD].iXX3P = memspec.mempowerspec.idd3p; memPowerSpec.push_back(MemPowerSpec()); memPowerSpec[VPP].vXX = memspec.mempowerspec.vpp; memPowerSpec[VPP].iXX0 = memspec.mempowerspec.ipp0; memPowerSpec[VPP].iXX2N = memspec.mempowerspec.ipp2n; memPowerSpec[VPP].iXX3N = memspec.mempowerspec.ipp3n; memPowerSpec[VPP].iXX4R = memspec.mempowerspec.ipp4r; memPowerSpec[VPP].iXX4W = memspec.mempowerspec.ipp4w; memPowerSpec[VPP].iXX6N = memspec.mempowerspec.ipp6n; memPowerSpec[VPP].iXX2P = memspec.mempowerspec.ipp2p; memPowerSpec[VPP].iXX3P = memspec.mempowerspec.ipp3p; vddq = memspec.mempowerspec.vddq; if (DRAMUtils::MemSpec::RefModeTypeDDR4::REF_MODE_2 == memspec.memarchitecturespec.RefMode) { memPowerSpec[VDD].iXX5X = memspec.mempowerspec.idd5F2; memPowerSpec[VPP].iXX5X = memspec.mempowerspec.ipp5F2; memTimingSpec.tRFC = memspec.memtimingspec.RFC2; } else if (DRAMUtils::MemSpec::RefModeTypeDDR4::REF_MODE_4 == memspec.memarchitecturespec.RefMode) { memPowerSpec[VDD].iXX5X = memspec.mempowerspec.idd5F4; memPowerSpec[VPP].iXX5X = memspec.mempowerspec.ipp5F4; memTimingSpec.tRFC = memspec.memtimingspec.RFC4; } else { // RefModeTypeDDR4::REF_MODE_1 || RefModeTypeDDR4::INVALID memPowerSpec[VDD].iXX5X = memspec.mempowerspec.idd5B; memPowerSpec[VPP].iXX5X = memspec.mempowerspec.ipp5B; memTimingSpec.tRFC = memspec.memtimingspec.RFC1; } memPowerSpec[VDD].iBeta = memspec.mempowerspec.iBeta_vdd.value_or(memspec.mempowerspec.idd0); memPowerSpec[VPP].iBeta = memspec.mempowerspec.iBeta_vpp.value_or(memspec.mempowerspec.ipp0); if(memspec.bankwisespec.has_value()) { bwParams.bwPowerFactRho = memspec.bankwisespec.value().factRho.value_or(1); } else { bwParams.bwPowerFactRho = 1; } memTimingSpec.tBurst = burstLength/dataRate; prechargeOffsetRD = memTimingSpec.tAL + memTimingSpec.tRTP; prechargeOffsetWR = memTimingSpec.tBurst + memTimingSpec.tWL + memTimingSpec.tWR; parseImpedanceSpec(memspec); parsePrePostamble(memspec); } void MemSpecDDR4::parseImpedanceSpec(const DRAMUtils::MemSpec::MemSpecDDR4 &memspec) { memImpedanceSpec = memspec.memimpedancespec; } void MemSpecDDR4::parsePrePostamble(const DRAMUtils::MemSpec::MemSpecDDR4 &memspec) { prePostamble.read_zeroes = memspec.prepostamble.read_zeroes; prePostamble.write_zeroes = memspec.prepostamble.write_zeroes; prePostamble.read_ones = memspec.prepostamble.read_ones; prePostamble.write_ones = memspec.prepostamble.write_ones; prePostamble.read_zeroes_to_ones = memspec.prepostamble.read_zeroes_to_ones; prePostamble.write_zeroes_to_ones = memspec.prepostamble.write_zeroes_to_ones; prePostamble.write_ones_to_zeroes = memspec.prepostamble.write_ones_to_zeroes; prePostamble.read_ones_to_zeroes = memspec.prepostamble.read_ones_to_zeroes; prePostamble.readMinTccd = memspec.prepostamble.readMinTccd; prePostamble.writeMinTccd = memspec.prepostamble.writeMinTccd;; } MemSpecDDR4 MemSpecDDR4::from_memspec(const DRAMUtils::MemSpec::MemSpecVariant& memSpec) { return std::get(memSpec.getVariant()); } ================================================ FILE: src/DRAMPower/DRAMPower/memspec/MemSpecDDR4.h ================================================ /* * Copyright (c) 2019, University of Kaiserslautern * Copyright (c) 2012-2021, 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: * * 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. * * Authors: * Lukas Steiner * Luiza Correa */ #ifndef DRAMPOWER_MEMSPEC_MEMSPECDDR4_H #define DRAMPOWER_MEMSPEC_MEMSPECDDR4_H #include "MemSpec.h" #include #include namespace DRAMPower { class MemSpecDDR4 final : public MemSpec { public: using MemImpedanceSpec = DRAMUtils::MemSpec::MemImpedanceSpecTypeDDR4; enum VoltageDomain { VDD = 0, VPP = 1, }; public: MemSpecDDR4() = delete; MemSpecDDR4(const DRAMUtils::MemSpec::MemSpecDDR4 &memspec); MemSpecDDR4(json_t &data) = delete; MemSpecDDR4(const json_t &data) = delete; // Copy constructor and assignment operator MemSpecDDR4(const MemSpecDDR4&) = default; MemSpecDDR4& operator=(const MemSpecDDR4&) = default; // Move constructor and assignment operator MemSpecDDR4(MemSpecDDR4&&) = default; MemSpecDDR4& operator=(MemSpecDDR4&&) = default; uint64_t numberOfBankGroups; uint64_t numberOfRanks; double vddq; // Memspec Variables: struct MemTimingSpec { double fck; double tCK; uint64_t tRAS; uint64_t tRCD; uint64_t tRL; uint64_t tRTP; uint64_t tWL; uint64_t tWR; uint64_t tRFC; uint64_t tRP; uint64_t tAL; uint64_t tBurst; }; // Currents and Voltages: struct MemPowerSpec { double iXX0; double iXX2N; double iXX3N; double iXX4R; double iXX4W; double iXX5X; double iXX6N; double vXX; double iXX2P; double iXX3P; double iBeta; }; struct BankWiseParams { // ACT Standby power factor double bwPowerFactRho; }; // Pre and Postamble // Total number of zero cycles relative to tCK (one cycle = tCK) for example, if tCK = 1ns, and read_zeroes = 2.5, then the total time is 2.5ns struct PrePostamble { // Total number of zero/one cycles per DQs differential pair // relative to tCK (one cycle = tCK) // for example, if tCK = 1ns, and read_zeroes = 2.5, then the total time is 2.5ns double read_zeroes; double write_zeroes; double read_ones; double write_ones; // Total number of zero to one and one to zero transitions per DQs differential pair uint64_t read_zeroes_to_ones; uint64_t write_zeroes_to_ones; uint64_t write_ones_to_zeroes; uint64_t read_ones_to_zeroes; // Minimum time interval between two consecutive read/write commands to prevent merging or seamless transition. uint64_t readMinTccd; uint64_t writeMinTccd; }; uint64_t refreshMode; MemTimingSpec memTimingSpec; std::vector memPowerSpec; MemImpedanceSpec memImpedanceSpec; PrePostamble prePostamble; BankWiseParams bwParams; private: void parseImpedanceSpec(const DRAMUtils::MemSpec::MemSpecDDR4 &memspec); void parsePrePostamble(const DRAMUtils::MemSpec::MemSpecDDR4 &memspec); public: static MemSpecDDR4 from_memspec(const DRAMUtils::MemSpec::MemSpecVariant&); }; } #endif /* DRAMPOWER_MEMSPEC_MEMSPECDDR4_H */ ================================================ FILE: src/DRAMPower/DRAMPower/memspec/MemSpecDDR5.cpp ================================================ #include "MemSpecDDR5.h" using namespace DRAMPower; MemSpecDDR5::MemSpecDDR5(const DRAMUtils::MemSpec::MemSpecDDR5 &memspec) : MemSpec(memspec) { numberOfBankGroups = memspec.memarchitecturespec.nbrOfBankGroups; numberOfRanks = memspec.memarchitecturespec.nbrOfRanks; banksPerGroup = numberOfBanks / numberOfBankGroups; memTimingSpec.tCK = memspec.memtimingspec.tCK; memTimingSpec.tRAS = memspec.memtimingspec.RAS; memTimingSpec.tRCD = memspec.memtimingspec.RCD; memTimingSpec.tRTP = memspec.memtimingspec.RTP; memTimingSpec.tWL = memspec.memtimingspec.WL; memTimingSpec.tWR = memspec.memtimingspec.WR; memTimingSpec.tRP = memspec.memtimingspec.RP; memTimingSpec.tRFCsb = memspec.memtimingspec.RFCsb_slr; auto VDD = VoltageDomain::VDD; auto VPP = VoltageDomain::VPP; memPowerSpec.push_back(MemPowerSpec()); memPowerSpec[VDD].vXX = memspec.mempowerspec.vdd; memPowerSpec[VDD].iXX0 = memspec.mempowerspec.idd0; memPowerSpec[VDD].iXX2N = memspec.mempowerspec.idd2n; memPowerSpec[VDD].iXX3N = memspec.mempowerspec.idd3n; memPowerSpec[VDD].iXX4R = memspec.mempowerspec.idd4r; memPowerSpec[VDD].iXX4W = memspec.mempowerspec.idd4w; memPowerSpec[VDD].iXX5C = memspec.mempowerspec.idd5c; memPowerSpec[VDD].iXX6N = memspec.mempowerspec.idd6n; memPowerSpec[VDD].iXX2P = memspec.mempowerspec.idd2p; memPowerSpec[VDD].iXX3P = memspec.mempowerspec.idd3p; memPowerSpec.push_back(MemPowerSpec()); memPowerSpec[VPP].vXX = memspec.mempowerspec.vpp; memPowerSpec[VPP].iXX0 = memspec.mempowerspec.ipp0; memPowerSpec[VPP].iXX2N = memspec.mempowerspec.ipp2n; memPowerSpec[VPP].iXX3N = memspec.mempowerspec.ipp3n; memPowerSpec[VPP].iXX4R = memspec.mempowerspec.ipp4r; memPowerSpec[VPP].iXX4W = memspec.mempowerspec.ipp4w; memPowerSpec[VPP].iXX5C = memspec.mempowerspec.ipp5c; memPowerSpec[VPP].iXX6N = memspec.mempowerspec.ipp6n; memPowerSpec[VPP].iXX2P = memspec.mempowerspec.ipp2p; memPowerSpec[VPP].iXX3P = memspec.mempowerspec.ipp3p; vddq = memspec.mempowerspec.vddq; if (DRAMUtils::MemSpec::RefModeTypeDDR5::REF_MODE_2 == memspec.memarchitecturespec.RefMode) { memPowerSpec[VDD].iXX5X = memspec.mempowerspec.idd5f; memPowerSpec[VPP].iXX5X = memspec.mempowerspec.ipp5f; memTimingSpec.tRFC = memspec.memtimingspec.RFC2_slr; } else { // RefModeTypeDDR5::REF_MODE_1 || RefModeTypeDDR5::INVALID memPowerSpec[VDD].iXX5X = memspec.mempowerspec.idd5b; memPowerSpec[VPP].iXX5X = memspec.mempowerspec.ipp5b; memTimingSpec.tRFC = memspec.memtimingspec.RFC1_slr; } memPowerSpec[VDD].iBeta = memspec.mempowerspec.iBeta_vdd.value_or(memspec.mempowerspec.idd0); memPowerSpec[VPP].iBeta = memspec.mempowerspec.iBeta_vpp.value_or(memspec.mempowerspec.ipp0); if (memspec.bankwisespec.has_value()) { bwParams.bwPowerFactRho = memspec.bankwisespec.value().factRho.value_or(1); } else { bwParams.bwPowerFactRho = 1; } memTimingSpec.tBurst = burstLength/dataRate; prechargeOffsetRD = memTimingSpec.tRTP; prechargeOffsetWR = memTimingSpec.tBurst + memTimingSpec.tWL + memTimingSpec.tWR; parseImpedanceSpec(memspec); parseDataRateSpec(memspec); } void MemSpecDDR5::parseImpedanceSpec(const DRAMUtils::MemSpec::MemSpecDDR5 &memspec) { memImpedanceSpec = memspec.memimpedancespec; } void MemSpecDDR5::parseDataRateSpec(const DRAMUtils::MemSpec::MemSpecDDR5 &memspec) { if (!memspec.dataratespec.has_value()) { dataRateSpec = {2, 2, 2}; return; } dataRateSpec.commandBusRate = memspec.dataratespec.value().ca_bus_rate; dataRateSpec.dataBusRate = memspec.dataratespec.value().dq_bus_rate; dataRateSpec.dqsBusRate = memspec.dataratespec.value().dqs_bus_rate; } MemSpecDDR5 MemSpecDDR5::from_memspec(const DRAMUtils::MemSpec::MemSpecVariant& memSpec) { return std::get(memSpec.getVariant()); } ================================================ FILE: src/DRAMPower/DRAMPower/memspec/MemSpecDDR5.h ================================================ #ifndef DRAMPOWER_MEMSPEC_MEMSPECDDR5_H #define DRAMPOWER_MEMSPEC_MEMSPECDDR5_H #include "MemSpec.h" #include namespace DRAMPower { class MemSpecDDR5 final : public MemSpec { public: using MemImpedanceSpec = DRAMUtils::MemSpec::MemImpedanceSpecTypeDDR5; enum VoltageDomain { VDD = 0, VPP = 1, }; public: MemSpecDDR5() = delete; MemSpecDDR5(const DRAMUtils::MemSpec::MemSpecDDR5 &memspec); MemSpecDDR5(json_t &data) = delete; MemSpecDDR5(const json_t &data) = delete; // Copy constructor and assignment operator MemSpecDDR5(const MemSpecDDR5&) = default; MemSpecDDR5& operator=(const MemSpecDDR5&) = default; // Move constructor and assignment operator MemSpecDDR5(MemSpecDDR5&&) = default; MemSpecDDR5& operator=(MemSpecDDR5&&) = default; uint64_t numberOfBankGroups; uint64_t banksPerGroup; uint64_t numberOfRanks; double vddq; // Memspec Variables: struct MemTimingSpec { double fck; double tCK; uint64_t tRAS; uint64_t tRCD; uint64_t tRL; uint64_t tRTP; uint64_t tWL; uint64_t tWR; uint64_t tRFC; uint64_t tRFCsb; uint64_t tRP; uint64_t tBurst; }; // Currents and Voltages: struct MemPowerSpec { double vXX; double iXX0; double iXX2N; double iXX3N; double iXX2P; double iXX3P; double iXX4R; double iXX4W; double iXX5X; double iXX5C; double iXX6N; double iBeta; }; struct DataRateSpec { uint64_t commandBusRate; uint64_t dataBusRate; uint64_t dqsBusRate; }; struct BankWiseParams { // ACT Standby power factor double bwPowerFactRho; }; MemTimingSpec memTimingSpec; MemImpedanceSpec memImpedanceSpec; DataRateSpec dataRateSpec; std::vector memPowerSpec; BankWiseParams bwParams; private: void parseImpedanceSpec(const DRAMUtils::MemSpec::MemSpecDDR5 &memspec); void parseDataRateSpec(const DRAMUtils::MemSpec::MemSpecDDR5 &memspec); public: static MemSpecDDR5 from_memspec(const DRAMUtils::MemSpec::MemSpecVariant&); }; } #endif /* DRAMPOWER_MEMSPEC_MEMSPECDDR5_H */ ================================================ FILE: src/DRAMPower/DRAMPower/memspec/MemSpecLPDDR4.cpp ================================================ #include "MemSpecLPDDR4.h" using namespace DRAMPower; MemSpecLPDDR4::MemSpecLPDDR4(const DRAMUtils::MemSpec::MemSpecLPDDR4 &memspec) : MemSpec(memspec), memImpedanceSpec{} { numberOfBankGroups = memspec.memarchitecturespec.nbrOfBankGroups; numberOfRanks = memspec.memarchitecturespec.nbrOfRanks; banksPerGroup = numberOfBanks / numberOfBankGroups; memTimingSpec.tCK = memspec.memtimingspec.tCK; memTimingSpec.tRAS = memspec.memtimingspec.RAS; memTimingSpec.tRCD = memspec.memtimingspec.RCD; memTimingSpec.tRL = memspec.memtimingspec.RL; memTimingSpec.tRTP = memspec.memtimingspec.RTP; memTimingSpec.tWL = memspec.memtimingspec.WL; memTimingSpec.tWR = memspec.memtimingspec.WR; memTimingSpec.tRP = memspec.memtimingspec.RPpb; memTimingSpec.tRFCPB = memspec.memtimingspec.RFCpb; memTimingSpec.tRFC = memspec.memtimingspec.RFCab; memTimingSpec.tREFI = memspec.memtimingspec.REFI; auto VDD1 = VoltageDomain::VDD1; auto VDD2 = VoltageDomain::VDD2; memPowerSpec.push_back(MemPowerSpec()); memPowerSpec.push_back(MemPowerSpec()); memPowerSpec[VDD1].vDDX = memspec.mempowerspec.vdd1; memPowerSpec[VDD1].iDD0X = memspec.mempowerspec.idd01; memPowerSpec[VDD1].iDD2NX = memspec.mempowerspec.idd2n1; memPowerSpec[VDD1].iDD3NX = memspec.mempowerspec.idd3n1; memPowerSpec[VDD1].iDD4RX = memspec.mempowerspec.idd4r1; memPowerSpec[VDD1].iDD4WX = memspec.mempowerspec.idd4w1; memPowerSpec[VDD1].iDD5X = memspec.mempowerspec.idd51; memPowerSpec[VDD1].iDD5PBX = memspec.mempowerspec.idd5pb1; memPowerSpec[VDD1].iDD6X = memspec.mempowerspec.idd61; memPowerSpec[VDD1].iDD2PX = memspec.mempowerspec.idd2p1; memPowerSpec[VDD1].iDD3PX = memspec.mempowerspec.idd3p1; memPowerSpec[VDD2].vDDX = memspec.mempowerspec.vdd2; memPowerSpec[VDD2].iDD0X = memspec.mempowerspec.idd02; memPowerSpec[VDD2].iDD2NX = memspec.mempowerspec.idd2n2; memPowerSpec[VDD2].iDD3NX = memspec.mempowerspec.idd3n2; memPowerSpec[VDD2].iDD4RX = memspec.mempowerspec.idd4r2; memPowerSpec[VDD2].iDD4WX = memspec.mempowerspec.idd4w2; memPowerSpec[VDD2].iDD5X = memspec.mempowerspec.idd52; memPowerSpec[VDD2].iDD5PBX = memspec.mempowerspec.idd5pb2; memPowerSpec[VDD2].iDD6X = memspec.mempowerspec.idd62; memPowerSpec[VDD2].iDD2PX = memspec.mempowerspec.idd2p2; memPowerSpec[VDD2].iDD3PX = memspec.mempowerspec.idd3p2; vddq = memspec.mempowerspec.vddq; memPowerSpec[VDD1].iBeta = memspec.mempowerspec.iBeta_vdd1.value_or(memspec.mempowerspec.idd01); memPowerSpec[VDD2].iBeta = memspec.mempowerspec.iBeta_vdd2.value_or(memspec.mempowerspec.idd02); if(memspec.bankwisespec.has_value()) { bwParams.bwPowerFactRho = memspec.bankwisespec.value().factRho.value_or(1); bwParams.bwPowerFactSigma = memspec.bankwisespec.value().factSigma.value_or(1); bwParams.flgPASR = memspec.bankwisespec.value().hasPASR.value_or(false); if (memspec.bankwisespec.value().pasrMode.has_value()) { if(memspec.bankwisespec.value().pasrMode.value() == DRAMUtils::MemSpec::pasrModesType::Invalid) { // pasrMode invalid bwParams.pasrMode = 0; } else { // pasrMode valid bwParams.pasrMode = static_cast(memspec.bankwisespec.value().pasrMode.value()); } } else { bwParams.pasrMode = 0; } } else { bwParams.bwPowerFactRho = 1; bwParams.bwPowerFactSigma = 1; bwParams.flgPASR = false; bwParams.pasrMode = 0; } if (bwParams.flgPASR) { /////////////////////////////////////////////////////////// // Activate banks for self refresh based on the PASR mode // ACTIVE - X // NOT ACTIVE - 0 /////////////////////////////////////////////////////////// switch(bwParams.pasrMode) { case(BankWiseParams::pasrModes::PASR_0): { // PASR MODE 0 // FULL ARRAY // |X X X X | // |X X X X | bwParams.activeBanks.resize(numberOfBanks); std::iota(bwParams.activeBanks.begin(), bwParams.activeBanks.end(), 0); break; } case(BankWiseParams::pasrModes::PASR_1): { // PASR MODE 1 // (1/2) ARRAY // |X X X X | // |0 0 0 0 | bwParams.activeBanks.resize(numberOfBanks - 4); std::iota(bwParams.activeBanks.begin(), bwParams.activeBanks.end(), 0); break; } case(BankWiseParams::pasrModes::PASR_2): { // PASR MODE 2 // (1/4) ARRAY // |X X 0 0 | // |0 0 0 0 | bwParams.activeBanks.resize(numberOfBanks - 6); std::iota(bwParams.activeBanks.begin(), bwParams.activeBanks.end(), 0); break; } case(BankWiseParams::pasrModes::PASR_3): { // PASR MODE 3 // (1/8) ARRAY // |X 0 0 0 | // |0 0 0 0 | bwParams.activeBanks.resize(numberOfBanks - 7); std::iota(bwParams.activeBanks.begin(), bwParams.activeBanks.end(), 0); break; } case(BankWiseParams::pasrModes::PASR_4): { // PASR MODE 4 // (3/4) ARRAY // |0 0 X X | // |X X X X | bwParams.activeBanks.resize(numberOfBanks - 2); std::iota(bwParams.activeBanks.begin(), bwParams.activeBanks.end(), 2); break; } case(BankWiseParams::pasrModes::PASR_5): { // PASR MODE 5 // (1/2) ARRAY // |0 0 0 0 | // |X X X X | bwParams.activeBanks.resize(numberOfBanks - 4); std::iota(bwParams.activeBanks.begin(), bwParams.activeBanks.end(), 4); break; } case(BankWiseParams::pasrModes::PASR_6): { // PASR MODE 6 // (1/4) ARRAY // |0 0 0 0 | // |0 0 X X | bwParams.activeBanks.resize(numberOfBanks - 6); std::iota(bwParams.activeBanks.begin(), bwParams.activeBanks.end(), 6); break; } case(BankWiseParams::pasrModes::PASR_7): { // PASR MODE 7 // (1/8) ARRAY // |0 0 0 0 | // |0 0 0 X | bwParams.activeBanks.resize(numberOfBanks - 7); std::iota(bwParams.activeBanks.begin(), bwParams.activeBanks.end(), 7); break; } default: { // PASR MODE 0 // FULL ARRAY // |X X X X | // |X X X X | bwParams.activeBanks.resize(numberOfBanks); std::iota(bwParams.activeBanks.begin(), bwParams.activeBanks.end(), 0); break; } } //end switch } // end IF flgPASR //Source: JESD209-4 (LPDDR4); Table 21 if(burstLength == 16){ prechargeOffsetRD = memTimingSpec.tRTP; }else{ prechargeOffsetRD = 8 + memTimingSpec.tRTP; } prechargeOffsetWR = memTimingSpec.tWL + burstLength/2 + memTimingSpec.tWR + 1; parseImpedanceSpec(memspec); } bool MemSpecLPDDR4::BankWiseParams::isBankActiveInPasr(const unsigned bankIdx) const { return (std::find(activeBanks.begin(), activeBanks.end(), bankIdx) != activeBanks.end()); } void MemSpecLPDDR4::parseImpedanceSpec(const DRAMUtils::MemSpec::MemSpecLPDDR4 &memspec) { memImpedanceSpec = memspec.memimpedancespec; } MemSpecLPDDR4 MemSpecLPDDR4::from_memspec(const DRAMUtils::MemSpec::MemSpecVariant& memSpec) { return std::get(memSpec.getVariant()); } ================================================ FILE: src/DRAMPower/DRAMPower/memspec/MemSpecLPDDR4.h ================================================ #ifndef DRAMPOWER_MEMSPEC_MEMSPECLPDDR4_H #define DRAMPOWER_MEMSPEC_MEMSPECLPDDR4_H #include "MemSpec.h" #include namespace DRAMPower { class MemSpecLPDDR4 final : public MemSpec { public: using MemImpedanceSpec = DRAMUtils::MemSpec::MemImpedanceSpecTypeLPDDR4; enum VoltageDomain { VDD1 = 0, VDD2 = 1, }; public: MemSpecLPDDR4() = delete; MemSpecLPDDR4(const DRAMUtils::MemSpec::MemSpecLPDDR4 &memspec); MemSpecLPDDR4(json_t &data) = delete; MemSpecLPDDR4(const json_t &data) = delete; // Copy constructor and assignment operator MemSpecLPDDR4(const MemSpecLPDDR4&) = default; MemSpecLPDDR4& operator=(const MemSpecLPDDR4&) = default; // Move constructor and assignment operator MemSpecLPDDR4(MemSpecLPDDR4&&) = default; MemSpecLPDDR4& operator=(MemSpecLPDDR4&&) = default; ~MemSpecLPDDR4() = default; uint64_t numberOfBankGroups; uint64_t banksPerGroup; uint64_t numberOfRanks; double vddq; struct MemTimingSpec { double fck; double tCK; uint64_t tRAS; uint64_t tRCD; uint64_t tRL; uint64_t tRTP; uint64_t tWL; uint64_t tWR; uint64_t tREFI; uint64_t tRFC; uint64_t tRFCPB; uint64_t tRP; }; // Currents and Voltages: struct MemPowerSpec { double vDDX; double iDD0X; double iDD2NX; double iDD3NX; double iDD2PX; double iDD3PX; double iDD4RX; double iDD4WX; double iDD5X; double iDD5PBX; double iDD6X; double iBeta; }; struct BankWiseParams { // Set of possible PASR modes enum pasrModes { PASR_0, PASR_1, PASR_2, PASR_3, PASR_4, PASR_5, PASR_6, PASR_7 }; // List of active banks under the specified PASR mode std::vector activeBanks; // ACT Standby power factor double bwPowerFactRho; // Self-Refresh power factor double bwPowerFactSigma; // Whether PASR is enabled ( true : enabled ) bool flgPASR; // PASR mode utilized (int 0-7) uint64_t pasrMode; // Whether bank is active in PASR bool isBankActiveInPasr(const unsigned bankIdx) const; }; MemTimingSpec memTimingSpec; MemImpedanceSpec memImpedanceSpec; std::vector memPowerSpec; BankWiseParams bwParams; private: void parseImpedanceSpec(const DRAMUtils::MemSpec::MemSpecLPDDR4 &memspec); public: static MemSpecLPDDR4 from_memspec(const DRAMUtils::MemSpec::MemSpecVariant&); }; } #endif /* DRAMPOWER_MEMSPEC_MEMSPECLPDDR4_H */ ================================================ FILE: src/DRAMPower/DRAMPower/memspec/MemSpecLPDDR5.cpp ================================================ #include "DRAMPower/memspec/MemSpecLPDDR5.h" using namespace DRAMPower; MemSpecLPDDR5::MemSpecLPDDR5(const DRAMUtils::MemSpec::MemSpecLPDDR5 &memspec) : MemSpec(memspec) { numberOfBankGroups = memspec.memarchitecturespec.nbrOfBankGroups; numberOfRanks = memspec.memarchitecturespec.nbrOfRanks; banksPerGroup = numberOfBanks / numberOfBankGroups; memTimingSpec.tCK = memspec.memtimingspec.tCK; memTimingSpec.WCKtoCK = memspec.memtimingspec.WCK2CK; memTimingSpec.tWCK = memTimingSpec.tCK / memTimingSpec.WCKtoCK; memTimingSpec.tRAS = memspec.memtimingspec.RAS; memTimingSpec.tRCD = memspec.memtimingspec.RCD_S; memTimingSpec.tRBTP = memspec.memtimingspec.RBTP; memTimingSpec.tWL = memspec.memtimingspec.WL; memTimingSpec.tWR = memspec.memtimingspec.WR; memTimingSpec.tRP = memspec.memtimingspec.RPpb; memTimingSpec.tRFCPB = memspec.memtimingspec.RFCpb; memTimingSpec.tRFC = memspec.memtimingspec.RFCab; memTimingSpec.tREFI = memspec.memtimingspec.REFI; auto VDD1 = VoltageDomain::VDD1; auto VDD2H = VoltageDomain::VDD2H; auto VDD2L = VoltageDomain::VDD2L; memPowerSpec.push_back(MemPowerSpec()); // VDD1 memPowerSpec.push_back(MemPowerSpec()); // VDD2H memPowerSpec.push_back(MemPowerSpec()); // VDD2L memPowerSpec[VDD1].vDDX = memspec.mempowerspec.vdd1; memPowerSpec[VDD1].iDD0X = memspec.mempowerspec.idd01; memPowerSpec[VDD1].iDD2NX = memspec.mempowerspec.idd2n1; memPowerSpec[VDD1].iDD3NX = memspec.mempowerspec.idd3n1; memPowerSpec[VDD1].iDD4RX = memspec.mempowerspec.idd4r1; memPowerSpec[VDD1].iDD4WX = memspec.mempowerspec.idd4w1; memPowerSpec[VDD1].iDD5X = memspec.mempowerspec.idd51; memPowerSpec[VDD1].iDD5PBX = memspec.mempowerspec.idd5pb1; memPowerSpec[VDD1].iDD6X = memspec.mempowerspec.idd61; memPowerSpec[VDD1].iDD6DSX = memspec.mempowerspec.idd6ds1; memPowerSpec[VDD1].iDD2PX = memspec.mempowerspec.idd2p1; memPowerSpec[VDD1].iDD3PX = memspec.mempowerspec.idd3p1; memPowerSpec[VDD2H].vDDX = memspec.mempowerspec.vdd2h; memPowerSpec[VDD2H].iDD0X = memspec.mempowerspec.idd02h; memPowerSpec[VDD2H].iDD2NX = memspec.mempowerspec.idd2n2h; memPowerSpec[VDD2H].iDD3NX = memspec.mempowerspec.idd3n2h; memPowerSpec[VDD2H].iDD4RX = memspec.mempowerspec.idd4r2h; memPowerSpec[VDD2H].iDD4WX = memspec.mempowerspec.idd4w2h; memPowerSpec[VDD2H].iDD5X = memspec.mempowerspec.idd52h; memPowerSpec[VDD2H].iDD5PBX = memspec.mempowerspec.idd5pb2h; memPowerSpec[VDD2H].iDD6X = memspec.mempowerspec.idd62h; memPowerSpec[VDD2H].iDD6DSX = memspec.mempowerspec.idd6ds2h; memPowerSpec[VDD2H].iDD2PX = memspec.mempowerspec.idd2p2h; memPowerSpec[VDD2H].iDD3PX = memspec.mempowerspec.idd3p2h; memPowerSpec[VDD2L].vDDX = memspec.mempowerspec.vdd2l; memPowerSpec[VDD2L].iDD0X = memspec.mempowerspec.idd02l; memPowerSpec[VDD2L].iDD2NX = memspec.mempowerspec.idd2n2l; memPowerSpec[VDD2L].iDD3NX = memspec.mempowerspec.idd3n2l; memPowerSpec[VDD2L].iDD4RX = memspec.mempowerspec.idd4r2l; memPowerSpec[VDD2L].iDD4WX = memspec.mempowerspec.idd4w2l; memPowerSpec[VDD2L].iDD5X = memspec.mempowerspec.idd52l; memPowerSpec[VDD2L].iDD5PBX = memspec.mempowerspec.idd5pb2l; memPowerSpec[VDD2L].iDD6X = memspec.mempowerspec.idd62l; memPowerSpec[VDD2L].iDD6DSX = memspec.mempowerspec.idd6ds2l; memPowerSpec[VDD2L].iDD2PX = memspec.mempowerspec.idd2p2l; memPowerSpec[VDD2L].iDD3PX = memspec.mempowerspec.idd3p2l; vddq = memspec.mempowerspec.vddq; memPowerSpec[VDD1].iBeta = memspec.mempowerspec.iBeta_vdd1.value_or(memspec.mempowerspec.idd01); memPowerSpec[VDD2H].iBeta = memspec.mempowerspec.iBeta_vdd2h.value_or(memspec.mempowerspec.idd02h); memPowerSpec[VDD2L].iBeta = memspec.mempowerspec.iBeta_vdd2l.value_or(memspec.mempowerspec.idd02l); if (memspec.bankwisespec.has_value()) { bwParams.bwPowerFactRho = memspec.bankwisespec.value().factRho.value_or(1); } else { bwParams.bwPowerFactRho = 1; } auto BankArchError = [this]() { std::cout << "Invalid bank architecture selected" << std::endl; std::cout << "Selected values:" << std::endl; std::cout << " - Number of banks: " << numberOfBanks << std::endl; std::cout << " - Number of bank groups: " << numberOfBankGroups << std::endl; std::cout << "Valid values are 16|1 (16B mode), 16|4 (BG mode) or 8|1 (8B mode)" << std::endl; std::cout << std::endl << "Assuming 16B architecture." << std::endl; bank_arch = BankArchitectureMode::M16B; numberOfBanks = 16; numberOfBankGroups = 1; }; if (numberOfBanks == 16) { if (numberOfBankGroups == 1 || numberOfBankGroups == 0) { bank_arch = BankArchitectureMode::M16B; } else if (numberOfBankGroups == 4) { bank_arch = BankArchitectureMode::MBG; } else { BankArchError(); } } else if (numberOfBanks == 8) { if (numberOfBankGroups > 1) { BankArchError(); } else { bank_arch = BankArchitectureMode::M8B; } } else { BankArchError(); } // Source: LPDDR5 standard; table 312 memTimingSpec.tBurst = burstLength/(dataRate * memTimingSpec.WCKtoCK); // Source: LPDDR5 standard; figure 96 prechargeOffsetRD = memTimingSpec.tBurst + memTimingSpec.tRBTP; // Source: LPDDR5 standard; figure 97 prechargeOffsetWR = memTimingSpec.tWL + memTimingSpec.tBurst + 1 + memTimingSpec.tWR; wckAlwaysOnMode = memspec.memarchitecturespec.WCKalwaysOn; parseImpedanceSpec(memspec); } void MemSpecLPDDR5::parseImpedanceSpec(const DRAMUtils::MemSpec::MemSpecLPDDR5 &memspec) { memImpedanceSpec = memspec.memimpedancespec; } MemSpecLPDDR5 MemSpecLPDDR5::from_memspec(const DRAMUtils::MemSpec::MemSpecVariant& memSpec) { return std::get(memSpec.getVariant()); } ================================================ FILE: src/DRAMPower/DRAMPower/memspec/MemSpecLPDDR5.h ================================================ #ifndef DRAMPOWER_MEMSPEC_MEMSPECLPDDR5_H #define DRAMPOWER_MEMSPEC_MEMSPECLPDDR5_H #include "MemSpec.h" #include namespace DRAMPower { class MemSpecLPDDR5 final : public MemSpec { public: using MemImpedanceSpec = DRAMUtils::MemSpec::MemImpedanceSpecTypeLPDDR5; enum VoltageDomain { VDD1 = 0, VDD2H, VDD2L, }; enum BankArchitectureMode { MBG, // 4 banks, 4 bank groups M16B, // 16 banks, no bank groups M8B // 8 banks, no bank groups }; public: MemSpecLPDDR5() = delete; MemSpecLPDDR5(const DRAMUtils::MemSpec::MemSpecLPDDR5 &memspec); MemSpecLPDDR5(json_t &data) = delete; MemSpecLPDDR5(const json_t &data) = delete; // Copy constructor and assignment operator MemSpecLPDDR5(const MemSpecLPDDR5&) = default; MemSpecLPDDR5& operator=(const MemSpecLPDDR5&) = default; // Move constructor and assignment operator MemSpecLPDDR5(MemSpecLPDDR5&&) = default; MemSpecLPDDR5& operator=(MemSpecLPDDR5&&) = default; ~MemSpecLPDDR5() = default; uint64_t numberOfBankGroups; uint64_t banksPerGroup; uint64_t numberOfRanks; std::size_t perTwoBankOffset = 8; BankArchitectureMode bank_arch; bool wckAlwaysOnMode; double vddq; // Memspec Variables: struct MemTimingSpec { double fck; double tCK; double tWCK; uint64_t WCKtoCK; uint64_t tRAS; uint64_t tRCD; uint64_t tRL; uint64_t tWL; uint64_t tWR; uint64_t tRFC; uint64_t tRFCPB; uint64_t tREFI; uint64_t tRP; uint64_t tRBTP; uint64_t tBurst; }; // Currents and Voltages: struct MemPowerSpec { double vDDX; double iDD0X; double iDD2NX; double iDD3NX; double iDD2PX; double iDD3PX; double iDD4RX; double iDD4WX; double iDD5X; double iDD5PBX; double iDD6X; double iDD6DSX; double iBeta; }; struct BankWiseParams { // ACT Standby power factor double bwPowerFactRho; }; MemTimingSpec memTimingSpec; MemImpedanceSpec memImpedanceSpec; std::vector memPowerSpec; BankWiseParams bwParams; private: void parseImpedanceSpec(const DRAMUtils::MemSpec::MemSpecLPDDR5 &memspec); public: static MemSpecLPDDR5 from_memspec(const DRAMUtils::MemSpec::MemSpecVariant&); }; } // namespace DRAMPower #endif /* DRAMPOWER_MEMSPEC_MEMSPECLPDDR5_H */ ================================================ FILE: src/DRAMPower/DRAMPower/simconfig/simconfig.h ================================================ #ifndef DRAMPOWER_SIMCONFIG_SIMCONFIG_H #define DRAMPOWER_SIMCONFIG_SIMCONFIG_H #include "DRAMUtils/util/json_utils.h" #include "DRAMUtils/config/toggling_rate.h" namespace DRAMPower::config { struct SimConfig { // Public type definitions public: using ToggleRateDefinition_t = DRAMUtils::Config::ToggleRateDefinition; // Public Constructors public: SimConfig() = default; // Public members public: std::optional toggleRateDefinition; }; NLOHMANN_JSONIFY_ALL_THINGS(SimConfig, toggleRateDefinition); } // DRAMPower::config #endif /* DRAMPOWER_SIMCONFIG_SIMCONFIG_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr4/DDR4.cpp ================================================ #include "DDR4.h" #include "DRAMPower/Types.h" #include "DRAMPower/data/stats.h" #include "DRAMPower/memspec/MemSpecDDR4.h" #include "DRAMPower/standards/ddr4/DDR4Interface.h" #include "DRAMPower/standards/ddr4/core_calculation_DDR4.h" #include "DRAMPower/standards/ddr4/interface_calculation_DDR4.h" #include "DRAMPower/util/cli_architecture_config.h" #include #include namespace DRAMPower { using namespace DRAMUtils::Config; DDR4::DDR4(const MemSpecDDR4 &memSpec, const config::SimConfig &simConfig) : m_memSpec(memSpec) , m_interface(m_memSpec, simConfig) , m_core(m_memSpec) { this->registerExtensions(); } // Extensions void DDR4::registerExtensions() { getExtensionManager().registerExtension([this](const timestamp_t, const bool enable) { // Assumption: the enabling of the DBI does not interleave with previous data on the bus // x4,x8,x16 devices: only x8 and x16 support dbi if (4 == m_memSpec.bitWidth) { std::cout << "[WARN] x4 devices don't support DBI" << std::endl; return false; } m_interface.enableDBI(enable); return true; }, false); } // Getters for CLI util::CLIArchitectureConfig DDR4::getCLIArchitectureConfig() { return util::CLIArchitectureConfig{ m_memSpec.numberOfDevices, m_memSpec.numberOfRanks, m_memSpec.numberOfBanks }; } // Calculation energy_t DDR4::calcCoreEnergyStats(const SimulationStats& stats) const { Calculation_DDR4 calculation(m_memSpec); return calculation.calcEnergy(stats); } interface_energy_info_t DDR4::calcInterfaceEnergyStats(const SimulationStats& stats) const { InterfaceCalculation_DDR4 calculation(m_memSpec); return calculation.calculateEnergy(stats); } // Stats SimulationStats DDR4::getWindowStats(timestamp_t timestamp) { SimulationStats stats; m_core.getWindowStats(timestamp, stats); m_interface.getWindowStats(timestamp, stats); return stats; } // Serialization void DDR4::serialize_impl(std::ostream& stream) const { m_core.serialize(stream); m_interface.serialize(stream); } void DDR4::deserialize_impl(std::istream& stream) { m_core.deserialize(stream); m_interface.deserialize(stream); } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr4/DDR4.h ================================================ #ifndef DRAMPOWER_STANDARDS_DDR4_DDR4_H #define DRAMPOWER_STANDARDS_DDR4_DDR4_H #include "DRAMPower/util/cli_architecture_config.h" #include #include #include #include #include #include #include #include #include #include #include "DRAMPower/simconfig/simconfig.h" #include namespace DRAMPower { class DDR4 : public dram_base { // Public constructors and assignment operators public: DDR4() = delete; // No default constructor DDR4(const DDR4& other) = default; // copy constructor DDR4(DDR4&& other) noexcept = default; // move constructor DDR4& operator=(const DDR4&) = default; // copy assignment operator DDR4& operator=(DDR4&&) = default; // move assignment operator ~DDR4() override = default; DDR4(const MemSpecDDR4 &memSpec, const config::SimConfig &simConfig = {}); // Public member functions public: // Member functions DDR4Core& getCore() { return m_core; } const DDR4Core& getCore() const { return m_core; } DDR4Interface& getInterface() { return m_interface; } const DDR4Interface& getInterface() const { return m_interface; } // Overrides energy_t calcCoreEnergyStats(const SimulationStats& stats) const override; interface_energy_info_t calcInterfaceEnergyStats(const SimulationStats& stats) const override; SimulationStats getWindowStats(timestamp_t timestamp) override; util::CLIArchitectureConfig getCLIArchitectureConfig() override; bool isSerializable() const override { return m_core.isSerializable(); } // Private member functions private: // Member functions void registerExtensions(); // Overrides void doCoreCommandImpl(const Command& command) override { m_core.doCommand(command); } void doInterfaceCommandImpl(const Command& command) override { m_interface.doCommand(command); } timestamp_t getLastCommandTime_impl() const override { return std::max(m_core.getLastCommandTime(), m_interface.getLastCommandTime()); } void serialize_impl(std::ostream& stream) const override; void deserialize_impl(std::istream& stream) override; // Private member variables private: MemSpecDDR4 m_memSpec; DDR4Interface m_interface; DDR4Core m_core; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_DDR4_DDR4_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr4/DDR4Core.cpp ================================================ #include "DDR4Core.h" #include "DRAMPower/Types.h" #include "DRAMPower/util/RegisterHelper.h" #include namespace DRAMPower { void DDR4Core::doCommand(const Command& cmd) { m_implicitCommandHandler.processImplicitCommandQueue(*this, cmd.timestamp, m_last_command_time); m_last_command_time = std::max(cmd.timestamp, m_last_command_time); switch(cmd.type) { case CmdType::ACT: util::coreHelpers::bankHandler(cmd, m_ranks, this, &DDR4Core::handleAct); break; case CmdType::PRE: util::coreHelpers::bankHandler(cmd, m_ranks, this, &DDR4Core::handlePre); break; case CmdType::PREA: util::coreHelpers::rankHandler(cmd, m_ranks, this, &DDR4Core::handlePreAll); break; case CmdType::REFA: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &DDR4Core::handleRefAll); break; case CmdType::RD: util::coreHelpers::bankHandler(cmd, m_ranks, this, &DDR4Core::handleRead); break; case CmdType::RDA: util::coreHelpers::bankHandlerIdx(cmd, m_ranks, this, &DDR4Core::handleReadAuto); break; case CmdType::WR: util::coreHelpers::bankHandler(cmd, m_ranks, this, &DDR4Core::handleWrite); break; case CmdType::WRA: util::coreHelpers::bankHandlerIdx(cmd, m_ranks, this, &DDR4Core::handleWriteAuto); break; case CmdType::SREFEN: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &DDR4Core::handleSelfRefreshEntry); break; case CmdType::SREFEX: util::coreHelpers::rankHandler(cmd, m_ranks, this, &DDR4Core::handleSelfRefreshExit); break; case CmdType::PDEA: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &DDR4Core::handlePowerDownActEntry); break; case CmdType::PDEP: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &DDR4Core::handlePowerDownPreEntry); break; case CmdType::PDXA: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &DDR4Core::handlePowerDownActExit); break; case CmdType::PDXP: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &DDR4Core::handlePowerDownPreExit); break; case CmdType::END_OF_SIMULATION: break; default: assert(false && "Unsupported command"); break; } } timestamp_t DDR4Core::getLastCommandTime() const { return m_last_command_time; } bool DDR4Core::isSerializable() const { return 0 == m_implicitCommandHandler.implicitCommandCount(); } void DDR4Core::handleAct(Rank &rank, Bank &bank, timestamp_t timestamp) { bank.counter.act++; bank.bankState = Bank::BankState::BANK_ACTIVE; bank.cycles.act.start_interval(timestamp); rank.cycles.act.start_interval_if_not_running(timestamp); //rank.cycles.pre.close_interval(timestamp); } void DDR4Core::handlePre(Rank &rank, Bank &bank, timestamp_t timestamp) { // If statement necessary for core power calculation // bank.counter.pre doesn't correspond to the number of pre commands // It corresponds to the number of state transisitons to the pre state if (bank.bankState == Bank::BankState::BANK_PRECHARGED) return; bank.counter.pre++; bank.bankState = Bank::BankState::BANK_PRECHARGED; bank.cycles.act.close_interval(timestamp); bank.latestPre = timestamp; // used for earliest power down calculation if ( !rank.isActive(timestamp) ) // stop rank active interval if no more banks active { // active counter increased if at least 1 bank is active, precharge counter increased if all banks are precharged rank.cycles.act.close_interval(timestamp); //rank.cycles.pre.start_interval_if_not_running(timestamp); } } void DDR4Core::handlePreAll(Rank &rank, timestamp_t timestamp) { for (auto &bank: rank.banks) handlePre(rank, bank, timestamp); } void DDR4Core::handleRefAll(std::size_t rank_idx, timestamp_t timestamp) { auto timestamp_end = timestamp + m_memSpec.tRFC; auto& rank = m_ranks[rank_idx]; rank.endRefreshTime = timestamp_end; rank.cycles.act.start_interval_if_not_running(timestamp); //rank.cycles.pre.close_interval(timestamp); for (std::size_t bank_idx = 0; bank_idx < rank.banks.size(); ++bank_idx) { auto& bank = rank.banks[bank_idx]; bank.bankState = Bank::BankState::BANK_ACTIVE; ++bank.counter.refAllBank; bank.cycles.act.start_interval_if_not_running(timestamp); bank.refreshEndTime = timestamp_end; // used for earliest power down calculation // Execute implicit pre-charge at refresh end m_implicitCommandHandler.addImplicitCommand(timestamp_end, [rank_idx, bank_idx = bank_idx, timestamp_end](DDR4Core& self) { auto& rank = self.m_ranks[rank_idx]; auto& bank = rank.banks[bank_idx]; bank.bankState = Bank::BankState::BANK_PRECHARGED; bank.cycles.act.close_interval(timestamp_end); // stop rank active interval if no more banks active if (!rank.isActive(timestamp_end)) // stop rank active interval if no more banks active { rank.cycles.act.close_interval(timestamp_end); //rank.cycles.pre.start_interval(timestamp_end); } }); } // Required for precharge power-down } void DDR4Core::handleRead(Rank&, Bank &bank, timestamp_t) { ++bank.counter.reads; } void DDR4Core::handleReadAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp) { auto& bank = m_ranks[rank_idx].banks[bank_idx]; ++bank.counter.readAuto; auto minBankActiveTime = bank.cycles.act.get_start() + m_memSpec.tRAS; auto minReadActiveTime = timestamp + m_memSpec.prechargeOffsetRD; auto delayed_timestamp = std::max(minBankActiveTime, minReadActiveTime); // Execute PRE after minimum active time m_implicitCommandHandler.addImplicitCommand(delayed_timestamp, [rank_idx, bank_idx, delayed_timestamp](DDR4Core& self) { self.handlePre(self.m_ranks[rank_idx], self.m_ranks[rank_idx].banks[bank_idx], delayed_timestamp); }); } void DDR4Core::handleWrite(Rank&, Bank &bank, timestamp_t) { ++bank.counter.writes; } void DDR4Core::handleWriteAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp) { auto& bank = m_ranks[rank_idx].banks[bank_idx]; ++bank.counter.writeAuto; auto minBankActiveTime = bank.cycles.act.get_start() + m_memSpec.tRAS; auto minWriteActiveTime = timestamp + m_memSpec.prechargeOffsetWR; auto delayed_timestamp = std::max(minBankActiveTime, minWriteActiveTime); // Execute PRE after minimum active time m_implicitCommandHandler.addImplicitCommand(delayed_timestamp, [rank_idx, bank_idx, delayed_timestamp](DDR4Core& self) { self.handlePre(self.m_ranks[rank_idx], self.m_ranks[rank_idx].banks[bank_idx], delayed_timestamp); }); } void DDR4Core::handleSelfRefreshEntry(std::size_t rank_idx, timestamp_t timestamp) { // Issue implicit refresh handleRefAll(rank_idx, timestamp); // Handle self-refresh entry after tRFC auto timestampSelfRefreshStart = timestamp + m_memSpec.tRFC; m_implicitCommandHandler.addImplicitCommand(timestampSelfRefreshStart, [rank_idx, timestampSelfRefreshStart](DDR4Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.counter.selfRefresh++; rank.cycles.sref.start_interval(timestampSelfRefreshStart); rank.memState = MemState::SREF; }); } void DDR4Core::handleSelfRefreshExit(Rank &rank, timestamp_t timestamp) { assert(rank.memState == MemState::SREF); // check for previous SelfRefreshEntry rank.cycles.sref.close_interval(timestamp); // Duration between entry and exit rank.memState = MemState::NOT_IN_PD; } void DDR4Core::handlePowerDownActEntry(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; auto earliestPossibleEntry = this->earliestPossiblePowerDownEntryTime(rank); auto entryTime = std::max(timestamp, earliestPossibleEntry); m_implicitCommandHandler.addImplicitCommand(entryTime, [rank_idx, entryTime](DDR4Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.memState = MemState::PDN_ACT; rank.cycles.powerDownAct.start_interval(entryTime); rank.cycles.act.close_interval(entryTime); //rank.cycles.pre.close_interval(entryTime); for (auto & bank : rank.banks) { bank.cycles.act.close_interval(entryTime); } }); } void DDR4Core::handlePowerDownActExit(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; assert(rank.memState == MemState::PDN_ACT); auto earliestPossibleExit = this->earliestPossiblePowerDownEntryTime(rank); auto exitTime = std::max(timestamp, earliestPossibleExit); m_implicitCommandHandler.addImplicitCommand(exitTime, [rank_idx, exitTime](DDR4Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.memState = MemState::NOT_IN_PD; rank.cycles.powerDownAct.close_interval(exitTime); // Activate banks that were active prior to PDA for (auto & bank : rank.banks) { if (bank.bankState==Bank::BankState::BANK_ACTIVE) { bank.cycles.act.start_interval(exitTime); } } // Activate rank if at least one bank is active // At least one bank must be active for PDA -> remove if statement? if(rank.isActive(exitTime)) rank.cycles.act.start_interval(exitTime); }); } void DDR4Core::handlePowerDownPreEntry(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; auto earliestPossibleEntry = this->earliestPossiblePowerDownEntryTime(rank); auto entryTime = std::max(timestamp, earliestPossibleEntry); m_implicitCommandHandler.addImplicitCommand(entryTime, [rank_idx, entryTime](DDR4Core& self) { auto& rank = self.m_ranks[rank_idx]; for (auto &bank : rank.banks) bank.cycles.act.close_interval(entryTime); rank.memState = MemState::PDN_PRE; rank.cycles.powerDownPre.start_interval(entryTime); //rank.cycles.pre.close_interval(entryTime); rank.cycles.act.close_interval(entryTime); }); } void DDR4Core::handlePowerDownPreExit(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; // The computation is necessary to exit at the earliest timestamp (upon entry) auto earliestPossibleExit = this->earliestPossiblePowerDownEntryTime(rank); auto exitTime = std::max(timestamp, earliestPossibleExit); m_implicitCommandHandler.addImplicitCommand(exitTime, [rank_idx, exitTime](DDR4Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.memState = MemState::NOT_IN_PD; rank.cycles.powerDownPre.close_interval(exitTime); // Precharge banks that were precharged prior to PDP for (auto & bank : rank.banks) { if (bank.bankState==Bank::BankState::BANK_ACTIVE) { bank.cycles.act.start_interval(exitTime); } } // Precharge rank if all banks are precharged // At least one bank must be precharged for PDP -> remove if statement? // If statement ensures right state diagramm traversal //if(!rank.isActive(exitTime)) // rank.cycles.pre.start_interval(exitTime); }); } timestamp_t DDR4Core::earliestPossiblePowerDownEntryTime(Rank & rank) const { timestamp_t entryTime = 0; for (const auto & bank : rank.banks) { entryTime = std::max({ entryTime, bank.counter.act == 0 ? 0 : bank.cycles.act.get_start() + m_memSpec.tRCD, bank.counter.pre == 0 ? 0 : bank.latestPre + m_memSpec.tRP, bank.refreshEndTime }); } return entryTime; } void DDR4Core::getWindowStats(timestamp_t timestamp, SimulationStats &stats) { m_implicitCommandHandler.processImplicitCommandQueue(*this, timestamp, m_last_command_time); // resize banks and ranks stats.bank.resize(m_memSpec.numberOfBanks * m_memSpec.numberOfRanks); stats.rank_total.resize(m_memSpec.numberOfRanks); auto simulation_duration = timestamp; for (size_t i = 0; i < m_memSpec.numberOfRanks; ++i) { const Rank &rank = m_ranks[i]; size_t bank_offset = i * m_memSpec.numberOfBanks; for (size_t j = 0; j < m_memSpec.numberOfBanks; ++j) { stats.bank[bank_offset + j].counter = rank.banks[j].counter; stats.bank[bank_offset + j].cycles.act = rank.banks[j].cycles.act.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.selfRefresh = rank.cycles.sref.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.powerDownAct = rank.cycles.powerDownAct.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.powerDownPre = rank.cycles.powerDownPre.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.pre = simulation_duration - (stats.bank[bank_offset + j].cycles.act + rank.cycles.powerDownAct.get_count_at(timestamp) + rank.cycles.powerDownPre.get_count_at(timestamp) + rank.cycles.sref.get_count_at(timestamp)); } stats.rank_total[i].cycles.act = rank.cycles.act.get_count_at(timestamp); stats.rank_total[i].cycles.powerDownAct = rank.cycles.powerDownAct.get_count_at(timestamp); stats.rank_total[i].cycles.powerDownPre = rank.cycles.powerDownPre.get_count_at(timestamp); stats.rank_total[i].cycles.selfRefresh = rank.cycles.sref.get_count_at(timestamp); stats.rank_total[i].cycles.pre = simulation_duration - ( stats.rank_total[i].cycles.act + stats.rank_total[i].cycles.powerDownAct + stats.rank_total[i].cycles.powerDownPre + stats.rank_total[i].cycles.selfRefresh ); //stats.rank_total[i].cycles.pre = rank.cycles.pre.get_count_at(timestamp); } } void DDR4Core::serialize(std::ostream& stream) const { stream.write(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); // Serialize the ranks for (const auto& rank : m_ranks) { rank.serialize(stream); } } void DDR4Core::deserialize(std::istream& stream) { stream.read(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); // Deserialize the ranks for (auto &rank : m_ranks) { rank.deserialize(stream); } } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr4/DDR4Core.h ================================================ #ifndef DRAMPOWER_STANDARDS_DDR4_DDR4CORE_H #define DRAMPOWER_STANDARDS_DDR4_DDR4CORE_H #include "DRAMPower/util/Deserialize.h" #include "DRAMPower/util/Serialize.h" #include #include #include #include #include #include #include #include namespace DRAMPower { namespace internal { template class TestAccessor; } struct DDR4CoreMemSpec { DDR4CoreMemSpec(const MemSpecDDR4& memSpec) : numberOfBanks(memSpec.numberOfBanks) , numberOfRanks(memSpec.numberOfRanks) , tRFC(memSpec.memTimingSpec.tRFC) , tRAS(memSpec.memTimingSpec.tRAS) , tRCD(memSpec.memTimingSpec.tRCD) , tRP(memSpec.memTimingSpec.tRP) , prechargeOffsetRD(memSpec.prechargeOffsetRD) , prechargeOffsetWR(memSpec.prechargeOffsetWR) {} uint64_t numberOfBanks; uint64_t numberOfRanks; uint64_t tRFC; uint64_t tRAS; uint64_t tRCD; uint64_t tRP; uint64_t prechargeOffsetRD; uint64_t prechargeOffsetWR; }; class DDR4Core : public util::Serialize, public util::Deserialize { // Friend classes friend class internal::TestAccessor; // Public constructors and assignment operators public: DDR4Core(const MemSpecDDR4& memSpec) : m_memSpec(memSpec) , m_ranks(memSpec.numberOfRanks, {static_cast(memSpec.numberOfBanks)}) {} // Public member functions public: // Member functions void doCommand(const Command& cmd); timestamp_t getLastCommandTime() const; bool isSerializable() const; void getWindowStats(timestamp_t timestamp, SimulationStats &stats); // Overrides void serialize(std::ostream& stream) const override; void deserialize(std::istream& stream) override; // Private member functions private: void handleAct(Rank & rank, Bank & bank, timestamp_t timestamp); void handlePre(Rank & rank, Bank & bank, timestamp_t timestamp); void handlePreAll(Rank & rank, timestamp_t timestamp); void handleRefAll(std::size_t rank_idx, timestamp_t timestamp); void handleSelfRefreshEntry(std::size_t rank_idx, timestamp_t timestamp); void handleSelfRefreshExit(Rank & rank, timestamp_t timestamp); void handleRead(Rank & rank, Bank & bank, timestamp_t timestamp); void handleWrite(Rank & rank, Bank & bank, timestamp_t timestamp); void handleReadAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp); void handleWriteAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp); void handlePowerDownActEntry(std::size_t rank_idx, timestamp_t timestamp); void handlePowerDownActExit(std::size_t rank_idx, timestamp_t timestamp); void handlePowerDownPreEntry(std::size_t rank_idx, timestamp_t timestamp); void handlePowerDownPreExit(std::size_t rank_idx, timestamp_t timestamp); timestamp_t earliestPossiblePowerDownEntryTime(Rank & rank) const; // Private members variables private: DDR4CoreMemSpec m_memSpec; std::vector m_ranks; ImplicitCommandHandler m_implicitCommandHandler; timestamp_t m_last_command_time = 0; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_DDR4_DDR4CORE_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr4/DDR4Interface.cpp ================================================ #include "DDR4Interface.h" #include "DRAMPower/util/databus_types.h" namespace DRAMPower { static constexpr DRAMUtils::Config::ToggleRateDefinition busConfig { 0, 0, 0, 0, DRAMUtils::Config::TogglingRateIdlePattern::H, DRAMUtils::Config::TogglingRateIdlePattern::H }; DDR4Interface::DDR4Interface(const MemSpecDDR4& memSpec, const config::SimConfig &simConfig) : m_memSpec(memSpec) , m_commandBus{cmdBusWidth, 1, util::BusIdlePatternSpec::H, util::BusInitPatternSpec::H} , m_dataBus{ util::databus_presets::getDataBusPreset( util::DataBusConfig{ memSpec.bitWidth * memSpec.numberOfDevices, memSpec.dataRate, simConfig.toggleRateDefinition.value_or(busConfig) }, simConfig.toggleRateDefinition.has_value() ? util::DataBusMode::TogglingRate : util::DataBusMode::Bus, false ) } , m_readDQS(memSpec.dataRate, true) , m_writeDQS(memSpec.dataRate, true) , m_clock(2, false) , m_dbi(memSpec.numberOfDevices * memSpec.bitWidth, memSpec.burstLength, [this](timestamp_t load_timestamp, timestamp_t, std::size_t pin, bool inversion_state, bool read) { this->handleDBIPinChange(load_timestamp, pin, inversion_state, read); }, false) , m_dbiread(m_dbi.getChunksPerWidth().value(), pin_dbi_t{m_dbi.getIdlePattern(), m_dbi.getIdlePattern()}) , m_dbiwrite(m_dbi.getChunksPerWidth().value(), pin_dbi_t{m_dbi.getIdlePattern(), m_dbi.getIdlePattern()}) , prepostambleReadMinTccd(memSpec.prePostamble.readMinTccd) , prepostambleWriteMinTccd(memSpec.prePostamble.writeMinTccd) , m_ranks(memSpec.numberOfRanks) , m_patternHandler(PatternEncoderOverrides { {pattern_descriptor::V, PatternEncoderBitSpec::H}, {pattern_descriptor::X, PatternEncoderBitSpec::H}, }, cmdBusInitPattern) { registerPatterns(); } void DDR4Interface::registerPatterns() { using namespace pattern_descriptor; // ACT m_patternHandler.registerPattern({ L, L, R16, R15, R14, BG0, BG1, BA0, BA1, V, V, V, R12, R17, R13, R11, R10, R0, R1, R2, R3, R4, R5, R6, R7, R8, R9 }); // PRE m_patternHandler.registerPattern({ L, H, L, H, L, BG0, BG1, BA0, BA1, V, V, V, V, V, V, V, L, V, V, V, V, V, V, V, V, V, V }); // PREA m_patternHandler.registerPattern({ L, H, L, H, L, V, V, V, V, V, V, V, V, V, V, V, H, V, V, V, V, V, V, V, V, V, V }); // REFA m_patternHandler.registerPattern({ L, H, L, L, H, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V }); // RD m_patternHandler.registerPattern({ L, H, H, L, H, BG0, BG1, BA0, BA1, V, V, V, V, V, V, V, L, C0, C1, C2, C3, C4, C5, C6, C7, C8, C9 }); // RDA m_patternHandler.registerPattern({ L, H, H, L, H, BG0, BG1, BA0, BA1, V, V, V, V, V, V, V, H, C0, C1, C2, C3, C4, C5, C6, C7, C8, C9 }); // WR m_patternHandler.registerPattern({ L, H, H, L, L, BG0, BG1, BA0, BA1, V, V, V, V, V, V, V, L, C0, C1, C2, C3, C4, C5, C6, C7, C8, C9 }); // WRA m_patternHandler.registerPattern({ L, H, H, L, L, BG0, BG1, BA0, BA1, V, V, V, V, V, V, V, H, C0, C1, C2, C3, C4, C5, C6, C7, C8, C9 }); // SREFEN m_patternHandler.registerPattern({ L, H, L, L, H, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V }); // SREFEX m_patternHandler.registerPattern({ H, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, L, H, H, H, H, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V }); // PDEA m_patternHandler.registerPattern({ H, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, }); // PDEP m_patternHandler.registerPattern({ H, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, }); // PDXA m_patternHandler.registerPattern({ H, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, }); // PDXP m_patternHandler.registerPattern({ H, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, }); } timestamp_t DDR4Interface::getLastCommandTime() const { return m_last_command_time; } void DDR4Interface::doCommand(const Command& cmd) { switch(cmd.type) { case CmdType::ACT: case CmdType::PRE: case CmdType::PREA: case CmdType::REFA: case CmdType::SREFEN: case CmdType::SREFEX: case CmdType::PDEA: case CmdType::PDEP: case CmdType::PDXA: case CmdType::PDXP: handleCommandBus(cmd); break; case CmdType::RD: case CmdType::RDA: handleData(cmd, true); break; case CmdType::WR: case CmdType::WRA: handleData(cmd, false); break; case CmdType::END_OF_SIMULATION: endOfSimulation(cmd.timestamp); break; default: assert(false && "Invalid command"); break; } m_last_command_time = cmd.timestamp; } void DDR4Interface::handleDBIPinChange(const timestamp_t load_timestamp, std::size_t pin, bool state, bool read) { assert(pin < m_dbiread.size() || pin < m_dbiwrite.size()); if (read) { this->m_dbiread[pin].set(load_timestamp, state ? util::PinState::L : util::PinState::H, 1); } else { this->m_dbiwrite[pin].set(load_timestamp, state ? util::PinState::L : util::PinState::H, 1); } } std::optional DDR4Interface::handleDBIInterface(timestamp_t timestamp, std::size_t n_bits, const uint8_t* data, bool read) { if (0 == n_bits || !data || !m_dbi.isEnabled()) { // No DBI or no data to process return std::nullopt; } timestamp_t virtual_time = timestamp * m_memSpec.dataRate; // updateDBI calls the given callback to handle pin changes return m_dbi.updateDBI(virtual_time, n_bits, data, read); } void DDR4Interface::handlePrePostamble( const timestamp_t timestamp, const uint64_t length, RankInterface &rank, bool read ) { uint64_t *lastAccess = &rank.lastWriteEnd; uint64_t diff = 0; if(read) { lastAccess = &rank.lastReadEnd; // minTccd = prepostambleReadMinTccd; } assert(timestamp >= *lastAccess); if(timestamp < *lastAccess) { std::cout << "[Error] PrePostamble diff is negative. The last read/write transaction was not completed" << std::endl; return; } diff = timestamp - *lastAccess; *lastAccess = timestamp + length; //assert(diff >= 0); // Pre and Postamble seamless if(diff == 0) { // Seamless read or write if(read) rank.seamlessPrePostambleCounter_read++; else rank.seamlessPrePostambleCounter_write++; } } void DDR4Interface::handleOverrides(size_t length, bool read) { // Set command bus pattern overrides switch(length) { case 4: if(read) { // Read m_patternHandler.getEncoder().settings.updateSettings({ {pattern_descriptor::C2, PatternEncoderBitSpec::L}, {pattern_descriptor::C1, PatternEncoderBitSpec::L}, {pattern_descriptor::C0, PatternEncoderBitSpec::L}, }); } else { // Write m_patternHandler.getEncoder().settings.updateSettings({ {pattern_descriptor::C2, PatternEncoderBitSpec::L}, {pattern_descriptor::C1, PatternEncoderBitSpec::H}, {pattern_descriptor::C0, PatternEncoderBitSpec::H}, }); } break; default: // Pull up // No interface power needed for PatternEncoderBitSpec::H // Defaults to burst length 8 case 8: if(read) { // Read m_patternHandler.getEncoder().settings.updateSettings({ {pattern_descriptor::C2, PatternEncoderBitSpec::L}, {pattern_descriptor::C1, PatternEncoderBitSpec::L}, {pattern_descriptor::C0, PatternEncoderBitSpec::L}, }); } else { // Write m_patternHandler.getEncoder().settings.updateSettings({ {pattern_descriptor::C2, PatternEncoderBitSpec::H}, {pattern_descriptor::C1, PatternEncoderBitSpec::H}, {pattern_descriptor::C0, PatternEncoderBitSpec::H}, }); } break; } } void DDR4Interface::handleCommandBus(const Command &cmd) { auto pattern = m_patternHandler.getCommandPattern(cmd); auto ca_length = m_patternHandler.getPattern(cmd.type).size() / m_commandBus.get_width(); this->m_commandBus.load(cmd.timestamp, pattern, ca_length); } void DDR4Interface::handleDQs(const Command &cmd, util::Clock &dqs, const size_t length) { dqs.start(cmd.timestamp); dqs.stop(cmd.timestamp + length / m_memSpec.dataRate); } void DDR4Interface::handleData(const Command &cmd, bool read) { auto loadfunc = read ? &databus_t::loadRead : &databus_t::loadWrite; util::Clock &dqs = read ? m_readDQS : m_writeDQS; size_t length = 0; if (0 == cmd.sz_bits) { // No data provided by command // Use default burst length if (m_dataBus.isTogglingRate()) { // If bus is enabled skip loading data length = m_memSpec.burstLength; (m_dataBus.*loadfunc)(cmd.timestamp, length * m_dataBus.getWidth(), nullptr); } } else { std::optional dbi_data = std::nullopt; // Data provided by command if (m_dataBus.isBus() && m_dbi.isEnabled()) { // Only compute dbi for bus mode dbi_data = handleDBIInterface(cmd.timestamp, cmd.sz_bits, cmd.data, read); } length = cmd.sz_bits / (m_dataBus.getWidth()); (m_dataBus.*loadfunc)(cmd.timestamp, cmd.sz_bits, dbi_data.value_or(cmd.data)); } handleOverrides(length, read); handleDQs(cmd, dqs, length); handleCommandBus(cmd); assert(m_ranks.size()>cmd.targetCoordinate.rank); auto & rank = m_ranks[cmd.targetCoordinate.rank]; handlePrePostamble(cmd.timestamp, length / m_memSpec.dataRate, rank, read); } void DDR4Interface::endOfSimulation(timestamp_t timestamp) { m_dbi.dispatchResetCallback(timestamp); } void DDR4Interface::getWindowStats(timestamp_t timestamp, SimulationStats &stats) const { // DDR4 x16 have 2 DQs differential pairs uint_fast8_t NumDQsPairs = 1; if(m_memSpec.bitWidth == 16) { NumDQsPairs = 2; } stats.rank_total.resize(m_memSpec.numberOfRanks); for (size_t i = 0; i < m_memSpec.numberOfRanks; ++i) { const RankInterface &rank_interface = m_ranks[i]; stats.rank_total[i].prepos.readSeamless = rank_interface.seamlessPrePostambleCounter_read; stats.rank_total[i].prepos.writeSeamless = rank_interface.seamlessPrePostambleCounter_write; stats.rank_total[i].prepos.readMerged = rank_interface.mergedPrePostambleCounter_read; stats.rank_total[i].prepos.readMergedTime = rank_interface.mergedPrePostambleTime_read; stats.rank_total[i].prepos.writeMerged = rank_interface.mergedPrePostambleCounter_write; stats.rank_total[i].prepos.writeMergedTime = rank_interface.mergedPrePostambleTime_write; } stats.commandBus = m_commandBus.get_stats(timestamp); m_dataBus.get_stats(timestamp, stats.readBus, stats.writeBus, stats.togglingStats.read, stats.togglingStats.write ); // single line stored in stats // differential power calculated in interface calculation stats.clockStats = 2u * m_clock.get_stats_at(timestamp); stats.readDQSStats = NumDQsPairs * 2u * m_readDQS.get_stats_at(timestamp); stats.writeDQSStats = NumDQsPairs * 2u * m_writeDQS.get_stats_at(timestamp); for (const auto &dbi_pin : m_dbiread) { stats.readDBI += dbi_pin.get_stats_at(timestamp, 2); } for (const auto &dbi_pin : m_dbiwrite) { stats.writeDBI += dbi_pin.get_stats_at(timestamp, 2); } } void DDR4Interface::serialize(std::ostream& stream) const { stream.write(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); m_patternHandler.serialize(stream); m_commandBus.serialize(stream); m_dataBus.serialize(stream); m_readDQS.serialize(stream); m_writeDQS.serialize(stream); m_clock.serialize(stream); m_dbi.serialize(stream); for (const auto& rank : m_ranks) { rank.serialize(stream); } for (const auto& pin : m_dbiread) { pin.serialize(stream); } for (const auto& pin : m_dbiwrite) { pin.serialize(stream); } } void DDR4Interface::deserialize(std::istream& stream) { stream.read(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); m_patternHandler.deserialize(stream); m_commandBus.deserialize(stream); m_dataBus.deserialize(stream); m_readDQS.deserialize(stream); m_writeDQS.deserialize(stream); m_clock.deserialize(stream); m_dbi.deserialize(stream); for (auto &rank : m_ranks) { rank.deserialize(stream); } for (auto &pin : m_dbiread) { pin.deserialize(stream); } for (auto &pin : m_dbiwrite) { pin.deserialize(stream); } } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr4/DDR4Interface.h ================================================ #ifndef DRAMPOWER_STANDARDS_DDR4_DDR4INTERFACE_H #define DRAMPOWER_STANDARDS_DDR4_DDR4INTERFACE_H #include "DRAMPower/util/pin.h" #include "DRAMPower/util/bus.h" #include "DRAMPower/util/databus_presets.h" #include "DRAMPower/util/clock.h" #include "DRAMPower/util/Serialize.h" #include "DRAMPower/util/Deserialize.h" #include "DRAMPower/Types.h" #include "DRAMPower/command/Command.h" #include "DRAMPower/dram/Rank.h" #include "DRAMPower/data/stats.h" #include "DRAMPower/util/PatternHandler.h" #include "DRAMPower/util/dbi.h" #include "DRAMPower/memspec/MemSpecDDR4.h" #include "DRAMPower/simconfig/simconfig.h" #include #include #include #include namespace DRAMPower { struct DDR4InterfaceMemSpec { DDR4InterfaceMemSpec(const MemSpecDDR4& memSpec) : dataRate(memSpec.dataRate) , burstLength(memSpec.burstLength) , bitWidth(memSpec.bitWidth) , numberOfRanks(memSpec.numberOfRanks) {} uint64_t dataRate; uint64_t burstLength; uint64_t bitWidth; uint64_t numberOfRanks; }; class DDR4Interface : public util::Serialize, public util::Deserialize { // Public constants public: const static std::size_t cmdBusWidth = 27; const static uint64_t cmdBusInitPattern = (1<; using pin_dbi_t = util::Pin<8>; // max_burst_length = 8 using databus_t = util::databus_presets::databus_preset_t; using patternHandler_t = PatternHandler; // Public constructors and assignment operators public: DDR4Interface(const MemSpecDDR4& memSpec, const config::SimConfig &simConfig = {}); // Public member functions public: // Member functions timestamp_t getLastCommandTime() const; void doCommand(const Command& cmd); void getWindowStats(timestamp_t timestamp, SimulationStats &stats) const; // Overrides void serialize(std::ostream& stream) const override; void deserialize(std::istream& stream) override; // Extensions void enableDBI(bool enable) { m_dbi.enable(enable); } // Private member functions private: void registerPatterns(); std::optional handleDBIInterface(timestamp_t timestamp, std::size_t n_bits, const uint8_t* data, bool read); void handleDBIPinChange(const timestamp_t load_timestamp, std::size_t pin, bool state, bool read); void handleOverrides(size_t length, bool read); void handleDQs(const Command& cmd, util::Clock &dqs, size_t length); void handleCommandBus(const Command& cmd); void handleData(const Command &cmd, bool read); void handlePrePostamble( const timestamp_t timestamp, const uint64_t length, RankInterface & rank, bool read ); void endOfSimulation(timestamp_t timestamp); // Private member variables private: DDR4InterfaceMemSpec m_memSpec; commandbus_t m_commandBus; databus_t m_dataBus; util::Clock m_readDQS; util::Clock m_writeDQS; util::Clock m_clock; util::DBI m_dbi; std::vector m_dbiread; std::vector m_dbiwrite; uint64_t prepostambleReadMinTccd; uint64_t prepostambleWriteMinTccd; std::vector m_ranks; patternHandler_t m_patternHandler; timestamp_t m_last_command_time = 0; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_DDR4_DDR4INTERFACE_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr4/core_calculation_DDR4.cpp ================================================ #include "core_calculation_DDR4.h" namespace DRAMPower { Calculation_DDR4::Calculation_DDR4(const MemSpecDDR4 &memSpec) : m_memSpec(memSpec) {} inline double Calculation_DDR4::E_BG_pre(std::size_t B, double VDD, double IDD2_N, double T_BG_pre) const { return (1.0 / B) * VDD * IDD2_N * T_BG_pre; }; inline double Calculation_DDR4::E_BG_act_shared(double VDD, double I_rho, double T_BG_act) const { return VDD * I_rho * T_BG_act; } inline double Calculation_DDR4::E_BG_act_star(std::size_t B, double VDD, double IDD3_N, double I_rho, double T_BG_act_star) const { return VDD * (1.0 / B) * (IDD3_N - I_rho) * T_BG_act_star; } inline double Calculation_DDR4::E_pre(double VDD, double IBeta, double IDD2_N, double t_RP, uint64_t N_pre) const { return VDD * (IBeta - IDD2_N) * t_RP * N_pre; } inline double Calculation_DDR4::E_act(double VDD, double I_theta, double I_1, double t_RAS, uint64_t N_act) const { return VDD * (I_theta - I_1) * t_RAS * N_act; } inline double Calculation_DDR4::E_RD(double VDD, double IDD4_R, double IDD3_N, double t_CK, std::size_t BL, std::size_t DR, uint64_t N_RD) const { return VDD * (IDD4_R - IDD3_N) * (double(BL) / DR) * t_CK * N_RD; } inline double Calculation_DDR4::E_WR(double VDD, double IDD4_W, double IDD3_N, double t_CK, std::size_t BL, std::size_t DR, uint64_t N_WR) const { return VDD * (IDD4_W - IDD3_N) * (BL / DR) * t_CK * N_WR; } inline double Calculation_DDR4::E_ref_ab(std::size_t B, double VDD, double IDD5B, double IDD3_N, double tRFC, uint64_t N_REF) const { return (1.0 / B) * VDD * (IDD5B - IDD3_N) * tRFC * N_REF; } energy_t Calculation_DDR4::calcEnergy(const SimulationStats &stats) const { // Timings double t_CK = m_memSpec.memTimingSpec.tCK; auto t_RAS = m_memSpec.memTimingSpec.tRAS * t_CK; auto t_RP = m_memSpec.memTimingSpec.tRP * t_CK; auto t_RFC = m_memSpec.memTimingSpec.tRFC * t_CK; auto rho = m_memSpec.bwParams.bwPowerFactRho; auto BL = m_memSpec.burstLength; auto DR = m_memSpec.dataRate; auto B = m_memSpec.numberOfBanks; energy_t energy(m_memSpec.numberOfBanks * m_memSpec.numberOfRanks * m_memSpec.numberOfDevices); for (auto vd : {MemSpecDDR4::VoltageDomain::VDD, MemSpecDDR4::VoltageDomain::VPP}) { auto VXX = m_memSpec.memPowerSpec[vd].vXX; auto IXX_0 = m_memSpec.memPowerSpec[vd].iXX0; auto IXX2N = m_memSpec.memPowerSpec[vd].iXX2N; auto IXX3N = m_memSpec.memPowerSpec[vd].iXX3N; auto IXX2P = m_memSpec.memPowerSpec[vd].iXX2P; auto IXX3P = m_memSpec.memPowerSpec[vd].iXX3P; auto IXX4R = m_memSpec.memPowerSpec[vd].iXX4R; auto IXX4W = m_memSpec.memPowerSpec[vd].iXX4W; auto IXX5X = m_memSpec.memPowerSpec[vd].iXX5X; auto IXX6N = m_memSpec.memPowerSpec[vd].iXX6N; auto IBeta = m_memSpec.memPowerSpec[vd].iBeta; auto I_rho = rho * (IXX3N - IXX2N) + IXX2N; auto I_theta = (IXX_0 * (t_RP + t_RAS) - IBeta * t_RP) * (1 / t_RAS); auto I_1 = (1.0 / B) * (IXX3N + (B - 1) * (rho * (IXX3N - IXX2N) + IXX2N)); size_t energy_offset = 0; size_t bank_offset = 0; for (size_t r = 0; r < m_memSpec.numberOfRanks; ++r) { for(size_t d = 0; d < m_memSpec.numberOfDevices; d++) { energy_offset = r * m_memSpec.numberOfDevices * m_memSpec.numberOfBanks + d * m_memSpec.numberOfBanks; // Bank offset doesn't include numberOfDevices, because one device is simulated // The stats only contain one device per rank bank_offset = r * m_memSpec.numberOfBanks; for (size_t b = 0; b < m_memSpec.numberOfBanks; ++b) { const auto &bank = stats.bank[bank_offset + b]; energy.bank_energy[energy_offset + b].E_act += E_act(VXX, I_theta, I_1, t_RAS, bank.counter.act); energy.bank_energy[energy_offset + b].E_pre += E_pre(VXX, IBeta, IXX2N, t_RP, bank.counter.pre); energy.bank_energy[energy_offset + b].E_bg_act += E_BG_act_star(B, VXX, IXX3N, I_rho, stats.bank[bank_offset + b].cycles.activeTime() * t_CK); energy.bank_energy[energy_offset + b].E_bg_pre += E_BG_pre(B, VXX, IXX2N, stats.rank_total[r].cycles.pre * t_CK); energy.bank_energy[energy_offset + b].E_RD += E_RD(VXX, IXX4R, IXX3N, t_CK, BL, DR, bank.counter.reads); energy.bank_energy[energy_offset + b].E_WR += E_WR(VXX, IXX4W, IXX3N, t_CK, BL, DR, bank.counter.writes); energy.bank_energy[energy_offset + b].E_RDA += E_RD(VXX, IXX4R, IXX3N, t_CK, BL, DR, bank.counter.readAuto); energy.bank_energy[energy_offset + b].E_WRA += E_WR(VXX, IXX4W, IXX3N, t_CK, BL, DR, bank.counter.writeAuto); energy.bank_energy[energy_offset + b].E_pre_RDA += E_pre(VXX, IBeta, IXX2N, t_RP, bank.counter.readAuto); energy.bank_energy[energy_offset + b].E_pre_WRA += E_pre(VXX, IBeta, IXX2N, t_RP, bank.counter.writeAuto); energy.bank_energy[energy_offset + b].E_ref_AB += E_ref_ab(B, VXX, IXX5X, IXX3N, t_RFC, bank.counter.refAllBank); } } energy.E_sref += VXX * IXX6N * stats.rank_total[r].cycles.selfRefresh * t_CK * m_memSpec.numberOfDevices; energy.E_PDNA += VXX * IXX3P * stats.rank_total[r].cycles.powerDownAct * t_CK * m_memSpec.numberOfDevices; energy.E_PDNP += VXX * IXX2P * stats.rank_total[r].cycles.powerDownPre * t_CK * m_memSpec.numberOfDevices; energy.E_bg_act_shared += E_BG_act_shared(VXX, I_rho, stats.rank_total[r].cycles.act * t_CK) * m_memSpec.numberOfDevices; } } return energy; } } ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr4/core_calculation_DDR4.h ================================================ #ifndef DRAMPOWER_STANDARDS_DDR4_CALCULATION_DDR4_H #define DRAMPOWER_STANDARDS_DDR4_CALCULATION_DDR4_H #pragma once #include #include #include #include #include #include namespace DRAMPower { class DDR4; class Calculation_DDR4 { public: Calculation_DDR4(const MemSpecDDR4 &memSpec); private: inline double E_act(double VDD, double I_theta, double I_1, double t_RAS, uint64_t N_act) const; inline double E_pre(double VDD, double IBeta, double IDD2_N, double t_RP, uint64_t N_pre) const; inline double E_BG_pre(std::size_t B, double VDD, double IDD2_N, double T_BG_pre) const; inline double E_BG_act_star(std::size_t B, double VDD, double IDD3_N, double I_rho, double T_BG_act_star) const; inline double E_BG_act_shared(double VDD, double I_rho, double T_BG_act) const; inline double E_RD(double VDD, double IDD4_R, double IDD3_N, double t_CK, std::size_t BL, std::size_t DR, uint64_t N_RD) const; inline double E_WR(double VDD, double IDD4_W, double IDD3_N, double t_CK, std::size_t BL, std::size_t DR, uint64_t N_WR) const; inline double E_ref_ab(std::size_t B, double VDD, double IDD5B, double IDD3_N, double tRFC, uint64_t N_REF) const; public: energy_t calcEnergy(const SimulationStats &stats) const; private: const MemSpecDDR4 &m_memSpec; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_DDR4_CALCULATION_DDR4_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr4/interface_calculation_DDR4.cpp ================================================ #include "DRAMPower/standards/ddr4/interface_calculation_DDR4.h" #include "DRAMPower/data/energy.h" namespace DRAMPower { static double calc_static_energy(double NxBits, double R_eq, double t_CK, double voltage) { return NxBits * ((voltage * voltage) / R_eq) * t_CK; // N * P * t = N * E }; static double calc_dynamic_energy(const uint64_t NxBits, const double energy) { return NxBits * energy; }; static double calcStaticTermination(const bool termination, const DRAMPower::util::bus_stats_t &stats, const double R_eq, const double t_CK, const uint64_t datarate, const double voltage) { if (termination == false) { return 0; // No static termination } // zeroes return calc_static_energy(stats.zeroes, R_eq, t_CK / datarate, voltage); }; InterfaceCalculation_DDR4::InterfaceCalculation_DDR4(const MemSpecDDR4 &memspec) : memspec_(memspec) , impedances_(memspec.memImpedanceSpec) , t_CK_(memspec.memTimingSpec.tCK) , VDD_(memspec.vddq) {} interface_energy_info_t InterfaceCalculation_DDR4::calculateEnergy(const SimulationStats &stats) const { interface_energy_info_t result; result += calcClockEnergy(stats); result += calcDQSEnergy(stats); result += calcDQEnergyTogglingRate(stats.togglingStats); result += calcDQEnergy(stats); result += calcCAEnergy(stats); result += calcDBIEnergy(stats); return result; } interface_energy_info_t InterfaceCalculation_DDR4::calcClockEnergy(const SimulationStats &stats) const { interface_energy_info_t result; // Pull up -> zeros result.controller.staticEnergy = calcStaticTermination(impedances_.ck_termination, stats.clockStats, impedances_.ck_R_eq, t_CK_, 2, VDD_); // datarate 2 -> half the time low other half high result.controller.dynamicEnergy = calc_dynamic_energy(stats.clockStats.zeroes_to_ones, impedances_.ck_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_DDR4::calcDQSEnergy(const SimulationStats &stats) const { interface_energy_info_t result; // Pull up -> zeros uint_fast8_t NumDQsPairs = 1; if(memspec_.bitWidth == 16) NumDQsPairs = 2; uint64_t readcount = 0; uint64_t writecount = 0; uint64_t preposreadseamless = 0; uint64_t preposwriteseamless = 0; double preposreadzeroes = memspec_.prePostamble.read_zeroes; double preposwritezeroes = memspec_.prePostamble.write_zeroes; uint64_t preposreadzero_to_one = memspec_.prePostamble.read_zeroes_to_ones; uint64_t preposwritezero_to_one = memspec_.prePostamble.write_zeroes_to_ones; for (auto& rank : stats.rank_total) { // Reads readcount += rank.counter.reads + rank.counter.readAuto; // Writes writecount += rank.counter.writes + rank.counter.writeAuto; // PrePostamble preposreadseamless += rank.prepos.readSeamless; preposwriteseamless += rank.prepos.writeSeamless; } // PrePostamble if (impedances_.wdqs_termination == true) { // Write result.controller.staticEnergy += calc_static_energy(NumDQsPairs * preposwritezeroes * (writecount - preposwriteseamless), impedances_.wdqs_R_eq, t_CK_, VDD_); result.controller.dynamicEnergy += calc_dynamic_energy(NumDQsPairs * preposwritezero_to_one * (writecount - preposwriteseamless), impedances_.wdqs_dyn_E); } if (impedances_.rdqs_termination == true) { // Read result.dram.staticEnergy += calc_static_energy(NumDQsPairs * preposreadzeroes * (readcount - preposreadseamless), impedances_.rdqs_R_eq, t_CK_, VDD_); result.dram.dynamicEnergy += calc_dynamic_energy(NumDQsPairs * preposreadzero_to_one * (readcount - preposreadseamless), impedances_.rdqs_dyn_E); } // Data // Write result.controller.staticEnergy += calcStaticTermination(impedances_.wdqs_termination, stats.writeDQSStats, impedances_.wdqs_R_eq, t_CK_, memspec_.dataRate, VDD_); result.controller.dynamicEnergy += calc_dynamic_energy(stats.writeDQSStats.zeroes_to_ones, impedances_.wdqs_dyn_E); // Read result.dram.staticEnergy += calcStaticTermination(impedances_.rdqs_termination, stats.readDQSStats, impedances_.rdqs_R_eq, t_CK_, memspec_.dataRate, VDD_); result.dram.dynamicEnergy += calc_dynamic_energy(stats.readDQSStats.zeroes_to_ones, impedances_.rdqs_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_DDR4::calcDQEnergyTogglingRate(const TogglingStats &stats) const { interface_energy_info_t result; // Write result.controller.staticEnergy += calcStaticTermination(impedances_.wdq_termination, stats.write, impedances_.wdq_R_eq, t_CK_, memspec_.dataRate, VDD_); result.controller.dynamicEnergy += calc_dynamic_energy(stats.write.zeroes_to_ones, impedances_.wdq_dyn_E); // Read result.dram.staticEnergy += calcStaticTermination(impedances_.rdq_termination, stats.read, impedances_.rdq_R_eq, t_CK_, memspec_.dataRate, VDD_); result.dram.dynamicEnergy += calc_dynamic_energy(stats.read.zeroes_to_ones, impedances_.rdq_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_DDR4::calcDQEnergy(const SimulationStats &stats) const { interface_energy_info_t result; // Read result.dram.staticEnergy += calcStaticTermination(impedances_.rdq_termination, stats.readBus, impedances_.rdq_R_eq, t_CK_, memspec_.dataRate, VDD_); result.dram.dynamicEnergy += calc_dynamic_energy(stats.readBus.zeroes_to_ones, impedances_.rdq_dyn_E); // Write result.controller.staticEnergy += calcStaticTermination(impedances_.wdq_termination, stats.writeBus, impedances_.wdq_R_eq, t_CK_, memspec_.dataRate, VDD_); result.controller.dynamicEnergy += calc_dynamic_energy(stats.writeBus.zeroes_to_ones, impedances_.wdq_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_DDR4::calcCAEnergy(const SimulationStats &stats) const { interface_energy_info_t result; // Pull up -> zeros result.controller.staticEnergy = calcStaticTermination(impedances_.ca_termination, stats.commandBus, impedances_.ca_R_eq, t_CK_, 1, VDD_); result.controller.dynamicEnergy = calc_dynamic_energy(stats.commandBus.zeroes_to_ones, impedances_.ca_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_DDR4::calcDBIEnergy(const SimulationStats &stats) const { interface_energy_info_t result; // Read result.dram.staticEnergy += calcStaticTermination(impedances_.rdbi_termination, stats.readDBI, impedances_.rdbi_R_eq, t_CK_, memspec_.dataRate, VDD_); result.dram.dynamicEnergy += calc_dynamic_energy(stats.readDBI.zeroes_to_ones, impedances_.rdbi_dyn_E); // Write result.controller.staticEnergy += calcStaticTermination(impedances_.wdbi_termination, stats.writeDBI, impedances_.wdbi_R_eq, t_CK_, memspec_.dataRate, VDD_); result.controller.dynamicEnergy += calc_dynamic_energy(stats.writeDBI.zeroes_to_ones, impedances_.wdbi_dyn_E); return result; } } ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr4/interface_calculation_DDR4.h ================================================ #ifndef STANDARDS_DDR4_INTERFACE_CALCULATION_DDR4_H #define STANDARDS_DDR4_INTERFACE_CALCULATION_DDR4_H #include "DRAMPower/data/energy.h" #include "DRAMPower/memspec/MemSpecDDR4.h" #include "DRAMPower/data/stats.h" namespace DRAMPower { class InterfaceCalculation_DDR4 { public: InterfaceCalculation_DDR4(const MemSpecDDR4 &memspec); interface_energy_info_t calculateEnergy(const SimulationStats &stats) const; private: const MemSpecDDR4 &memspec_; const MemSpecDDR4::MemImpedanceSpec &impedances_; double t_CK_; double VDD_; interface_energy_info_t calcClockEnergy(const SimulationStats &stats) const; interface_energy_info_t calcDQSEnergy(const SimulationStats &stats) const; interface_energy_info_t calcDQEnergyTogglingRate(const TogglingStats &stats) const; interface_energy_info_t calcDQEnergy(const SimulationStats &stats) const; interface_energy_info_t calcCAEnergy(const SimulationStats &stats) const; interface_energy_info_t calcDBIEnergy(const SimulationStats &stats) const; }; } // namespace DRAMPower #endif /* STANDARDS_DDR4_INTERFACE_CALCULATION_DDR4_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr4/types.h ================================================ #ifndef DRAMPOWER_STANDARDS_DDR4_TYPES_H #define DRAMPOWER_STANDARDS_DDR4_TYPES_H #include #include "DRAMPower/memspec/MemSpecDDR4.h" #include "DRAMPower/standards/ddr4/DDR4Core.h" #include "DRAMPower/standards/ddr4/DDR4Interface.h" #include "DRAMPower/standards/ddr4/core_calculation_DDR4.h" #include "DRAMPower/standards/ddr4/interface_calculation_DDR4.h" namespace DRAMPower { struct DDR4Types { using DRAMUtilsMemSpec_t = DRAMUtils::MemSpec::MemSpecDDR4; using MemSpec_t = MemSpecDDR4; using Core_t = DDR4Core; using Interface_t = DDR4Interface; using CalcCore_t = Calculation_DDR4; using CalcInterface_t = InterfaceCalculation_DDR4; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_DDR4_TYPES_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr5/DDR5.cpp ================================================ #include "DRAMPower/standards/ddr5/DDR5.h" #include #include "DRAMPower/data/stats.h" #include "DRAMPower/memspec/MemSpecDDR5.h" #include "DRAMPower/standards/ddr5/DDR5Interface.h" #include "DRAMPower/standards/ddr5/core_calculation_DDR5.h" #include "DRAMPower/standards/ddr5/interface_calculation_DDR5.h" #include namespace DRAMPower { using namespace DRAMUtils::Config; DDR5::DDR5(const MemSpecDDR5 &memSpec, const config::SimConfig &simConfig) : m_memSpec(memSpec) , m_interface(m_memSpec, simConfig) , m_core(m_memSpec) {} // Getters for CLI util::CLIArchitectureConfig DDR5::getCLIArchitectureConfig() { return util::CLIArchitectureConfig{ m_memSpec.numberOfDevices, m_memSpec.numberOfRanks, m_memSpec.numberOfBanks }; } // Calculation energy_t DDR5::calcCoreEnergyStats(const SimulationStats& stats) const { Calculation_DDR5 calculation(m_memSpec); return calculation.calcEnergy(stats); } interface_energy_info_t DDR5::calcInterfaceEnergyStats(const SimulationStats& stats) const { InterfaceCalculation_DDR5 calculation(m_memSpec); return calculation.calculateEnergy(stats); } // Stats SimulationStats DDR5::getWindowStats(timestamp_t timestamp) { SimulationStats stats; m_core.getWindowStats(timestamp, stats); m_interface.getWindowStats(timestamp, stats); return stats; } // Serialization void DDR5::serialize_impl(std::ostream &stream) const { m_core.serialize(stream); m_interface.serialize(stream); } void DDR5::deserialize_impl(std::istream &stream) { m_core.deserialize(stream); m_interface.deserialize(stream); } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr5/DDR5.h ================================================ #ifndef DRAMPOWER_STANDARDS_DDR5_DDR5_H #define DRAMPOWER_STANDARDS_DDR5_DDR5_H #include "DRAMPower/util/cli_architecture_config.h" #include "DRAMPower/Types.h" #include "DRAMPower/dram/dram_base.h" #include #include "DRAMPower/data/energy.h" #include #include "DRAMPower/standards/ddr5/DDR5Core.h" #include "DRAMPower/standards/ddr5/DDR5Interface.h" #include "DRAMPower/memspec/MemSpecDDR5.h" #include "DRAMPower/simconfig/simconfig.h" #include namespace DRAMPower { class DDR5 : public dram_base { // public constructors and assignment operators public: DDR5() = delete; // No default constructor DDR5(const DDR5&) = default; // copy constructor DDR5& operator=(const DDR5&) = default; // copy assignment operator DDR5(DDR5&&) = default; // move constructor DDR5& operator=(DDR5&&) = default; // move assignment operator ~DDR5() override = default; DDR5(const MemSpecDDR5& memSpec, const config::SimConfig &simConfig = {}); // Public member functions public: energy_t calcCoreEnergyStats(const SimulationStats& stats) const override; interface_energy_info_t calcInterfaceEnergyStats(const SimulationStats& stats) const override; SimulationStats getWindowStats(timestamp_t timestamp) override; util::CLIArchitectureConfig getCLIArchitectureConfig() override; bool isSerializable() const override { return m_core.isSerializable(); } // member functions DDR5Core& getCore() { return m_core; } const DDR5Core& getCore() const { return m_core; } DDR5Interface& getInterface() { return m_interface; } const DDR5Interface& getInterface() const { return m_interface; } // Private member functions private: // Overrides void doCoreCommandImpl(const Command& command) override { m_core.doCommand(command); } void doInterfaceCommandImpl(const Command& command) override { m_interface.doCommand(command); } timestamp_t getLastCommandTime_impl() const override { return std::max(m_core.getLastCommandTime(), m_interface.getLastCommandTime()); } void serialize_impl(std::ostream& stream) const override; void deserialize_impl(std::istream& stream) override; // Private member variables private: MemSpecDDR5 m_memSpec; DDR5Interface m_interface; DDR5Core m_core; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_DDR5_DDR5_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr5/DDR5Core.cpp ================================================ #include "DDR5Core.h" #include "DRAMPower/util/RegisterHelper.h" namespace DRAMPower { void DDR5Core::doCommand(const Command& cmd) { m_implicitCommandHandler.processImplicitCommandQueue(*this, cmd.timestamp, m_last_command_time); m_last_command_time = std::max(cmd.timestamp, m_last_command_time); switch(cmd.type) { case CmdType::ACT: util::coreHelpers::bankHandler(cmd, m_ranks, this, &DDR5Core::handleAct); break; case CmdType::PRE: util::coreHelpers::bankHandler(cmd, m_ranks, this, &DDR5Core::handlePre); break; case CmdType::RD: util::coreHelpers::bankHandler(cmd, m_ranks, this, &DDR5Core::handleRead); break; case CmdType::RDA: util::coreHelpers::bankHandlerIdx(cmd, m_ranks, this, &DDR5Core::handleReadAuto); break; case CmdType::WR: util::coreHelpers::bankHandler(cmd, m_ranks, this, &DDR5Core::handleWrite); break; case CmdType::WRA: util::coreHelpers::bankHandlerIdx(cmd, m_ranks, this, &DDR5Core::handleWriteAuto); break; case CmdType::PRESB: util::coreHelpers::bankGroupHandler(cmd, m_ranks, this, &DDR5Core::handlePreSameBank); break; case CmdType::REFSB: util::coreHelpers::bankGroupHandlerIdx(cmd, m_ranks, this, &DDR5Core::handleRefSameBank); break; case CmdType::REFA: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &DDR5Core::handleRefAll); break; case CmdType::PREA: util::coreHelpers::rankHandler(cmd, m_ranks, this, &DDR5Core::handlePreAll); break; case CmdType::SREFEN: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &DDR5Core::handleSelfRefreshEntry); break; case CmdType::SREFEX: util::coreHelpers::rankHandler(cmd, m_ranks, this, &DDR5Core::handleSelfRefreshExit); break; case CmdType::PDEA: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &DDR5Core::handlePowerDownActEntry); break; case CmdType::PDEP: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &DDR5Core::handlePowerDownPreEntry); break; case CmdType::PDXA: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &DDR5Core::handlePowerDownActExit); break; case CmdType::PDXP: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &DDR5Core::handlePowerDownPreExit); break; case CmdType::NOP: case CmdType::END_OF_SIMULATION: break; default: assert(false && "Unsupported command"); break; } } timestamp_t DDR5Core::getLastCommandTime() const { return m_last_command_time; } bool DDR5Core::isSerializable() const { return 0 == m_implicitCommandHandler.implicitCommandCount(); } void DDR5Core::handleAct(Rank &rank, Bank &bank, timestamp_t timestamp) { bank.counter.act++; bank.cycles.act.start_interval(timestamp); if ( !rank.isActive(timestamp) ) { rank.cycles.act.start_interval(timestamp); }; bank.bankState = Bank::BankState::BANK_ACTIVE; } void DDR5Core::handlePre(Rank &rank, Bank &bank, timestamp_t timestamp) { if (bank.bankState == Bank::BankState::BANK_PRECHARGED) return; bank.counter.pre++; bank.cycles.act.close_interval(timestamp); bank.latestPre = timestamp; bank.bankState = Bank::BankState::BANK_PRECHARGED; if ( !rank.isActive(timestamp) ) { rank.cycles.act.close_interval(timestamp); } } void DDR5Core::handlePreSameBank(Rank & rank, std::size_t bank_id, timestamp_t timestamp) { auto bank_id_inside_bg = bank_id % m_memSpec.banksPerGroup; for(unsigned bank_group = 0; bank_group < m_memSpec.numberOfBankGroups; bank_group++) { auto & bank = rank.banks[bank_group * m_memSpec.banksPerGroup + bank_id_inside_bg]; handlePre(rank, bank, timestamp); } } void DDR5Core::handlePreAll(Rank &rank, timestamp_t timestamp) { for (auto &bank: rank.banks) { handlePre(rank, bank, timestamp); } } void DDR5Core::handleRefSameBank(std::size_t rank_idx, std::size_t bank_id, timestamp_t timestamp) { auto bank_id_inside_bg = bank_id % m_memSpec.banksPerGroup; for(unsigned bank_group = 0; bank_group < m_memSpec.numberOfBankGroups; bank_group++) { std::size_t bank_idx = bank_group * m_memSpec.banksPerGroup + bank_id_inside_bg; auto& counter = m_ranks[rank_idx].banks[bank_idx].counter.refSameBank; handleRefreshOnBank(rank_idx, bank_idx, timestamp, m_memSpec.tRFCsb, counter); } } void DDR5Core::handleRefAll(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; for (std::size_t bank_idx = 0; bank_idx < rank.banks.size(); ++bank_idx) { auto& counter = m_ranks[rank_idx].banks[bank_idx].counter.refAllBank; handleRefreshOnBank(rank_idx, bank_idx, timestamp, m_memSpec.tRFC, counter); } rank.endRefreshTime = timestamp + m_memSpec.tRFC; } void DDR5Core::handleRefreshOnBank(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp, uint64_t timing, uint64_t & counter){ ++counter; auto& rank = m_ranks[rank_idx]; auto& bank = rank.banks[bank_idx]; if (!rank.isActive(timestamp)) { rank.cycles.act.start_interval(timestamp); } bank.bankState = Bank::BankState::BANK_ACTIVE; auto timestamp_end = timestamp + timing; bank.refreshEndTime = timestamp_end; if (!bank.cycles.act.is_open()) bank.cycles.act.start_interval(timestamp); // Execute implicit pre-charge at refresh end m_implicitCommandHandler.addImplicitCommand(timestamp_end, [rank_idx, bank_idx, timestamp_end](DDR5Core& self) { auto& rank = self.m_ranks[rank_idx]; auto& bank = rank.banks[bank_idx]; bank.bankState = Bank::BankState::BANK_PRECHARGED; bank.cycles.act.close_interval(timestamp_end); if (!rank.isActive(timestamp_end)) { rank.cycles.act.close_interval(timestamp_end); } }); } void DDR5Core::handleRead(Rank&, Bank &bank, timestamp_t){ ++bank.counter.reads; } void DDR5Core::handleReadAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp) { auto& bank = m_ranks[rank_idx].banks[bank_idx]; ++bank.counter.readAuto; auto minBankActiveTime = bank.cycles.act.get_start() + m_memSpec.tRAS; auto minReadActiveTime = timestamp + m_memSpec.prechargeOffsetRD; auto delayed_timestamp = std::max(minBankActiveTime, minReadActiveTime); // Execute PRE after minimum active time m_implicitCommandHandler.addImplicitCommand(delayed_timestamp, [rank_idx, bank_idx, delayed_timestamp](DDR5Core& self) { auto& rank = self.m_ranks[rank_idx]; auto& bank = rank.banks[bank_idx]; self.handlePre(rank, bank, delayed_timestamp); }); } void DDR5Core::handleWrite(Rank&, Bank &bank, timestamp_t) { ++bank.counter.writes; } void DDR5Core::handleWriteAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp) { auto& bank = m_ranks[rank_idx].banks[bank_idx]; ++bank.counter.writeAuto; auto minBankActiveTime = bank.cycles.act.get_start() + m_memSpec.tRAS; auto minWriteActiveTime = timestamp + m_memSpec.prechargeOffsetWR; auto delayed_timestamp = std::max(minBankActiveTime, minWriteActiveTime); // Execute PRE after minimum active time m_implicitCommandHandler.addImplicitCommand(delayed_timestamp, [rank_idx, bank_idx, delayed_timestamp](DDR5Core& self) { auto& rank = self.m_ranks[rank_idx]; auto& bank = rank.banks[bank_idx]; self.handlePre(rank, bank, delayed_timestamp); }); } void DDR5Core::handleSelfRefreshEntry(std::size_t rank_idx, timestamp_t timestamp) { // Issue implicit refresh handleRefAll(rank_idx, timestamp); // Handle self-refresh entry after tRFC auto timestampSelfRefreshStart = timestamp + m_memSpec.tRFC; m_implicitCommandHandler.addImplicitCommand(timestampSelfRefreshStart, [rank_idx, timestampSelfRefreshStart](DDR5Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.counter.selfRefresh++; rank.cycles.sref.start_interval(timestampSelfRefreshStart); rank.memState = MemState::SREF; }); } void DDR5Core::handleSelfRefreshExit(Rank &rank, timestamp_t timestamp) { assert(rank.memState == MemState::SREF); rank.cycles.sref.close_interval(timestamp); // Duration between entry and exit rank.memState = MemState::NOT_IN_PD; } void DDR5Core::handlePowerDownActEntry(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; auto earliestPossibleEntry = this->earliestPossiblePowerDownEntryTime(rank); auto entryTime = std::max(timestamp, earliestPossibleEntry); m_implicitCommandHandler.addImplicitCommand(entryTime, [rank_idx, entryTime](DDR5Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.cycles.powerDownAct.start_interval(entryTime); rank.memState = MemState::PDN_ACT; if (rank.cycles.act.is_open()) { rank.cycles.act.close_interval(entryTime); } for (auto & bank : rank.banks) { if (bank.cycles.act.is_open()) { bank.cycles.act.close_interval(entryTime); } }; }); } void DDR5Core::handlePowerDownActExit(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; auto earliestPossibleExit = this->earliestPossiblePowerDownEntryTime(rank); auto exitTime = std::max(timestamp, earliestPossibleExit); m_implicitCommandHandler.addImplicitCommand(exitTime, [rank_idx, exitTime](DDR5Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.memState = MemState::NOT_IN_PD; rank.cycles.powerDownAct.close_interval(exitTime); bool rank_active = false; for (auto & bank : rank.banks) { if (bank.counter.act != 0 && bank.cycles.act.get_end() == rank.cycles.powerDownAct.get_start()) { rank_active = true; bank.cycles.act.start_interval(exitTime); } } if (rank_active) { rank.cycles.act.start_interval(exitTime); } }); } void DDR5Core::handlePowerDownPreEntry(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; auto earliestPossibleEntry = this->earliestPossiblePowerDownEntryTime(rank); auto entryTime = std::max(timestamp, earliestPossibleEntry); m_implicitCommandHandler.addImplicitCommand(entryTime, [rank_idx, entryTime](DDR5Core &self) { auto& rank = self.m_ranks[rank_idx]; rank.cycles.powerDownPre.start_interval(entryTime); rank.memState = MemState::PDN_PRE; }); } void DDR5Core::handlePowerDownPreExit(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; auto earliestPossibleExit = this->earliestPossiblePowerDownEntryTime(rank); auto exitTime = std::max(timestamp, earliestPossibleExit); m_implicitCommandHandler.addImplicitCommand(exitTime, [rank_idx, exitTime](DDR5Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.memState = MemState::NOT_IN_PD; rank.cycles.powerDownPre.close_interval(exitTime); }); } timestamp_t DDR5Core::earliestPossiblePowerDownEntryTime(Rank &rank) { timestamp_t entryTime = 0; for (const auto &bank : rank.banks) { entryTime = std::max( {entryTime, bank.counter.act == 0 ? 0 : bank.cycles.act.get_start() + m_memSpec.tRCD, bank.counter.pre == 0 ? 0 : bank.latestPre + m_memSpec.tRP, bank.refreshEndTime}); } return entryTime; } void DDR5Core::getWindowStats(timestamp_t timestamp, SimulationStats &stats) { m_implicitCommandHandler.processImplicitCommandQueue(*this, timestamp, m_last_command_time); stats.bank.resize(m_memSpec.numberOfBanks * m_memSpec.numberOfRanks); stats.rank_total.resize(m_memSpec.numberOfRanks); auto simulation_duration = timestamp; for (size_t i = 0; i < m_memSpec.numberOfRanks; ++i) { const Rank &rank = m_ranks[i]; size_t bank_offset = i * m_memSpec.numberOfBanks; for (std::size_t j = 0; j < m_memSpec.numberOfBanks; ++j) { stats.bank[bank_offset + j].counter = rank.banks[j].counter; stats.bank[bank_offset + j].cycles.act = rank.banks[j].cycles.act.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.selfRefresh = rank.cycles.sref.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.powerDownAct = rank.cycles.powerDownAct.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.powerDownPre = rank.cycles.powerDownPre.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.pre = simulation_duration - (stats.bank[bank_offset + j].cycles.act + rank.cycles.powerDownAct.get_count_at(timestamp) + rank.cycles.powerDownPre.get_count_at(timestamp) + rank.cycles.sref.get_count_at(timestamp)); } stats.rank_total[i].cycles.pre = simulation_duration - (rank.cycles.act.get_count_at(timestamp) + rank.cycles.powerDownAct.get_count_at(timestamp) + rank.cycles.powerDownPre.get_count_at(timestamp) + rank.cycles.sref.get_count_at(timestamp)); stats.rank_total[i].cycles.act = rank.cycles.act.get_count_at(timestamp); stats.rank_total[i].cycles.powerDownAct = rank.cycles.powerDownAct.get_count_at(timestamp); stats.rank_total[i].cycles.powerDownPre = rank.cycles.powerDownPre.get_count_at(timestamp); stats.rank_total[i].cycles.deepSleepMode = rank.cycles.deepSleepMode.get_count_at(timestamp); stats.rank_total[i].cycles.selfRefresh = rank.cycles.sref.get_count_at(timestamp); } } void DDR5Core::serialize(std::ostream& stream) const { stream.write(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); for (const auto& rank : m_ranks) { rank.serialize(stream); } } void DDR5Core::deserialize(std::istream& stream) { stream.read(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); for (auto& rank : m_ranks) { rank.deserialize(stream); } } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr5/DDR5Core.h ================================================ #ifndef DRAMPOWER_STANDARDS_DDR5_DDR5CORE_H #define DRAMPOWER_STANDARDS_DDR5_DDR5CORE_H #include "DRAMPower/command/Command.h" #include "DRAMPower/dram/Rank.h" #include "DRAMPower/util/ImplicitCommandHandler.h" #include "DRAMPower/util/Deserialize.h" #include "DRAMPower/util/Serialize.h" #include "DRAMPower/memspec/MemSpecDDR5.h" #include #include namespace DRAMPower { namespace internal { template class TestAccessor; } struct DDR5CoreMemSpec { DDR5CoreMemSpec(const MemSpecDDR5& memSpec) : numberOfBanks(memSpec.numberOfBanks) , numberOfRanks(memSpec.numberOfRanks) , banksPerGroup(memSpec.banksPerGroup) , numberOfBankGroups(memSpec.numberOfBankGroups) , tRFC(memSpec.memTimingSpec.tRFC) , tRFCsb(memSpec.memTimingSpec.tRFCsb) , tRAS(memSpec.memTimingSpec.tRAS) , tRCD(memSpec.memTimingSpec.tRCD) , tRP(memSpec.memTimingSpec.tRP) , prechargeOffsetRD(memSpec.prechargeOffsetRD) , prechargeOffsetWR(memSpec.prechargeOffsetWR) {} uint64_t numberOfBanks; uint64_t numberOfRanks; uint64_t banksPerGroup; uint64_t numberOfBankGroups; uint64_t tRFC; uint64_t tRFCsb; uint64_t tRAS; uint64_t tRCD; uint64_t tRP; uint64_t prechargeOffsetRD; uint64_t prechargeOffsetWR; }; class DDR5Core : public util::Serialize, public util::Deserialize { // Friend classes friend class internal::TestAccessor; // Public constructors and assignment operators public: DDR5Core(const MemSpecDDR5& memSpec) : m_memSpec(memSpec) , m_ranks(memSpec.numberOfRanks, {static_cast(memSpec.numberOfBanks)}) {} // Public member functions public: // Member functions void doCommand(const Command& cmd); timestamp_t getLastCommandTime() const; bool isSerializable() const; void getWindowStats(timestamp_t timestamp, SimulationStats &stats); // Overrides void serialize(std::ostream& stream) const override; void deserialize(std::istream& stream) override; // Private member functions private: void handleAct(Rank& rank, Bank& bank, timestamp_t timestamp); void handlePre(Rank& rank, Bank& bank, timestamp_t timestamp); void handlePreSameBank(Rank& rank, std::size_t bank_id, timestamp_t timestamp); void handlePreAll(Rank& rank, timestamp_t timestamp); void handleRefSameBank(std::size_t rank_idx, std::size_t bank_id, timestamp_t timestamp); void handleRefAll(std::size_t rank_idx, timestamp_t timestamp); void handleRefreshOnBank(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp, uint64_t timing, uint64_t& counter); void handleSelfRefreshEntry(std::size_t rank_idx, timestamp_t timestamp); void handleSelfRefreshExit(Rank& rank, timestamp_t timestamp); void handleRead(Rank& rank, Bank& bank, timestamp_t timestamp); void handleWrite(Rank& rank, Bank& bank, timestamp_t timestamp); void handleReadAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp); void handleWriteAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp); void handlePowerDownActEntry(std::size_t rank_idx, timestamp_t timestamp); void handlePowerDownActExit(std::size_t rank_idx, timestamp_t timestamp); void handlePowerDownPreEntry(std::size_t rank_idx, timestamp_t timestamp); void handlePowerDownPreExit(std::size_t rank_idx, timestamp_t timestamp); timestamp_t earliestPossiblePowerDownEntryTime(Rank& rank); // Private member variables private: DDR5CoreMemSpec m_memSpec; std::vector m_ranks; ImplicitCommandHandler m_implicitCommandHandler; timestamp_t m_last_command_time = 0; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_DDR5_DDR5CORE_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr5/DDR5Interface.cpp ================================================ #include "DDR5Interface.h" #include "DRAMPower/simconfig/simconfig.h" namespace DRAMPower { static constexpr DRAMUtils::Config::ToggleRateDefinition busConfig { 0, 0, 0, 0, DRAMUtils::Config::TogglingRateIdlePattern::H, DRAMUtils::Config::TogglingRateIdlePattern::H }; DDR5Interface::DDR5Interface(const MemSpecDDR5& memSpec, const config::SimConfig& simConfig) : m_memSpec(memSpec) , m_commandBus{cmdBusWidth, 1, util::BusIdlePatternSpec::H, util::BusInitPatternSpec::H} , m_dataBus{ util::databus_presets::getDataBusPreset( util::DataBusConfig { memSpec.bitWidth * memSpec.numberOfDevices, memSpec.dataRate, simConfig.toggleRateDefinition.value_or(busConfig) }, simConfig.toggleRateDefinition.has_value() ? util::DataBusMode::TogglingRate : util::DataBusMode::Bus, false ) } , m_readDQS(memSpec.dataRateSpec.dqsBusRate, true) , m_writeDQS(memSpec.dataRateSpec.dqsBusRate, true) , m_patternHandler(PatternEncoderOverrides{ {pattern_descriptor::V, PatternEncoderBitSpec::H}, {pattern_descriptor::X, PatternEncoderBitSpec::H}, {pattern_descriptor::C0, PatternEncoderBitSpec::H}, {pattern_descriptor::C1, PatternEncoderBitSpec::H}, // Default value for CID0-3 is H in Pattern.h // {pattern_descriptor::CID0, PatternEncoderBitSpec::H}, // {pattern_descriptor::CID1, PatternEncoderBitSpec::H}, // {pattern_descriptor::CID2, PatternEncoderBitSpec::H}, // {pattern_descriptor::CID3, PatternEncoderBitSpec::H}, }, DDR5Interface::cmdBusInitPattern) { registerPatterns(); } void DDR5Interface::registerPatterns() { using namespace pattern_descriptor; // ACT m_patternHandler.registerPattern({ // note: CID3 is mutually exclusive with R17 and depends on usage mode L, L, R0, R1, R2, R3, BA0, BA1, BG0, BG1, BG2, CID0, CID1, CID2, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, R16, CID3 }); // PRE m_patternHandler.registerPattern({ H, H, L, H, H, CID3, BA0, BA1, BG0, BG1, BG2, CID0, CID1, CID2 }); // RD m_patternHandler.registerPattern({ H, L, H, H, H, H, BA0, BA1, BG0, BG1, BG2, CID0, CID1, CID2, C2, C3, C4, C5, C6, C7, C8, C9, C10, V, H, V, V, CID3 }); // RDA m_patternHandler.registerPattern({ H, L, H, H, H, H, BA0, BA1, BG0, BG1, BG2, CID0, CID1, CID2, C2, C3, C4, C5, C6, C7, C8, C9, C10, V, L, V, V, CID3 }); // WR m_patternHandler.registerPattern({ H, L, H, H, L, H, BA0, BA1, BG0, BG1, BG2, CID0, CID1, CID2, V, C3, C4, C5, C6, C7, C8, C9, C10, V, H, H, V, CID3 }); // WRA m_patternHandler.registerPattern({ H, L, H, H, L, H, BA0, BA1, BG0, BG1, BG2, CID0, CID1, CID2, V, C3, C4, C5, C6, C7, C8, C9, C10, V, L, H, V, CID3 }); // PRESB m_patternHandler.registerPattern({ H, H, L, H, L, CID3, BA0, BA1, V, V, H, CID0, CID1, CID2 }); // REFSB m_patternHandler.registerPattern({ H, H, L, L, H, CID3, BA0, BA1, V, V, H, CID0, CID1, CID2 }); // REFA m_patternHandler.registerPattern({ H, H, L, L, H, CID3, V, V, V, V, L, CID0, CID1, CID2 }); // PREA m_patternHandler.registerPattern({ H, H, L, H, L, CID3, V, V, V, V, L, CID0, CID1, CID2 }); // SREFEN m_patternHandler.registerPattern({ H, H, H, L, H, V, V, V, V, H, L, V, V, V }); // PDEA m_patternHandler.registerPattern({ H, H, H, L, H, V, V, V, V, V, H, L, V, V }); // PDEP m_patternHandler.registerPattern({ H, H, H, L, H, V, V, V, V, V, H, L, V, V }); // PDXA m_patternHandler.registerPattern({ H, H, H, H, H, V, V, V, V, V, V, V, V, V }); // PDXP m_patternHandler.registerPattern({ H, H, H, H, H, V, V, V, V, V, V, V, V, V }); // NOP m_patternHandler.registerPattern({ H, H, H, H, H, V, V, V, V, V, V, V, V, V }); } timestamp_t DDR5Interface::getLastCommandTime() const { return m_last_command_time; } void DDR5Interface::doCommand(const Command& cmd) { switch(cmd.type) { case CmdType::NOP: case CmdType::ACT: case CmdType::PRE: case CmdType::PRESB: case CmdType::REFSB: case CmdType::REFA: case CmdType::PREA: case CmdType::SREFEN: case CmdType::SREFEX: case CmdType::PDEA: case CmdType::PDEP: case CmdType::PDXA: case CmdType::PDXP: handleCommandBus(cmd); break; case CmdType::RD: case CmdType::RDA: handleData(cmd, true); break; case CmdType::WR: case CmdType::WRA: handleData(cmd, false); break; case CmdType::END_OF_SIMULATION: break; default: assert(false && "Invalid command"); break; } m_last_command_time = cmd.timestamp; } // Interface void DDR5Interface::handleOverrides(size_t length, bool read) { // Set command bus pattern overrides switch(length) { case 8: m_patternHandler.getEncoder().settings.removeSetting(pattern_descriptor::C10); if(read) { // Read m_patternHandler.getEncoder().settings.updateSettings({ {pattern_descriptor::C3, PatternEncoderBitSpec::L}, {pattern_descriptor::C2, PatternEncoderBitSpec::L}, }); } else { // Write m_patternHandler.getEncoder().settings.updateSettings({ {pattern_descriptor::C3, PatternEncoderBitSpec::L}, {pattern_descriptor::C2, PatternEncoderBitSpec::H}, }); } break; default: // Pull down // No interface power needed for PatternEncoderBitSpec::L // Defaults to burst length 16 case 16: m_patternHandler.getEncoder().settings.removeSetting(pattern_descriptor::C10); if(read) { // Read m_patternHandler.getEncoder().settings.updateSettings({ {pattern_descriptor::C3, PatternEncoderBitSpec::L}, {pattern_descriptor::C2, PatternEncoderBitSpec::L}, }); } else { // Write m_patternHandler.getEncoder().settings.updateSettings({ {pattern_descriptor::C3, PatternEncoderBitSpec::H}, {pattern_descriptor::C2, PatternEncoderBitSpec::H}, }); } break; case 32: if(read) { // Read m_patternHandler.getEncoder().settings.updateSettings({ {pattern_descriptor::C10, PatternEncoderBitSpec::L}, {pattern_descriptor::C3, PatternEncoderBitSpec::L}, {pattern_descriptor::C2, PatternEncoderBitSpec::L}, }); } else { // Write m_patternHandler.getEncoder().settings.updateSettings({ {pattern_descriptor::C10, PatternEncoderBitSpec::L}, {pattern_descriptor::C3, PatternEncoderBitSpec::H}, {pattern_descriptor::C2, PatternEncoderBitSpec::H}, }); } break; } } void DDR5Interface::handleCommandBus(const Command& cmd) { auto pattern = m_patternHandler.getCommandPattern(cmd); auto ca_length = m_patternHandler.getPattern(cmd.type).size() / m_commandBus.get_width(); m_commandBus.load(cmd.timestamp, pattern, ca_length); } void DDR5Interface::handleDQs(const Command& cmd, util::Clock &dqs, size_t length) { dqs.start(cmd.timestamp); dqs.stop(cmd.timestamp + length / m_memSpec.dataRate); } void DDR5Interface::handleData(const Command &cmd, bool read) { auto loadfunc = read ? &databus_t::loadRead : &databus_t::loadWrite; util::Clock &dqs = read ? m_readDQS : m_writeDQS; size_t length = 0; if (0 == cmd.sz_bits) { // No data provided by command // Use default burst length if (m_dataBus.isTogglingRate()) { length = m_memSpec.burstLength; (m_dataBus.*loadfunc)(cmd.timestamp, length * m_dataBus.getWidth(), nullptr); } } else { // Data provided by command length = cmd.sz_bits / (m_dataBus.getWidth()); (m_dataBus.*loadfunc)(cmd.timestamp, cmd.sz_bits, cmd.data); } handleDQs(cmd, dqs, length); handleOverrides(length, read); handleCommandBus(cmd); } void DDR5Interface::getWindowStats(timestamp_t timestamp, SimulationStats &stats) const { stats.commandBus = m_commandBus.get_stats(timestamp); m_dataBus.get_stats(timestamp, stats.readBus, stats.writeBus, stats.togglingStats.read, stats.togglingStats.write ); stats.clockStats = 2 * m_clock.get_stats_at(timestamp); stats.readDQSStats = 2 * m_readDQS.get_stats_at(timestamp); stats.writeDQSStats = 2 * m_writeDQS.get_stats_at(timestamp); // x16 devices have two dqs pairs if(m_memSpec.bitWidth == 16) { stats.readDQSStats *= 2; stats.writeDQSStats *= 2; } } void DDR5Interface::serialize(std::ostream& stream) const { stream.write(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); m_patternHandler.serialize(stream); m_commandBus.serialize(stream); m_dataBus.serialize(stream); m_readDQS.serialize(stream); m_writeDQS.serialize(stream); m_clock.serialize(stream); } void DDR5Interface::deserialize(std::istream& stream) { stream.read(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); m_patternHandler.deserialize(stream); m_commandBus.deserialize(stream); m_dataBus.deserialize(stream); m_readDQS.deserialize(stream); m_writeDQS.deserialize(stream); m_clock.deserialize(stream); } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr5/DDR5Interface.h ================================================ #ifndef DRAMPOWER_STANDARDS_DDR5_DDR5INTERFACE_H #define DRAMPOWER_STANDARDS_DDR5_DDR5INTERFACE_H #include "DRAMPower/util/bus.h" #include "DRAMPower/util/databus_presets.h" #include "DRAMPower/util/clock.h" #include "DRAMPower/util/Serialize.h" #include "DRAMPower/util/Deserialize.h" #include "DRAMPower/Types.h" #include "DRAMPower/command/Command.h" #include "DRAMPower/data/stats.h" #include "DRAMPower/util/PatternHandler.h" #include "DRAMPower/memspec/MemSpecDDR5.h" #include "DRAMPower/simconfig/simconfig.h" #include #include namespace DRAMPower { struct DDR5InterfaceMemSpec { DDR5InterfaceMemSpec(const MemSpecDDR5& memSpec) : dataRate(memSpec.dataRate) , burstLength(memSpec.burstLength) , bitWidth(memSpec.bitWidth) {} uint64_t dataRate; uint64_t burstLength; uint64_t bitWidth; }; class DDR5Interface : public util::Serialize, public util::Deserialize { // Public constants public: const static std::size_t cmdBusWidth = 14; const static uint64_t cmdBusInitPattern = (1<; using databus_t = util::databus_presets::databus_preset_t; using patternHandler_t = PatternHandler; // Public constructors and assignment operators public: DDR5Interface(const MemSpecDDR5& memSpec, const config::SimConfig &simConfig = {}); // Public member functions public: // Member functions timestamp_t getLastCommandTime() const; void doCommand(const Command& cmd); void getWindowStats(timestamp_t timestamp, SimulationStats &stats) const; // Overrides void serialize(std::ostream& stream) const override; void deserialize(std::istream& stream) override; // Private member functions private: void registerPatterns(); void handleOverrides(size_t length, bool read); void handleDQs(const Command& cmd, util::Clock &dqs, size_t length); void handleCommandBus(const Command& cmd); void handleData(const Command &cmd, bool read); // Private member variables private: DDR5InterfaceMemSpec m_memSpec; commandbus_t m_commandBus; databus_t m_dataBus; util::Clock m_readDQS; util::Clock m_writeDQS; util::Clock m_clock; patternHandler_t m_patternHandler; timestamp_t m_last_command_time = 0; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_DDR5_DDR5INTERFACE_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr5/core_calculation_DDR5.cpp ================================================ #include "core_calculation_DDR5.h" #include namespace DRAMPower { Calculation_DDR5::Calculation_DDR5(const MemSpecDDR5 &memSpec) : m_memSpec(memSpec) {} double Calculation_DDR5::E_BG_pre(std::size_t B, double VDD, double IDD2_N, double T_BG_pre) const { return (1.0 / B) * VDD * IDD2_N * T_BG_pre; }; double Calculation_DDR5::E_BG_act_shared(double VDD, double I_rho, double T_BG_act) const { return VDD * I_rho * T_BG_act; } double Calculation_DDR5::E_BG_act_star(std::size_t B, double VDD, double IDD3_N, double I_rho, double T_BG_act_star) const { return VDD * (1.0 / B) * (IDD3_N - I_rho) * T_BG_act_star; } double Calculation_DDR5::E_pre(double VDD, double IBeta, double IDD2_N, double t_RP, uint64_t N_pre) const { return VDD * (IBeta - IDD2_N) * t_RP * N_pre; } double Calculation_DDR5::E_act(double VDD, double I_theta, double I_1, double t_RAS, uint64_t N_act) const { return VDD * (I_theta - I_1) * t_RAS * N_act; } double Calculation_DDR5::E_RD(double VDD, double IDD4_R, double IDD3_N, double t_CK, std::size_t BL, std::size_t DR, uint64_t N_RD) const { return VDD * (IDD4_R - IDD3_N) * (double(BL) / DR) * t_CK * N_RD; } double Calculation_DDR5::E_WR(double VDD, double IDD4_W, double IDD3_N, double t_CK, std::size_t BL, std::size_t DR, uint64_t N_WR) const { return VDD * (IDD4_W - IDD3_N) * (BL / DR) * t_CK * N_WR; } double Calculation_DDR5::E_ref_ab(std::size_t B, double VDD, double IDD5B, double IDD3_N, double tRFC, uint64_t N_REF) const { return (1.0 / B) * VDD * (IDD5B - IDD3_N) * tRFC * N_REF; } double Calculation_DDR5::E_ref_sb(double VDD, double IDD5C, double I_BG, double tRFCsb, std::size_t BG, uint64_t N_SB_REF) const { return VDD * (IDD5C - I_BG) * tRFCsb * N_SB_REF * (1.0 / BG); } energy_t Calculation_DDR5::calcEnergy(const SimulationStats &stats) const { double t_CK = m_memSpec.memTimingSpec.tCK; auto t_RAS = m_memSpec.memTimingSpec.tRAS * t_CK; auto t_RP = m_memSpec.memTimingSpec.tRP * t_CK; auto t_RFC = m_memSpec.memTimingSpec.tRFC * t_CK; auto t_RFCsb = m_memSpec.memTimingSpec.tRFCsb * t_CK; auto rho = m_memSpec.bwParams.bwPowerFactRho; auto BL = m_memSpec.burstLength; auto DR = m_memSpec.dataRate; auto B = m_memSpec.numberOfBanks; auto BG = m_memSpec.numberOfBankGroups; energy_t energy(m_memSpec.numberOfBanks * m_memSpec.numberOfRanks * m_memSpec.numberOfDevices); for (auto vd : {MemSpecDDR5::VoltageDomain::VDD, MemSpecDDR5::VoltageDomain::VPP}) { auto VXX = m_memSpec.memPowerSpec[vd].vXX; auto IXX_0 = m_memSpec.memPowerSpec[vd].iXX0; auto IXX2N = m_memSpec.memPowerSpec[vd].iXX2N; auto IXX3N = m_memSpec.memPowerSpec[vd].iXX3N; auto IXX2P = m_memSpec.memPowerSpec[vd].iXX2P; auto IXX3P = m_memSpec.memPowerSpec[vd].iXX3P; auto IXX4R = m_memSpec.memPowerSpec[vd].iXX4R; auto IXX4W = m_memSpec.memPowerSpec[vd].iXX4W; auto IXX5X = m_memSpec.memPowerSpec[vd].iXX5X; auto IXX5C = m_memSpec.memPowerSpec[vd].iXX5C; auto IXX6N = m_memSpec.memPowerSpec[vd].iXX6N; auto IBeta = m_memSpec.memPowerSpec[vd].iBeta; auto I_rho = rho * (IXX3N - IXX2N) + IXX2N; auto I_theta = (IXX_0 * (t_RP + t_RAS) - IBeta * t_RP) * (1 / t_RAS); auto I_1 = (1.0 / B) * (IXX3N + (B - 1) * (rho * (IXX3N - IXX2N) + IXX2N)); auto I_BG = I_rho + (I_1 - I_rho) * BG; size_t energy_offset = 0; size_t bank_offset = 0; for (size_t i = 0; i < m_memSpec.numberOfRanks; ++i) { for (size_t d = 0; d < m_memSpec.numberOfDevices; ++d) { energy_offset = i * m_memSpec.numberOfDevices * m_memSpec.numberOfBanks + d * m_memSpec.numberOfBanks; bank_offset = i * m_memSpec.numberOfBanks; for (std::size_t b = 0; b < m_memSpec.numberOfBanks; ++b) { const auto &bank = stats.bank[bank_offset + b]; energy.bank_energy[energy_offset + b].E_act += E_act(VXX, I_theta, I_1, t_RAS, bank.counter.act); energy.bank_energy[energy_offset + b].E_pre += E_pre(VXX, IBeta, IXX2N, t_RP, bank.counter.pre); energy.bank_energy[energy_offset + b].E_bg_act += E_BG_act_star(B, VXX, IXX3N, I_rho, stats.bank[bank_offset + b].cycles.activeTime() * t_CK); energy.bank_energy[energy_offset + b].E_bg_pre += E_BG_pre(B, VXX, IXX2N, stats.rank_total[i].cycles.pre * t_CK); energy.bank_energy[energy_offset + b].E_RD += E_RD(VXX, IXX4R, IXX3N, t_CK, BL, DR, bank.counter.reads); energy.bank_energy[energy_offset + b].E_WR += E_WR(VXX, IXX4W, IXX3N, t_CK, BL, DR, bank.counter.writes); energy.bank_energy[energy_offset + b].E_RDA += E_RD(VXX, IXX4R, IXX3N, t_CK, BL, DR, bank.counter.readAuto); energy.bank_energy[energy_offset + b].E_WRA += E_WR(VXX, IXX4W, IXX3N, t_CK, BL, DR, bank.counter.writeAuto); energy.bank_energy[energy_offset + b].E_pre_RDA += E_pre(VXX, IBeta, IXX2N, t_RP, bank.counter.readAuto); energy.bank_energy[energy_offset + b].E_pre_WRA += E_pre(VXX, IBeta, IXX2N, t_RP, bank.counter.writeAuto); energy.bank_energy[energy_offset + b].E_ref_AB += E_ref_ab(B, VXX, IXX5X, IXX3N, t_RFC, bank.counter.refAllBank); energy.bank_energy[energy_offset + b].E_ref_SB += E_ref_sb(VXX, IXX5C, I_BG, t_RFCsb, BG, bank.counter.refSameBank); } } energy.E_sref += VXX * IXX6N * stats.rank_total[i].cycles.selfRefresh * t_CK * m_memSpec.numberOfDevices; energy.E_PDNA += VXX * IXX3P * stats.rank_total[i].cycles.powerDownAct * t_CK * m_memSpec.numberOfDevices; energy.E_PDNP += VXX * IXX2P * stats.rank_total[i].cycles.powerDownPre * t_CK * m_memSpec.numberOfDevices; energy.E_bg_act_shared += E_BG_act_shared(VXX, I_rho, stats.rank_total[i].cycles.act * t_CK) * m_memSpec.numberOfDevices; } } return energy; } } ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr5/core_calculation_DDR5.h ================================================ #ifndef DRAMPOWER_STANDARDS_DDR5_CALCULATION_DDR5_H #define DRAMPOWER_STANDARDS_DDR5_CALCULATION_DDR5_H #include "DRAMPower/data/stats.h" #include "DRAMPower/memspec/MemSpecDDR5.h" #include #include #include #include namespace DRAMPower { class DDR5; class Calculation_DDR5 { public: Calculation_DDR5(const MemSpecDDR5 &memSpec); private: inline double E_act(double VDD, double I_theta, double I_1, double t_RAS, uint64_t N_act) const; inline double E_pre(double VDD, double IBeta, double IDD2_N, double t_RP, uint64_t N_pre) const; inline double E_BG_pre(std::size_t B, double VDD, double IDD2_N, double T_BG_pre) const; inline double E_BG_act_star(std::size_t B, double VDD, double IDD3_N, double I_rho, double T_BG_act_star) const; inline double E_BG_act_shared(double VDD, double I_rho, double T_BG_act) const; inline double E_RD(double VDD, double IDD4_R, double IDD3_N, double t_CK, std::size_t BL, std::size_t DR, uint64_t N_RD) const; inline double E_WR(double VDD, double IDD4_W, double IDD3_N, double t_CK, std::size_t BL, std::size_t DR, uint64_t N_WR) const; inline double E_ref_ab(std::size_t B, double VDD, double IDD5B, double IDD3_N, double tRFC, uint64_t N_REF) const; inline double E_ref_sb(double VDD, double IDD5C, double I_BG, double tRFC, std::size_t BG, uint64_t N_SB_REF) const; public: energy_t calcEnergy(const SimulationStats &stats) const; private: const MemSpecDDR5 &m_memSpec; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_DDR5_CALCULATION_DDR5_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr5/interface_calculation_DDR5.cpp ================================================ #include "DRAMPower/standards/ddr5/interface_calculation_DDR5.h" #include "DRAMPower/data/energy.h" namespace DRAMPower { static double calc_static_energy(uint64_t NxBits, const double R_eq, const double t_CK, const double voltage) { return NxBits * (voltage * voltage) * t_CK / R_eq; }; static double calc_dynamic_energy(const uint64_t transitions, const double energy) { return transitions * energy; }; static double calcStaticTermination(const bool termination, const DRAMPower::util::bus_stats_t &stats, const double R_eq, const double t_CK, const uint64_t datarate, const double voltage) { if (termination == false) { return 0; // No static termination } // zeroes return calc_static_energy(stats.zeroes, R_eq, t_CK / datarate, voltage); } InterfaceCalculation_DDR5::InterfaceCalculation_DDR5(const MemSpecDDR5 &memspec) : memspec_(memspec), impedances_(memspec_.memImpedanceSpec) { t_CK_ = memspec_.memTimingSpec.tCK; VDDQ_ = memspec_.vddq; } interface_energy_info_t InterfaceCalculation_DDR5::calculateEnergy(const SimulationStats &stats) const { interface_energy_info_t clock_energy = calcClockEnergy(stats); interface_energy_info_t DQS_energy = calcDQSEnergy(stats); interface_energy_info_t DQ_energy = calcDQEnergy(stats); DQ_energy += calcDQEnergyTogglingRate(stats.togglingStats); interface_energy_info_t CA_energy = calcCAEnergy(stats); interface_energy_info_t result; result += clock_energy; result += DQS_energy; result += CA_energy; result += DQ_energy; return result; } interface_energy_info_t InterfaceCalculation_DDR5::calcClockEnergy(const SimulationStats &stats) const { interface_energy_info_t result; result.controller.staticEnergy = calcStaticTermination(impedances_.ck_termination, stats.clockStats, impedances_.ck_R_eq, t_CK_, 2, VDDQ_); // datarate 2 -> half the time low other half high result.controller.dynamicEnergy = calc_dynamic_energy(stats.clockStats.zeroes_to_ones, impedances_.ck_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_DDR5::calcDQSEnergy(const SimulationStats &stats) const { interface_energy_info_t result; result.dram.staticEnergy += calcStaticTermination(impedances_.rdqs_termination, stats.readDQSStats, impedances_.rdqs_R_eq, t_CK_, memspec_.dataRateSpec.dqsBusRate, VDDQ_); result.controller.staticEnergy += calcStaticTermination(impedances_.wdqs_termination, stats.writeDQSStats, impedances_.wdqs_R_eq, t_CK_, memspec_.dataRateSpec.dqsBusRate, VDDQ_); result.dram.dynamicEnergy += calc_dynamic_energy(stats.readDQSStats.zeroes_to_ones, impedances_.rdqs_dyn_E); result.controller.dynamicEnergy += calc_dynamic_energy(stats.writeDQSStats.zeroes_to_ones, impedances_.wdqs_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_DDR5::calcDQEnergyTogglingRate(const TogglingStats &stats) const { interface_energy_info_t result; // Read result.dram.staticEnergy += calcStaticTermination(impedances_.rdq_R_eq, stats.read, impedances_.rdq_R_eq, t_CK_, memspec_.dataRate, VDDQ_); result.dram.dynamicEnergy += calc_dynamic_energy(stats.read.zeroes_to_ones, impedances_.rdq_dyn_E); // Write result.controller.staticEnergy += calcStaticTermination(impedances_.wdq_R_eq, stats.write, impedances_.wdq_R_eq, t_CK_, memspec_.dataRate, VDDQ_); result.controller.dynamicEnergy += calc_dynamic_energy(stats.write.zeroes_to_ones, impedances_.wdq_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_DDR5::calcDQEnergy(const SimulationStats &stats) const { interface_energy_info_t result; // Read result.dram.staticEnergy += calcStaticTermination(impedances_.rdq_termination, stats.readBus, impedances_.rdq_R_eq, t_CK_, memspec_.dataRate , VDDQ_); result.dram.dynamicEnergy += calc_dynamic_energy(stats.readBus.zeroes_to_ones, impedances_.rdq_dyn_E); // Write result.controller.staticEnergy += calcStaticTermination(impedances_.wdq_termination, stats.writeBus, impedances_.wdq_R_eq, t_CK_, memspec_.dataRate , VDDQ_); result.controller.dynamicEnergy += calc_dynamic_energy(stats.writeBus.zeroes_to_ones, impedances_.wdq_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_DDR5::calcCAEnergy(const SimulationStats &stats) const { interface_energy_info_t result; result.controller.staticEnergy = calcStaticTermination(impedances_.ca_termination, stats.commandBus, impedances_.ca_R_eq, t_CK_, 1, VDDQ_); result.controller.dynamicEnergy = calc_dynamic_energy(stats.commandBus.zeroes_to_ones, impedances_.ca_dyn_E); return result; } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr5/interface_calculation_DDR5.h ================================================ #ifndef DRAMPOWER_STANDARDS_DDR5_INTERFACE_CALCULATION_DDR5_H #define DRAMPOWER_STANDARDS_DDR5_INTERFACE_CALCULATION_DDR5_H #include "DRAMPower/Types.h" #include "DRAMPower/data/energy.h" #include "DRAMPower/memspec/MemSpecDDR5.h" #include "DRAMPower/data/stats.h" namespace DRAMPower { class InterfaceCalculation_DDR5 { public: InterfaceCalculation_DDR5(const MemSpecDDR5 &memspec); interface_energy_info_t calculateEnergy(const SimulationStats &stats) const; private: const MemSpecDDR5 &memspec_; const MemSpecDDR5::MemImpedanceSpec &impedances_; double t_CK_; double VDDQ_; interface_energy_info_t calcClockEnergy(const SimulationStats &stats) const; interface_energy_info_t calcDQSEnergy(const SimulationStats &stats) const; interface_energy_info_t calcDQEnergy(const SimulationStats &stats) const; interface_energy_info_t calcCAEnergy(const SimulationStats &stats) const; interface_energy_info_t calcDQEnergyTogglingRate(const TogglingStats &stats) const; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_DDR5_INTERFACE_CALCULATION_DDR5_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/ddr5/types.h ================================================ #ifndef DRAMPOWER_STANDARDS_DDR5_TYPES_H #define DRAMPOWER_STANDARDS_DDR5_TYPES_H #include #include "DRAMPower/memspec/MemSpecDDR5.h" #include "DRAMPower/standards/ddr5/DDR5Core.h" #include "DRAMPower/standards/ddr5/DDR5Interface.h" #include "DRAMPower/standards/ddr5/core_calculation_DDR5.h" #include "DRAMPower/standards/ddr5/interface_calculation_DDR5.h" namespace DRAMPower { struct DDR5Types { using DRAMUtilsMemSpec_t = DRAMUtils::MemSpec::MemSpecDDR5; using MemSpec_t = MemSpecDDR5; using Core_t = DDR5Core; using Interface_t = DDR5Interface; using CalcCore_t = Calculation_DDR5; using CalcInterface_t = InterfaceCalculation_DDR5; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_DDR5_TYPES_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr4/LPDDR4.cpp ================================================ #include "LPDDR4.h" #include "DRAMPower/data/stats.h" #include #include #include #include namespace DRAMPower { using namespace DRAMUtils::Config; LPDDR4::LPDDR4(const MemSpecLPDDR4 &memSpec, const config::SimConfig &simConfig) : m_memSpec(memSpec) , m_interface(m_memSpec, simConfig) , m_core(m_memSpec) { registerExtensions(); } // Extensions void LPDDR4::registerExtensions() { getExtensionManager().registerExtension([this](const timestamp_t, const bool enable){ // Assumption: the enabling of the DBI does not interleave with previous data on the bus // x8,x16 devices: support dbi m_interface.enableDBI(enable); return true; }, false); } // Getters for CLI util::CLIArchitectureConfig LPDDR4::getCLIArchitectureConfig() { return util::CLIArchitectureConfig{ m_memSpec.numberOfDevices, m_memSpec.numberOfRanks, m_memSpec.numberOfBanks }; } // Calculation energy_t LPDDR4::calcCoreEnergyStats(const SimulationStats& stats) const { Calculation_LPDDR4 calculation(m_memSpec); return calculation.calcEnergy(stats); } interface_energy_info_t LPDDR4::calcInterfaceEnergyStats(const SimulationStats& stats) const { InterfaceCalculation_LPDDR4 interface_calc(m_memSpec); return interface_calc.calculateEnergy(stats); } // Stats SimulationStats LPDDR4::getWindowStats(timestamp_t timestamp) { SimulationStats stats; m_core.getWindowStats(timestamp, stats); m_interface.getWindowStats(timestamp, stats); return stats; } // Serialization void LPDDR4::serialize_impl(std::ostream& stream) const { m_core.serialize(stream); m_interface.serialize(stream); } void LPDDR4::deserialize_impl(std::istream& stream) { m_core.deserialize(stream); m_interface.deserialize(stream); } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr4/LPDDR4.h ================================================ #ifndef DRAMPOWER_STANDARDS_LPDDR4_LPDDR4_H #define DRAMPOWER_STANDARDS_LPDDR4_LPDDR4_H #include "DRAMPower/util/cli_architecture_config.h" #include #include #include #include #include #include #include #include #include "DRAMPower/simconfig/simconfig.h" #include namespace DRAMPower { class LPDDR4 : public dram_base{ // public constructors and assignment operators public: LPDDR4() = delete; // No default constructor LPDDR4(const LPDDR4&) = default; // copy constructor LPDDR4& operator=(const LPDDR4&) = default; // copy assignment operator LPDDR4(LPDDR4&&) = default; // move constructor LPDDR4& operator=(LPDDR4&&) = default; // move assignment operator ~LPDDR4() override = default; LPDDR4(const MemSpecLPDDR4& memSpec, const config::SimConfig &simConfig = {}); // Public member functions public: // Member functions LPDDR4Core& getCore() { return m_core; } const LPDDR4Core& getCore() const { return m_core; } LPDDR4Interface& getInterface() { return m_interface; } const LPDDR4Interface& getInterface() const { return m_interface; } // Overrides energy_t calcCoreEnergyStats(const SimulationStats& stats) const override; interface_energy_info_t calcInterfaceEnergyStats(const SimulationStats& stats) const override; SimulationStats getWindowStats(timestamp_t timestamp) override; util::CLIArchitectureConfig getCLIArchitectureConfig() override; bool isSerializable() const override { return m_core.isSerializable(); } // Private member functions private: // Member functions void registerExtensions(); // Overrides void doCoreCommandImpl(const Command& command) override { m_core.doCommand(command); } void doInterfaceCommandImpl(const Command& command) override { m_interface.doCommand(command); } timestamp_t getLastCommandTime_impl() const override { return std::max(m_core.getLastCommandTime(), m_interface.getLastCommandTime()); } void serialize_impl(std::ostream& stream) const override; void deserialize_impl(std::istream& stream) override; // Private member variables private: MemSpecLPDDR4 m_memSpec; LPDDR4Interface m_interface; LPDDR4Core m_core; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_LPDDR4_LPDDR4_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr4/LPDDR4Core.cpp ================================================ #include "LPDDR4Core.h" #include "DRAMPower/util/RegisterHelper.h" namespace DRAMPower { void LPDDR4Core::doCommand(const Command& cmd) { m_implicitCommandHandler.processImplicitCommandQueue(*this, cmd.timestamp, m_last_command_time); m_last_command_time = std::max(cmd.timestamp, m_last_command_time); switch(cmd.type) { case CmdType::ACT: util::coreHelpers::bankHandler(cmd, m_ranks, this, &LPDDR4Core::handleAct); break; case CmdType::PRE: util::coreHelpers::bankHandler(cmd, m_ranks, this, &LPDDR4Core::handlePre); break; case CmdType::PREA: util::coreHelpers::rankHandler(cmd, m_ranks, this, &LPDDR4Core::handlePreAll); break; case CmdType::REFB: util::coreHelpers::bankHandlerIdx(cmd, m_ranks, this, &LPDDR4Core::handleRefPerBank); break; case CmdType::RD: util::coreHelpers::bankHandler(cmd, m_ranks, this, &LPDDR4Core::handleRead); break; case CmdType::RDA: util::coreHelpers::bankHandlerIdx(cmd, m_ranks, this, &LPDDR4Core::handleReadAuto); break; case CmdType::WR: util::coreHelpers::bankHandler(cmd, m_ranks, this, &LPDDR4Core::handleWrite); break; case CmdType::WRA: util::coreHelpers::bankHandlerIdx(cmd, m_ranks, this, &LPDDR4Core::handleWriteAuto); break; case CmdType::REFA: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &LPDDR4Core::handleRefAll); break; case CmdType::PDEA: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &LPDDR4Core::handlePowerDownActEntry); break; case CmdType::PDXA: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &LPDDR4Core::handlePowerDownActExit); break; case CmdType::PDEP: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &LPDDR4Core::handlePowerDownPreEntry); break; case CmdType::PDXP: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &LPDDR4Core::handlePowerDownPreExit); break; case CmdType::SREFEN: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &LPDDR4Core::handleSelfRefreshEntry); break; case CmdType::SREFEX: util::coreHelpers::rankHandler(cmd, m_ranks, this, &LPDDR4Core::handleSelfRefreshExit); break; case CmdType::END_OF_SIMULATION: break; default: assert(false && "Unsupported command"); break; } } timestamp_t LPDDR4Core::getLastCommandTime() const { return m_last_command_time; } bool LPDDR4Core::isSerializable() const { return 0 == m_implicitCommandHandler.implicitCommandCount(); } void LPDDR4Core::handleAct(Rank &rank, Bank &bank, timestamp_t timestamp) { bank.counter.act++; bank.cycles.act.start_interval(timestamp); if (!rank.isActive(timestamp)) { rank.cycles.act.start_interval(timestamp); } bank.bankState = Bank::BankState::BANK_ACTIVE; } void LPDDR4Core::handlePre(Rank &rank, Bank &bank, timestamp_t timestamp) { if (bank.bankState == Bank::BankState::BANK_PRECHARGED) return; bank.counter.pre++; bank.cycles.act.close_interval(timestamp); bank.latestPre = timestamp; bank.bankState = Bank::BankState::BANK_PRECHARGED; if (!rank.isActive(timestamp)) { rank.cycles.act.close_interval(timestamp); } } void LPDDR4Core::handlePreAll(Rank &rank, timestamp_t timestamp) { for (auto &bank: rank.banks) { handlePre(rank, bank, timestamp); } } void LPDDR4Core::handleRefreshOnBank(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp, uint64_t timing, uint64_t &counter) { ++counter; auto& rank = m_ranks[rank_idx]; auto& bank = rank.banks[bank_idx]; if (!rank.isActive(timestamp)) { rank.cycles.act.start_interval(timestamp); } bank.bankState = Bank::BankState::BANK_ACTIVE; auto timestamp_end = timestamp + timing; bank.refreshEndTime = timestamp_end; if (!bank.cycles.act.is_open()) bank.cycles.act.start_interval(timestamp); // Execute implicit pre-charge at refresh end m_implicitCommandHandler.addImplicitCommand(timestamp_end, [rank_idx, bank_idx, timestamp_end](LPDDR4Core& self) { auto& rank = self.m_ranks[rank_idx]; auto& bank = rank.banks[bank_idx]; bank.bankState = Bank::BankState::BANK_PRECHARGED; bank.cycles.act.close_interval(timestamp_end); if (!rank.isActive(timestamp_end)) { rank.cycles.act.close_interval(timestamp_end); } }); } void LPDDR4Core::handleRefAll(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; for (std::size_t bank_idx = 0; bank_idx < rank.banks.size(); ++bank_idx) { auto& counter = rank.banks[bank_idx].counter.refAllBank; handleRefreshOnBank(rank_idx, bank_idx, timestamp, m_memSpec.tRFC, counter); } rank.endRefreshTime = timestamp + m_memSpec.tRFC; } void LPDDR4Core::handleRefPerBank(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp) { auto& counter = m_ranks[rank_idx].banks[bank_idx].counter.refPerBank; handleRefreshOnBank(rank_idx, bank_idx, timestamp, m_memSpec.tRFCPB, counter); } void LPDDR4Core::handleSelfRefreshEntry(std::size_t rank_idx, timestamp_t timestamp) { // Issue implicit refresh handleRefAll(rank_idx, timestamp); // Handle self-refresh entry after tRFC auto timestampSelfRefreshStart = timestamp + m_memSpec.tRFC; m_implicitCommandHandler.addImplicitCommand(timestampSelfRefreshStart, [rank_idx, timestampSelfRefreshStart](LPDDR4Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.counter.selfRefresh++; rank.cycles.sref.start_interval(timestampSelfRefreshStart); rank.memState = MemState::SREF; }); } void LPDDR4Core::handleSelfRefreshExit(Rank &rank, timestamp_t timestamp) { assert(rank.memState == MemState::SREF); rank.cycles.sref.close_interval(timestamp); // Duration start between entry and exit rank.memState = MemState::NOT_IN_PD; } void LPDDR4Core::handlePowerDownActEntry(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; auto earliestPossibleEntry = this->earliestPossiblePowerDownEntryTime(rank); auto entryTime = std::max(timestamp, earliestPossibleEntry); m_implicitCommandHandler.addImplicitCommand(entryTime, [rank_idx, entryTime](LPDDR4Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.cycles.powerDownAct.start_interval(entryTime); rank.memState = MemState::PDN_ACT; if (rank.cycles.act.is_open()) { rank.cycles.act.close_interval(entryTime); } for (auto &bank: rank.banks) { if (bank.cycles.act.is_open()) { bank.cycles.act.close_interval(entryTime); } } }); } void LPDDR4Core::handlePowerDownActExit(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; auto earliestPossibleExit = this->earliestPossiblePowerDownEntryTime(rank); auto exitTime = std::max(timestamp, earliestPossibleExit); m_implicitCommandHandler.addImplicitCommand(exitTime, [rank_idx, exitTime](LPDDR4Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.memState = MemState::NOT_IN_PD; rank.cycles.powerDownAct.close_interval(exitTime); bool rank_active = false; for (auto & bank : rank.banks) { if (bank.counter.act != 0 && bank.cycles.act.get_end() == rank.cycles.powerDownAct.get_start()) { rank_active = true; bank.cycles.act.start_interval(exitTime); } } if (rank_active) { rank.cycles.act.start_interval(exitTime); } }); }; void LPDDR4Core::handlePowerDownPreEntry(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; auto earliestPossibleEntry = this->earliestPossiblePowerDownEntryTime(rank); auto entryTime = std::max(timestamp, earliestPossibleEntry); m_implicitCommandHandler.addImplicitCommand(entryTime, [rank_idx, entryTime](LPDDR4Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.cycles.powerDownPre.start_interval(entryTime); rank.memState = MemState::PDN_PRE; }); } void LPDDR4Core::handlePowerDownPreExit(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; auto earliestPossibleExit = this->earliestPossiblePowerDownEntryTime(rank); auto exitTime = std::max(timestamp, earliestPossibleExit); m_implicitCommandHandler.addImplicitCommand(exitTime, [rank_idx, exitTime](LPDDR4Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.memState = MemState::NOT_IN_PD; rank.cycles.powerDownPre.close_interval(exitTime); }); } void LPDDR4Core::handleRead(Rank&, Bank &bank, timestamp_t) { ++bank.counter.reads; } void LPDDR4Core::handleWrite(Rank&, Bank &bank, timestamp_t) { ++bank.counter.writes; } void LPDDR4Core::handleReadAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp) { auto& bank = m_ranks[rank_idx].banks[bank_idx]; ++bank.counter.readAuto; auto minBankActiveTime = bank.cycles.act.get_start() + m_memSpec.tRAS; auto minReadActiveTime = timestamp + m_memSpec.prechargeOffsetRD; auto delayed_timestamp = std::max(minBankActiveTime, minReadActiveTime); // Execute PRE after minimum active time m_implicitCommandHandler.addImplicitCommand(delayed_timestamp, [rank_idx, bank_idx, delayed_timestamp](LPDDR4Core& self) { auto& rank = self.m_ranks[rank_idx]; auto& bank = rank.banks[bank_idx]; self.handlePre(rank, bank, delayed_timestamp); }); } void LPDDR4Core::handleWriteAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp) { auto& bank = m_ranks[rank_idx].banks[bank_idx]; ++bank.counter.writeAuto; auto minBankActiveTime = bank.cycles.act.get_start() + m_memSpec.tRAS; auto minWriteActiveTime = timestamp + m_memSpec.prechargeOffsetWR; auto delayed_timestamp = std::max(minBankActiveTime, minWriteActiveTime); // Execute PRE after minimum active time m_implicitCommandHandler.addImplicitCommand(delayed_timestamp, [rank_idx, bank_idx, delayed_timestamp](LPDDR4Core& self) { auto& rank = self.m_ranks[rank_idx]; auto& bank = rank.banks[bank_idx]; self.handlePre(rank, bank, delayed_timestamp); }); } timestamp_t LPDDR4Core::earliestPossiblePowerDownEntryTime(Rank & rank) const { timestamp_t entryTime = 0; for (const auto & bank : rank.banks) { entryTime = std::max({ entryTime, bank.counter.act == 0 ? 0 : bank.cycles.act.get_start() + m_memSpec.tRCD, bank.counter.pre == 0 ? 0 : bank.latestPre + m_memSpec.tRP, bank.refreshEndTime }); } return entryTime; }; void LPDDR4Core::getWindowStats(timestamp_t timestamp, SimulationStats &stats) { m_implicitCommandHandler.processImplicitCommandQueue(*this, timestamp, m_last_command_time); stats.bank.resize(m_memSpec.numberOfBanks * m_memSpec.numberOfRanks); stats.rank_total.resize(m_memSpec.numberOfRanks); auto simulation_duration = timestamp; for (size_t i = 0; i < m_memSpec.numberOfRanks; ++i) { const Rank &rank = m_ranks[i]; size_t bank_offset = i * m_memSpec.numberOfBanks; for (std::size_t j = 0; j < m_memSpec.numberOfBanks; ++j) { stats.bank[bank_offset + j].counter = rank.banks[j].counter; stats.bank[bank_offset + j].cycles.act = rank.banks[j].cycles.act.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.selfRefresh = rank.cycles.sref.get_count_at(timestamp) - rank.cycles.deepSleepMode.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.deepSleepMode = rank.cycles.deepSleepMode.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.powerDownAct = rank.cycles.powerDownAct.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.powerDownPre = rank.cycles.powerDownPre.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.pre = simulation_duration - (stats.bank[bank_offset + j].cycles.act + rank.cycles.powerDownAct.get_count_at(timestamp) + rank.cycles.powerDownPre.get_count_at(timestamp) + rank.cycles.sref.get_count_at(timestamp)); } stats.rank_total[i].cycles.pre = simulation_duration - (rank.cycles.act.get_count_at(timestamp) + rank.cycles.powerDownAct.get_count_at(timestamp) + rank.cycles.powerDownPre.get_count_at(timestamp) + rank.cycles.sref.get_count_at(timestamp)); stats.rank_total[i].cycles.act = rank.cycles.act.get_count_at(timestamp); stats.rank_total[i].cycles.powerDownAct = rank.cycles.powerDownAct.get_count_at(timestamp); stats.rank_total[i].cycles.powerDownPre = rank.cycles.powerDownPre.get_count_at(timestamp); stats.rank_total[i].cycles.selfRefresh = rank.cycles.sref.get_count_at(timestamp); } } void LPDDR4Core::serialize(std::ostream& stream) const { stream.write(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); for (const auto& rank : m_ranks) { rank.serialize(stream); } } void LPDDR4Core::deserialize(std::istream& stream) { stream.read(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); for (auto& rank : m_ranks) { rank.deserialize(stream); } } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr4/LPDDR4Core.h ================================================ #ifndef DRAMPOWER_STANDARDS_LPDDR4_LPDDR4CORE_H #define DRAMPOWER_STANDARDS_LPDDR4_LPDDR4CORE_H #include "DRAMPower/util/Deserialize.h" #include "DRAMPower/util/Serialize.h" #include #include "DRAMPower/dram/Rank.h" #include "DRAMPower/command/Command.h" #include #include #include "DRAMPower/memspec/MemSpecLPDDR4.h" #include namespace DRAMPower { namespace internal { template class TestAccessor; } struct LPDDR4CoreMemSpec { LPDDR4CoreMemSpec(const MemSpecLPDDR4& memSpec) : numberOfBanks(memSpec.numberOfBanks) , numberOfRanks(memSpec.numberOfRanks) , tRFC(memSpec.memTimingSpec.tRFC) , tRFCPB(memSpec.memTimingSpec.tRFCPB) , tRAS(memSpec.memTimingSpec.tRAS) , tRCD(memSpec.memTimingSpec.tRCD) , tRP(memSpec.memTimingSpec.tRP) , prechargeOffsetRD(memSpec.prechargeOffsetRD) , prechargeOffsetWR(memSpec.prechargeOffsetWR) {} uint64_t numberOfBanks; uint64_t numberOfRanks; uint64_t tRFC; uint64_t tRFCPB; uint64_t tRAS; uint64_t tRCD; uint64_t tRP; uint64_t prechargeOffsetRD; uint64_t prechargeOffsetWR; }; class LPDDR4Core : public util::Serialize, public util::Deserialize { // Friend classes friend class internal::TestAccessor; // Public constructors amd assignment operators public: LPDDR4Core(const MemSpecLPDDR4& memSpec) : m_memSpec(memSpec) , m_ranks(memSpec.numberOfRanks, {static_cast(memSpec.numberOfBanks)}) {} // Public member functions public: // Member functions void doCommand(const Command& cmd); timestamp_t getLastCommandTime() const; bool isSerializable() const; void getWindowStats(timestamp_t timestamp, SimulationStats &stats); // Overrides void serialize(std::ostream& stream) const override; void deserialize(std::istream& stream) override; // Private member functions private: void handleAct(Rank & rank, Bank & bank, timestamp_t timestamp); void handlePre(Rank & rank, Bank & bank, timestamp_t timestamp); void handlePreAll(Rank & rank, timestamp_t timestamp); void handleRead(Rank & rank, Bank & bank, timestamp_t timestamp); void handleWrite(Rank & rank, Bank & bank, timestamp_t timestamp); void handleReadAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp); void handleWriteAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp); void handleRefAll(std::size_t rank_idx, timestamp_t timestamp); void handleRefPerBank(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp); void handleRefreshOnBank(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp, uint64_t timing, uint64_t & counter); void handleSelfRefreshEntry(std::size_t rank_idx, timestamp_t timestamp); void handleSelfRefreshExit(Rank & rank, timestamp_t timestamp); void handlePowerDownActEntry(std::size_t rank_idx, timestamp_t timestamp); void handlePowerDownActExit(std::size_t rank_idx, timestamp_t timestamp); void handlePowerDownPreEntry(std::size_t rank_idx, timestamp_t timestamp); void handlePowerDownPreExit(std::size_t rank_idx, timestamp_t timestamp); timestamp_t earliestPossiblePowerDownEntryTime(Rank & rank) const; // Private member variables private: LPDDR4CoreMemSpec m_memSpec; std::vector m_ranks; ImplicitCommandHandler m_implicitCommandHandler; timestamp_t m_last_command_time = 0; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_LPDDR4_LPDDR4CORE_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr4/LPDDR4Interface.cpp ================================================ #include "LPDDR4Interface.h" #include "DRAMPower/command/CmdType.h" #include "DRAMPower/data/stats.h" #include "DRAMPower/util/pin.h" namespace DRAMPower { static constexpr DRAMUtils::Config::ToggleRateDefinition busConfig { 0, 0, 0, 0, DRAMUtils::Config::TogglingRateIdlePattern::L, DRAMUtils::Config::TogglingRateIdlePattern::L }; LPDDR4Interface::LPDDR4Interface(const MemSpecLPDDR4& memSpec, const config::SimConfig& simConfig) : m_memSpec(memSpec) , m_commandBus{6, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L} , m_dataBus{ util::databus_presets::getDataBusPreset( util::DataBusConfig { memSpec.bitWidth * memSpec.numberOfDevices, memSpec.dataRate, simConfig.toggleRateDefinition.value_or(busConfig) }, simConfig.toggleRateDefinition.has_value() ? util::DataBusMode::TogglingRate : util::DataBusMode::Bus, false ) } , m_readDQS(memSpec.dataRate, true) , m_writeDQS(memSpec.dataRate, true) , m_dbi(memSpec.numberOfDevices * memSpec.bitWidth, m_memSpec.burstLength, [this](timestamp_t load_timestamp, timestamp_t, std::size_t pin, bool inversion_state, bool read) { this->handleDBIPinChange(load_timestamp, pin, inversion_state, read); }, false) , m_dbiread(m_dbi.getChunksPerWidth().value(), pin_dbi_t{m_dbi.getIdlePattern(), m_dbi.getIdlePattern()}) , m_dbiwrite(m_dbi.getChunksPerWidth().value(), pin_dbi_t{m_dbi.getIdlePattern(), m_dbi.getIdlePattern()}) , m_patternHandler(PatternEncoderOverrides{ {pattern_descriptor::C0, PatternEncoderBitSpec::L}, {pattern_descriptor::C1, PatternEncoderBitSpec::L}, }) { registerPatterns(); } void LPDDR4Interface::registerPatterns() { using namespace pattern_descriptor; // ACT m_patternHandler.registerPattern({ H, L, R12, R13, R14, R15, BA0, BA1, BA2, R16, R10, R11, R17, R18, R6, R7, R8, R9, R0, R1, R2, R3, R4, R5, }); // PRE m_patternHandler.registerPattern({ L, L, L, L, H, L, BA0, BA1, BA2, V, V, V, }); // PREA m_patternHandler.registerPattern({ L, L, L, L, H, H, V, V, V, V, V, V, }); // REFB m_patternHandler.registerPattern({ L, L, L, H, L, L, BA0, BA1, BA2, V, V, V, }); // RD m_patternHandler.registerPattern({ L, H, L, L, L, BL, BA0, BA1, BA2, V, C9, L, L, H, L, L, H, C8, C2, C3, C4, C5, C6, C7 }); // RDA m_patternHandler.registerPattern({ L, H, L, L, L, BL, BA0, BA1, BA2, V, C9, H, L, H, L, L, H, C8, C2, C3, C4, C5, C6, C7 }); // WR m_patternHandler.registerPattern({ L, L, H, L, L, BL, BA0, BA1, BA2, V, C9, L, L, H, L, L, H, C8, C2, C3, C4, C5, C6, C7, }); // WRA m_patternHandler.registerPattern({ L, L, H, L, L, BL, BA0, BA1, BA2, V, C9, H, L, H, L, L, H, C8, C2, C3, C4, C5, C6, C7, }); // REFA m_patternHandler.registerPattern({ L, L, L, H, L, H, V, V, V, V, V, V, }); // SREFEN m_patternHandler.registerPattern({ L, L, L, H, H, V, V, V, V, V, V, V, }); // SREFEX m_patternHandler.registerPattern({ L, L, H, L, H, V, V, V, V, V, V, V, }); } timestamp_t LPDDR4Interface::getLastCommandTime() const { return m_last_command_time; } void LPDDR4Interface::doCommand(const Command& cmd) { switch(cmd.type) { case CmdType::ACT: case CmdType::PRE: case CmdType::PREA: case CmdType::REFB: case CmdType::REFA: case CmdType::SREFEN: case CmdType::SREFEX: handleCommandBus(cmd); break; case CmdType::RD: case CmdType::RDA: handleData(cmd, true); break; case CmdType::WR: case CmdType::WRA: handleData(cmd, false); break; case CmdType::END_OF_SIMULATION: endOfSimulation(cmd.timestamp); break; case CmdType::PDEA: case CmdType::PDXA: case CmdType::PDEP: case CmdType::PDXP: break; default: assert(false && "Invalid command"); break; } m_last_command_time = cmd.timestamp; } void LPDDR4Interface::handleDBIPinChange(const timestamp_t load_timestamp, std::size_t pin, bool state, bool read) { assert(pin < m_dbiread.size() || pin < m_dbiwrite.size()); if (read) { this->m_dbiread[pin].set(load_timestamp, state ? util::PinState::H : util::PinState::L, 1); } else { this->m_dbiwrite[pin].set(load_timestamp, state ? util::PinState::H : util::PinState::L, 1); } } std::optional LPDDR4Interface::handleDBIInterface(timestamp_t timestamp, std::size_t n_bits, const uint8_t* data, bool read) { if (0 == n_bits || !data || !m_dbi.isEnabled()) { // No DBI or no data to process return std::nullopt; } timestamp_t virtual_time = timestamp * m_memSpec.dataRate; // updateDBI calls the given callback to handle pin changes return m_dbi.updateDBI(virtual_time, n_bits, data, read); } void LPDDR4Interface::handleOverrides(size_t length, bool /*read*/) { // Set command bus pattern overrides switch(length) { case 32: m_patternHandler.getEncoder().settings.updateSettings({ {pattern_descriptor::C4, PatternEncoderBitSpec::L}, {pattern_descriptor::C3, PatternEncoderBitSpec::L}, {pattern_descriptor::C2, PatternEncoderBitSpec::L}, {pattern_descriptor::BL, PatternEncoderBitSpec::H}, }); break; default: // Pull down // No interface power needed for PatternEncoderBitSpec::L // Defaults to burst length 16 case 16: m_patternHandler.getEncoder().settings.removeSetting(pattern_descriptor::C4); m_patternHandler.getEncoder().settings.updateSettings({ {pattern_descriptor::C3, PatternEncoderBitSpec::L}, {pattern_descriptor::C2, PatternEncoderBitSpec::L}, {pattern_descriptor::BL, PatternEncoderBitSpec::L}, }); break; } } void LPDDR4Interface::handleCommandBus(const Command &cmd) { auto pattern = m_patternHandler.getCommandPattern(cmd); auto ca_length = m_patternHandler.getPattern(cmd.type).size() / m_commandBus.get_width(); m_commandBus.load(cmd.timestamp, pattern, ca_length); } void LPDDR4Interface::handleDQs(const Command& cmd, util::Clock &dqs, size_t length) { dqs.start(cmd.timestamp); dqs.stop(cmd.timestamp + length / m_memSpec.dataRate); } void LPDDR4Interface::handleData(const Command &cmd, bool read) { auto loadfunc = read ? &databus_t::loadRead : &databus_t::loadWrite; util::Clock &dqs = read ? m_readDQS : m_writeDQS; size_t length = 0; if (0 == cmd.sz_bits) { // No data provided by command // Use default burst length if (m_dataBus.isTogglingRate()) { length = m_memSpec.burstLength; (m_dataBus.*loadfunc)(cmd.timestamp, length * m_dataBus.getWidth(), nullptr); } } else { std::optional dbi_data = std::nullopt; // Data provided by command if (m_dataBus.isBus() && m_dbi.isEnabled()) { // Only compute dbi for bus mode dbi_data = handleDBIInterface(cmd.timestamp, cmd.sz_bits, cmd.data, read); } length = cmd.sz_bits / (m_dataBus.getWidth()); (m_dataBus.*loadfunc)(cmd.timestamp, cmd.sz_bits, dbi_data.value_or(cmd.data)); } handleDQs(cmd, dqs, length); handleOverrides(length, read); handleCommandBus(cmd); } void LPDDR4Interface::endOfSimulation(timestamp_t timestamp) { m_dbi.dispatchResetCallback(timestamp); } void LPDDR4Interface::getWindowStats(timestamp_t timestamp, SimulationStats &stats) const { stats.commandBus = m_commandBus.get_stats(timestamp); m_dataBus.get_stats(timestamp, stats.readBus, stats.writeBus, stats.togglingStats.read, stats.togglingStats.write ); stats.clockStats = 2 * m_clock.get_stats_at(timestamp); stats.readDQSStats = 2 * m_readDQS.get_stats_at(timestamp); stats.writeDQSStats = 2 * m_writeDQS.get_stats_at(timestamp); for (const auto &dbi_pin : m_dbiread) { stats.readDBI += dbi_pin.get_stats_at(timestamp, 2); } for (const auto &dbi_pin : m_dbiwrite) { stats.writeDBI += dbi_pin.get_stats_at(timestamp, 2); } if (m_memSpec.bitWidth == 16) { stats.readDQSStats *= 2; stats.writeDQSStats *= 2; } } void LPDDR4Interface::serialize(std::ostream& stream) const { stream.write(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); m_patternHandler.serialize(stream); m_commandBus.serialize(stream); m_dataBus.serialize(stream); m_readDQS.serialize(stream); m_writeDQS.serialize(stream); m_clock.serialize(stream); } void LPDDR4Interface::deserialize(std::istream& stream) { stream.read(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); m_patternHandler.deserialize(stream); m_commandBus.deserialize(stream); m_dataBus.deserialize(stream); m_readDQS.deserialize(stream); m_writeDQS.deserialize(stream); m_clock.deserialize(stream); } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr4/LPDDR4Interface.h ================================================ #ifndef DRAMPOWER_STANDARDS_LPDDR4_LPDDR4INTERFACE_H #define DRAMPOWER_STANDARDS_LPDDR4_LPDDR4INTERFACE_H #include "DRAMPower/util/pin.h" #include "DRAMPower/util/bus.h" #include "DRAMPower/util/databus_presets.h" #include "DRAMPower/util/clock.h" #include "DRAMPower/util/Serialize.h" #include "DRAMPower/util/Deserialize.h" #include "DRAMPower/Types.h" #include "DRAMPower/command/Command.h" #include "DRAMPower/data/stats.h" #include "DRAMPower/util/PatternHandler.h" #include "DRAMPower/util/dbi.h" #include "DRAMPower/memspec/MemSpecLPDDR4.h" #include "DRAMPower/simconfig/simconfig.h" #include #include #include namespace DRAMPower { struct LPDDR4InterfaceMemSpec { LPDDR4InterfaceMemSpec(const MemSpecLPDDR4& memSpec) : dataRate(memSpec.dataRate) , burstLength(memSpec.burstLength) , bitWidth(memSpec.bitWidth) {} uint64_t dataRate; uint64_t burstLength; uint64_t bitWidth; }; class LPDDR4Interface : public util::Serialize, public util::Deserialize { // Public constants public: const static std::size_t cmdBusWidth = 6; const static uint64_t cmdBusInitPattern = (1<; using pin_dbi_t = util::Pin<32>; // max_burst_length = 32 using databus_t = util::databus_presets::databus_preset_t; using patternHandler_t = PatternHandler; // Public constructors and assignment operators public: LPDDR4Interface(const MemSpecLPDDR4& memSpec, const config::SimConfig &simConfig = {}); // Public member functions public: // Member functions timestamp_t getLastCommandTime() const; void doCommand(const Command& cmd); void getWindowStats(timestamp_t timestamp, SimulationStats &stats) const; // Overrides void serialize(std::ostream& stream) const override; void deserialize(std::istream& stream) override; // Extensions void enableDBI(bool enable) { m_dbi.enable(enable); } // Private Member functions private: void registerPatterns(); std::optional handleDBIInterface(timestamp_t timestamp, std::size_t n_bits, const uint8_t* data, bool read); void handleDBIPinChange(const timestamp_t load_timestamp, std::size_t pin, bool state, bool read); void handleOverrides(size_t length, bool read); void handleDQs(const Command& cmd, util::Clock &dqs, size_t length); void handleCommandBus(const Command& cmd); void handleData(const Command &cmd, bool read); void endOfSimulation(timestamp_t timestamp); // Public member variables private: LPDDR4InterfaceMemSpec m_memSpec; commandbus_t m_commandBus; databus_t m_dataBus; util::Clock m_readDQS; util::Clock m_writeDQS; util::Clock m_clock; util::DBI m_dbi; std::vector m_dbiread; std::vector m_dbiwrite; patternHandler_t m_patternHandler; timestamp_t m_last_command_time = 0; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_LPDDR4_LPDDR4INTERFACE_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr4/core_calculation_LPDDR4.cpp ================================================ #include "core_calculation_LPDDR4.h" #include namespace DRAMPower { Calculation_LPDDR4::Calculation_LPDDR4(const MemSpecLPDDR4 &memSpec) : m_memSpec(memSpec) {} double Calculation_LPDDR4::E_pre(double VDD, double IBeta, double IDD2N, double t_RP, uint64_t N_pre) const { return VDD * (IBeta - IDD2N) * t_RP * N_pre; } double Calculation_LPDDR4::E_act(double VDD, double I_theta, double IDD3N, double t_RAS, uint64_t N_act) const { return VDD * (I_theta - IDD3N) * t_RAS * N_act; } double Calculation_LPDDR4::E_BG_pre(std::size_t B, double VDD, double IDD2N, double T_BG_pre) const { return (1.0 / B) * VDD * IDD2N * T_BG_pre; } double Calculation_LPDDR4::E_BG_act_star(std::size_t B, double VDD, double IDD3N, double I_p, double T_BG_act_star) const { return VDD * (1.0 / B) * (IDD3N - I_p) * T_BG_act_star; } double Calculation_LPDDR4::E_BG_act_shared(double VDD, double I_p, double T_bg_act) const { return VDD * I_p * T_bg_act; } double Calculation_LPDDR4::E_RD(double VDD, double IDD4_R, double IDD3N, std::size_t BL, std::size_t DR, double t_CK, uint64_t N_RD) const { return VDD * (IDD4_R - IDD3N) * (BL / DR) * t_CK * N_RD; } double Calculation_LPDDR4::E_WR(double VDD, double IDD4_W, double IDD3N, std::size_t BL, std::size_t DR, double t_CK, uint64_t N_WR) const { return VDD * (IDD4_W - IDD3N) * (BL / DR) * t_CK * N_WR; } double Calculation_LPDDR4::E_ref_ab(std::size_t B, double VDD, double IDD5, double approx_IDD3N, double tRFC, uint64_t N_REF) const { return (1.0 / B) * VDD * (IDD5 - approx_IDD3N) * tRFC * N_REF; } double Calculation_LPDDR4::E_ref_pb(double VDD, double IDD5PB_B, double IDD3N, double tRFCPB, uint64_t N_PB_REF) const { return VDD * (IDD5PB_B - IDD3N) * tRFCPB * N_PB_REF; } energy_t Calculation_LPDDR4::calcEnergy(const SimulationStats &stats) const { auto t_CK = m_memSpec.memTimingSpec.tCK; auto t_RAS = m_memSpec.memTimingSpec.tRAS * t_CK; auto t_RP = m_memSpec.memTimingSpec.tRP * t_CK; auto t_RFC = m_memSpec.memTimingSpec.tRFC * t_CK; auto t_RFCPB = m_memSpec.memTimingSpec.tRFCPB * t_CK; auto t_REFI = m_memSpec.memTimingSpec.tREFI * t_CK; auto rho = m_memSpec.bwParams.bwPowerFactRho; auto BL = m_memSpec.burstLength; auto DR = m_memSpec.dataRate; auto B = m_memSpec.numberOfBanks; energy_t energy(m_memSpec.numberOfBanks * m_memSpec.numberOfRanks * m_memSpec.numberOfDevices); for (auto vd : {MemSpecLPDDR4::VoltageDomain::VDD1, MemSpecLPDDR4::VoltageDomain::VDD2}) { auto VDD = m_memSpec.memPowerSpec[vd].vDDX; auto IDD_0 = m_memSpec.memPowerSpec[vd].iDD0X; auto IDD2N = m_memSpec.memPowerSpec[vd].iDD2NX; auto IDD3N = m_memSpec.memPowerSpec[vd].iDD3NX; auto IDD2P = m_memSpec.memPowerSpec[vd].iDD2PX; auto IDD3P = m_memSpec.memPowerSpec[vd].iDD3PX; auto IDD4R = m_memSpec.memPowerSpec[vd].iDD4RX; auto IDD4W = m_memSpec.memPowerSpec[vd].iDD4WX; auto IDD5 = m_memSpec.memPowerSpec[vd].iDD5X; auto IDD5PB = m_memSpec.memPowerSpec[vd].iDD5PBX; auto IDD6 = m_memSpec.memPowerSpec[vd].iDD6X; auto IBeta = m_memSpec.memPowerSpec[vd].iBeta; auto I_rho = rho * (IDD3N - IDD2N) + IDD2N; auto I_theta = (IDD_0 * (t_RP + t_RAS) - IBeta * t_RP) * (1 / t_RAS); auto IDD5PB_B = (IDD5PB * (t_REFI / 8) - IDD2N * ((t_REFI / 8) - t_RFCPB)) * (1.0 / t_RFCPB); auto approx_IDD3N = I_rho + B * (IDD3N - I_rho); size_t energy_offset = 0; size_t bank_offset = 0; for (size_t i = 0; i < m_memSpec.numberOfRanks; ++i) { for (size_t d = 0; d < m_memSpec.numberOfDevices; ++d) { energy_offset = i * m_memSpec.numberOfDevices * m_memSpec.numberOfBanks + d * m_memSpec.numberOfBanks; bank_offset = i * m_memSpec.numberOfBanks; for (std::size_t b = 0; b < m_memSpec.numberOfBanks; ++b) { const auto &bank = stats.bank[bank_offset + b]; energy.bank_energy[energy_offset + b].E_act += E_act(VDD, I_theta, IDD3N, t_RAS, bank.counter.act); energy.bank_energy[energy_offset + b].E_pre += E_pre(VDD, IBeta, IDD2N, t_RP, bank.counter.pre); energy.bank_energy[energy_offset + b].E_bg_act += E_BG_act_star(B, VDD, approx_IDD3N, I_rho, stats.bank[bank_offset + b].cycles.activeTime() * t_CK); energy.bank_energy[energy_offset + b].E_bg_pre += E_BG_pre(B, VDD, IDD2N, stats.rank_total[i].cycles.pre * t_CK); energy.bank_energy[energy_offset + b].E_RD += E_RD(VDD, IDD4R, IDD3N, BL, DR, t_CK, bank.counter.reads); energy.bank_energy[energy_offset + b].E_WR += E_WR(VDD, IDD4W, IDD3N, BL, DR, t_CK, bank.counter.writes); energy.bank_energy[energy_offset + b].E_RDA += E_RD(VDD, IDD4R, IDD3N, BL, DR, t_CK, bank.counter.readAuto); energy.bank_energy[energy_offset + b].E_WRA += E_WR(VDD, IDD4W, IDD3N, BL, DR, t_CK, bank.counter.writeAuto); energy.bank_energy[energy_offset + b].E_pre_RDA += E_pre(VDD, IBeta, IDD2N, t_RP, bank.counter.readAuto); energy.bank_energy[energy_offset + b].E_pre_WRA += E_pre(VDD, IBeta, IDD2N, t_RP, bank.counter.writeAuto); energy.bank_energy[energy_offset + b].E_ref_AB += E_ref_ab(B, VDD, IDD5, approx_IDD3N, t_RFC, bank.counter.refAllBank); energy.bank_energy[energy_offset + b].E_ref_PB += E_ref_pb(VDD, IDD5PB_B, IDD3N, t_RFCPB, bank.counter.refPerBank); } } energy.E_sref += VDD * IDD6 * stats.rank_total[i].cycles.selfRefresh * t_CK * m_memSpec.numberOfDevices; energy.E_PDNA += VDD * IDD3P * stats.rank_total[i].cycles.powerDownAct * t_CK * m_memSpec.numberOfDevices; energy.E_PDNP += VDD * IDD2P * stats.rank_total[i].cycles.powerDownPre * t_CK * m_memSpec.numberOfDevices; energy.E_bg_act_shared += E_BG_act_shared(VDD, I_rho, stats.rank_total[i].cycles.act * t_CK) * m_memSpec.numberOfDevices; } } return energy; } } ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr4/core_calculation_LPDDR4.h ================================================ #ifndef DRAMPOWER_STANDARDS_LPDDR4_CALCULATION_LPDDR4_H #define DRAMPOWER_STANDARDS_LPDDR4_CALCULATION_LPDDR4_H #include "DRAMPower/data/stats.h" #pragma once #include #include #include #include #include namespace DRAMPower { class LPDDR4; class Calculation_LPDDR4 { public: Calculation_LPDDR4(const MemSpecLPDDR4 &memSpec); private: double E_act(double VDD, double I_theta, double IDD3N, double tRAS, uint64_t N_act) const; double E_pre(double VDD, double IBeta, double IDD2N, double tRP, uint64_t N_pre) const; double E_BG_pre(std::size_t B, double VDD, double IDD2N, double T_BG_pre) const; double E_BG_act_star(std::size_t B, double VDD, double IDD3N, double I_rho, double T_BG_act_star) const; double E_BG_act_shared(double VDD, double I_rho, double T_bg_act) const; double E_RD(double VDD, double IDD4R, double IDD3N, std::size_t BL, std::size_t DR, double t_CK, uint64_t N_RD) const; double E_WR(double VDD, double IDD4W, double IDD3N, std::size_t BL, std::size_t DR, double t_CK, uint64_t N_WR) const; double E_ref_ab(std::size_t B, double VDD, double IDD5B, double approx_IDD3N, double tRFC, uint64_t N_REF) const; double E_ref_pb(double VDD, double IDD5PB_B, double IDD3N, double tRFCPB, uint64_t N_PB_REF) const; public: energy_t calcEnergy(const SimulationStats &stats) const; private: const MemSpecLPDDR4 &m_memSpec; }; }; #endif /* DRAMPOWER_STANDARDS_LPDDR4_CALCULATION_LPDDR4_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr4/interface_calculation_LPDDR4.cpp ================================================ #include "interface_calculation_LPDDR4.h" namespace DRAMPower { InterfaceCalculation_LPDDR4::InterfaceCalculation_LPDDR4(const MemSpecLPDDR4 & memspec) : memspec_(memspec) , impedances_(memspec.memImpedanceSpec) , t_CK(memspec.memTimingSpec.tCK) , VDDQ(memspec.vddq) {} static double calc_static_energy(const uint64_t NxBits, const double R_eq, const double t_CK, const double voltage) { return NxBits * ((voltage * voltage) / R_eq) * t_CK; // N * P * t = N * E } static double calc_dynamic_energy(const uint64_t NxBits, const double energy) { return NxBits * energy; } static double calcStaticTermination(const bool termination, const DRAMPower::util::bus_stats_t &stats, const double R_eq, const double t_CK, const uint64_t datarate, const double voltage) { if (termination == false) { return 0; // No static termination } // ones return calc_static_energy(stats.ones, R_eq, t_CK / datarate, voltage); } interface_energy_info_t InterfaceCalculation_LPDDR4::calcClockEnergy(const SimulationStats &stats) const { interface_energy_info_t result; result.controller.staticEnergy = calcStaticTermination(impedances_.ck_termination, stats.clockStats, impedances_.ck_R_eq, t_CK, 2, VDDQ); // datarate 2 -> half the time low other half high result.controller.dynamicEnergy = calc_dynamic_energy(stats.clockStats.zeroes_to_ones, impedances_.ck_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_LPDDR4::calcDQSEnergy(const SimulationStats & stats) const { // Datarate of data bus interface_energy_info_t result; // Write result.controller.staticEnergy += calcStaticTermination(impedances_.wdqs_termination, stats.writeDQSStats, impedances_.wdqs_R_eq, t_CK, memspec_.dataRate, VDDQ); result.controller.dynamicEnergy += calc_dynamic_energy(stats.writeDQSStats.zeroes_to_ones, impedances_.wdqs_dyn_E); // Read result.dram.staticEnergy += calcStaticTermination(impedances_.rdqs_termination, stats.readDQSStats, impedances_.rdqs_R_eq, t_CK, memspec_.dataRate, VDDQ); result.dram.dynamicEnergy += calc_dynamic_energy(stats.readDQSStats.zeroes_to_ones, impedances_.rdqs_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_LPDDR4::calcCAEnergy(const SimulationStats& bus_stats) const { interface_energy_info_t result; result.controller.staticEnergy = calcStaticTermination(impedances_.ca_termination, bus_stats.commandBus, impedances_.ca_R_eq, t_CK, 1, VDDQ); result.controller.dynamicEnergy = calc_dynamic_energy(bus_stats.commandBus.zeroes_to_ones, impedances_.ca_dyn_E);; return result; } interface_energy_info_t InterfaceCalculation_LPDDR4::calcDQEnergy(const SimulationStats& bus_stats) const { interface_energy_info_t result; // Write result.controller.staticEnergy += calcStaticTermination(impedances_.wdq_termination, bus_stats.writeBus, impedances_.wdq_R_eq, t_CK, memspec_.dataRate, VDDQ); result.controller.dynamicEnergy += calc_dynamic_energy(bus_stats.writeBus.zeroes_to_ones, impedances_.wdq_dyn_E); // Read result.dram.staticEnergy += calcStaticTermination(impedances_.rdq_termination, bus_stats.readBus, impedances_.rdq_R_eq, t_CK, memspec_.dataRate, VDDQ); result.dram.dynamicEnergy += calc_dynamic_energy(bus_stats.readBus.zeroes_to_ones, impedances_.rdq_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_LPDDR4::calcDQEnergyTogglingRate(const TogglingStats &stats) const { interface_energy_info_t result; // Write result.controller.staticEnergy += calcStaticTermination(impedances_.wdq_termination, stats.write, impedances_.wdq_R_eq, t_CK, memspec_.dataRate, VDDQ); result.controller.dynamicEnergy += calc_dynamic_energy(stats.write.zeroes_to_ones, impedances_.wdq_dyn_E); // Read result.dram.staticEnergy += calcStaticTermination(impedances_.rdq_termination, stats.read, impedances_.rdq_R_eq, t_CK, memspec_.dataRate, VDDQ); result.dram.dynamicEnergy += calc_dynamic_energy(stats.read.zeroes_to_ones, impedances_.rdq_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_LPDDR4::calculateEnergy(const SimulationStats& stats) const { interface_energy_info_t result; result += calcClockEnergy(stats); result += calcDQSEnergy(stats); result += calcDQEnergyTogglingRate(stats.togglingStats); result += calcDQEnergy(stats); result += calcCAEnergy(stats); result += calcDBIEnergy(stats); return result; } interface_energy_info_t InterfaceCalculation_LPDDR4::calcDBIEnergy(const SimulationStats &stats) const { interface_energy_info_t result; // Read result.dram.staticEnergy += calcStaticTermination(impedances_.rdbi_termination, stats.readDBI, impedances_.rdbi_R_eq, t_CK, memspec_.dataRate, VDDQ); result.dram.dynamicEnergy += calc_dynamic_energy(stats.readDBI.zeroes_to_ones, impedances_.rdbi_dyn_E); // Write result.controller.staticEnergy += calcStaticTermination(impedances_.wdbi_termination, stats.writeDBI, impedances_.wdbi_R_eq, t_CK, memspec_.dataRate, VDDQ); result.controller.dynamicEnergy += calc_dynamic_energy(stats.writeDBI.zeroes_to_ones, impedances_.wdbi_dyn_E); return result; } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr4/interface_calculation_LPDDR4.h ================================================ #ifndef DRAMPOWER_STANDARDS_LPDDR4_INTERFACE_CALCULATION_LPDDR4_H #define DRAMPOWER_STANDARDS_LPDDR4_INTERFACE_CALCULATION_LPDDR4_H #pragma once #include #include #include #include namespace DRAMPower { class DRAM; class InterfaceCalculation_LPDDR4 { private: const MemSpecLPDDR4 &memspec_; const MemSpecLPDDR4::MemImpedanceSpec &impedances_; double t_CK; double VDDQ; public: InterfaceCalculation_LPDDR4(const MemSpecLPDDR4 & memspec); interface_energy_info_t calculateEnergy(const SimulationStats& stats) const; private: interface_energy_info_t calcClockEnergy(const SimulationStats &stats) const; interface_energy_info_t calcDQSEnergy(const SimulationStats & stats) const; interface_energy_info_t calcCAEnergy(const SimulationStats& bus_stats) const; interface_energy_info_t calcDQEnergy(const SimulationStats& bus_stats) const; interface_energy_info_t calcDQEnergyTogglingRate(const TogglingStats &stats) const; interface_energy_info_t calcDBIEnergy(const SimulationStats &stats) const; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_LPDDR4_INTERFACE_CALCULATION_LPDDR4_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr4/types.h ================================================ #ifndef DRAMPOWER_STANDARDS_LPDDR4_TYPES_H #define DRAMPOWER_STANDARDS_LPDDR4_TYPES_H #include #include "DRAMPower/memspec/MemSpecLPDDR4.h" #include "DRAMPower/standards/lpddr4/LPDDR4Core.h" #include "DRAMPower/standards/lpddr4/LPDDR4Interface.h" #include "DRAMPower/standards/lpddr4/core_calculation_LPDDR4.h" #include "DRAMPower/standards/lpddr4/interface_calculation_LPDDR4.h" namespace DRAMPower { struct LPDDR4Types { using DRAMUtilsMemSpec_t = DRAMUtils::MemSpec::MemSpecLPDDR4; using MemSpec_t = MemSpecLPDDR4; using Core_t = LPDDR4Core; using Interface_t = LPDDR4Interface; using CalcCore_t = Calculation_LPDDR4; using CalcInterface_t = InterfaceCalculation_LPDDR4; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_LPDDR4_TYPES_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr5/LPDDR5.cpp ================================================ #include "LPDDR5.h" #include "DRAMPower/Types.h" #include "DRAMPower/data/stats.h" #include #include #include #include #include #include namespace DRAMPower { LPDDR5::LPDDR5(const MemSpecLPDDR5 &memSpec, const config::SimConfig& simConfig) : m_memSpec(memSpec) , m_interface(m_memSpec, simConfig) , m_core(m_memSpec) { registerExtensions(); } // Extensions void LPDDR5::registerExtensions() { getExtensionManager().registerExtension([this](const timestamp_t, const bool enable){ // Assumption: the enabling of the DBI does not interleave with previous data on the bus m_interface.enableDBI(enable); return true; }, false); } // Getters for CLI util::CLIArchitectureConfig LPDDR5::getCLIArchitectureConfig() { return util::CLIArchitectureConfig{ m_memSpec.numberOfDevices, m_memSpec.numberOfRanks, m_memSpec.numberOfBanks }; } // Calculation energy_t LPDDR5::calcCoreEnergyStats(const SimulationStats& stats) const { Calculation_LPDDR5 calculation(m_memSpec); return calculation.calcEnergy(stats); } interface_energy_info_t LPDDR5::calcInterfaceEnergyStats(const SimulationStats& stats) const { InterfaceCalculation_LPDDR5 calculation(m_memSpec); return calculation.calculateEnergy(stats); } // Stats SimulationStats LPDDR5::getWindowStats(timestamp_t timestamp) { SimulationStats stats; m_core.getWindowStats(timestamp, stats); m_interface.getWindowStats(timestamp, stats); return stats; } // Serialization void LPDDR5::serialize_impl(std::ostream& stream) const { m_core.serialize(stream); m_interface.serialize(stream); } void LPDDR5::deserialize_impl(std::istream& stream) { m_core.deserialize(stream); m_interface.deserialize(stream); } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr5/LPDDR5.h ================================================ #ifndef DRAMPOWER_STANDARDS_LPDDR5_LPDDR5_H #define DRAMPOWER_STANDARDS_LPDDR5_LPDDR5_H #include "DRAMPower/util/cli_architecture_config.h" #include #include #include #include #include #include #include #include #include "DRAMPower/simconfig/simconfig.h" #include namespace DRAMPower { class LPDDR5 : public dram_base { // Public constructors and assignment operators public: LPDDR5() = delete; // No default constructor LPDDR5(const LPDDR5&) = default; // copy constructor LPDDR5& operator=(const LPDDR5&) = default; // copy assignment operator LPDDR5(LPDDR5&&) = default; // move constructor LPDDR5& operator=(LPDDR5&&) = default; // move assignment operator ~LPDDR5() override = default; LPDDR5(const MemSpecLPDDR5& memSpec, const config::SimConfig& toggleRate = {}); // Public member functions public: // Member functions LPDDR5Core& getCore() { return m_core; } const LPDDR5Core& getCore() const { return m_core; } LPDDR5Interface& getInterface() { return m_interface; } const LPDDR5Interface& getInterface() const { return m_interface; } // Overrides energy_t calcCoreEnergyStats(const SimulationStats& stats) const override; interface_energy_info_t calcInterfaceEnergyStats(const SimulationStats& stats) const override; SimulationStats getWindowStats(timestamp_t timestamp) override; util::CLIArchitectureConfig getCLIArchitectureConfig() override; bool isSerializable() const override { return m_core.isSerializable(); } // Private member functions private: // Member functions void registerExtensions(); // Overrides void doCoreCommandImpl(const Command& command) override { m_core.doCommand(command); } void doInterfaceCommandImpl(const Command& command) override { m_interface.doCommand(command); } timestamp_t getLastCommandTime_impl() const override { return std::max(m_core.getLastCommandTime(), m_interface.getLastCommandTime()); } void serialize_impl(std::ostream& stream) const override; void deserialize_impl(std::istream& stream) override; // Private member variables private: MemSpecLPDDR5 m_memSpec; LPDDR5Interface m_interface; LPDDR5Core m_core; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_LPDDR5_LPDDR5_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr5/LPDDR5Core.cpp ================================================ #include "LPDDR5Core.h" #include "DRAMPower/Exceptions.h" #include "DRAMPower/util/RegisterHelper.h" namespace DRAMPower { void LPDDR5Core::doCommand(const Command& cmd) { m_implicitCommandHandler.processImplicitCommandQueue(*this, cmd.timestamp, m_last_command_time); m_last_command_time = std::max(cmd.timestamp, m_last_command_time); switch(cmd.type) { case CmdType::ACT: util::coreHelpers::bankHandler(cmd, m_ranks, this, &LPDDR5Core::handleAct); break; case CmdType::PRE: util::coreHelpers::bankHandler(cmd, m_ranks, this, &LPDDR5Core::handlePre); break; case CmdType::PREA: util::coreHelpers::rankHandler(cmd, m_ranks, this, &LPDDR5Core::handlePreAll); break; case CmdType::REFB: util::coreHelpers::bankHandlerIdx(cmd, m_ranks, this, &LPDDR5Core::handleRefPerBank); break; case CmdType::RD: util::coreHelpers::bankHandler(cmd, m_ranks, this, &LPDDR5Core::handleRead); break; case CmdType::RDA: util::coreHelpers::bankHandlerIdx(cmd, m_ranks, this, &LPDDR5Core::handleReadAuto); break; case CmdType::WR: util::coreHelpers::bankHandler(cmd, m_ranks, this, &LPDDR5Core::handleWrite); break; case CmdType::WRA: util::coreHelpers::bankHandlerIdx(cmd, m_ranks, this, &LPDDR5Core::handleWriteAuto); break; case CmdType::REFP2B: if (m_memSpec.bank_arch != MemSpecLPDDR5::MBG && m_memSpec.bank_arch != MemSpecLPDDR5::M16B) { throw Exception(std::string("REFP2B command is not supported for this bank architecture: ") + CmdTypeUtil::to_string(CmdType::REFP2B)); } util::coreHelpers::bankGroupHandlerIdx(cmd, m_ranks, this, &LPDDR5Core::handleRefPerTwoBanks); break; case CmdType::REFA: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &LPDDR5Core::handleRefAll); break; case CmdType::SREFEN: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &LPDDR5Core::handleSelfRefreshEntry); break; case CmdType::SREFEX: util::coreHelpers::rankHandler(cmd, m_ranks, this, &LPDDR5Core::handleSelfRefreshExit); break; case CmdType::PDEA: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &LPDDR5Core::handlePowerDownActEntry); break; case CmdType::PDEP: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &LPDDR5Core::handlePowerDownPreEntry); break; case CmdType::PDXA: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &LPDDR5Core::handlePowerDownActExit); break; case CmdType::PDXP: util::coreHelpers::rankHandlerIdx(cmd, m_ranks, this, &LPDDR5Core::handlePowerDownPreExit); break; case CmdType::DSMEN: util::coreHelpers::rankHandler(cmd, m_ranks, this, &LPDDR5Core::handleDSMEntry); break; case CmdType::DSMEX: util::coreHelpers::rankHandler(cmd, m_ranks, this, &LPDDR5Core::handleDSMExit); break; case CmdType::END_OF_SIMULATION: break; default: assert(false && "Unsupported command"); break; } } timestamp_t LPDDR5Core::getLastCommandTime() const { return m_last_command_time; } bool LPDDR5Core::isSerializable() const { return 0 == m_implicitCommandHandler.implicitCommandCount(); } void LPDDR5Core::handleAct(Rank &rank, Bank &bank, timestamp_t timestamp) { bank.counter.act++; bank.cycles.act.start_interval(timestamp); if ( !rank.isActive(timestamp) ) { rank.cycles.act.start_interval(timestamp); } bank.bankState = Bank::BankState::BANK_ACTIVE; } void LPDDR5Core::handlePre(Rank &rank, Bank &bank, timestamp_t timestamp) { if (bank.bankState == Bank::BankState::BANK_PRECHARGED) return; bank.counter.pre++; bank.cycles.act.close_interval(timestamp); bank.latestPre = timestamp; bank.bankState = Bank::BankState::BANK_PRECHARGED; if (!rank.isActive(timestamp)) { rank.cycles.act.close_interval(timestamp); } } void LPDDR5Core::handlePreAll(Rank &rank, timestamp_t timestamp) { for (auto &bank: rank.banks) { handlePre(rank, bank, timestamp); } } void LPDDR5Core::handleRefPerBank(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp) { auto& counter = m_ranks[rank_idx].banks[bank_idx].counter.refPerBank; handleRefreshOnBank(rank_idx, bank_idx, timestamp, m_memSpec.tRFCPB, counter); } void LPDDR5Core::handleRefPerTwoBanks(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; std::size_t bank_2_idx = (bank_idx + m_memSpec.perTwoBankOffset) % 16; auto& counter1 = rank.banks[bank_idx].counter.refPerTwoBanks; auto& counter2 = rank.banks[bank_2_idx].counter.refPerTwoBanks; handleRefreshOnBank(rank_idx, bank_idx, timestamp, m_memSpec.tRFCPB, counter1); handleRefreshOnBank(rank_idx, bank_2_idx, timestamp, m_memSpec.tRFCPB, counter2); } void LPDDR5Core::handleRefAll(std::size_t rank_idx, timestamp_t timestamp) { auto &rank = m_ranks[rank_idx]; for (std::size_t bank_idx = 0; bank_idx < rank.banks.size(); ++bank_idx) { auto& counter = rank.banks[bank_idx].counter.refAllBank; handleRefreshOnBank(rank_idx, bank_idx, timestamp, m_memSpec.tRFC, counter); } rank.endRefreshTime = timestamp + m_memSpec.tRFC; } void LPDDR5Core::handleRefreshOnBank(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp, uint64_t timing, uint64_t & counter){ ++counter; auto& rank = m_ranks[rank_idx]; auto& bank = rank.banks[bank_idx]; if (!rank.isActive(timestamp)) { rank.cycles.act.start_interval(timestamp); } bank.bankState = Bank::BankState::BANK_ACTIVE; auto timestamp_end = timestamp + timing; bank.refreshEndTime = timestamp_end; if (!bank.cycles.act.is_open()) bank.cycles.act.start_interval(timestamp); // Execute implicit pre-charge at refresh end m_implicitCommandHandler.addImplicitCommand(timestamp_end, [rank_idx, bank_idx, timestamp_end](LPDDR5Core& self) { auto& rank = self.m_ranks[rank_idx]; auto& bank = rank.banks[bank_idx]; bank.bankState = Bank::BankState::BANK_PRECHARGED; bank.cycles.act.close_interval(timestamp_end); if (!rank.isActive(timestamp_end)) { rank.cycles.act.close_interval(timestamp_end); } }); } void LPDDR5Core::handleRead(Rank&, Bank &bank, timestamp_t) { ++bank.counter.reads; } void LPDDR5Core::handleReadAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp) { auto& bank = m_ranks[rank_idx].banks[bank_idx]; ++bank.counter.readAuto; auto minBankActiveTime = bank.cycles.act.get_start() + this->m_memSpec.tRAS; auto minReadActiveTime = timestamp + this->m_memSpec.prechargeOffsetRD; auto delayed_timestamp = std::max(minBankActiveTime, minReadActiveTime); // Execute PRE after minimum active time m_implicitCommandHandler.addImplicitCommand(delayed_timestamp, [rank_idx, bank_idx, delayed_timestamp](LPDDR5Core& self) { auto& rank = self.m_ranks[rank_idx]; auto& bank = rank.banks[bank_idx]; self.handlePre(rank, bank, delayed_timestamp); }); } void LPDDR5Core::handleWrite(Rank&, Bank &bank, timestamp_t) { ++bank.counter.writes; } void LPDDR5Core::handleWriteAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp) { auto& bank = m_ranks[rank_idx].banks[bank_idx]; ++bank.counter.writeAuto; auto minBankActiveTime = bank.cycles.act.get_start() + this->m_memSpec.tRAS; auto minWriteActiveTime = timestamp + this->m_memSpec.prechargeOffsetWR; auto delayed_timestamp = std::max(minBankActiveTime, minWriteActiveTime); // Execute PRE after minimum active time m_implicitCommandHandler.addImplicitCommand(delayed_timestamp, [rank_idx, bank_idx, delayed_timestamp](LPDDR5Core& self) { auto& rank = self.m_ranks[rank_idx]; auto& bank = rank.banks[bank_idx]; self.handlePre(rank, bank, delayed_timestamp); }); } void LPDDR5Core::handleSelfRefreshEntry(std::size_t rank_idx, timestamp_t timestamp) { // Issue implicit refresh handleRefAll(rank_idx, timestamp); // Handle self-refresh entry after tRFC auto timestampSelfRefreshStart = timestamp + m_memSpec.tRFC; m_implicitCommandHandler.addImplicitCommand(timestampSelfRefreshStart, [rank_idx, timestampSelfRefreshStart](LPDDR5Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.counter.selfRefresh++; rank.cycles.sref.start_interval(timestampSelfRefreshStart); rank.memState = MemState::SREF; }); } void LPDDR5Core::handleSelfRefreshExit(Rank &rank, timestamp_t timestamp) { assert(rank.memState == MemState::SREF); rank.cycles.sref.close_interval(timestamp); rank.memState = MemState::NOT_IN_PD; } void LPDDR5Core::handlePowerDownActEntry(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; auto earliestPossibleEntry = this->earliestPossiblePowerDownEntryTime(rank); auto entryTime = std::max(timestamp, earliestPossibleEntry); m_implicitCommandHandler.addImplicitCommand(entryTime, [rank_idx, entryTime](LPDDR5Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.cycles.powerDownAct.start_interval(entryTime); rank.memState = MemState::PDN_ACT; if (rank.cycles.act.is_open()) { rank.cycles.act.close_interval(entryTime); } for (auto & bank : rank.banks) { if (bank.cycles.act.is_open()) { bank.cycles.act.close_interval(entryTime); } } }); } void LPDDR5Core::handlePowerDownActExit(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; auto earliestPossibleExit = this->earliestPossiblePowerDownEntryTime(rank); auto exitTime = std::max(timestamp, earliestPossibleExit); m_implicitCommandHandler.addImplicitCommand(exitTime, [rank_idx, exitTime](LPDDR5Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.memState = MemState::NOT_IN_PD; rank.cycles.powerDownAct.close_interval(exitTime); bool rank_active = false; for (auto & bank : rank.banks) { if (bank.counter.act != 0 && bank.cycles.act.get_end() == rank.cycles.powerDownAct.get_start()) { rank_active = true; bank.cycles.act.start_interval(exitTime); } } if (rank_active) { rank.cycles.act.start_interval(exitTime); } }); } void LPDDR5Core::handlePowerDownPreEntry(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; auto earliestPossibleEntry = this->earliestPossiblePowerDownEntryTime(rank); auto entryTime = std::max(timestamp, earliestPossibleEntry); m_implicitCommandHandler.addImplicitCommand(entryTime, [rank_idx, entryTime](LPDDR5Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.cycles.powerDownPre.start_interval(entryTime); rank.memState = MemState::PDN_PRE; }); } void LPDDR5Core::handlePowerDownPreExit(std::size_t rank_idx, timestamp_t timestamp) { auto& rank = m_ranks[rank_idx]; auto earliestPossibleExit = this->earliestPossiblePowerDownEntryTime(rank); auto exitTime = std::max(timestamp, earliestPossibleExit); m_implicitCommandHandler.addImplicitCommand(exitTime, [rank_idx, exitTime](LPDDR5Core& self) { auto& rank = self.m_ranks[rank_idx]; rank.memState = MemState::NOT_IN_PD; rank.cycles.powerDownPre.close_interval(exitTime); }); } void LPDDR5Core::handleDSMEntry(Rank &rank, timestamp_t timestamp) { assert(rank.memState == MemState::SREF); rank.cycles.deepSleepMode.start_interval(timestamp); rank.counter.deepSleepMode++; rank.memState = MemState::DSM; } void LPDDR5Core::handleDSMExit(Rank &rank, timestamp_t timestamp) { assert(rank.memState == MemState::DSM); rank.cycles.deepSleepMode.close_interval(timestamp); rank.memState = MemState::SREF; } timestamp_t LPDDR5Core::earliestPossiblePowerDownEntryTime(Rank & rank) const { timestamp_t entryTime = 0; for (const auto &bank : rank.banks) { entryTime = std::max( {entryTime, bank.counter.act == 0 ? 0 : bank.cycles.act.get_start() + m_memSpec.tRCD, bank.counter.pre == 0 ? 0 : bank.latestPre + m_memSpec.tRP, bank.refreshEndTime}); } return entryTime; } void LPDDR5Core::getWindowStats(timestamp_t timestamp, SimulationStats &stats) { m_implicitCommandHandler.processImplicitCommandQueue(*this, timestamp, m_last_command_time); stats.bank.resize(m_memSpec.numberOfBanks * m_memSpec.numberOfRanks); stats.rank_total.resize(m_memSpec.numberOfRanks); auto simulation_duration = timestamp; for (size_t i = 0; i < m_memSpec.numberOfRanks; ++i) { const Rank &rank = m_ranks[i]; size_t bank_offset = i * m_memSpec.numberOfBanks; for (std::size_t j = 0; j < m_memSpec.numberOfBanks; ++j) { stats.bank[bank_offset + j].counter = rank.banks[j].counter; stats.bank[bank_offset + j].cycles.act = rank.banks[j].cycles.act.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.selfRefresh = rank.cycles.sref.get_count_at(timestamp) - rank.cycles.deepSleepMode.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.deepSleepMode = rank.cycles.deepSleepMode.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.powerDownAct = rank.cycles.powerDownAct.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.powerDownPre = rank.cycles.powerDownPre.get_count_at(timestamp); stats.bank[bank_offset + j].cycles.pre = simulation_duration - (stats.bank[bank_offset + j].cycles.act + rank.cycles.powerDownAct.get_count_at(timestamp) + rank.cycles.powerDownPre.get_count_at(timestamp) + rank.cycles.sref.get_count_at(timestamp)); } stats.rank_total[i].cycles.pre = simulation_duration - (rank.cycles.act.get_count_at(timestamp) + rank.cycles.powerDownAct.get_count_at(timestamp) + rank.cycles.powerDownPre.get_count_at(timestamp) + rank.cycles.sref.get_count_at(timestamp)); stats.rank_total[i].cycles.act = rank.cycles.act.get_count_at(timestamp); stats.rank_total[i].cycles.powerDownAct = rank.cycles.powerDownAct.get_count_at(timestamp); stats.rank_total[i].cycles.powerDownPre = rank.cycles.powerDownPre.get_count_at(timestamp); stats.rank_total[i].cycles.selfRefresh = rank.cycles.sref.get_count_at(timestamp) - rank.cycles.deepSleepMode.get_count_at(timestamp); stats.rank_total[i].cycles.deepSleepMode = rank.cycles.deepSleepMode.get_count_at(timestamp); } } void LPDDR5Core::serialize(std::ostream& stream) const { stream.write(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); for (const auto& rank : m_ranks) { rank.serialize(stream); } } void LPDDR5Core::deserialize(std::istream& stream) { stream.read(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); for (auto& rank : m_ranks) { rank.deserialize(stream); } } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr5/LPDDR5Core.h ================================================ #ifndef DRAMPOWER_STANDARDS_LPDDR5_LPDDR5CORE_H #define DRAMPOWER_STANDARDS_LPDDR5_LPDDR5CORE_H #include "DRAMPower/util/Deserialize.h" #include "DRAMPower/util/Serialize.h" #include #include "DRAMPower/dram/Rank.h" #include "DRAMPower/command/Command.h" #include #include "DRAMPower/util/ImplicitCommandHandler.h" #include "DRAMPower/memspec/MemSpecLPDDR5.h" #include namespace DRAMPower { namespace internal { template class TestAccessor; } struct LPDDR5CoreMemSpec { LPDDR5CoreMemSpec(const MemSpecLPDDR5& memSpec) : numberOfBanks(memSpec.numberOfBanks) , numberOfRanks(memSpec.numberOfRanks) , tRFC(memSpec.memTimingSpec.tRFC) , tRFCPB(memSpec.memTimingSpec.tRFCPB) , tRAS(memSpec.memTimingSpec.tRAS) , tRCD(memSpec.memTimingSpec.tRCD) , tRP(memSpec.memTimingSpec.tRP) , bank_arch(memSpec.bank_arch) , perTwoBankOffset(memSpec.perTwoBankOffset) , prechargeOffsetRD(memSpec.prechargeOffsetRD) , prechargeOffsetWR(memSpec.prechargeOffsetWR) {} uint64_t numberOfBanks; uint64_t numberOfRanks; uint64_t tRFC; uint64_t tRFCPB; uint64_t tRAS; uint64_t tRCD; uint64_t tRP; MemSpecLPDDR5::BankArchitectureMode bank_arch; std::size_t perTwoBankOffset; uint64_t prechargeOffsetRD; uint64_t prechargeOffsetWR; }; class LPDDR5Core : public util::Serialize, public util::Deserialize { // Friend classes friend class internal::TestAccessor; // Public constructors public: LPDDR5Core(const MemSpecLPDDR5& memSpec) : m_memSpec(memSpec) , m_ranks(memSpec.numberOfRanks, {static_cast(memSpec.numberOfBanks)}) {} // Public member functions public: // Member functions void doCommand(const Command& cmd); timestamp_t getLastCommandTime() const; bool isSerializable() const; void getWindowStats(timestamp_t timestamp, SimulationStats &stats); // Overrides void serialize(std::ostream& stream) const override; void deserialize(std::istream& stream) override; // Private member functions private: void handleAct(Rank& rank, Bank& bank, timestamp_t timestamp); void handlePre(Rank& rank, Bank& bank, timestamp_t timestamp); void handlePreAll(Rank& rank, timestamp_t timestamp); void handleRead(Rank& rank, Bank& bank, timestamp_t timestamp); void handleWrite(Rank& rank, Bank& bank, timestamp_t timestamp); void handleReadAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp); void handleWriteAuto(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp); void handleRefAll(std::size_t rank_idx, timestamp_t timestamp); void handleRefPerBank(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp); void handleRefPerTwoBanks(std::size_t rank_idx, std::size_t bank_id, timestamp_t timestamp); void handleRefreshOnBank(std::size_t rank_idx, std::size_t bank_idx, timestamp_t timestamp, uint64_t timing, uint64_t& counter); void handleSelfRefreshEntry(std::size_t rank_idx, timestamp_t timestamp); void handleSelfRefreshExit(Rank& rank, timestamp_t timestamp); void handlePowerDownActEntry(std::size_t rank_idx, timestamp_t timestamp); void handlePowerDownActExit(std::size_t rank_idx, timestamp_t timestamp); void handlePowerDownPreEntry(std::size_t rank_idx, timestamp_t timestamp); void handlePowerDownPreExit(std::size_t rank_idx, timestamp_t timestamp); void handleDSMEntry(Rank& rank, timestamp_t timestamp); void handleDSMExit(Rank& rank, timestamp_t timestamp); timestamp_t earliestPossiblePowerDownEntryTime(Rank & rank) const; // Private member variables private: LPDDR5CoreMemSpec m_memSpec; std::vector m_ranks; ImplicitCommandHandler m_implicitCommandHandler; timestamp_t m_last_command_time = 0; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_LPDDR5_LPDDR5CORE_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr5/LPDDR5Interface.cpp ================================================ #include "LPDDR5Interface.h" #include "DRAMPower/Exceptions.h" namespace DRAMPower { static constexpr DRAMUtils::Config::ToggleRateDefinition busConfig { 0, 0, 0, 0, DRAMUtils::Config::TogglingRateIdlePattern::L, DRAMUtils::Config::TogglingRateIdlePattern::L }; LPDDR5Interface::LPDDR5Interface(const MemSpecLPDDR5& memSpec, const config::SimConfig& simConfig) : m_memSpec(memSpec) , m_commandBus{cmdBusWidth, 2, // modelled with datarate 2 util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L} , m_dataBus{ util::databus_presets::getDataBusPreset( util::DataBusConfig { memSpec.bitWidth * memSpec.numberOfDevices, memSpec.dataRate, simConfig.toggleRateDefinition.value_or(busConfig) }, simConfig.toggleRateDefinition.has_value() ? util::DataBusMode::TogglingRate : util::DataBusMode::Bus, false ) } , m_readDQS(memSpec.dataRate, true) , m_wck(memSpec.dataRate / memSpec.memTimingSpec.WCKtoCK, !memSpec.wckAlwaysOnMode) , m_dbi(memSpec.numberOfDevices * memSpec.bitWidth, m_memSpec.burstLength, [this](timestamp_t load_timestamp, timestamp_t, std::size_t pin, bool inversion_state, bool read) { this->handleDBIPinChange(load_timestamp, pin, inversion_state, read); }, false) , m_dbiread(m_dbi.getChunksPerWidth().value(), pin_dbi_t{m_dbi.getIdlePattern(), m_dbi.getIdlePattern()}) , m_dbiwrite(m_dbi.getChunksPerWidth().value(), pin_dbi_t{m_dbi.getIdlePattern(), m_dbi.getIdlePattern()}) , m_patternHandler(PatternEncoderOverrides{}) // No overrides { registerPatterns(); } void LPDDR5Interface::registerPatterns() { using namespace pattern_descriptor; using commandPattern_t = std::vector; // ACT // LPDDR5 needs 2 commands for activation (ACT-1 and ACT-2) // ACT-1 must be followed by ACT-2 in almost every case (CAS, WRITE, // MASK WRITE and READ commands can be issued inbetween ACT-1 and ACT-2) // Here we consider ACT = ACT-1 + ACT-2, not considering interleaving commandPattern_t act_pattern = { // ACT-1 // R1 H, H, H, R14, R15, R16, R17, // F1 BA0, BA1, BA2, BA3, R11, R12, R13, // ACT-2 // R2 H, H, L, R7, R8, R9, R10, // F2 R0, R1, R2, R3, R4, R5, R6 }; if (m_memSpec.bank_arch == MemSpecLPDDR5::MBG) { act_pattern[9] = BG0; act_pattern[10] = BG1; } else if (m_memSpec.bank_arch == MemSpecLPDDR5::M8B) { act_pattern[10] = V; } m_patternHandler.registerPattern(act_pattern); // PRE commandPattern_t pre_pattern = { // R1 L, L, L, H, H, H, H, // F1 BA0, BA1, BA2, BA3, V, V, L }; if (m_memSpec.bank_arch == MemSpecLPDDR5::MBG) { pre_pattern[9] = BG0; pre_pattern[10] = BG1; } else if (m_memSpec.bank_arch == MemSpecLPDDR5::M8B) { pre_pattern[10] = V; } m_patternHandler.registerPattern(pre_pattern); // PREA commandPattern_t prea_pattern = { // R1 L, L, L, H, H, H, H, // F1 V, V, V, V, V, V, H }; if (m_memSpec.bank_arch == MemSpecLPDDR5::MBG) { prea_pattern[9] = BG0; prea_pattern[10] = BG1; } else if (m_memSpec.bank_arch == MemSpecLPDDR5::M8B) { prea_pattern[10] = V; } m_patternHandler.registerPattern(prea_pattern); // REFB // For refresh commands LPDDR5 has RFM (Refresh Management) // Considering RFM is disabled, CA3 is V commandPattern_t refb_pattern = { // R1 L, L, L, H, H, H, L, // F1 BA0, BA1, BA2, V, V, V, L }; if (m_memSpec.bank_arch == MemSpecLPDDR5::MBG) { refb_pattern[9] = BG0; } m_patternHandler.registerPattern(refb_pattern); // RD commandPattern_t rd_pattern = { // R1 H, L, L, C0, C3, C4, C5, // F1 BA0, BA1, BA2, BA3, C1, C2, L }; if (m_memSpec.bank_arch == MemSpecLPDDR5::MBG) { rd_pattern[9] = BG0; rd_pattern[10] = BG1; } else if (m_memSpec.bank_arch == MemSpecLPDDR5::M8B) { rd_pattern[10] = L; // B4 } m_patternHandler.registerPattern(rd_pattern); // RDA commandPattern_t rda_pattern = { // R1 H, L, L, C0, C3, C4, C5, // F1 BA0, BA1, BA2, BA3, C1, C2, H }; if (m_memSpec.bank_arch == MemSpecLPDDR5::MBG) { rda_pattern[9] = BG0; rda_pattern[10] = BG1; } else if (m_memSpec.bank_arch == MemSpecLPDDR5::M8B) { rda_pattern[10] = L; } m_patternHandler.registerPattern(rda_pattern); // WR commandPattern_t wr_pattern = { // R1 L, H, H, C0, C3, C4, C5, // F1 BA0, BA1, BA2, BA3, C1, C2, L }; if (m_memSpec.bank_arch == MemSpecLPDDR5::MBG) { wr_pattern[9] = BG0; wr_pattern[10] = BG1; } else if (m_memSpec.bank_arch == MemSpecLPDDR5::M8B) { wr_pattern[10] = V; } m_patternHandler.registerPattern(wr_pattern); // WRA commandPattern_t wra_pattern = { // R1 L, H, H, C0, C3, C4, C5, // F1 BA0, BA1, BA2, BA3, C1, C2, H }; if (m_memSpec.bank_arch == MemSpecLPDDR5::MBG) { wra_pattern[9] = BG0; wra_pattern[10] = BG1; } else if (m_memSpec.bank_arch == MemSpecLPDDR5::M8B) { wra_pattern[10] = V; } m_patternHandler.registerPattern(wra_pattern); // REFP2B if (m_memSpec.bank_arch == MemSpecLPDDR5::MBG || m_memSpec.bank_arch == MemSpecLPDDR5::M16B) { m_patternHandler.registerPattern(refb_pattern); } // REFA commandPattern_t refa_pattern = { // R1 L, L, L, H, H, H, L, // F1 V, V, V, V, V, V, H }; if (m_memSpec.bank_arch == MemSpecLPDDR5::MBG) { refa_pattern[9] = BG0; } m_patternHandler.registerPattern(refa_pattern); // SREFEN m_patternHandler.registerPattern({ // R1 L, L, L, H, L, H, H, // F1 V, V, V, V, V, L, L }); // SREFEX m_patternHandler.registerPattern({ // R1 L, L, L, H, L, H, L, // F1 V, V, V, V, V, V, V }); // PDEA m_patternHandler.registerPattern({ // R1 L, L, L, H, L, H, H, // F1 V, V, V, V, V, L, H }); // PDEP m_patternHandler.registerPattern({ // R1 L, L, L, H, L, H, H, // F1 V, V, V, V, V, L, H }); // PDXA m_patternHandler.registerPattern({ // R1 L, L, L, H, L, H, L, // F1 V, V, V, V, V, V, V }); // PDXP m_patternHandler.registerPattern({ // R1 L, L, L, H, L, H, L, // F1 V, V, V, V, V, V, V }); // DSMEN m_patternHandler.registerPattern({ // R1 L, L, L, H, L, H, H, // F1 V, V, V, V, V, H, L }); // DSMEX m_patternHandler.registerPattern({ // R1 L, L, L, H, L, H, L, // F1 V, V, V, V, V, V, V }); } timestamp_t LPDDR5Interface::getLastCommandTime() const { return m_last_command_time; } void LPDDR5Interface::doCommand(const Command& cmd) { switch(cmd.type) { case CmdType::ACT: case CmdType::PRE: case CmdType::PREA: case CmdType::REFB: case CmdType::REFA: case CmdType::SREFEN: case CmdType::SREFEX: case CmdType::PDEA: case CmdType::PDEP: case CmdType::PDXA: case CmdType::PDXP: case CmdType::DSMEN: case CmdType::DSMEX: handleCommandBus(cmd); break; case CmdType::REFP2B: if (m_memSpec.bank_arch != MemSpecLPDDR5::MBG && m_memSpec.bank_arch != MemSpecLPDDR5::M16B) { throw Exception(std::string("REFP2B command is not supported for this bank architecture: ") + CmdTypeUtil::to_string(CmdType::REFP2B)); } handleCommandBus(cmd); break; case CmdType::RD: case CmdType::RDA: handleData(cmd, true); break; case CmdType::WR: case CmdType::WRA: handleData(cmd, false); break; case CmdType::END_OF_SIMULATION: endOfSimulation(cmd.timestamp); break; default: assert(false && "Invalid command"); break; } m_last_command_time = cmd.timestamp; } void LPDDR5Interface::handleDBIPinChange(const timestamp_t load_timestamp, std::size_t pin, bool state, bool read) { assert(pin < m_dbiread.size() || pin < m_dbiwrite.size()); if (read) { this->m_dbiread[pin].set(load_timestamp, state ? util::PinState::H : util::PinState::L, 1); } else { this->m_dbiwrite[pin].set(load_timestamp, state ? util::PinState::H : util::PinState::L, 1); } } std::optional LPDDR5Interface::handleDBIInterface(timestamp_t timestamp, std::size_t n_bits, const uint8_t* data, bool read) { if (0 == n_bits || !data || !m_dbi.isEnabled()) { // No DBI or no data to process return std::nullopt; } timestamp_t virtual_time = timestamp * m_memSpec.dataRate; // updateDBI calls the given callback to handle pin changes return m_dbi.updateDBI(virtual_time, n_bits, data, read); } void LPDDR5Interface::handleOverrides(size_t length, bool /*read*/) { // Set command bus pattern overrides switch(length) { case 32: m_patternHandler.getEncoder().settings.updateSettings({ {pattern_descriptor::C0, PatternEncoderBitSpec::L}, }); break; default: // Pull down // No interface power needed for PatternEncoderBitSpec::L // Defaults to burst length 16 case 16: m_patternHandler.getEncoder().settings.removeSetting(pattern_descriptor::C0); break; } } void LPDDR5Interface::handleCommandBus(const Command &cmd) { auto pattern = m_patternHandler.getCommandPattern(cmd); auto ca_length = m_patternHandler.getPattern(cmd.type).size() / m_commandBus.get_width(); m_commandBus.load(cmd.timestamp, pattern, ca_length); } void LPDDR5Interface::handleData(const Command &cmd, bool read) { auto loadfunc = read ? &databus_t::loadRead : &databus_t::loadWrite; size_t length = 0; if (0 == cmd.sz_bits) { // No data provided by command if (m_dataBus.isTogglingRate()) { length = m_memSpec.burstLength; (m_dataBus.*loadfunc)(cmd.timestamp, length * m_dataBus.getWidth(), nullptr); } } else { std::optional dbi_data = std::nullopt; // Data provided by command if (m_dataBus.isBus() && m_dbi.isEnabled()) { // Only compute dbi for bus mode dbi_data = handleDBIInterface(cmd.timestamp, cmd.sz_bits, cmd.data, read); } length = cmd.sz_bits / (m_dataBus.getWidth()); (m_dataBus.*loadfunc)(cmd.timestamp, cmd.sz_bits, dbi_data.value_or(cmd.data)); } // DQS if (read) { // Read m_readDQS.start(cmd.timestamp); m_readDQS.stop(cmd.timestamp + length / m_memSpec.dataRate); if (!m_memSpec.wckAlwaysOnMode) { m_wck.start(cmd.timestamp); m_wck.stop(cmd.timestamp + length / m_memSpec.dataRate); } } else { // Write if (!m_memSpec.wckAlwaysOnMode) { m_wck.start(cmd.timestamp); m_wck.stop(cmd.timestamp + length / m_memSpec.dataRate); } } handleOverrides(length, read); handleCommandBus(cmd); } void LPDDR5Interface::endOfSimulation(timestamp_t timestamp) { m_dbi.dispatchResetCallback(timestamp); } void LPDDR5Interface::getWindowStats(timestamp_t timestamp, SimulationStats &stats) const { stats.commandBus = m_commandBus.get_stats(timestamp); m_dataBus.get_stats(timestamp, stats.readBus, stats.writeBus, stats.togglingStats.read, stats.togglingStats.write ); stats.clockStats = 2.0 * m_clock.get_stats_at(timestamp); stats.wClockStats = 2.0 * m_wck.get_stats_at(timestamp); stats.readDQSStats = 2.0 * m_readDQS.get_stats_at(timestamp); for (const auto &dbi_pin : m_dbiread) { stats.readDBI += dbi_pin.get_stats_at(timestamp, 2); } for (const auto &dbi_pin : m_dbiwrite) { stats.writeDBI += dbi_pin.get_stats_at(timestamp, 2); } } void LPDDR5Interface::serialize(std::ostream& stream) const { stream.write(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); m_patternHandler.serialize(stream); m_commandBus.serialize(stream); m_dataBus.serialize(stream); m_readDQS.serialize(stream); m_wck.serialize(stream); m_clock.serialize(stream); } void LPDDR5Interface::deserialize(std::istream& stream) { stream.read(reinterpret_cast(&m_last_command_time), sizeof(m_last_command_time)); m_patternHandler.deserialize(stream); m_commandBus.deserialize(stream); m_dataBus.deserialize(stream); m_readDQS.deserialize(stream); m_wck.deserialize(stream); m_clock.deserialize(stream); } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr5/LPDDR5Interface.h ================================================ #ifndef DRAMPOWER_STANDARDS_LPDDR5_LPDDR5INTERFACE_H #define DRAMPOWER_STANDARDS_LPDDR5_LPDDR5INTERFACE_H #include "DRAMPower/util/pin.h" #include "DRAMPower/util/bus.h" #include "DRAMPower/util/databus_presets.h" #include "DRAMPower/util/clock.h" #include "DRAMPower/util/Serialize.h" #include "DRAMPower/util/Deserialize.h" #include "DRAMPower/Types.h" #include "DRAMPower/command/Command.h" #include "DRAMPower/data/stats.h" #include "DRAMPower/util/PatternHandler.h" #include "DRAMPower/util/dbi.h" #include "DRAMPower/memspec/MemSpecLPDDR5.h" #include "DRAMPower/simconfig/simconfig.h" #include #include #include namespace DRAMPower { struct LPDDR5InterfaceMemSpec { LPDDR5InterfaceMemSpec(const MemSpecLPDDR5& memSpec) : dataRate(memSpec.dataRate) , burstLength(memSpec.burstLength) , bitWidth(memSpec.bitWidth) , bank_arch(memSpec.bank_arch) , wckAlwaysOnMode(memSpec.wckAlwaysOnMode) {} uint64_t dataRate; uint64_t burstLength; uint64_t bitWidth; MemSpecLPDDR5::BankArchitectureMode bank_arch; bool wckAlwaysOnMode; }; class LPDDR5Interface : public util::Serialize, public util::Deserialize { // Public constants public: const static std::size_t cmdBusWidth = 7; const static uint64_t cmdBusInitPattern = (1<; using pin_dbi_t = util::Pin<32>; // max_burst_length = 32 using databus_t = util::databus_presets::databus_preset_t; using patternHandler_t = PatternHandler; // Public constructors and assignment operators public: LPDDR5Interface(const MemSpecLPDDR5& memSpec, const config::SimConfig& simConfig); // Public member functions public: // Member functions timestamp_t getLastCommandTime() const; void doCommand(const Command& cmd); void getWindowStats(timestamp_t timestamp, SimulationStats &stats) const; // Override void serialize(std::ostream& stream) const override; void deserialize(std::istream& stream) override; // Extensions void enableDBI(bool enable) { m_dbi.enable(enable); } // Public member functions private: void registerPatterns(); std::optional handleDBIInterface(timestamp_t timestamp, std::size_t n_bits, const uint8_t* data, bool read); void handleDBIPinChange(const timestamp_t load_timestamp, std::size_t pin, bool state, bool read); void handleOverrides(size_t length, bool read); void handleDQs(const Command& cmd, util::Clock &dqs, size_t length, uint64_t datarate); void handleCommandBus(const Command& cmd); void handleData(const Command &cmd, bool read); void endOfSimulation(timestamp_t timestamp); // Private member variables private: LPDDR5InterfaceMemSpec m_memSpec; commandbus_t m_commandBus; databus_t m_dataBus; util::Clock m_readDQS; util::Clock m_wck; util::Clock m_clock; util::DBI m_dbi; std::vector m_dbiread; std::vector m_dbiwrite; patternHandler_t m_patternHandler; timestamp_t m_last_command_time = 0; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_LPDDR5_LPDDR5INTERFACE_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr5/core_calculation_LPDDR5.cpp ================================================ #include "core_calculation_LPDDR5.h" #include "DRAMPower/data/stats.h" #include namespace DRAMPower { Calculation_LPDDR5::Calculation_LPDDR5(const MemSpecLPDDR5 &memSpec) : m_memSpec(memSpec) {} double Calculation_LPDDR5::E_pre(double VDD, double IBeta, double IDD2_N, double t_RP, uint64_t N_pre) const { return VDD * (IBeta - IDD2_N) * t_RP * N_pre; } double Calculation_LPDDR5::E_act(double VDD, double I_theta, double I_1, double t_RAS, uint64_t N_act) const { return VDD * (I_theta - I_1) * t_RAS * N_act; } double Calculation_LPDDR5::E_BG_pre(std::size_t B, double VDD, double IDD2_N, double T_BG_pre) const { return (1.0 / B) * VDD * IDD2_N * T_BG_pre; } double Calculation_LPDDR5::E_BG_act_star(std::size_t B, double VDD, double IDD3_N, double I_p, double T_BG_act_star) const { return VDD * (1.0 / B) * (IDD3_N - I_p) * T_BG_act_star; } double Calculation_LPDDR5::E_BG_act_shared(double VDD, double I_p, double T_bg_act) const { return VDD * I_p * T_bg_act; } double Calculation_LPDDR5::E_RD(double VDD, double IDD4_R, double I_i, std::size_t BL, std::size_t DR, double t_WCK, uint64_t N_RD) const { return VDD * (IDD4_R - I_i) * (BL / DR) * t_WCK * N_RD; } double Calculation_LPDDR5::E_WR(double VDD, double IDD4_R, double I_i, std::size_t BL, std::size_t DR, double t_WCK, uint64_t N_WR) const { return VDD * (IDD4_R - I_i) * (BL / DR) * t_WCK * N_WR; } double Calculation_LPDDR5::E_ref_ab(std::size_t B, double VDD, double IDD5B, double IDD3_N, double tRFC, uint64_t N_REF) const { return (1.0 / B) * VDD * (IDD5B - IDD3_N) * tRFC * N_REF; } double Calculation_LPDDR5::E_ref_pb(double VDD, double IDD5PB_B, double I_1, double tRFCPB, uint64_t N_PB_REF) const { return VDD * (IDD5PB_B - I_1) * tRFCPB * N_PB_REF; } double Calculation_LPDDR5::E_ref_p2b(double VDD, double IDD5PB_B, double I_2, double tRFCPB, uint64_t N_P2B_REF) const { return 0.5 * VDD * (IDD5PB_B - I_2) * tRFCPB * N_P2B_REF; } energy_t Calculation_LPDDR5::calcEnergy(const SimulationStats &stats) const { auto t_CK = m_memSpec.memTimingSpec.tCK; auto t_WCK = m_memSpec.memTimingSpec.tWCK; auto t_RAS = m_memSpec.memTimingSpec.tRAS * t_CK; auto t_RP = m_memSpec.memTimingSpec.tRP * t_CK; auto t_RFC = m_memSpec.memTimingSpec.tRFC * t_CK; auto t_RFCPB = m_memSpec.memTimingSpec.tRFCPB * t_CK; auto t_REFI = m_memSpec.memTimingSpec.tREFI * t_CK; auto rho = m_memSpec.bwParams.bwPowerFactRho; auto BL = m_memSpec.burstLength; auto DR = m_memSpec.dataRate; auto B = m_memSpec.numberOfBanks; energy_t energy(m_memSpec.numberOfBanks * m_memSpec.numberOfRanks * m_memSpec.numberOfDevices); for (auto vd : {MemSpecLPDDR5::VoltageDomain::VDD1, MemSpecLPDDR5::VoltageDomain::VDD2H, MemSpecLPDDR5::VoltageDomain::VDD2L}) { auto VDD = m_memSpec.memPowerSpec[vd].vDDX; auto IDD_0 = m_memSpec.memPowerSpec[vd].iDD0X; auto IDD2N = m_memSpec.memPowerSpec[vd].iDD2NX; auto IDD3N = m_memSpec.memPowerSpec[vd].iDD3NX; auto IDD2P = m_memSpec.memPowerSpec[vd].iDD2PX; auto IDD3P = m_memSpec.memPowerSpec[vd].iDD3PX; auto IDD4R = m_memSpec.memPowerSpec[vd].iDD4RX; auto IDD4W = m_memSpec.memPowerSpec[vd].iDD4WX; auto IDD5 = m_memSpec.memPowerSpec[vd].iDD5X; auto IDD5PB = m_memSpec.memPowerSpec[vd].iDD5PBX; auto IDD6 = m_memSpec.memPowerSpec[vd].iDD6X; auto IDD6DS = m_memSpec.memPowerSpec[vd].iDD6DSX; auto IBeta = m_memSpec.memPowerSpec[vd].iBeta; auto I_rho = rho * (IDD3N - IDD2N) + IDD2N; auto I_2 = IDD3N + (IDD3N - I_rho); auto I_theta = (IDD_0 * (t_RP + t_RAS) - IBeta * t_RP) * (1 / t_RAS); auto IDD5PB_B = (IDD5PB * (t_REFI / 8) - IDD2N * ((t_REFI / 8) - t_RFCPB)) * (1.0 / t_RFCPB); auto approx_IDD3N = I_rho + B * (IDD3N - I_rho); size_t energy_offset = 0; size_t bank_offset = 0; for (size_t i = 0; i < m_memSpec.numberOfRanks; ++i) { for (size_t d = 0; d < m_memSpec.numberOfDevices; ++d) { energy_offset = i * m_memSpec.numberOfDevices * m_memSpec.numberOfBanks + d * m_memSpec.numberOfBanks; bank_offset = i * m_memSpec.numberOfBanks; for (std::size_t b = 0; b < m_memSpec.numberOfBanks; ++b) { const auto &bank = stats.bank[bank_offset + b]; energy.bank_energy[energy_offset + b].E_act += E_act(VDD, I_theta, IDD3N, t_RAS, bank.counter.act); energy.bank_energy[energy_offset + b].E_pre += E_pre(VDD, IBeta, IDD2N, t_RP, bank.counter.pre); energy.bank_energy[energy_offset + b].E_bg_act += E_BG_act_star(B, VDD, approx_IDD3N, I_rho, stats.bank[bank_offset + b].cycles.activeTime() * t_CK); energy.bank_energy[energy_offset + b].E_bg_pre += E_BG_pre(B, VDD, IDD2N, stats.rank_total[i].cycles.pre * t_CK); if (m_memSpec.bank_arch == MemSpecLPDDR5::MBG) { energy.bank_energy[energy_offset + b].E_RD += E_RD(VDD, IDD4R, I_2, BL, DR, t_WCK, bank.counter.reads); energy.bank_energy[energy_offset + b].E_WR += E_WR(VDD, IDD4W, I_2, BL, DR, t_WCK, bank.counter.writes); energy.bank_energy[energy_offset + b].E_RDA += E_RD(VDD, IDD4R, I_2, BL, DR, t_WCK, bank.counter.readAuto); energy.bank_energy[energy_offset + b].E_WRA += E_WR(VDD, IDD4W, I_2, BL, DR, t_WCK, bank.counter.writeAuto); } else { energy.bank_energy[energy_offset + b].E_RD += E_RD(VDD, IDD4R, IDD3N, BL, DR, t_WCK, bank.counter.reads); energy.bank_energy[energy_offset + b].E_WR += E_WR(VDD, IDD4W, IDD3N, BL, DR, t_WCK, bank.counter.writes); energy.bank_energy[energy_offset + b].E_RDA += E_RD(VDD, IDD4R, IDD3N, BL, DR, t_WCK, bank.counter.readAuto); energy.bank_energy[energy_offset + b].E_WRA += E_WR(VDD, IDD4W, IDD3N, BL, DR, t_WCK, bank.counter.writeAuto); } energy.bank_energy[energy_offset + b].E_pre_RDA += E_pre(VDD, IBeta, IDD2N, t_RP, bank.counter.readAuto); energy.bank_energy[energy_offset + b].E_pre_WRA += E_pre(VDD, IBeta, IDD2N, t_RP, bank.counter.writeAuto); energy.bank_energy[energy_offset + b].E_ref_AB += E_ref_ab(B, VDD, IDD5, approx_IDD3N, t_RFC, bank.counter.refAllBank); energy.bank_energy[energy_offset + b].E_ref_PB += E_ref_pb(VDD, IDD5PB_B, IDD3N, t_RFCPB, bank.counter.refPerBank); energy.bank_energy[energy_offset + b].E_ref_2B += E_ref_p2b(VDD, IDD5PB_B, I_2, t_RFCPB, bank.counter.refPerTwoBanks); } } energy.E_sref += VDD * IDD6 * stats.rank_total[i].cycles.selfRefresh * t_CK * m_memSpec.numberOfDevices; energy.E_PDNA += VDD * IDD3P * stats.rank_total[i].cycles.powerDownAct * t_CK * m_memSpec.numberOfDevices; energy.E_PDNP += VDD * IDD2P * stats.rank_total[i].cycles.powerDownPre * t_CK * m_memSpec.numberOfDevices; energy.E_dsm += VDD * IDD6DS * stats.rank_total[i].cycles.deepSleepMode * t_CK * m_memSpec.numberOfDevices; energy.E_bg_act_shared += E_BG_act_shared(VDD, I_rho, stats.rank_total[i].cycles.act * t_CK) * m_memSpec.numberOfDevices; } } return energy; } } ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr5/core_calculation_LPDDR5.h ================================================ #ifndef DRAMPOWER_STANDARDS_LPDDR5_CALCULATION_LPDDR5_H #define DRAMPOWER_STANDARDS_LPDDR5_CALCULATION_LPDDR5_H #include "DRAMPower/data/stats.h" #pragma once #include "DRAMPower/memspec/MemSpecLPDDR5.h" #include #include #include #include namespace DRAMPower { class LPDDR5; class Calculation_LPDDR5 { public: Calculation_LPDDR5(const MemSpecLPDDR5 &memSpec); private: double E_act(double VDD, double I_theta, double I_1, double t_RAS, uint64_t N_act) const; double E_pre(double VDD, double IBeta, double IDD2_N, double t_RP, uint64_t N_pre) const; double E_BG_pre(std::size_t B, double VDD, double IDD2_N, double T_BG_pre) const; double E_BG_act_star(std::size_t B, double VDD, double IDD3_N, double I_p, double T_BG_act_star) const; double E_BG_act_shared(double VDD, double I_p, double T_bg_act) const; double E_RD(double VDD, double IDD4_R, double I_i, std::size_t BL, std::size_t DR, double t_WCK, uint64_t N_RD) const; double E_WR(double VDD, double IDD4_W, double I_i, std::size_t BL, std::size_t DR, double t_WCK, uint64_t N_WR) const; double E_ref_ab(std::size_t B, double VDD, double IDD5B, double IDD3_N, double tRFC, uint64_t N_REF) const; double E_ref_pb(double VDD, double IDD5PB_B, double I_1, double tRFCPB, uint64_t N_PB_REF) const; double E_ref_p2b(double VDD, double IDD5PB_B, double I_2, double tRFCPB, uint64_t N_P2B_REF) const; public: energy_t calcEnergy(const SimulationStats &stats) const; private: const MemSpecLPDDR5 &m_memSpec; }; }; #endif /* DRAMPOWER_STANDARDS_LPDDR5_CALCULATION_LPDDR5_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr5/interface_calculation_LPDDR5.cpp ================================================ #include "DRAMPower/standards/lpddr5/interface_calculation_LPDDR5.h" #include "DRAMPower/data/energy.h" namespace DRAMPower { static double calc_static_energy(uint64_t NxBits, double R_eq, double t_CK, double voltage) { return NxBits * (voltage * voltage) * t_CK / R_eq; }; static double calc_dynamic_energy(const uint64_t NxBits, const double energy) { return NxBits * energy; }; static double calcStaticTermination(const bool termination, const DRAMPower::util::bus_stats_t &stats, const double R_eq, const double t_CK, const uint64_t datarate, const double voltage) { if (termination == false) { return 0; // No static termination } // ones return calc_static_energy(stats.ones, R_eq, t_CK / datarate, voltage); } InterfaceCalculation_LPDDR5::InterfaceCalculation_LPDDR5(const MemSpecLPDDR5 &memspec) : memspec_(memspec), impedances_(memspec_.memImpedanceSpec) { t_CK_ = memspec_.memTimingSpec.tCK; t_WCK_ = memspec_.memTimingSpec.tWCK; VDDQ_ = memspec_.vddq; } interface_energy_info_t InterfaceCalculation_LPDDR5::calculateEnergy(const SimulationStats &stats) const { interface_energy_info_t clock_energy = calcClockEnergy(stats); interface_energy_info_t DQS_energy = calcDQSEnergy(stats); interface_energy_info_t DQ_energy = calcDQEnergy(stats); DQ_energy += calcDQEnergyTogglingRate(stats.togglingStats); interface_energy_info_t CA_energy = calcCAEnergy(stats); interface_energy_info_t DBI_energy = calcDBIEnergy(stats); interface_energy_info_t result; result += clock_energy; result += DQS_energy; result += CA_energy; result += DQ_energy; result += DBI_energy; return result; } interface_energy_info_t InterfaceCalculation_LPDDR5::calcClockEnergy(const SimulationStats &stats) const { interface_energy_info_t result; result.controller.staticEnergy = calcStaticTermination(impedances_.ck_termination, stats.clockStats, impedances_.ck_R_eq, t_CK_, 2, VDDQ_); // datarate 2 -> half the time low other half high result.controller.dynamicEnergy = calc_dynamic_energy(stats.clockStats.zeroes_to_ones, impedances_.ck_dyn_E); result.controller.staticEnergy += calcStaticTermination(impedances_.wck_termination, stats.wClockStats, impedances_.wck_R_eq, t_WCK_, 2, VDDQ_); // datarate 2 -> half the time low other half high result.controller.dynamicEnergy += calc_dynamic_energy(stats.wClockStats.zeroes_to_ones, impedances_.wck_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_LPDDR5::calcDQSEnergy(const SimulationStats &stats) const { interface_energy_info_t result; // Read result.dram.staticEnergy += calcStaticTermination(impedances_.rdqs_termination, stats.readDQSStats, impedances_.rdqs_R_eq, t_CK_, memspec_.dataRate, VDDQ_); result.dram.dynamicEnergy += calc_dynamic_energy(stats.readDQSStats.zeroes_to_ones, impedances_.rdqs_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_LPDDR5::calcDQEnergyTogglingRate(const TogglingStats &stats) const { interface_energy_info_t result; // Write result.controller.staticEnergy += calcStaticTermination(impedances_.wdq_termination, stats.write, impedances_.wdq_R_eq, t_CK_, memspec_.dataRate, VDDQ_); result.controller.dynamicEnergy += calc_dynamic_energy(stats.write.zeroes_to_ones, impedances_.wdq_dyn_E); // Read result.dram.staticEnergy += calcStaticTermination(impedances_.rdq_termination, stats.read, impedances_.rdq_R_eq, t_CK_, memspec_.dataRate, VDDQ_); result.dram.dynamicEnergy += calc_dynamic_energy(stats.read.zeroes_to_ones, impedances_.rdq_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_LPDDR5::calcDQEnergy(const SimulationStats &stats) const { interface_energy_info_t result; // Write result.controller.staticEnergy += calcStaticTermination(impedances_.wdq_termination, stats.writeBus, impedances_.wdq_R_eq, t_CK_, memspec_.dataRate, VDDQ_); result.controller.dynamicEnergy += calc_dynamic_energy(stats.writeBus.zeroes_to_ones, impedances_.wdq_dyn_E); // Read result.dram.staticEnergy += calcStaticTermination(impedances_.rdq_termination, stats.readBus, impedances_.rdq_R_eq, t_CK_, memspec_.dataRate, VDDQ_); result.dram.dynamicEnergy += calc_dynamic_energy(stats.readBus.zeroes_to_ones, impedances_.rdq_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_LPDDR5::calcCAEnergy(const SimulationStats &stats) const { interface_energy_info_t result; result.controller.staticEnergy = calcStaticTermination(impedances_.ca_termination, stats.commandBus, impedances_.ca_R_eq, t_CK_, 2, VDDQ_); result.controller.dynamicEnergy = calc_dynamic_energy(stats.commandBus.zeroes_to_ones, impedances_.ca_dyn_E); return result; } interface_energy_info_t InterfaceCalculation_LPDDR5::calcDBIEnergy(const SimulationStats &stats) const { interface_energy_info_t result; // Read result.dram.staticEnergy += calcStaticTermination(impedances_.rdbi_termination, stats.readDBI, impedances_.rdbi_R_eq, t_CK_, memspec_.dataRate, VDDQ_); result.dram.dynamicEnergy += calc_dynamic_energy(stats.readDBI.zeroes_to_ones, impedances_.rdbi_dyn_E); // Write result.controller.staticEnergy += calcStaticTermination(impedances_.wdbi_termination, stats.writeDBI, impedances_.wdbi_R_eq, t_CK_, memspec_.dataRate, VDDQ_); result.controller.dynamicEnergy += calc_dynamic_energy(stats.writeDBI.zeroes_to_ones, impedances_.wdbi_dyn_E); return result; } } // namespace DRAMPower ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr5/interface_calculation_LPDDR5.h ================================================ #ifndef DRAMPOWER_STANDARDS_LPDDR5_INTERFACE_CALCULATION_LPDDR5_H #define DRAMPOWER_STANDARDS_LPDDR5_INTERFACE_CALCULATION_LPDDR5_H #include "DRAMPower/Types.h" #include "DRAMPower/data/energy.h" #include "DRAMPower/memspec/MemSpecLPDDR5.h" #include "DRAMPower/data/stats.h" namespace DRAMPower { class InterfaceCalculation_LPDDR5 { public: InterfaceCalculation_LPDDR5(const MemSpecLPDDR5 &memspec); interface_energy_info_t calculateEnergy(const SimulationStats &stats) const; private: const MemSpecLPDDR5 &memspec_; const MemSpecLPDDR5::MemImpedanceSpec &impedances_; double t_CK_; double t_WCK_; double VDDQ_; interface_energy_info_t calcClockEnergy(const SimulationStats &stats) const; interface_energy_info_t calcDQSEnergy(const SimulationStats &stats) const; interface_energy_info_t calcDQEnergy(const SimulationStats &stats) const; interface_energy_info_t calcCAEnergy(const SimulationStats &stats) const; interface_energy_info_t calcDQEnergyTogglingRate(const TogglingStats &stats) const; interface_energy_info_t calcDBIEnergy(const SimulationStats &stats) const; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_LPDDR5_INTERFACE_CALCULATION_LPDDR5_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/lpddr5/types.h ================================================ #ifndef DRAMPOWER_STANDARDS_LPDDR5_TYPES_H #define DRAMPOWER_STANDARDS_LPDDR5_TYPES_H #include #include "DRAMPower/memspec/MemSpecLPDDR5.h" #include "DRAMPower/standards/lpddr5/LPDDR5Core.h" #include "DRAMPower/standards/lpddr5/LPDDR5Interface.h" #include "DRAMPower/standards/lpddr5/core_calculation_LPDDR5.h" #include "DRAMPower/standards/lpddr5/interface_calculation_LPDDR5.h" namespace DRAMPower { struct LPDDR5Types { using DRAMUtilsMemSpec_t = DRAMUtils::MemSpec::MemSpecLPDDR5; using MemSpec_t = MemSpecLPDDR5; using Core_t = LPDDR5Core; using Interface_t = LPDDR5Interface; using CalcCore_t = Calculation_LPDDR5; using CalcInterface_t = InterfaceCalculation_LPDDR5; }; } // namespace DRAMPower #endif /* DRAMPOWER_STANDARDS_LPDDR5_TYPES_H */ ================================================ FILE: src/DRAMPower/DRAMPower/standards/test_accessor.h ================================================ #ifndef DRAMPOWER_STANDARDS_TEST_ACCESSOR_H #define DRAMPOWER_STANDARDS_TEST_ACCESSOR_H #include "DRAMPower/standards/ddr4/DDR4Core.h" #include "DRAMPower/standards/ddr5/DDR5Core.h" #include "DRAMPower/standards/lpddr4/LPDDR4Core.h" #include "DRAMPower/standards/lpddr5/LPDDR5Core.h" namespace DRAMPower::internal { // Tests access to private members of DDR4, DDR5, LPDDR4, and LPDDR5 classes. // https://github.com/google/googletest/blob/main/docs/advanced.md#testing-private-code template class TestAccessor { public: static std::vector& getRanks(Core& core) { return core.m_ranks; } }; // Specializations for each standard static const TestAccessor DDR4TestAccessor; static const TestAccessor DDR5TestAccessor; static const TestAccessor LPDDR4TestAccessor; static const TestAccessor LPDDR5TestAccessor; #ifndef DRAMPOWER_TESTING #error "test-internal.h should only be included in test files" #endif } // namespace DRAMPower::internal #endif /* DRAMPOWER_STANDARDS_TEST_ACCESSOR_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/Deserialize.h ================================================ /* * Copyright (c) 2023, RPTU Kaiserslautern-Landau * 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. * * Author: * Derek Christ * Marco Mörz */ #ifndef DRAMPOWER_UTIL_DESERIALIZE_H #define DRAMPOWER_UTIL_DESERIALIZE_H #include namespace DRAMPower::util { class Deserialize { protected: Deserialize() = default; Deserialize(const Deserialize&) = default; Deserialize(Deserialize&&) = default; Deserialize& operator=(const Deserialize&) = default; Deserialize& operator=(Deserialize&&) = default; public: virtual ~Deserialize() = default; virtual void deserialize(std::istream& stream) = 0; }; } // namespace DRAMPower::util #endif /* DRAMPOWER_UTIL_DESERIALIZE_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/ImplicitCommandHandler.h ================================================ #ifndef DRAMPOWER_UTIL_IMPLICITCOMMANDHANDLER_H #define DRAMPOWER_UTIL_IMPLICITCOMMANDHANDLER_H #include #include #include #include #include #include #include namespace DRAMPower { namespace details { template void addImplicitCommand(Queue& queue, timestamp_t timestamp, Func&& func) { auto entry = std::make_pair(timestamp, std::forward(func)); auto upper = std::upper_bound(queue.begin(), queue.end(), entry, [](const auto& lhs, const auto& rhs) { return lhs.first < rhs.first; }); queue.emplace(upper, entry); } } // namespace details template class ImplicitCommandHandler; template class ImplicitCommandHandler { // Public type definitions public: using CommandContext_t = std::add_lvalue_reference_t>; using implicitCommand_t = std::function; using implicitCommandListEntry_t = std::pair; using implicitCommandList_t = std::deque; // Public member functions public: template void addImplicitCommand(timestamp_t timestamp, Func&& func) { details::addImplicitCommand(m_implicitCommandList, timestamp, std::forward(func)); } void processImplicitCommandQueue(CommandContext_t context, timestamp_t timestamp, timestamp_t &last_command_time) { while (!m_implicitCommandList.empty() && m_implicitCommandList.front().first <= timestamp) { // Execute implicit command functor auto& [i_timestamp, i_implicitCommand] = m_implicitCommandList.front(); i_implicitCommand(context); last_command_time = i_timestamp; m_implicitCommandList.pop_front(); } } std::size_t implicitCommandCount() const { return m_implicitCommandList.size(); } // Private member variables private: implicitCommandList_t m_implicitCommandList; }; template<> class ImplicitCommandHandler { // Public type definitions public: using implicitCommand_t = std::function; using implicitCommandListEntry_t = std::pair; using implicitCommandList_t = std::deque; // Public member functions public: template void addImplicitCommand(timestamp_t timestamp, Func&& func) { details::addImplicitCommand(m_implicitCommandList, timestamp, std::forward(func)); } void processImplicitCommandQueue(timestamp_t timestamp, timestamp_t &last_command_time) { while (!m_implicitCommandList.empty() && m_implicitCommandList.front().first <= timestamp) { // Execute implicit command functor auto& [i_timestamp, i_implicitCommand] = m_implicitCommandList.front(); i_implicitCommand(); last_command_time = i_timestamp; m_implicitCommandList.pop_front(); } } std::size_t implicitCommandCount() const { return m_implicitCommandList.size(); } // Private member variables private: implicitCommandList_t m_implicitCommandList; }; } // namespace DRAMPower #endif /* DRAMPOWER_UTIL_IMPLICITCOMMANDHANDLER_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/PatternHandler.h ================================================ #ifndef DRAMPOWER_UTIL_PATTERNHANDLER_H #define DRAMPOWER_UTIL_PATTERNHANDLER_H #include #include #include #include #include #include #include #include #include #include namespace DRAMPower { template class PatternHandler : public util::Serialize, public util::Deserialize { // Public type definitions+ public: using commandEnum_t = CommandEnum; using commandPattern_t = std::vector; using commandPatternMap_t = std::vector; // Constructors and assignment operators public: PatternHandler() = delete; // No default constructor PatternHandler(const PatternHandler&) = default; PatternHandler& operator=(const PatternHandler&) = default; PatternHandler(PatternHandler&&) = default; PatternHandler& operator=(PatternHandler&&) = default; ~PatternHandler() = default; // Constructor with encoder overrides and initial patterns explicit PatternHandler(PatternEncoderOverrides encoderoverrides, uint64_t initPattern = 0) : m_encoder(encoderoverrides) , m_commandPatternMap(static_cast(commandEnum_t::COUNT), commandPattern_t {}) , m_lastPattern(initPattern) {} // Constructor with no encoder overrides and initial patterns explicit PatternHandler(uint64_t initPattern = 0) : m_lastPattern(initPattern) , m_commandPatternMap(static_cast(commandEnum_t::COUNT), commandPattern_t {}) {} // Public member functions public: PatternEncoder& getEncoder() { return m_encoder; } const PatternEncoder& getEncoder() const { return m_encoder; } template void registerPattern(const commandPattern_t &pattern) { assert(m_commandPatternMap.size() > static_cast(cmd_type)); m_commandPatternMap[static_cast(cmd_type)] = pattern; } template void registerPattern(std::initializer_list pattern) { assert(m_commandPatternMap.size() > static_cast(cmd_type)); m_commandPatternMap[static_cast(cmd_type)] = commandPattern_t(pattern); } const commandPattern_t& getPattern(CmdType cmd_type) const { assert(m_commandPatternMap.size() > static_cast(cmd_type)); return m_commandPatternMap[static_cast(cmd_type)]; } uint64_t getCommandPattern(const Command& cmd) { if (m_commandPatternMap[static_cast(cmd.type)].empty()) { // No pattern registered for this command throw std::runtime_error("No pattern registered for this command"); } const auto& pattern = m_commandPatternMap[static_cast(cmd.type)]; m_lastPattern = m_encoder.encode(cmd, pattern, m_lastPattern); return m_lastPattern; } uint64_t getCoordinatePattern(const TargetCoordinate& coordinate, const commandPattern_t& pattern) { if (pattern.empty()) { // No pattern provided throw std::runtime_error("No pattern provided for this coordinate"); } m_lastPattern = m_encoder.encode(coordinate, pattern, m_lastPattern); return m_lastPattern; } // Overrides public: void serialize(std::ostream& stream) const override { m_encoder.serialize(stream); } void deserialize(std::istream& stream) override { m_encoder.deserialize(stream); } // Private member variables private: PatternEncoder m_encoder; commandPatternMap_t m_commandPatternMap; uint64_t m_lastPattern; }; } // namespace DRAMPower #endif /* DRAMPOWER_UTIL_PATTERNHANDLER_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/RegisterHelper.h ================================================ #ifndef DRAMPOWER_UTIL_REGISTERHELPER_H #define DRAMPOWER_UTIL_REGISTERHELPER_H #include #include #include namespace DRAMPower::util { namespace coreHelpers { template decltype(auto) bankHandler(const Command& cmd, std::vector& ranks, Owner owner, Func &&member_func) { assert(ranks.size()>cmd.targetCoordinate.rank); auto & rank = ranks.at(cmd.targetCoordinate.rank); assert(rank.banks.size()>cmd.targetCoordinate.bank); auto & bank = rank.banks.at(cmd.targetCoordinate.bank); rank.commandCounter.inc(cmd.type); return (owner->*member_func)(rank, bank, cmd.timestamp); } template decltype(auto) bankHandlerIdx(const Command& cmd, std::vector& ranks, Owner owner, Func &&member_func) { assert(ranks.size()>cmd.targetCoordinate.rank); assert(ranks.at(cmd.targetCoordinate.rank).banks.size()>cmd.targetCoordinate.bank); ranks.at(cmd.targetCoordinate.rank).commandCounter.inc(cmd.type); return (owner->*member_func)(cmd.targetCoordinate.rank, cmd.targetCoordinate.bank, cmd.timestamp); } template decltype(auto) rankHandler(const Command& cmd, std::vector& ranks, Owner owner, Func &&member_func) { assert(ranks.size()>cmd.targetCoordinate.rank); auto & rank = ranks.at(cmd.targetCoordinate.rank); rank.commandCounter.inc(cmd.type); return (owner->*member_func)(rank, cmd.timestamp); } template decltype(auto) rankHandlerIdx(const Command& cmd, std::vector& ranks, Owner owner, Func &&member_func) { assert(ranks.size()>cmd.targetCoordinate.rank); ranks.at(cmd.targetCoordinate.rank).commandCounter.inc(cmd.type); return (owner->*member_func)(cmd.targetCoordinate.rank, cmd.timestamp); } template decltype(auto) bankGroupHandler(const Command& cmd, std::vector& ranks, Owner owner, Func &&member_func) { assert(ranks.size()>cmd.targetCoordinate.rank); auto& rank = ranks.at(cmd.targetCoordinate.rank); assert(rank.banks.size()>cmd.targetCoordinate.bank); if (cmd.targetCoordinate.bank >= rank.banks.size()) { throw std::invalid_argument("Invalid bank targetcoordinate"); } auto bank_id = cmd.targetCoordinate.bank; rank.commandCounter.inc(cmd.type); return (owner->*member_func)(rank, bank_id, cmd.timestamp); } template decltype(auto) bankGroupHandlerIdx(const Command& cmd, std::vector& ranks, Owner owner, Func &&member_func) { assert(ranks.size()>cmd.targetCoordinate.rank); auto& rank = ranks.at(cmd.targetCoordinate.rank); assert(rank.banks.size()>cmd.targetCoordinate.bank); if (cmd.targetCoordinate.bank >= rank.banks.size()) { throw std::invalid_argument("Invalid bank targetcoordinate"); } auto bank_id = cmd.targetCoordinate.bank; rank.commandCounter.inc(cmd.type); return (owner->*member_func)(cmd.targetCoordinate.rank, bank_id, cmd.timestamp); } } // namespace coreHelpers } // namespace DRAMPower::util #endif /* DRAMPOWER_UTIL_REGISTERHELPER_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/Router.h ================================================ #ifndef DRAMPOWER_UTIL_ROUTER_H #define DRAMPOWER_UTIL_ROUTER_H #include #include #include #include #include #include namespace DRAMPower { template class Router { // Public type definitions public: using commandEnum_t = CommandEnum; using commandHandler_t = std::function; using commandRouter_t = std::vector; // Constructors and assignment operators public: Router() = delete; // No default constructor Router(const Router&) = default; Router& operator=(const Router&) = default; Router(Router&&) = default; Router& operator=(Router&&) = default; ~Router() = default; // constructor with default handler Router(std::optional defaultHandler = std::nullopt) : m_router(static_cast(commandEnum_t::COUNT), defaultHandler.value_or([](const Command&) {})) {} // Public member functions public: // Add a command handler to the router for the given command template void routeCommand(Func&& func) { assert(m_router.size() > static_cast(cmd)); m_router[static_cast(cmd)] = std::forward(func); } // Execute the command handler for the given command void executeCommand(const Command& command) const { assert(m_router.size() > static_cast(command.type)); m_router[static_cast(command.type)](command); } std::size_t size() const { return m_router.size(); } // Private member variables private: commandRouter_t m_router; }; } // namespace DRAMPower #endif /* DRAMPOWER_UTIL_ROUTER_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/Serialize.h ================================================ /* * Copyright (c) 2023, RPTU Kaiserslautern-Landau * 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. * * Author: * Derek Christ * Marco Mörz */ #ifndef DRAMPOWER_UTIL_SERIALIZE_H #define DRAMPOWER_UTIL_SERIALIZE_H #include namespace DRAMPower::util { class Serialize { protected: Serialize() = default; Serialize(const Serialize&) = default; Serialize(Serialize&&) = default; Serialize& operator=(const Serialize&) = default; Serialize& operator=(Serialize&&) = default; public: virtual ~Serialize() = default; virtual void serialize(std::ostream& stream) const = 0; }; } // namespace DRAMPower::util #endif /* DRAMPOWER_UTIL_SERIALIZE_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/binary_ops.h ================================================ #ifndef DRAMPOWER_UTIL_BINARY_OPS_H #define DRAMPOWER_UTIL_BINARY_OPS_H #include #include #if defined(__GNUC__) #define POPCOUNT __builtin_popcountll #elif defined(_MSC_VER) #include #if defined(_M_X64) #define POPCOUNT _mm_popcnt_u64 #else #define POPCOUNT _mm_popcnt_u32 #endif #elif defined(__cpp_lib_bitops) #include #define POPCOUNT std::popcount #else #include inline std::size_t popcount_(uint64_t n) { return std::bitset<64>(n).count(); }; #define POPCOUNT popcount_ #endif namespace DRAMPower::util { template inline std::string to_string(const T& bitset) { std::stringstream ss; ss << '['; for (std::size_t i = bitset.size(); i > 0;) { ss << bitset[--i] ? '1' : '0'; } ss << ']'; return ss.str(); }; struct BinaryOps { template static std::size_t popcount(const T& bitset) { return POPCOUNT(bitset); } template static std::size_t popcount(const std::bitset& bitset) { return bitset.count(); } template static std::size_t zero_to_ones(const T& p, const T& q) { return popcount(~p & q); } template static std::size_t one_to_zeroes(const T& p, const T& q) { return popcount(p & ~q); } template static std::size_t bit_changes(const T& p, const T& q) { return popcount(p ^ q); } }; }; #endif /* DRAMPOWER_UTIL_BINARY_OPS_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/burst_storage.h ================================================ #ifndef DRAMPOWER_UTIL_BURST_STORAGE_H #define DRAMPOWER_UTIL_BURST_STORAGE_H #include "DRAMUtils/util/types.h" #include "DRAMPower/Types.h" #include "DRAMPower/util/binary_ops.h" #include "DRAMPower/util/bus_types.h" #include "DRAMPower/util/Serialize.h" #include "DRAMPower/util/Deserialize.h" #include #include #include #include #include #include #include #include namespace DRAMPower::util { template struct BitsetContainer { using bitset_t = std::bitset; bitset_t bitset{}; std::size_t count = 0; timestamp_t load_time = 0; }; template struct BusContainer { using bitset_t = std::bitset; std::vector bursts{}; std::size_t width = 0; std::size_t count = 0; timestamp_t load_time = 0; }; // Declaration template struct burst_storage_impl { static_assert(DRAMUtils::util::always_false::value, "No specialization available for type Burst"); }; // Specializations template struct burst_storage_impl >{ using burst_t = bool; using data_t = BitsetContainer; using bitset_t = typename data_t::bitset_t; using stats_t = util::bus_stats_t; static inline void init(data_t&) { /* No initialization necessary */} static inline stats_t count(const data_t& data, timestamp_t start, timestamp_t end, std::optional prev = std::nullopt) { assert(start >= data.load_time && "start timestamp must be greater or equal to load timestamp"); std::size_t length = end - start; // Clamp length to count if (length > data.count) { length = data.count; } const std::size_t offset = start - data.load_time; assert(length + offset <= data.count && "Invalid bitset access"); bitset_t val = data.bitset >> offset; // Mask calculation bitset_t mask; mask.set(); if (length < bitset_size) { mask >>= (bitset_size - length); } if (0 == length) { mask.reset(); } // Extract window depending on load, start, end val &= mask; // Stats bus_stats_t stats{}; // Prev to offset toggles if (prev.has_value() && length != 0 && 0 == offset) { stats.ones_to_zeroes = prev.value() && ~val[0]; stats.zeroes_to_ones = !prev.value() && val[0]; stats.bit_changes += (0 != stats.ones_to_zeroes && 0 != stats.zeroes_to_ones) ? 1 : 0; } // Count ones, zeroes stats.ones = val.count(); stats.zeroes = length - stats.ones; // Extract toggles bitset_t shifted = val >> 1; bitset_t trans_mask = mask >> 1; bitset_t toggles = (val ^ shifted) & trans_mask; // Calculate transitions stats.bit_changes = util::BinaryOps::popcount(toggles); bitset_t z2o = toggles & shifted; stats.zeroes_to_ones = util::BinaryOps::popcount(z2o); stats.ones_to_zeroes = stats.bit_changes - stats.zeroes_to_ones; return stats; } static inline void resize(data_t&, std::size_t size) { assert(size <= bitset_size && "bitset_size not enough"); } static inline void push_back(data_t& data, timestamp_t load_time, burst_t bit) { if (load_time != data.load_time) { data.count = 0; data.load_time = load_time; } resize(data, data.count + 1); data.bitset.set(data.count, bit); data.count++; } static inline burst_t& get(data_t& data, std::size_t index) { assert((data.count > index) && "Invalid access"); return data.bitset[index]; } static inline burst_t get_const(const data_t& data, std::size_t n) { assert((data.count > n) && "Invalid access"); return data.bitset[data.count - 1 - n]; } static inline std::size_t size(const data_t& data) { return data.count; } static inline void clear(data_t& data) { data.count = 0; data.load_time = 0; } static inline std::size_t getCount(const data_t& data) { return data.count; } static inline std::size_t setCount(data_t& data, std::size_t count) { return data.count = count; } static inline timestamp_t endTime(const data_t& data) { return data.load_time + data.count; } static inline void setLoadTime(data_t& data, timestamp_t timestamp) { data.load_time = timestamp; } static inline void serialize(const data_t& data, std::ostream& stream) { stream.write(reinterpret_cast(&data.count), sizeof(data.count)); stream.write(reinterpret_cast(&data.load_time), sizeof(data.load_time)); std::array burst_data; for (std::size_t i = 0; i < burst_data.size(); ++i) { burst_data[i] = 0; for (std::size_t j = 0; j < 8 && (i * 8 + j) < bitset_size; ++j) { if (data.bitset.test(i * 8 + j)) { burst_data[i] |= (1 << j); } } } stream.write(reinterpret_cast(burst_data.data()), burst_data.size()); } static inline void deserialize(data_t& data, std::istream& stream) { stream.read(reinterpret_cast(&data.count), sizeof(data.count)); stream.read(reinterpret_cast(&data.load_time), sizeof(data.load_time)); std::array burst_data{}; stream.read(reinterpret_cast(burst_data.data()), burst_data.size()); for (std::size_t i = 0; i < burst_data.size(); ++i) { for (std::size_t j = 0; j < 8 && (i * 8 + j) < bitset_size; ++j) { data.bitset.set(i * 8 + j, (burst_data[i] >> j) & 1); } } } }; template struct burst_storage_impl > { using data_t = BusContainer; // using data_t = BusContainer<32>; using bitset_t = typename data_t::bitset_t; using burst_t = bitset_t; // using bitset_t = std::bitset<32>; using stats_t = util::bus_stats_t; static inline void init(data_t& data, std::size_t width) { data.width = width; } static inline stats_t count(const data_t& data, timestamp_t start, timestamp_t end, std::optional prev = std::nullopt) { assert(start >= data.load_time && "start timestamp must be greater or equal to load timestamp"); std::size_t length = end - start; // Clamp length to count if (length > data.count) { length = data.count; } const std::size_t offset = start - data.load_time; assert(length + offset <= data.bursts.size() && "Invalid access"); // Stats bus_stats_t stats{}; for (std::size_t i = offset; i < length; i++) { const auto& entry = data.bursts[i]; if (0 == offset) { // Last Burst to this burst stats += diff(data, prev, entry); } else { auto ones = util::BinaryOps::popcount(entry); stats.ones += ones; stats.zeroes += data.width - ones; const auto& entry_prev = data.bursts[i - 1]; stats.bit_changes += util::BinaryOps::bit_changes(entry_prev, entry); stats.ones_to_zeroes += util::BinaryOps::one_to_zeroes(entry_prev, entry); stats.zeroes_to_ones += stats.bit_changes - stats.ones_to_zeroes; } } return stats; } static inline void resize(data_t& data, std::size_t size) { if (data.bursts.size() < size) { data.bursts.resize(size); } } static inline void push_back(data_t& data, timestamp_t load_time, burst_t burst) { if (load_time != data.load_time) { data.count = 0; data.load_time = load_time; } resize(data, data.count + 1); data.bursts[data.count] = burst; data.count++; } static inline burst_t& get(data_t& data, std::size_t index) { assert((data.bursts.size() > index) && "Invalid access"); return data.bursts[index]; } static inline const burst_t& get_const(const data_t& data, std::size_t n) { assert((data.count > n) && "Invalid access"); return data.bursts[data.count - 1 - n]; } static inline std::size_t size(const data_t& data) { return data.bursts.size(); } static inline void clear(data_t& data) { data.count = 0; data.load_time = 0; } static inline std::size_t getCount(const data_t& data) { return data.count; } static inline std::size_t setCount(data_t& data, std::size_t count) { return data.count = count; } static inline timestamp_t endTime(const data_t& data) { return data.load_time + data.count; } static inline void setLoadTime(data_t& data, timestamp_t timestamp) { data.load_time = timestamp; } static inline void serializeBurst(std::ostream& stream, const burst_t& burst) { std::array burst_data; for (std::size_t i = 0; i < burst_data.size(); ++i) { burst_data[i] = 0; for (std::size_t j = 0; j < 8 && (i * 8 + j) < bitset_size; ++j) { if (burst.test(i * 8 + j)) { burst_data[i] |= (1 << j); } } } stream.write(reinterpret_cast(burst_data.data()), burst_data.size()); } static inline void deserializeBurst(std::istream& stream, burst_t& burst) { std::array burst_data{}; stream.read(reinterpret_cast(burst_data.data()), burst_data.size()); for (std::size_t i = 0; i < burst_data.size(); ++i) { for (std::size_t j = 0; j < 8 && (i * 8 + j) < bitset_size; ++j) { burst.set(i * 8 + j, (burst_data[i] >> j) & 1); } } } static inline void serialize(const data_t& data, std::ostream& stream) { std::size_t totalBursts = data.bursts.size(); stream.write(reinterpret_cast(&data.count), sizeof(data.count)); stream.write(reinterpret_cast(&data.load_time), sizeof(data.load_time)); stream.write(reinterpret_cast(&totalBursts), sizeof(totalBursts)); for (const auto& burst : data.bursts) { serializeBurst(stream, burst); } } static inline void deserialize(data_t& data, std::istream& stream) { std::size_t totalBursts = 0; stream.read(reinterpret_cast(&data.count), sizeof(data.count)); stream.read(reinterpret_cast(&data.load_time), sizeof(data.load_time)); stream.read(reinterpret_cast(&totalBursts), sizeof(totalBursts)); data.bursts.clear(); data.bursts.resize(totalBursts); for (std::size_t i = 0; i < totalBursts; ++i) { deserializeBurst(stream, data.bursts[i]); } } }; template class burst_storage : public Serialize, public Deserialize { public: using impl_data_t = T; using impl_t = burst_storage_impl; using burst_t = typename impl_t::burst_t; private: impl_data_t m_bursts; public: template explicit burst_storage(Args&&... args) { impl_t::init(m_bursts, std::forward(args)...); } public: void push_back(timestamp_t load_time, burst_t burst) { impl_t::push_back(m_bursts, load_time, burst); } inline decltype(auto) count(timestamp_t start, timestamp_t end, std::optional prev = std::nullopt) const { return impl_t::count(m_bursts, start, end, prev); } inline burst_t& get_or_add(std::size_t index) { impl_t::resize(m_bursts, index + 1); return impl_t::get(m_bursts, index); } void setCount(std::size_t count) { impl_t::setCount(m_bursts, count); } bool empty() const { return 0 == impl_t::getCount(m_bursts); }; std::size_t size() const { return impl_t::getCount(m_bursts); }; burst_t get_burst(std::size_t n) const { return impl_t::get_const(m_bursts, n); }; void clear() { impl_t::clear(m_bursts); } void setLoadTime(timestamp_t timestamp) { impl_t::setLoadTime(m_bursts, timestamp); } timestamp_t endTime() const { return impl_t::endTime(m_bursts); } void serialize(std::ostream& stream) const override { impl_t::serialize(m_bursts, stream); } void deserialize(std::istream& stream) override { impl_t::deserialize(m_bursts, stream); } }; struct BurstStorageInsertHelper { template static inline void insert_data(burst_storage>& burst_storage, timestamp_t timestamp, std::size_t width, const uint8_t* data, std::size_t n_bits, bool invert = false) { size_t n_bursts = n_bits / width; size_t burst_offset = 0; size_t byte_index = 0; size_t bit_index = 0; for (std::size_t i = 0; i < n_bursts; ++i) { // Extract bursts std::bitset &bits = burst_storage.get_or_add(i); burst_offset = i * width; for (std::size_t j = 0; j < width && (burst_offset + j) < n_bits; ++j) { // Extract bit std::size_t bit_position = bit_index % 8; bool bit_value = (data[byte_index] >> bit_position) & 1; bits.set(j, invert ? !bit_value : bit_value); bit_index++; if (bit_index % 8 == 0) { ++byte_index; } } } burst_storage.setLoadTime(timestamp); burst_storage.setCount(n_bursts); } }; } // namespace DRAMPower::util #endif /* DRAMPOWER_UTIL_BURST_STORAGE_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/bus.cpp ================================================ ================================================ FILE: src/DRAMPower/DRAMPower/util/bus.h ================================================ #ifndef DRAMPOWER_UTIL_BUS_H #define DRAMPOWER_UTIL_BUS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DRAMPower::util { template class Bus : public Serialize, public Deserialize { private: enum class BusInitPatternSpec_ { L = 0, H = 1, Z = 2, CUSTOM = 3 }; public: using burst_storage_t = util::burst_storage>; using burst_t = typename burst_storage_t::burst_t; using stats_t = bus_stats_t; stats_t stats; private: burst_storage_t burst_storage; timestamp_t last_load = 0; bool enableflag = true; timestamp_t virtual_disable_timestamp = 0; std::size_t width = 0; uint64_t datarate = 1; bool init_load = false; PendingStats pending_stats; std::optional last_pattern; burst_t zero_pattern; burst_t one_pattern; BusIdlePatternSpec idle_pattern; BusInitPatternSpec_ init_pattern; std::optional custom_init_pattern; private: Bus(std::size_t width, uint64_t datarate, BusIdlePatternSpec idle_pattern, BusInitPatternSpec_ init_pattern, std::optional custom_init_pattern = std::nullopt, bool enableflag = true ) : burst_storage(width) , enableflag(enableflag) , width(width) , datarate(datarate) , idle_pattern(idle_pattern) , init_pattern(init_pattern) , custom_init_pattern (custom_init_pattern) { // Initialize zero and one patterns this->zero_pattern = burst_t(); this->one_pattern = burst_t(); this->zero_pattern.reset(); for (std::size_t i = 0; i < width; i++) { this->one_pattern.set(i, true); } // Initialize last pattern and init stats switch(init_pattern) { case BusInitPatternSpec_::L: this->last_pattern = zero_pattern; break; case BusInitPatternSpec_::H: this->last_pattern = one_pattern; break; case BusInitPatternSpec_::Z: this->last_pattern = std::nullopt; break; case BusInitPatternSpec_::CUSTOM: // Custom init pattern with no value is equivalent to Z this->last_pattern = custom_init_pattern; if(custom_init_pattern.has_value()) { assert(custom_init_pattern.value().size() == width); } else { this->init_pattern = BusInitPatternSpec_::Z; } break; default: assert(false); this->last_pattern = std::nullopt; } }; static BusInitPatternSpec_ convertInitPattern(BusInitPatternSpec pattern) { switch(pattern) { case BusInitPatternSpec::L: return BusInitPatternSpec_::L; case BusInitPatternSpec::H: return BusInitPatternSpec_::H; case BusInitPatternSpec::Z: return BusInitPatternSpec_::Z; default: assert(false); // Invalid init pattern return BusInitPatternSpec_::Z; } } void add_previous_stats(timestamp_t virtual_timestamp) { // Add pending stats from last load assert(this->pending_stats.getTimestamp() <= virtual_timestamp); // No interleaved commands if(this->pending_stats.isPending() && this->pending_stats.getTimestamp() < virtual_timestamp) { this->stats += this->pending_stats.getStats(); this->pending_stats.clear(); } // Advance counters to new timestamp for (auto n = this->last_load; virtual_timestamp != 0 && n < virtual_timestamp - 1; n++) { this->stats += diff(this->at(n), this->at(n + 1)); // Last: (virtual_timestamp - 2, virtual_timestamp - 1) }; // Pattern for pending stats (virtual_timestamp - 1, virtual_timestamp) if(virtual_timestamp > 0) { this->last_pattern = this->at(virtual_timestamp - 1); } } void add_data(timestamp_t virtual_timestamp, const uint8_t * data, std::size_t n_bits) { // Add new burst to storage BurstStorageInsertHelper::insert_data(this->burst_storage, virtual_timestamp, width, data, n_bits); // Adjust statistics for new data this->pending_stats.setPendingStats(virtual_timestamp, diff( this->last_pattern, this->at(this->last_load) )); // last pattern for idle pattern if (this->burst_storage.size() != 0) { this->last_pattern = this->burst_storage.get_burst(this->burst_storage.size()-1); } } public: // Ensure type safety for init_pattern with 2 seperate constructors Bus(std::size_t width, uint64_t datarate, BusIdlePatternSpec idle_pattern, BusInitPatternSpec init_pattern, bool enable = true) : Bus(width, datarate, idle_pattern, convertInitPattern(init_pattern), std::nullopt, enable) {} Bus(std::size_t width, uint64_t datarate, BusIdlePatternSpec idle_pattern, burst_t custom_init_pattern, bool enable) : Bus(width, datarate, idle_pattern, BusInitPatternSpec_::CUSTOM, custom_init_pattern, enable) {} void set_idle_pattern(BusIdlePatternSpec idle_pattern) { this->idle_pattern = idle_pattern; } void load(timestamp_t timestamp, const uint8_t * data, std::size_t n_bits) { if (!this->enableflag) { return; } // check timestamp * this->datarate no overflow assert(timestamp <= std::numeric_limits::max() / this->datarate); if(timestamp > std::numeric_limits::max() / this->datarate) { std::cout << "[Error] timestamp * datarate overflows" << std::endl; } timestamp_t virtual_timestamp = timestamp * this->datarate; assert(this->last_load + burst_storage.size() <= virtual_timestamp); // No interleaved commands // Init stats if(!this->init_load && virtual_timestamp == 0) { // stats added as pending_stats this->init_load = true; } else if(!this->init_load) { // virtual_timestamp > 0 this->init_load = true; this->stats += diff(this->last_pattern, this->at(0)); } add_previous_stats(virtual_timestamp); this->last_load = virtual_timestamp; // Assumption: A load can only be executed with a virtual_timestamp > last_load + burst_storage.size() // given the assumption the burst_storage can safely be cleared this->burst_storage.clear(); add_data(virtual_timestamp, data, n_bits); }; void load(timestamp_t timestamp, uint64_t data, std::size_t burst_length) { const std::size_t n_bits = burst_length * width; assert(n_bits <= std::numeric_limits::digits); // Ensure data fits in a uint64_t const std::size_t n_bytes = (n_bits + 7) / 8; // Round up to nearest byte std::array bytes; // Extract bytes in little-endian order for consistency across platforms for (std::size_t i = 0; i < n_bytes; ++i) { bytes[i] = static_cast((data >> (i * 8)) & 0xFF); } this->load(timestamp, bytes.data(), n_bits); }; // Returns optional burst (std::nullopt if idle pattern is Z) std::optional at(timestamp_t n) const { // Assert timestamp does not lie in past assert(n >= last_load); if (n - this->last_load >= burst_storage.size()) { switch(this->idle_pattern) { case BusIdlePatternSpec::L: return std::make_optional(this->zero_pattern); case BusIdlePatternSpec::H: return std::make_optional(this->one_pattern); case BusIdlePatternSpec::Z: return std::nullopt; case BusIdlePatternSpec::LAST_PATTERN: return this->last_pattern; default: assert(false); return std::nullopt; } } auto burst = this->burst_storage.get_burst(std::size_t(n - this->last_load)); return std::make_optional(burst); }; // Returns timestamp of last burst not including burst to idle_pattern transition timestamp_t get_lastburst_timestamp(bool relative_to_clock = true) const { timestamp_t lastburst = this->last_load; // an init load with burst can be checked with the burst_storage size // if there is no init_load the toggles are computed in get_stats if (this->burst_storage.size() != 0) { // for each load there is at least one burst // get last burst timestamp lastburst += this->burst_storage.size(); } // no burst or pending_stats present -> no load of the bus (last_load == 0) if (relative_to_clock) { auto remainder = lastburst % this->datarate; if (remainder != 0) { lastburst += this->datarate - remainder; } return lastburst / this->datarate; } return lastburst; } // Returns the timestamp when the bus is disabled timestamp_t disable(timestamp_t timestamp) { // Already disabled if (!this->enableflag) { assert(timestamp * this->datarate >= this->virtual_disable_timestamp); return timestamp; } this->virtual_disable_timestamp = timestamp * this->datarate; assert(this->virtual_disable_timestamp >= this->last_load); // Init stats if(!this->init_load && this->virtual_disable_timestamp == 0) { // tranition from init_pattern cannot be computed this->init_load = true; } else if(!this->init_load) { // virtual_disable_timestamp > 0 this->init_load = true; this->stats += diff(this->last_pattern, this->at(0)); } add_previous_stats(this->virtual_disable_timestamp); // Clear pending stats this->burst_storage.clear(); this->last_load = this->virtual_disable_timestamp; this->enableflag = false; return this->virtual_disable_timestamp / this->datarate; } void enable(timestamp_t timestamp) { if (this->enableflag) { return; } assert(timestamp * datarate > this->virtual_disable_timestamp); // Shift the counters to the enabled timestamp this->last_load = timestamp * this->datarate; // Add pending stats at enable timestamp this->pending_stats.setPendingStats(this->last_load, this->diff(std::nullopt, this->at(this->last_load))); this->enableflag = true; } size_t get_width() const { return width; }; // Get stats not including timestamp t stats_t get_stats(timestamp_t timestamp) const { timestamp_t t_virtual = timestamp * this->datarate; assert(t_virtual >= this->last_load); // Return empty stats for t_virtual = 0 if(t_virtual == 0) { return this->stats; } auto stats = this->stats; if(!this->init_load && this->enableflag) { // t > 0 and no load on bus // only add init_transition if the bus was enabled at t=0 // Add transition from init pattern to idle pattern stats += diff(this->last_pattern, this->at(0)); } // Add pending stats from last load if(this->pending_stats.isPending() && this->pending_stats.getTimestamp() < t_virtual) { stats += this->pending_stats.getStats(); } // Advance stats to new timestamp if enabled if (this->enableflag) { // stats += burst_storage.count(this->last_load, t_virtual - 1); for (auto n = this->last_load; n < t_virtual - 1; n++) { stats += diff(this->at(n), this->at(n + 1)); // Last: (timestamp - 2, timestamp - 1) } } return stats; }; stats_t diff(std::optional high, std::optional low) const { stats_t stats; if(low.has_value()) { stats.ones += util::BinaryOps::popcount(low.value()); stats.zeroes += width - stats.ones; } if(high.has_value() && low.has_value()) { stats.bit_changes += util::BinaryOps::bit_changes(high.value(), low.value()); stats.ones_to_zeroes += util::BinaryOps::one_to_zeroes(high.value(), low.value()); stats.zeroes_to_ones += util::BinaryOps::zero_to_ones(high.value(), low.value()); } return stats; }; stats_t diff(std::optional high, burst_t low) const { return diff(high, std::make_optional(low)); }; void serialize(std::ostream& stream) const override { this->stats.serialize(stream); this->burst_storage.serialize(stream); stream.write(reinterpret_cast(&this->last_load), sizeof(this->last_load)); stream.write(reinterpret_cast(&this->enableflag), sizeof(this->enableflag)); stream.write(reinterpret_cast(&this->virtual_disable_timestamp), sizeof(this->virtual_disable_timestamp)); stream.write(reinterpret_cast(&this->init_load), sizeof(this->init_load)); this->pending_stats.serialize(stream); stream.write(reinterpret_cast(&this->idle_pattern), sizeof(this->idle_pattern)); stream.write(reinterpret_cast(&this->init_pattern), sizeof(this->init_pattern)); }; void deserialize(std::istream& stream) override { this->stats.deserialize(stream); this->burst_storage.deserialize(stream); stream.read(reinterpret_cast(&this->last_load), sizeof(this->last_load)); stream.read(reinterpret_cast(&this->enableflag), sizeof(this->enableflag)); stream.read(reinterpret_cast(&this->virtual_disable_timestamp), sizeof(this->virtual_disable_timestamp)); stream.read(reinterpret_cast(&this->init_load), sizeof(this->init_load)); this->pending_stats.deserialize(stream); stream.read(reinterpret_cast(&this->idle_pattern), sizeof(this->idle_pattern)); stream.read(reinterpret_cast(&this->init_pattern), sizeof(this->init_pattern)); }; }; } #endif /* DRAMPOWER_UTIL_BUS_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/bus_types.h ================================================ #ifndef DRAMPOWER_UTIL_BUS_TYPES #define DRAMPOWER_UTIL_BUS_TYPES #include #include #include namespace DRAMPower::util { enum class BusIdlePatternSpec { L = 0, H = 1, Z = 2, LAST_PATTERN }; enum class BusInitPatternSpec { L = 0, H = 1, Z = 2, }; struct bus_stats_t :public Serialize, public Deserialize { uint64_t ones = 0; uint64_t zeroes = 0; uint64_t bit_changes = 0; uint64_t ones_to_zeroes = 0; uint64_t zeroes_to_ones = 0; bus_stats_t& operator+=(const bus_stats_t& rhs) { this->bit_changes += rhs.bit_changes; this->ones += rhs.ones; this->zeroes += rhs.zeroes; this->ones_to_zeroes += rhs.ones_to_zeroes; this->zeroes_to_ones += rhs.zeroes_to_ones; return *this; }; bus_stats_t& operator*=(const uint64_t rhs) { this->bit_changes *= rhs; this->ones *= rhs; this->zeroes *= rhs; this->ones_to_zeroes *= rhs; this->zeroes_to_ones *= rhs; return *this; }; friend bus_stats_t operator+(bus_stats_t lhs, const bus_stats_t& rhs) { return lhs += rhs; } friend bus_stats_t operator*(bus_stats_t lhs, const uint64_t rhs) { return lhs *= rhs; } friend bus_stats_t operator*(const uint64_t lhs, bus_stats_t rhs) { return rhs *= lhs; } void serialize(std::ostream& stream) const override { stream.write(reinterpret_cast(&ones), sizeof(ones)); stream.write(reinterpret_cast(&zeroes), sizeof(zeroes)); stream.write(reinterpret_cast(&bit_changes), sizeof(bit_changes)); stream.write(reinterpret_cast(&ones_to_zeroes), sizeof(ones_to_zeroes)); stream.write(reinterpret_cast(&zeroes_to_ones), sizeof(zeroes_to_ones)); } void deserialize(std::istream& stream) override { stream.read(reinterpret_cast(&ones), sizeof(ones)); stream.read(reinterpret_cast(&zeroes), sizeof(zeroes)); stream.read(reinterpret_cast(&bit_changes), sizeof(bit_changes)); stream.read(reinterpret_cast(&ones_to_zeroes), sizeof(ones_to_zeroes)); stream.read(reinterpret_cast(&zeroes_to_ones), sizeof(zeroes_to_ones)); } // Operator == bool operator==(const bus_stats_t& rhs) const { return ones == rhs.ones && zeroes == rhs.zeroes && bit_changes == rhs.bit_changes && ones_to_zeroes == rhs.ones_to_zeroes && zeroes_to_ones == rhs.zeroes_to_ones; } }; } // namespace DRAMPower::util #endif /* DRAMPOWER_UTIL_BUS_TYPES */ ================================================ FILE: src/DRAMPower/DRAMPower/util/cli_architecture_config.h ================================================ #ifndef DRAMPOWER_UTIL_CLI_ARCHITECTURE_CONFIG_H #define DRAMPOWER_UTIL_CLI_ARCHITECTURE_CONFIG_H #include namespace DRAMPower::util { struct CLIArchitectureConfig { uint64_t deviceCount; uint64_t rankCount; uint64_t bankCount; }; }; #endif /* DRAMPOWER_UTIL_CLI_ARCHITECTURE_CONFIG_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/clock.h ================================================ #ifndef DRAMPOWER_UTIL_CLOCK_H #define DRAMPOWER_UTIL_CLOCK_H #include #include #include #include #include #include #include namespace DRAMPower::util { class Clock : public Serialize, public Deserialize { public: using clock_stats_t = bus_stats_t; private: std::optional last_start; clock_stats_t stats; std::size_t dataRate; private: clock_stats_t count(timestamp_t duration) const { // __--__--__--__--__--__--__--__--__--__--__--__-- // f(t) = t / 2; clock_stats_t stats; stats.ones = duration * dataRate / 2; stats.zeroes = duration * dataRate / 2; stats.zeroes_to_ones = duration * dataRate / 2; stats.ones_to_zeroes = duration * dataRate / 2; stats.bit_changes = stats.zeroes_to_ones + stats.ones_to_zeroes; return stats; }; public: Clock(std::size_t _dataRate = 2, bool stopped = false) : dataRate(_dataRate) { if (stopped) last_start = std::nullopt; else last_start = 0; }; public: void stop(timestamp_t t) { assert(last_start.has_value()); assert(*last_start < t); this->stats += count(t - *last_start); last_start.reset(); }; void start(timestamp_t t) { assert(!last_start.has_value()); last_start = t; }; clock_stats_t get_stats_at(timestamp_t t) const { auto stats = this->stats; if (last_start) { stats += count(t - *last_start); }; return stats; }; void serialize(std::ostream& stream) const override { bool hasLastStart = last_start.has_value(); stream.write(reinterpret_cast(&hasLastStart), sizeof(hasLastStart)); if (hasLastStart) { stream.write(reinterpret_cast(&last_start), sizeof(last_start)); } stats.serialize(stream); }; void deserialize(std::istream& stream) override { bool hasLastStart = false; stream.read(reinterpret_cast(&hasLastStart), sizeof(hasLastStart)); if (hasLastStart) { last_start = timestamp_t(); stream.read(reinterpret_cast(&last_start), sizeof(last_start)); } else { last_start.reset(); } stats.deserialize(stream); }; }; }; #endif /* DRAMPOWER_UTIL_CLOCK_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/command_counter.h ================================================ #ifndef DRAMPOWER_UTIL_COMMAND_COUNTER_H #define DRAMPOWER_UTIL_COMMAND_COUNTER_H #include #include #include #include "DRAMPower/util/Serialize.h" #include "DRAMPower/util/Deserialize.h" namespace DRAMPower::util { template class CommandCounter : public Serialize, public Deserialize { public: using counter_t = std::array(CommandEnum::COUNT)>; private: counter_t counter = {0}; public: CommandCounter() = default; public: void inc(CommandEnum cmd) { assert(counter.size() > static_cast(cmd)); counter[static_cast(cmd)] += 1; }; std::size_t get(CommandEnum cmd) const { assert(counter.size() > static_cast(cmd)); return counter[static_cast(cmd)]; }; void serialize(std::ostream& stream) const override { for (const auto &count : counter) { stream.write(reinterpret_cast(&count), sizeof(count)); } } void deserialize(std::istream& stream) override { for (auto &count : counter) { stream.read(reinterpret_cast(&count), sizeof(count)); } } }; } #endif /* DRAMPOWER_UTIL_COMMAND_COUNTER_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/cycle_stats.h ================================================ #ifndef DRAMPOWER_UTIL_CYCLE_STATS_H #define DRAMPOWER_UTIL_CYCLE_STATS_H #include #include #include #include namespace DRAMPower::util { template class interval_counter : public Serialize, public Deserialize { private: T count{ 0 }; std::optional start; std::optional end; public: interval_counter() = default; interval_counter(T start) : start(start) {}; interval_counter(const interval_counter&) = default; interval_counter& operator=(const interval_counter&) = default; interval_counter(interval_counter&&) = default; interval_counter& operator=(interval_counter&&) = default; public: T get_start() const { return start.value_or(0); }; T get_end() const { return end.value_or(0); }; public: bool is_open() const { return start && !end; }; bool is_closed() const { return start && end; }; T get_count() const { return count; }; T get_count_at(T timestamp) const { if (is_open() && timestamp > *start) return count + timestamp - *start; return get_count(); } void add(T value) { this->count += value; }; uint64_t close_interval(uint64_t timestamp) { if (!is_open()) return T{ 0 }; end = timestamp; auto diff = timestamp - *start; count += diff; return diff; } void reset_interval() { this->start.reset(); this->end.reset(); } void start_interval(T start) { this->start = start; this->end.reset(); } void start_interval_if_not_running(T start) { if(!is_open()) start_interval(start); } void serialize(std::ostream& stream) const override { stream.write(reinterpret_cast(&count), sizeof(count)); bool starthasValue = start.has_value(); stream.write(reinterpret_cast(&starthasValue), sizeof(starthasValue)); if (starthasValue) { stream.write(reinterpret_cast(&start), sizeof(start)); } bool endhasValue = end.has_value(); stream.write(reinterpret_cast(&endhasValue), sizeof(endhasValue)); if (endhasValue) { stream.write(reinterpret_cast(&end), sizeof(end)); } } void deserialize(std::istream& stream) override { stream.read(reinterpret_cast(&count), sizeof(count)); bool starthasValue = false; stream.read(reinterpret_cast(&starthasValue), sizeof(starthasValue)); if (starthasValue) { stream.read(reinterpret_cast(&start), sizeof(start)); } else { start.reset(); } bool endhasValue = false; stream.read(reinterpret_cast(&endhasValue), sizeof(endhasValue)); if (endhasValue) { stream.read(reinterpret_cast(&end), sizeof(end)); } else { end.reset(); } } }; } #endif /* DRAMPOWER_UTIL_CYCLE_STATS_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/databus.h ================================================ #ifndef DRAMPOWER_UTIL_DATABUS #define DRAMPOWER_UTIL_DATABUS #include "DRAMPower/Exceptions.h" #include "DRAMPower/util/bus_types.h" #include #include #include #include #include #include #include #include #include #include #include namespace DRAMPower::util { // DataBus class template class DataBus : public Serialize, public Deserialize { public: using Bus_t = util::Bus; using IdlePattern_t = util::BusIdlePatternSpec; using InitPattern_t = util::BusInitPatternSpec; static constexpr BusIdlePatternSpec convertIdlePattern(const DRAMUtils::Config::TogglingRateIdlePattern idlePattern, bool lastPattern) { using namespace DRAMUtils::Config; if (lastPattern) { return BusIdlePatternSpec::LAST_PATTERN; } switch(idlePattern) { case TogglingRateIdlePattern::L: return BusIdlePatternSpec::L; case TogglingRateIdlePattern::H: return BusIdlePatternSpec::H; case TogglingRateIdlePattern::Z: return BusIdlePatternSpec::Z; case TogglingRateIdlePattern::Invalid: throw Exception("Invalid TogglingRateIdlePattern"); } return BusIdlePatternSpec::Z; // Unreachable } static constexpr BusInitPatternSpec convertInitPattern(const DRAMUtils::Config::TogglingRateIdlePattern initPattern) { using namespace DRAMUtils::Config; switch(initPattern) { case TogglingRateIdlePattern::L: return BusInitPatternSpec::L; case TogglingRateIdlePattern::H: return BusInitPatternSpec::H; case TogglingRateIdlePattern::Z: return BusInitPatternSpec::Z; case TogglingRateIdlePattern::Invalid: throw Exception("Invalid TogglingRateIdlePattern"); } return BusInitPatternSpec::Z; // Unreachable } // BusInitPatternSpec public: DataBus(DataBusConfig&& config, DataBusMode mode, bool IdlelastPatternOverride) : busRead( config.width, config.dataRate, convertIdlePattern(config.toggleRateConf.idlePatternRead, IdlelastPatternOverride), convertInitPattern(config.toggleRateConf.idlePatternRead), DataBusMode::Bus == mode ) , busWrite( config.width, config.dataRate, convertIdlePattern(config.toggleRateConf.idlePatternWrite, IdlelastPatternOverride), convertInitPattern(config.toggleRateConf.idlePatternWrite), DataBusMode::Bus == mode ) , togglingHandleRead( config.width, config.dataRate, config.toggleRateConf.togglingRateRead, config.toggleRateConf.dutyCycleRead, config.toggleRateConf.idlePatternRead, DataBusMode::TogglingRate == mode ) , togglingHandleWrite( config.width, config.dataRate, config.toggleRateConf.togglingRateWrite, config.toggleRateConf.dutyCycleWrite, config.toggleRateConf.idlePatternWrite, DataBusMode::TogglingRate == mode ) , busType(mode) , dataRate(config.dataRate) , width(config.width) {} private: void load(Bus_t &bus, TogglingHandle &togglingHandle, timestamp_t timestamp, std::size_t n_bits, const uint8_t *data = nullptr) { switch(busType) { case DataBusMode::Bus: { if (nullptr == data || 0 == n_bits) { // No data to load, skip burst return; } bus.load(timestamp, data, n_bits); break; } case DataBusMode::TogglingRate: togglingHandle.incCountBitLength(timestamp, n_bits); break; } } public: void loadWrite(timestamp_t timestamp, std::size_t n_bits, const uint8_t *data = nullptr) { load(busWrite, togglingHandleWrite, timestamp, n_bits, data); } void loadRead(timestamp_t timestamp, std::size_t n_bits, const uint8_t *data = nullptr) { load(busRead, togglingHandleRead, timestamp, n_bits, data); } void enableBus(timestamp_t timestamp) { busRead.enable(timestamp); busWrite.enable(timestamp); togglingHandleRead.disable(timestamp); togglingHandleWrite.disable(timestamp); busType = DataBusMode::Bus; } void enableTogglingRate(timestamp_t timestamp) { busRead.disable(timestamp); busWrite.disable(timestamp); togglingHandleRead.enable(timestamp); togglingHandleWrite.enable(timestamp); busType = DataBusMode::TogglingRate; } void setTogglingRateDefinition(DRAMUtils::Config::ToggleRateDefinition toggleratedefinition) { togglingHandleRead.setTogglingRateAndDutyCycle(toggleratedefinition.togglingRateRead, toggleratedefinition.dutyCycleRead, toggleratedefinition.idlePatternRead); togglingHandleWrite.setTogglingRateAndDutyCycle(toggleratedefinition.togglingRateWrite, toggleratedefinition.dutyCycleWrite, toggleratedefinition.idlePatternWrite); } timestamp_t lastBurst() const { switch(busType) { case DataBusMode::Bus: return std::max(busWrite.get_lastburst_timestamp(), busRead.get_lastburst_timestamp()); case DataBusMode::TogglingRate: return std::max(togglingHandleRead.get_lastburst_timestamp(), togglingHandleWrite.get_lastburst_timestamp()); } assert(false); return 0; } bool isBus() const { return DataBusMode::Bus == busType; } bool isTogglingRate() const { return DataBusMode::TogglingRate == busType; } std::size_t getWidth() const { return width; } std::size_t getDataRate() const { return dataRate; } void get_stats(timestamp_t timestamp, util::bus_stats_t &busReadStats, util::bus_stats_t &busWriteStats, util::bus_stats_t &togglingHandleReadStats, util::bus_stats_t &togglingHandleWriteStats) const { busReadStats += busRead.get_stats(timestamp); busWriteStats += busWrite.get_stats(timestamp); togglingHandleReadStats += togglingHandleRead.get_stats(timestamp); togglingHandleWriteStats += togglingHandleWrite.get_stats(timestamp); } void serialize(std::ostream &stream) const override { busRead.serialize(stream); busWrite.serialize(stream); togglingHandleRead.serialize(stream); togglingHandleWrite.serialize(stream); stream.write(reinterpret_cast(&busType), sizeof(busType)); } void deserialize(std::istream &stream) override { busRead.deserialize(stream); busWrite.deserialize(stream); togglingHandleRead.deserialize(stream); togglingHandleWrite.deserialize(stream); stream.read(reinterpret_cast(&busType), sizeof(busType)); } private: Bus_t busRead; Bus_t busWrite; TogglingHandle togglingHandleRead; TogglingHandle togglingHandleWrite; DataBusMode busType; std::size_t dataRate; std::size_t width; }; /** DataBusContainer class * This class allows the selection of multiple DataBus types. * The DataBus types are defined in a type_sequence and must be unique. * At least one DataBus type must be defined. * Internally, the DataBusContainer uses a std::variant to store the DataBus types. */ template class DataBusContainer { static_assert(DRAMUtils::util::always_false::value, "DataBusContainer cannot be initialized. Check the DataBus type_sequence."); }; template class DataBusContainer, // Types must be unique and the sequence length must be greater than 0 std::enable_if_t::value && sizeof...(Ts) != 0> > { // Public type definitions public: using UnifiedVariantSequence_t = DRAMUtils::util::type_sequence; using UnifiedVariant_t = std::variant; // Internal storage private: std::size_t m_width; UnifiedVariant_t m_databusVariant; // Constructors public: // The constructor is only valid if T is in the valid variant types Ts... / in the type sequence of the variant template , UnifiedVariantSequence_t>::value, int> = 0> explicit DataBusContainer(T&& databus) : m_width(databus.getWidth()) , m_databusVariant(std::move(databus)) { } // No default constructor DataBusContainer() = delete; // Member functions public: std::size_t getWidth() const { return m_width; } UnifiedVariant_t& getVariant() { return m_databusVariant; } const UnifiedVariant_t& getVariant() const { return m_databusVariant; } }; template class DataBusContainerProxy { static_assert(DRAMUtils::util::always_false::value, "DataBusContainerProxy cannot be initialized. Check the DataBus type_sequence."); }; template class DataBusContainerProxy> : public Serialize, public Deserialize { // Public type definitions public: using DataBusContainer_t = DataBusContainer>; // Private member variables private: DataBusContainer_t m_dataBusContainer; public: // Forwarding constructor template DataBusContainerProxy(Args&&... args) : m_dataBusContainer(std::forward(args)...) {} // Deleted default constructor DataBusContainerProxy() = delete; // Forwarding member functions void loadWrite(timestamp_t timestamp, std::size_t n_bits, const uint8_t *data = nullptr) { std::visit([timestamp, n_bits, data](auto && arg) { arg.loadWrite(timestamp, n_bits, data); }, m_dataBusContainer.getVariant()); } void loadRead(timestamp_t timestamp, std::size_t n_bits, const uint8_t *data = nullptr) { std::visit([timestamp, n_bits, data](auto && arg) { arg.loadRead(timestamp, n_bits, data); }, m_dataBusContainer.getVariant()); } void enableTogglingRate(timestamp_t timestamp) { std::visit([timestamp](auto && arg) { arg.enableTogglingRate(timestamp); }, m_dataBusContainer.getVariant()); } void enableBus(timestamp_t timestamp) { std::visit([timestamp](auto && arg) { arg.enableBus(timestamp); }, m_dataBusContainer.getVariant()); } void setTogglingRateDefinition(const DRAMUtils::Config::ToggleRateDefinition &toggleRateDefinition) { std::visit([&toggleRateDefinition](auto && arg) { arg.setTogglingRateDefinition(toggleRateDefinition); }, m_dataBusContainer.getVariant()); } bool isTogglingRate() const { return std::visit([](auto && arg) { return arg.isTogglingRate(); }, m_dataBusContainer.getVariant()); } bool isBus() const { return std::visit([](auto && arg) { return arg.isBus(); }, m_dataBusContainer.getVariant()); } timestamp_t lastBurst() const { return std::visit([](auto && arg) { return arg.lastBurst(); }, m_dataBusContainer.getVariant()); } std::size_t getWidth() const { return m_dataBusContainer.getWidth(); } void get_stats(timestamp_t timestamp, util::bus_stats_t &busReadStats, util::bus_stats_t &busWriteStats, util::bus_stats_t &togglingReadState, util::bus_stats_t &togglingWriteState) const { std::visit([timestamp, &busReadStats, &busWriteStats, &togglingReadState, &togglingWriteState](auto && arg) { arg.get_stats(timestamp, busReadStats, busWriteStats, togglingReadState, togglingWriteState); }, m_dataBusContainer.getVariant()); } void serialize(std::ostream& stream) const override { std::visit([&stream](const auto& arg) { arg.serialize(stream); }, m_dataBusContainer.getVariant()); } void deserialize(std::istream& stream) override { std::visit([&stream](auto& arg) { arg.deserialize(stream); }, m_dataBusContainer.getVariant()); } }; } // namespace DRAMPower::util #endif /* DRAMPOWER_UTIL_DATABUS */ ================================================ FILE: src/DRAMPower/DRAMPower/util/databus_presets.h ================================================ #ifndef DRAMPOWER_UTIL_DATABUS_PRESETS #define DRAMPOWER_UTIL_DATABUS_PRESETS #include "DRAMPower/util/databus_types.h" #include #include #include namespace DRAMPower::util::databus_presets { using databus_64_t = DataBus<64>; using databus_256_t = DataBus<256>; using databus_1024_t = DataBus<1024>; using databus_4096_t = DataBus<4096>; using databus_preset_sequence_t = DRAMUtils::util::type_sequence< databus_64_t, databus_256_t, databus_1024_t, databus_4096_t >; using databus_preset_t = util::DataBusContainerProxy; inline databus_preset_t getDataBusPreset( DataBusConfig&& busConfig, util::DataBusMode mode, bool IdleLastPatternOverride ) { if (busConfig.width <= 64) { return databus_64_t(std::move(busConfig), mode, IdleLastPatternOverride); } else if (busConfig.width <= 256) { return databus_256_t(std::move(busConfig), mode, IdleLastPatternOverride); } else if (busConfig.width <= 1024) { return databus_1024_t(std::move(busConfig), mode, IdleLastPatternOverride); } else if (busConfig.width > 4096) { assert(false); throw std::runtime_error("Data bus width exceeds maximum."); } return databus_4096_t(std::move(busConfig), mode, IdleLastPatternOverride); } } // namespace DRAMPower::util::databus_presets #endif /* DRAMPOWER_UTIL_DATABUS_PRESETS */ ================================================ FILE: src/DRAMPower/DRAMPower/util/databus_types.h ================================================ #ifndef DRAMPOWER_UTIL_DATABUS_TYPES #define DRAMPOWER_UTIL_DATABUS_TYPES #include #include #include namespace DRAMPower::util { enum class DataBusMode { Bus = 0, TogglingRate }; struct DataBusConfig { std::size_t width; std::size_t dataRate; DRAMUtils::Config::ToggleRateDefinition toggleRateConf; }; } // namespace DRAMPower::util #endif /* DRAMPOWER_UTIL_DATABUS_TYPES */ ================================================ FILE: src/DRAMPower/DRAMPower/util/dbi.h ================================================ #ifndef DRAMPOWER_UTIL_DBI_H #define DRAMPOWER_UTIL_DBI_H #include "DRAMPower/Types.h" #include "DRAMPower/util/pin_types.h" #include "DRAMPower/util/Serialize.h" #include "DRAMPower/util/Deserialize.h" #include "DRAMPower/util/dbialgos.h" #include "DRAMPower/util/dbitypes.h" #include "DRAMPower/util/dbihelpers.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DRAMPower::util { // Valid types for DataType_t are -> integral types f.e. uint8_t, uint16_t, uint32_t or bitset template class DBI : public Serialize, public Deserialize { static_assert(std::is_integral_v || types::is_bitset_v, "DataType_t must be an integral type or a std::bitset."); // Public type definitions public: using Self_t = DBI; using DataBuffer_t = std::vector; using DataBufferIterator_t = typename DataBuffer_t::iterator; static constexpr std::size_t DATATYPEDIGITS = types::digit_count_v; static constexpr std::size_t ChunkSize = SUBCHUNKS * DATATYPEDIGITS; // This limitation allows future algorithm improvements static_assert(types::is_contiguous_container_v, "DataBuffer_t must contain a buffer with contiguous data."); static_assert(DATATYPEDIGITS > 0, "The BufferType must contain at least one digit."); // Algorithm using Algorithm_t = Algorithm; using SelectedAlgorithmType_t = typename Algorithm_t::iterator_type_t; using IteratorType_t = typename IteratorSelector::type; using AlgorithmPreviousConcat_t = ConcatIterator; using AlgorithmPrevious_t = std::tuple; using AlgorithmPreviousOptional_t = std::optional; using State_t = bool; // true for inverted, false for normal // Callback signature void(timestamp_t load_time, timestamp_t chunk_time, std::size_t chunk_idx, bool inversion_state, bool read) // chunk_time equals load_time if no bus width is provided. The chunk_idx is then absolute to the beginning of the request using ChangeCallback_t = std::function; using IdlePattern_t = PinState; // Private type definitions private: class LastBurst_t : public Serialize, public Deserialize { public: std::size_t beats() const { return m_end - m_start; } std::size_t chunks() const { return m_n_chunks; } std::size_t entries() const { return m_n_chunks * SUBCHUNKS; } timestamp_t end() const { return m_end; } bool isInitialized() const { return m_init; } const std::vector& getInversionState() const { return m_inversions; } std::vector& getInversionState() { return m_inversions; } void update(timestamp_t timestamp, const std::optional& m_width, std::size_t m_burstLength, std::size_t n_bits) { m_start = timestamp; if (m_width.has_value()) { m_end = timestamp + (n_bits / m_width.value()); } else { m_end = timestamp + m_burstLength; } m_n_chunks = n_bits / ChunkSize; m_init = true; } void serialize(std::ostream &stream) const override { stream.write(reinterpret_cast(&m_start), sizeof(m_start)); stream.write(reinterpret_cast(&m_end), sizeof(m_end)); stream.write(reinterpret_cast(&m_n_chunks), sizeof(m_n_chunks)); stream.write(reinterpret_cast(&m_n_bits), sizeof(m_n_bits)); stream.write(reinterpret_cast(&m_init), sizeof(m_init)); std::size_t m_inversionsSize = m_inversions.size(); stream.write(reinterpret_cast(&m_inversionsSize), sizeof(m_inversionsSize)); for (const auto& entry : m_inversions) { stream.write(reinterpret_cast(&entry), sizeof(entry)); } std::size_t m_lastbeatSize = m_lastbeat.size(); stream.write(reinterpret_cast(&m_lastbeatSize), sizeof(m_lastbeatSize)); for (const auto& entry : m_lastbeat) { stream.write(reinterpret_cast(&entry), sizeof(entry)); } } void deserialize(std::istream& stream) override { stream.read(reinterpret_cast(&m_start), sizeof(m_start)); stream.read(reinterpret_cast(&m_end), sizeof(m_end)); stream.read(reinterpret_cast(&m_n_chunks), sizeof(m_n_chunks)); stream.read(reinterpret_cast(&m_n_bits), sizeof(m_n_bits)); stream.read(reinterpret_cast(&m_init), sizeof(m_init)); std::size_t m_inversionsSize; stream.read(reinterpret_cast(&m_inversionsSize), sizeof(m_inversionsSize)); m_inversions.clear(); m_inversions.reserve(m_inversionsSize); for (std::size_t i = 0; i < m_inversionsSize; ++i) { bool entry; stream.read(reinterpret_cast(&entry), sizeof(entry)); m_inversions.push_back(entry); } std::size_t m_lastbeatSize; stream.read(reinterpret_cast(&m_lastbeatSize), sizeof(m_lastbeatSize)); m_lastbeat.clear(); m_lastbeat.reserve(m_lastbeatSize); for (std::size_t i = 0; i < m_lastbeatSize; ++i) { bool entry; stream.read(reinterpret_cast(&entry), sizeof(entry)); m_lastbeat.push_back(entry); } } public: DataBuffer_t m_lastbeat{}; bool m_init = false; private: timestamp_t m_start = 0; // Start timestamp of the last burst timestamp_t m_end = 0; // End timestamp of the last burst std::size_t m_n_chunks = 0; std::size_t m_n_bits = 0; std::vector m_inversions; }; // Public contructors, destructors and assignment operators public: template constexpr std::enable_if_t> set_all_bits(bool ones, T& out_val) const { out_val = ones ? ~T{0} : T{0}; } template constexpr void set_all_bits(bool ones, std::bitset& out_val) const { out_val = ones ? std::bitset{}.set() : std::bitset{}; } DataBuffer_t getIdlePattern(std::size_t busWidth) const { DataType_t data{}; if constexpr (PinState::H == IDLEPATTERN) { set_all_bits(true, data); } else { // (PinState::L == IDLEPATTERN) || (PinState::Z == IDLEPATTERN) set_all_bits(false, data); } // Size: SubChunks per busWidth return DataBuffer_t(static_cast(busWidth / DATATYPEDIGITS), data); } template DBI (std::optional busWidth, std::size_t burstLength, Func&& func, bool enable, Args&&... args) : m_width(busWidth) , m_burstLength(burstLength) , m_idleData(busWidth.has_value() ? std::optional{getIdlePattern(busWidth.value())} : std::nullopt) , m_changeCallback(std::forward(func)) , m_enable(enable) , m_algorithm(std::forward(args)...) { assert(!busWidth.has_value() || (0 == (busWidth.value() % ChunkSize) && "busWidth must be a multiple of CHUNKSIZE")); } // Private member functions private: void invertChunk(std::size_t chunk_start) { std::size_t subchunk_start = chunk_start * SUBCHUNKS; for (std::size_t subchunk = subchunk_start; subchunk < subchunk_start + SUBCHUNKS; ++subchunk) { m_invertedData[subchunk] = ~m_invertedData[subchunk]; } } // Public member functions public: void enable(bool enable) { m_enable = enable; } bool isEnabled() const { return m_enable; } const std::vector& getInversionStateRead() const { return m_lastBurst_read.getInversionState(); } std::size_t getInversionSizeRead() const { return m_lastBurst_read.entries(); } const std::vector& getInversionStateWrite() const { return m_lastBurst_write.getInversionState(); } std::size_t getInversionSizeWrite() const { return m_lastBurst_write.entries(); } std::optional getLastBurstEnd(bool read) const { const auto& lastBurst = read ? m_lastBurst_read : m_lastBurst_write; if (lastBurst.isInitialized()) { return read ? m_lastBurst_read.end() : m_lastBurst_write.end(); } return std::nullopt; } std::optional getChunksPerWidth() const { return m_width.has_value() ? std::optional(m_width.value() / ChunkSize) : std::nullopt; } IdlePattern_t getIdlePattern() const { return IDLEPATTERN; } template void setChangeCallback(Func&& func) { m_changeCallback = std::forward(func); } std::optional updateDBI(timestamp_t timestamp, std::size_t n_bits, const DataType_t* data, bool read) { if (!data || 0 == n_bits || !m_enable || IdlePattern_t::Z == IDLEPATTERN) return std::nullopt; // Select read or write members auto &lastInvert = read ? m_lastBurst_read.getInversionState() : m_lastBurst_write.getInversionState(); auto &m_lastBurst = read ? m_lastBurst_read : m_lastBurst_write; // Reset to Idle Pattern if this burst and the last burst are not seamless dispatchResetCallback(timestamp, read); // Calculate number of bytes needed to store the data assert(0 == (n_bits % DATATYPEDIGITS) && "n_bits must be a multiple of DATATYPEDIGITS"); std::size_t entries_needed = n_bits / DATATYPEDIGITS; // Save lastBeat for seamless Bursts saveLastBurst(timestamp, m_lastBurst); // SAFETY: the assigning is necessary so there are no data copies during dbi calculation m_invertedData.assign(data, data + entries_needed); // Create previousIterator from last burst auto previous_iterator = createPreviousIterator(timestamp, m_lastBurst); // Process each chunk independently assert(n_bits % ChunkSize == 0); // Ensure n_bits is a multiple of chunk size std::size_t n_chunks = n_bits / ChunkSize; // Resize lastInvert vector if needed if (lastInvert.capacity() < n_chunks) { lastInvert.resize(n_chunks, false); } // SAFETY: inverting the chunk while iterating over the data is safe because the invertedData is resized to the maximum size auto invert_visitor = [this, &lastInvert, timestamp, read] (bool invert_chunk, const std::size_t chunk) { dispatchCallback(timestamp, chunk, invert_chunk, read); // Update the last inversion state lastInvert[chunk] = invert_chunk; // Invert Chunk if (invert_chunk) { invertChunk(chunk); } }; // Compute DBI m_algorithm.computeDBI( std::make_tuple(m_invertedData.begin(), m_invertedData.end()), previous_iterator, IDLEPATTERN, std::move(invert_visitor)); // Store burst information m_lastBurst.update(timestamp, m_width, m_burstLength, n_bits); lastBurstRead = read; return m_invertedData.data(); } std::tuple getInvertedData() const { auto &m_lastBurst = lastBurstRead ? m_lastBurst_read : m_lastBurst_write; assert(m_invertedData.size() >= m_lastBurst.chunks() * SUBCHUNKS && "invalid inverted Data container size"); return std::make_tuple(m_invertedData.data(), m_lastBurst.chunks() * SUBCHUNKS); } inline std::size_t getInvertedDataSize() const { auto &m_lastBurst = lastBurstRead ? m_lastBurst_read : m_lastBurst_write; return m_lastBurst.chunks() * SUBCHUNKS; } void dispatchResetCallback(timestamp_t timestamp) { dispatchResetCallback(timestamp, true); dispatchResetCallback(timestamp, false); } void serialize(std::ostream& stream) const override { stream.write(reinterpret_cast(&m_enable), sizeof(m_enable)); stream.write(reinterpret_cast(&lastBurstRead), sizeof(lastBurstRead)); m_lastBurst_read.serialize(stream); m_lastBurst_write.serialize(stream); std::size_t invertedDataSize = m_invertedData.size(); stream.write(reinterpret_cast(&invertedDataSize), sizeof(invertedDataSize)); for (const auto& byte : m_invertedData) { stream.write(reinterpret_cast(&byte), sizeof(byte)); } } void deserialize(std::istream& stream) override { stream.read(reinterpret_cast(&m_enable), sizeof(m_enable)); stream.read(reinterpret_cast(&lastBurstRead), sizeof(lastBurstRead)); m_lastBurst_read.deserialize(stream); m_lastBurst_write.deserialize(stream); std::size_t invertedDataSize; stream.read(reinterpret_cast(&invertedDataSize), sizeof(invertedDataSize)); m_invertedData.clear(); m_invertedData.reserve(invertedDataSize); for (std::size_t i = 0; i < invertedDataSize; ++i) { uint8_t byte; stream.read(reinterpret_cast(&byte), sizeof(byte)); m_invertedData.push_back(byte); } } // Private member functions private: void saveLastBurst(timestamp_t timestamp, LastBurst_t& m_lastBurst) { if (m_lastBurst.m_init && m_lastBurst.end() == timestamp && m_width.has_value() // seamless burst and busWidth provided && m_lastBurst.beats() != 0 && m_invertedData.size() > m_width.value() / DATATYPEDIGITS) // at least one beat is required { assert(0 <= (m_invertedData.size() - (m_width.value() / DATATYPEDIGITS)) && "Invalid m_invertedData size"); m_lastBurst.m_lastbeat.clear(); std::size_t n_subChunks = m_width.value() / DATATYPEDIGITS; m_lastBurst.m_lastbeat.resize(n_subChunks); std::copy(m_invertedData.end() - n_subChunks, m_invertedData.end(), m_lastBurst.m_lastbeat.begin()); } } AlgorithmPreviousOptional_t createPreviousIterator(timestamp_t timestamp, LastBurst_t& m_lastBurst) { if (m_lastBurst.m_init && m_lastBurst.end() == timestamp && m_width.has_value() // seamless burst and busWidth provided && m_lastBurst.beats() != 0 && m_invertedData.size() > m_width.value() / DATATYPEDIGITS) // at least one beat is required { assert(0 <= (m_invertedData.size() - (m_width.value() / DATATYPEDIGITS)) && "Invalid m_invertedData size"); // Create concat iterator of lastburst and current burst return std::make_optional(std::make_tuple( AlgorithmPreviousConcat_t{m_lastBurst.m_lastbeat.begin(), m_lastBurst.m_lastbeat.end(), m_invertedData.begin()}, AlgorithmPreviousConcat_t{m_lastBurst.m_lastbeat.end(), m_lastBurst.m_lastbeat.end(), m_invertedData.end() - m_width.value() / DATATYPEDIGITS} )); } else if (m_width.has_value() && m_idleData.has_value()) { assert(0 <= (m_invertedData.size() - (m_width.value() / DATATYPEDIGITS)) && "Invalid m_invertedData size"); // Create concat iterator of idleData and current burst return std::make_optional(std::make_tuple( AlgorithmPreviousConcat_t{m_idleData.value().begin(), m_idleData.value().end(), m_invertedData.begin()}, AlgorithmPreviousConcat_t{m_idleData.value().end(), m_idleData.value().end(), m_invertedData.end() - m_width.value() / DATATYPEDIGITS} )); } else { return std::nullopt; } } void dispatchCallback(timestamp_t timestamp, std::size_t chunk, bool invert_chunk, bool read) { std::optional chunks_per_width = getChunksPerWidth(); if (nullptr != m_changeCallback) { if (!chunks_per_width.has_value()) { m_changeCallback(timestamp, timestamp, chunk, invert_chunk, read); } else { std::size_t beat_idx = chunk % chunks_per_width.value(); timestamp_t t = timestamp + (chunk / chunks_per_width.value()); m_changeCallback(timestamp, t, beat_idx, invert_chunk, read); } } } void dispatchResetCallback(timestamp_t timestamp, bool read) { const auto &m_lastBurst = read ? m_lastBurst_read : m_lastBurst_write; auto &lastInvert = read ? m_lastBurst_read.getInversionState() : m_lastBurst_write.getInversionState(); // Ensure there is at least one chunk in the last inversion assert(!m_lastBurst.m_init || (!getChunksPerWidth().has_value() || m_lastBurst.chunks() >= getChunksPerWidth().value())); if (!m_lastBurst.m_init) { return; } else if (timestamp < m_lastBurst.end()) { // previous burst present and timestamp is before the last burst end return; } else if (m_lastBurst.end() == timestamp) { // Seamless burst // if new burstlength is smaller then last burstlength not all pins are reset for a seamless burst in absolute chunk mode (m_width not set) // In absolute mode the callback is called for every chunk return; } // The last burst is initialized // and no seamleass burst // and timestamp >= m_lastBurst.end // Dispatch callback for the inversion back to the idle pattern if (nullptr != m_changeCallback) { timestamp_t t = m_lastBurst.end(); if (getChunksPerWidth().has_value()) { // First timestamp after the last chunk std::size_t start = m_lastBurst.chunks() - getChunksPerWidth().value(); // Loop over the chunks/dbi_pins in the last pattern for (std::size_t chunk = start; chunk < m_lastBurst.chunks(); ++chunk) { std::size_t chunk_idx = chunk % getChunksPerWidth().value(); // Pin index if (lastInvert[chunk]) { // Reset the inversion state m_changeCallback(timestamp, t, chunk_idx, false, read); } // Reset the inversion state for next pin updates lastInvert[chunk] = false; } } else { // TODO split the callbacks in dedicated update / reset callbacks? // no bus width is defined. The dbi executes in absolute chunk mode. // The number of dbi pins cannot be calculated // Looping over all chunks of the lastBurst for (std::size_t chunk = 0; chunk < m_lastBurst.chunks(); chunk++) { if (lastInvert[chunk]) { m_changeCallback(timestamp, t, chunk, false, read); } // Reset the inversion state for next updates lastInvert[chunk] = false; } } } } // Private member variables private: std::optional m_width = std::nullopt; // Width of the complete bus std::size_t m_burstLength = 0; // Width of the complete bus bool lastBurstRead = false; LastBurst_t m_lastBurst_read; // Last burst end timestamp LastBurst_t m_lastBurst_write; // Last burst end timestamp DataBuffer_t m_invertedData; // Buffer for inverted data std::optional m_idleData; // Buffer for idle data ChangeCallback_t m_changeCallback = nullptr; // Callback called in updateDBI bool m_enable = false; Algorithm_t m_algorithm; }; } // namespace DRAMPower::util #endif /* DRAMPOWER_UTIL_DBI_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/dbialgos.h ================================================ #ifndef DRAMPOWER_UTIL_DBIALGOS_H #define DRAMPOWER_UTIL_DBIALGOS_H #include "DRAMPower/util/dbihelpers.h" #include "DRAMPower/util/binary_ops.h" #include "DRAMPower/util/pin_types.h" #include "DRAMUtils/util/types.h" #include namespace DRAMPower::util { // Iterator selector for algorithms struct AlgorithmIteratorType_SubChunk {}; // Iterator selector for dbi class template struct IteratorSelector { static_assert(DRAMUtils::util::always_false::value, "No matching Iterator found for AlgorithmType."); }; template struct IteratorSelector { using type = SubChunkView; }; // Algorithms struct StaticDBI { using iterator_type_t = AlgorithmIteratorType_SubChunk; template static void computeDBI(std::tuple current, std::optional>, PinState idlePattern, InvertCallbackFunctor&& invert_callback ) { auto [cur_it, cur_end] = current; std::size_t ones = 0; std::size_t zeros = 0; for (; cur_it != cur_end; ++cur_it) { auto chunk_value = *cur_it; ones += BinaryOps::popcount(chunk_value); zeros += cur_it.getDigits() - BinaryOps::popcount(chunk_value); if (cur_it.last()) { const bool invert = (idlePattern == PinState::L && zeros < ones) || (idlePattern == PinState::H && ones < zeros); std::forward(invert_callback)(invert, cur_it.getTotalChunkIdx()); ones = 0; zeros = 0; } } } }; template struct DynamicDBI { using iterator_type_t = AlgorithmIteratorType_SubChunk; template static void computeDBI(std::tuple current, std::optional> previous, PinState, InvertCallbackFunctor&& invert_callback ) { auto [cur_it, cur_end] = current; if (!previous) { // No previous burst -> cannot compute transitions for (; cur_it != cur_end; ++cur_it) { std::forward(invert_callback)( false, cur_it.getTotalChunkIdx()); } } else { auto [prev_it, prev_end] = *previous; std::size_t costnormal = 0; for (; cur_it != cur_end; ++cur_it, ++prev_it) { // normal transition cost costnormal += BinaryOps::popcount(*cur_it ^ *prev_it); if (cur_it.last()) { const bool invert = costnormal > threshold; std::forward(invert_callback)( invert, cur_it.getTotalChunkIdx()); costnormal = 0; } } } } }; } // namespace DRAMPower::util #endif /* DRAMPOWER_UTIL_DBIALGOS_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/dbihelpers.h ================================================ #ifndef DRAMPOWER_UTIL_DBIHELPERS_H #define DRAMPOWER_UTIL_DBIHELPERS_H #include #include #include "DRAMPower/util/dbitypes.h" namespace DRAMPower::util { template class ConcatIterator { public: using value_type = std::common_type_t< typename std::iterator_traits::value_type, typename std::iterator_traits::value_type>; using reference = std::common_type_t< typename std::iterator_traits::reference, typename std::iterator_traits::reference>; using iterator_category = std::input_iterator_tag; ConcatIterator(It1 cur1, It1 end1, It2 cur2) : m_it1(cur1), m_end1(end1), m_it2(cur2) {} reference operator*() const { return (m_it1 != m_end1) ? *m_it1 : *m_it2; } ConcatIterator& operator++() { if (m_it1 != m_end1) { ++m_it1; } else { ++m_it2; } return *this; } bool operator==(const ConcatIterator& other) const { return (m_it1 == other.m_it1) && (m_it2 == other.m_it2) && (m_end1 == other.m_end1); } bool operator!=(const ConcatIterator& other) const { return !(*this == other); } private: It1 m_it1; It1 m_end1; It2 m_it2; }; /** SubChunkView * SubChunkView is a iterator_value class for iterating over the chunks in a burst if * the bus width is a multiple of the chunk size. The class itself provides */ template class SubChunkView { // Public type definitions, constructors, member functions public: using parent_iterator_value_type = typename iterator::value_type; using iterator_category = std::input_iterator_tag; using value_type = typename iterator::value_type; using difference_type = typename iterator::difference_type; using pointer = typename iterator::pointer; using reference = typename iterator::reference; static constexpr std::size_t DATATYPEDIGITS = types::digit_count_v; static_assert(DATATYPEDIGITS > 0, "The BufferType must contain at least one digit"); static_assert(std::is_integral_v || std::is_same_v>, "Parent iterator value type must be an integral type or a std::bitset of the chunk size"); static_assert(0 == (CHUNKSIZE % DATATYPEDIGITS), "CHUNKSIZE must be a multiple of DATATYPEDIGITS."); static_assert(CHUNKSIZE >= DATATYPEDIGITS, "CHUNKSIZE must be greater or equal to DATATYPEDIGITS"); static constexpr std::size_t COUNT_DATA = CHUNKSIZE / DATATYPEDIGITS; private: SubChunkView(iterator it, std::size_t current_chunk_idx, std::size_t current_data_idx) : m_it(it) , m_current_chunk_idx(current_chunk_idx) , m_current_data_idx(current_data_idx) {} public: SubChunkView(iterator it) : SubChunkView{it, 0, 0} {} public: SubChunkView& operator++() { m_current_data_idx++; m_it++; if (m_current_data_idx >= COUNT_DATA) { m_current_data_idx = 0; m_current_chunk_idx++; } return *this; } SubChunkView operator++(int) { SubChunkView tmp = *this; ++(*this); return tmp; } inline const std::size_t& getDataIdx() const { return m_current_data_idx; } inline std::size_t getTotalChunkIdx() const { return m_current_chunk_idx; } inline bool last() const { return 0 == (m_current_data_idx % COUNT_DATA); } inline static constexpr std::size_t getDataTotal() { return COUNT_DATA; } inline static constexpr std::size_t getDigits() { return DATATYPEDIGITS; } bool operator==(const SubChunkView& other) const noexcept { return (m_it == other.m_it); } bool operator!=(const SubChunkView& other) const noexcept { return !(*this == other); } decltype(auto) operator*() const { return *m_it; } // Private members private: iterator m_it; std::size_t m_current_chunk_idx = 0; std::size_t m_current_data_idx = 0; }; } // namespace DRAMPower::util #endif /* DRAMPOWER_UTIL_DBIHELPERS_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/dbitypes.h ================================================ #ifndef DRAMPOWER_UTIL_DBI_TYPES_H #define DRAMPOWER_UTIL_DBI_TYPES_H #include #include #include #include #include namespace DRAMPower::util::types { // contiguous containers // non contiguous container fallback template struct is_contiguous_container : std::false_type {}; // std::vector specialization template struct is_contiguous_container> : std::true_type {}; // std::array specialization template struct is_contiguous_container> : std::true_type {}; // std::string specialization template struct is_contiguous_container> : std::true_type {}; // value shortcut template inline constexpr bool is_contiguous_container_v = is_contiguous_container::value; // is_bitset // no bitset fallback template struct is_bitset : std::false_type{}; // std::bitset specialization template struct is_bitset> : std::true_type {}; // value shortcut template inline constexpr bool is_bitset_v = is_bitset<_Tp>::value; // digit_count // std::numeric_limits fallback template struct digit_count { static constexpr std::size_t value = std::numeric_limits::digits; }; // std::bitset specialization template struct digit_count> { static constexpr std::size_t value = N; }; // value shortcut template constexpr std::size_t digit_count_v = digit_count::value; } // namespace DRAMPower::util::types #endif /* DRAMPOWER_UTIL_DBI_TYPES_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/dynamic_bitset.h ================================================ #ifndef DRAMPOWER_UTIL_DYNAMIC_BITSET_H #define DRAMPOWER_UTIL_DYNAMIC_BITSET_H #include #include #include #include #include #include #include #include #include namespace DRAMPower::util { template class dynamic_bitset { public: using buffer_element_t = std::bitset; using buffer_t = std::vector; private: std::size_t num_bits = 0; std::bitset lastelementmask; buffer_t buffer; public: explicit dynamic_bitset(std::size_t num_bits) : num_bits(num_bits) , buffer(static_cast((0 != num_bits % blocksize) ? ((num_bits / blocksize) + 1) : (num_bits / blocksize)), 0) { makeLastElementMask(); } explicit dynamic_bitset(std::size_t num_bits, uint64_t value) : num_bits(num_bits) , buffer(static_cast((0 != num_bits % blocksize) ? ((num_bits / blocksize) + 1) : (num_bits / blocksize))) { uint64_t accumulator = 1; for (std::size_t bit_index = 0; bit_index < num_bits; ++bit_index) { buffer[bit_index / blocksize][bit_index % blocksize] = value & accumulator; accumulator <<= 1; } lastelementmask = std::bitset(0); for (std::size_t i = 0; i < num_bits % blocksize; ++i) { lastelementmask.set(i); } } dynamic_bitset() = default; public: dynamic_bitset(const dynamic_bitset&) = default; dynamic_bitset(dynamic_bitset&&) noexcept = default; dynamic_bitset& operator=(const dynamic_bitset&) = default; dynamic_bitset& operator=(dynamic_bitset&&) noexcept = default; public: inline void makeLastElementMask() { lastelementmask.reset(); for (std::size_t i = 0; i < num_bits % blocksize; ++i) { lastelementmask.set(i); } } inline std::size_t size() const { return num_bits; }; std::size_t count() const { std::size_t n = 0; // Iterate over all block including the last block which can be smaller than the block_size std::size_t i_blocks = blocksize; for (auto &element : buffer) { if (i_blocks > num_bits) { n += (element & lastelementmask).count(); } else { n += element.count(); } i_blocks += blocksize; } return n; } public: // element manipulation void push_back(bool bit) { if (num_bits % blocksize == 0) { // New entry buffer.push_back(buffer_element_t{0}); } buffer.back().set(num_bits % blocksize, bit); num_bits++; makeLastElementMask(); } void set(std::size_t n, bool value) { buffer.at(n / blocksize).set(n % blocksize, value); } void set() { for (auto &element : buffer) { element.set(); } } void reset() { for (auto &element : buffer) { element.reset(); } } void flip() { for (auto &element : buffer) { element.flip(); } } void flip(std::size_t n) { buffer.at(n / blocksize).flip(n % blocksize); } void clear() { buffer.clear(); num_bits = 0; } public: // element access // inline auto at(std::size_t n) -> decltype(buffer.at(n)) { return buffer.at(n); } // inline auto at(std::size_t n) -> decltype(buffer.at(n)) const { return buffer.at(n); } public: inline auto operator[](std::size_t n) { return buffer.at(n / blocksize).test(n % blocksize); }; inline auto operator[](std::size_t n) const { return buffer.at(n / blocksize).test(n % blocksize); }; public: // comparison operations template = 0> bool operator==(const dynamic_bitset& rhs) const { if (this->size() != rhs.size()) { return false; } for (std::size_t i = 0; i < this->buffer.size(); ++i) { if (buffer.at(i) != rhs.buffer.at(i)) { return false; } } return true; } template = 0> bool operator==(const dynamic_bitset& rhs) const { if (this->size() != rhs.size()) { return false; } for (std::size_t i = 0; i < this->size(); ++i) { if (this->operator[](i) != rhs[i]) { return false; } } return true; } template bool operator!=(const dynamic_bitset& rhs) const { return !(*this == rhs); } bool operator==(unsigned long rhs) const { return *this == dynamic_bitset { this->size(), rhs }; } bool operator!=(unsigned long rhs) const { return !(*this == rhs); } public: // bitset operations dynamic_bitset operator~() const { auto bitset = *this; for (std::size_t i = 0; i < this->buffer.size(); ++i) { bitset.buffer.at(i) = ~buffer.at(i); }; return bitset; } dynamic_bitset& operator^=(const dynamic_bitset& rhs) { assert(this->size() == rhs.size()); for (std::size_t i = 0; i < this->buffer.size(); ++i) { buffer.at(i) = buffer.at(i) ^ rhs.buffer.at(i); }; return *this; } dynamic_bitset& operator&=(const dynamic_bitset& rhs) { assert(this->size() == rhs.size()); for (std::size_t i = 0; i < this->buffer.size(); ++i) { buffer.at(i) &= rhs.buffer.at(i); }; return *this; } dynamic_bitset& operator|=(const dynamic_bitset& rhs) { assert(this->size() == rhs.size()); for (std::size_t i = 0; i < this->buffer.size(); ++i) { buffer.at(i) |= rhs.buffer.at(i); }; return *this; } public: }; template inline dynamic_bitset operator^(dynamic_bitset lhs, const dynamic_bitset& rhs) { return lhs ^= rhs; }; template inline dynamic_bitset operator&(dynamic_bitset lhs, const dynamic_bitset& rhs) { return lhs &= rhs; }; template inline dynamic_bitset operator|(dynamic_bitset lhs, const dynamic_bitset& rhs) { return lhs |= rhs; }; } // namespace DRAMPower::util #endif /* DRAMPOWER_UTIL_DYNAMIC_BITSET_H */ ================================================ FILE: src/DRAMPower/DRAMPower/util/extension_base.h ================================================ #ifndef DRAMPOWER_UTIL_EXTENSION_BASE #define DRAMPOWER_UTIL_EXTENSION_BASE namespace DRAMPower::util::extension_manager { // dynamic extension base class Extension { public: Extension() = default; virtual ~Extension() = default; }; // dynamic extension with hooks template class ExtensionWithHooks : public Extension { public: using Extension::Extension; // Return supported hooks virtual Hooks getSupportedHooks() const = 0; }; } // namespace DRAMPower::util::extension_manager #endif /* DRAMPOWER_UTIL_EXTENSION_BASE */ ================================================ FILE: src/DRAMPower/DRAMPower/util/extension_manager.h ================================================ #ifndef DRAMPOWER_UTIL_EXTENSION_MANAGER #define DRAMPOWER_UTIL_EXTENSION_MANAGER #include "DRAMPower/util/Serialize.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DRAMPower::util::extension_manager { // Helper function to count trailing zeros in an integral type template constexpr int countZeros(T value) { static_assert(std::is_integral_v, "countZeros only works with integral types"); if (value == 0) return std::numeric_limits::digits; int count = 0; constexpr T one = 1; while ((value & one) == 0) { count++; value >>= 1; } return count; } // dynamic extension manager // BaseExtension class is used for type erasure template class ExtensionManager : public util::Serialize, public util::Deserialize { private: // Type definitions using Extension_storage_t = std::unordered_map>; using Serialize_storage_t = std::vector>; // Member variables Extension_storage_t m_extensions; Serialize_storage_t m_serStorage; // The std::vector guarantees the order of the Extensions // Asserts static_assert(std::is_base_of_v, "BaseExtension must derive from util::Serialize"); static_assert(std::is_base_of_v, "BaseExtension must derive from util::Deserialize"); protected: // Protected member functions template decltype(auto) registerExtension_impl(Args&&... args) { static_assert(std::is_base_of_v, "T must derive from BaseExtension"); auto ext = std::make_shared(std::forward(args)...); auto typeIndex = std::type_index(typeid(T)); m_extensions[typeIndex] = ext; m_serStorage.emplace_back(ext); return ext; } public: // Constructor ExtensionManager() = default; // Public member functions // Register an extension, forwarding any additional arguments. template void registerExtension(Args&&... args) { registerExtension_impl(std::forward(args)...); } // Retrieve an extension (returns an empty weak_ptr if not found) template std::weak_ptr getExtension() { static_assert(std::is_base_of_v, "T must derive from BaseExtension"); if (m_extensions.empty()) { return std::weak_ptr{}; } auto it = m_extensions.find(std::type_index(typeid(T))); if (it != m_extensions.end()) { return std::static_pointer_cast(it->second); } return std::weak_ptr{}; } // Check if an extension is registered template bool hasExtension() const { static_assert(std::is_base_of_v, "T must derive from BaseExtension"); if (m_extensions.empty()) { return false; } return m_extensions.find(std::type_index(typeid(T))) != m_extensions.end(); } // Visitor pattern for extensions template void withExtension(Func&& func) { static_assert(std::is_base_of_v, "T must derive from BaseExtension"); if (!m_extensions.empty()) { auto it = m_extensions.find(std::type_index(typeid(T))); if (it != m_extensions.end()) { std::forward(func)(*std::static_pointer_cast(it->second)); } } } void serialize(std::ostream& stream) const override { for (const auto& wp : m_serStorage) { if(auto sp = wp.lock()) { sp->serialize(stream); } else { throw Exception("Empty shared_pointer in ExtensionManager is not allowed"); } } }; void deserialize(std::istream& stream) override { for (auto& wp : m_serStorage) { if (auto sp = wp.lock()) { sp->deserialize(stream); } else { throw Exception("Empty shared_pointer in ExtensionManager is not allowed"); } } } }; template class ExtensionManagerWithHooks : ExtensionManager { static_assert(DRAMUtils::util::always_false::value, "BaseExtension must inherit from ExtensionWithHooks" ); }; template class ExtensionManagerWithHooks< BaseExtension, Hooks, std::enable_if_t, BaseExtension>> > : public ExtensionManager { private: // Type definitions using Hooks_t = Hooks; using HookValue_t = std::underlying_type_t; using HookCache_t = std::vector>>; // Member variables constexpr static std::size_t m_numHooks = std::numeric_limits::digits; HookCache_t m_hookCache; // Private member functions static constexpr std::size_t getHookPosition(Hooks_t hook) { return countZeros(static_cast(hook)); } void initializeHooks() { if (m_hookCache.empty()) { m_hookCache.resize(m_numHooks); } } void updateHookCache(std::shared_ptr&& extension) { // dynamic cast used for additional type safety auto hookExt = std::dynamic_pointer_cast>(extension); if (!hookExt) return; // dynamic_cast checked in std::enable_if_t const HookValue_t supportedHooks = static_cast(hookExt->getSupportedHooks()); for (std::size_t i = 0; i < m_hookCache.size(); ++i) { if ((supportedHooks & (1 << i)) != 0) { m_hookCache[i].push_back(extension); } } } public: // Constructor ExtensionManagerWithHooks() { initializeHooks(); } // Public member functions // Register an extension, forwarding any additional arguments. template void registerExtension(Args&&... args) { updateHookCache(this->template registerExtension_impl(std::forward(args)...)); } // Call the hook function for all registered extensions supporting the hook template void callHook(Hooks_t hook, Func&& func) { const std::size_t bitPosition = getHookPosition(hook); if (bitPosition < 0 || bitPosition >= m_hookCache.size()) { return; // Invalid hook } for (const auto& extension_ptr : m_hookCache[bitPosition]) { std::forward(func)(*extension_ptr); } } }; } // namespace DRAMPower::util::extension_manager #endif /* DRAMPOWER_UTIL_EXTENSION_MANAGER */ ================================================ FILE: src/DRAMPower/DRAMPower/util/extension_manager_static.h ================================================ #ifndef DRAMPOWER_UTIL_EXTENSION_MANAGER_STATIC #define DRAMPOWER_UTIL_EXTENSION_MANAGER_STATIC #include #include #include #include #include #include #include #include #include #include #include namespace DRAMPower::util::extension_manager_static { // Helpers namespace detail { template constexpr auto tryCallFunctor(Func&& func, Ext& ext) -> decltype(std::forward(func)(ext)) { if constexpr (std::is_invocable_v) { return std::forward(func)(ext); } else { // If the function is not invocable, do nothing return; } } template constexpr void callHookIfSupportedImpl(Ext& extension, FuncTuple&& funcTuple, std::index_sequence) { (detail::tryCallFunctor(std::get(std::forward(funcTuple)), extension), ...); } } // namespace detail /** static extensionmanager * Template arguments: * - Parent: Parent type of the type which holds the StaticExtensionManager * - Seq: DRAMUtils::util::type_sequence of the StaticExtensions * - Hook: enum for retrieving the extensions supporting the given hook in callHook */ template class StaticExtensionManager{ static_assert(DRAMUtils::util::always_false::value, "Cannot construct StaticExtensionManager."); }; template class StaticExtensionManager, Hook> { private: // Type definitions using Extension_type_sequence_t = DRAMUtils::util::type_sequence; using Extension_tuple_t = std::tuple; using Extension_index_sequence_t = std::index_sequence_for; using Hook_t = Hook; using Hook_value_t = std::underlying_type_t; // Constexpr helpers constexpr static std::size_t m_numExtensions = sizeof...(StaticExtensions); // Members Extension_tuple_t m_extensions; private: // Private member functions template constexpr void callHookImpl(FuncTuple&& funcTuple, std::index_sequence) { // use index_sequence to loop over callHookIfSupported // example: callHookIfSupported<0>(...), callHookIfSupported<1>(...), ... if constexpr (sizeof...(Is) > 0) { (callHookIfSupported(std::forward(funcTuple)), ...); }/* else { // Prevent "unused parameter" warning (void) hook; (void) func; }*/ } template constexpr void callHookIfSupported(FuncTuple&& funcTuple) { // Use index from callHookImpl to get the tuple elementtype to query for the supported hooks using ExtensionType = std::tuple_element_t; constexpr Hook_t supportedHooks = ExtensionType::getSupportedHooks(); if constexpr ((static_cast(supportedHooks) & static_cast(hook)) != 0) { // retrieve the extension from the tuple and call the functor if the extension is supported auto& extension = std::get(m_extensions); detail::callHookIfSupportedImpl(extension, std::forward(funcTuple), std::make_index_sequence>>{}); } } public: // Constructor StaticExtensionManager() : m_extensions(StaticExtensions{}...) {} // Public member functions template constexpr void callHook(Func&&... funcs) { using FuncTuple = std::tuple; // Explicit functor copy to ensure memory safety FuncTuple funcTuple{std::forward(funcs)...}; if constexpr (sizeof...(Func) > 0) { // Call Hook implementation with integer sequence of length sizeof...(StaticExtensions) callHookImpl(std::move(funcTuple), Extension_index_sequence_t{}); } } // has extension template constexpr static bool hasExtension() { return DRAMUtils::util::is_one_of::value; } // get extension reference template constexpr T& getExtension() { static_assert(hasExtension(), "Extension not found"); return std::get(m_extensions); } // get extension reference template constexpr const T& getExtension() const { static_assert(hasExtension(), "Extension not found"); return std::get(m_extensions); } // visitor template constexpr decltype(auto) withExtension(Func&& func) { static_assert(hasExtension(), "Extension not found"); return std::forward(func)(getExtension()); } }; } // namespace DRAMPower::util::extension_manager_static #endif /* DRAMPOWER_UTIL_EXTENSION_MANAGER_STATIC */ ================================================ FILE: src/DRAMPower/DRAMPower/util/extensions.cpp ================================================ #include #include namespace DRAMPower::extensions { using namespace DRAMPower::util; bool DBI::enable(timestamp_t timestamp, bool enable) { bool result = true; // Dispatch callback if set if (m_callback) { result = (*m_callback)(timestamp, enable); } if (result) { // Update member m_enabled = enable; } return result; } bool DBI::isEnabled() const { return m_enabled; } void DBI::serialize(std::ostream& stream) const { stream.write(reinterpret_cast(&m_enabled), sizeof(m_enabled)); } void DBI::deserialize(std::istream& stream) { stream.read(reinterpret_cast(&m_enabled), sizeof(m_enabled)); } } // namespace DRAMPower::extensions ================================================ FILE: src/DRAMPower/DRAMPower/util/extensions.h ================================================ #ifndef DRAMPOWER_UTIL_EXTENSIONS #define DRAMPOWER_UTIL_EXTENSIONS #include #include #include #include #include #include #include #include namespace DRAMPower::extensions { class Base : public util::Serialize, public util::Deserialize { protected: // Protected constructor to prevent instantiation of Base class Base() = default; Base(const Base&) = default; Base(Base&&) = default; Base& operator=(const Base&) = default; Base& operator=(Base&&) = default; public: virtual ~Base() = default; }; class DBI : public Base { // Public type definitions public: using enable_callback_t = std::function; using timestamp_t = DRAMPower::timestamp_t; // Constructors public: template explicit DBI(Func&& callback, bool initstate) : m_enabled(initstate) , m_callback(std::forward(callback)) {} // Public member functions public: bool enable(timestamp_t timestamp, bool enable); bool isEnabled() const; // Overrides public: void serialize(std::ostream& stream) const override; void deserialize(std::istream& stream) override; // Private member variables private: bool m_enabled = false; std::optional m_callback; }; } // namespace DRAMPower::extensions #endif /* DRAMPOWER_UTIL_EXTENSIONS */ ================================================ FILE: src/DRAMPower/DRAMPower/util/pending_stats.h ================================================ #ifndef DRAMPOWER_UTIL_PENDING_STATS #define DRAMPOWER_UTIL_PENDING_STATS #include #include #include #include namespace DRAMPower::util { template class PendingStats { // Constructor, assignment operator and destructor public: PendingStats() : m_timestamp(0) , m_stats() , m_pending(false) {} ~PendingStats() = default; PendingStats(const PendingStats&) = default; PendingStats& operator=(const PendingStats&) = default; PendingStats(PendingStats&&) = default; PendingStats& operator=(PendingStats&&) = default; // Public member functions public: void setPendingStats(timestamp_t timestamp, T stats) { m_timestamp = timestamp; m_stats = stats; m_pending = true; } bool isPending() const { return m_pending; } void clear() { m_pending = false; } timestamp_t getTimestamp() const { return m_timestamp; } T getStats() const { return m_stats; } void serialize(std::ostream& stream) const { stream.write(reinterpret_cast(&m_timestamp), sizeof(m_timestamp)); m_stats.serialize(stream); stream.write(reinterpret_cast(&m_pending), sizeof(m_pending)); } void deserialize(std::istream& stream) { stream.read(reinterpret_cast(&m_timestamp), sizeof(m_timestamp)); m_stats.deserialize(stream); stream.read(reinterpret_cast(&m_pending), sizeof(m_pending)); } // Private member variables private: timestamp_t m_timestamp; T m_stats; bool m_pending; }; } // namespace DRAMPower::util #endif /* DRAMPOWER_UTIL_PENDING_STATS */ ================================================ FILE: src/DRAMPower/DRAMPower/util/pin.h ================================================ #ifndef DRAMPOWER_UTIL_PINH #define DRAMPOWER_UTIL_PINH #include #include #include #include #include #include #include #include #include namespace DRAMPower::util { struct PinPendingStats : public Serialize, public Deserialize { PinState fromstate; PinState newstate; PinPendingStats() = default; PinPendingStats(PinState from, PinState to) : fromstate(from), newstate(to) {} void serialize(std::ostream &stream) const override { stream.write(reinterpret_cast(&fromstate), sizeof(fromstate)); stream.write(reinterpret_cast(&newstate), sizeof(newstate)); } void deserialize(std::istream &stream) override { stream.read(reinterpret_cast(&fromstate), sizeof(fromstate)); stream.read(reinterpret_cast(&newstate), sizeof(newstate)); } }; struct PinTempChange { timestamp_t change_time; PinState state; }; template class Pin : public Serialize, public Deserialize { // Public type definitions public: using pin_stats_t = bus_stats_t; using burst_storage_t = util::burst_storage>; using burst_t = typename burst_storage_t::burst_t; // Constructor public: explicit Pin(PinState initstate, PinState idlestate) : m_last_state(initstate) , m_idle_state(idlestate) {} // Private member functions private: void addPendingStats(timestamp_t t, PendingStats pending_stats, pin_stats_t &stats) const { // add stats from last load stored in pending_stats if (pending_stats.isPending() && pending_stats.getTimestamp() < t) { stats += getPinChangeStats(pending_stats.getStats().fromstate, pending_stats.getStats().newstate); } } void addPendingStats(timestamp_t t, timestamp_t pending_t, PinState to, PinState from, pin_stats_t &stats) const { auto pending_change = PendingStats{}; pending_change.setPendingStats(pending_t, { from, // from to // to }); addPendingStats(t, pending_change, stats); } [[nodiscard]] pin_stats_t getPinChangeStats(const PinState &fromState, const PinState &newState) const { pin_stats_t stats; // Add pin change stats if (newState != fromState) { // L to H or H to L result in bit changes // X to Z or Z to X are not counted if (fromState == PinState::L && newState == PinState::H) { stats.bit_changes++; stats.zeroes_to_ones++; } else if (fromState == PinState::H && newState == PinState::L) { stats.bit_changes++; stats.ones_to_zeroes++; } } return stats; } void count(timestamp_t end, timestamp_t start, const PinState& state, pin_stats_t &stats) const { if (end <= start) { return; } // Burst Storage if (m_burst_storage.endTime() > start) { // TODO stats += m_burst_storage.count(start, end); // Adjust start for idle counting start = m_burst_storage.endTime(); // Transitions last burst to idle_pattern if (end > start) { burst_t lastburst = m_burst_storage.get_burst(0); stats += getPinChangeStats(lastburst ? PinState::H : PinState::L, state); } } // Add duration of state if (end > start) { switch (state) { case PinState::L: stats.zeroes += end - start; break; case PinState::H: stats.ones += end - start; break; case PinState::Z: // Nothing to do break; } } } void set_internal(timestamp_t t, std::size_t dataRate, PendingStats pending_stats, pin_stats_t &stats) const { // Check if change is needed // if (m_last_state == state) { // return; // } timestamp_t virtual_time = t * dataRate; assert(virtual_time >= m_last_set); // Count init transition if (m_init_load && (0 < virtual_time)) { stats += getPinChangeStats(m_last_state, m_idle_state); } // count stats addPendingStats(virtual_time, pending_stats, stats); count(virtual_time, m_last_set, m_idle_state, stats); } // Public member functions public: // The timestamp t is relative to the clock frequency void set(timestamp_t load_time, PinState state, std::size_t dataRate = 1) { timestamp_t virtual_load_time = load_time * dataRate; // Count stats to virtual_load_time set_internal(virtual_load_time, dataRate, m_pending_stats, m_stats); if (m_pending_stats.isPending() && m_pending_stats.getTimestamp() < virtual_load_time) { m_pending_stats.clear(); } if ((m_last_set != virtual_load_time) || m_init_load) { // New Burst const PinState& laststate = [this, virtual_load_time](){ if ((virtual_load_time == m_last_set + m_burst_storage.size()) || (m_init_load && (0 == virtual_load_time))) { // seamless or first load return m_last_state; } return m_idle_state; }(); m_pending_stats.setPendingStats(virtual_load_time, { laststate, // From state state // New state }); } m_burst_storage.push_back(virtual_load_time, PinState::H == state); m_last_set = virtual_load_time; m_last_state = state; m_init_load = false; } // The timestamp t is relative to the clock frequency [[nodiscard]] pin_stats_t get_stats_at(timestamp_t t, std::size_t dataRate = 1) const { timestamp_t virtual_time = t * dataRate; assert(virtual_time >= m_last_set); if (virtual_time == m_last_set) { return m_stats; } // virtual_time > m_last_set // Add stats from m_last_set to t auto stats = m_stats; // Init stats if (m_init_load && (0 < virtual_time)) { stats += getPinChangeStats(m_last_state, m_idle_state); } addPendingStats(virtual_time, m_pending_stats, stats); count(virtual_time, m_last_set, m_idle_state, stats); return stats; } // Overrides public: void serialize(std::ostream &stream) const override { stream.write(reinterpret_cast(&m_last_state), sizeof(m_last_state)); stream.write(reinterpret_cast(&m_last_set), sizeof(m_last_set)); stream.write(reinterpret_cast(&m_init_load), sizeof(m_init_load)); m_burst_storage.serialize(stream); m_pending_stats.serialize(stream); m_stats.serialize(stream); } void deserialize(std::istream &stream) override { stream.read(reinterpret_cast(&m_last_state), sizeof(m_last_state)); stream.read(reinterpret_cast(&m_last_set), sizeof(m_last_set)); stream.read(reinterpret_cast(&m_init_load), sizeof(m_init_load)); m_burst_storage.deserialize(stream); m_pending_stats.deserialize(stream); m_stats.deserialize(stream); } // Private member variables private: PendingStats m_pending_stats; pin_stats_t m_stats; PinState m_last_state = PinState::Z; const PinState m_idle_state = PinState::L; bool m_init_load = true; timestamp_t m_last_set = 0; burst_storage_t m_burst_storage{}; }; }; #endif /* DRAMPOWER_UTIL_PINH */ ================================================ FILE: src/DRAMPower/DRAMPower/util/pin_types.h ================================================ #ifndef DRAMPOWER_UTIL_PIN_TYPES #define DRAMPOWER_UTIL_PIN_TYPES namespace DRAMPower::util { enum class PinState { L = 0, H = 1, Z = 2, }; } // namespace DRAMPower::util #endif /* DRAMPOWER_UTIL_PIN_TYPES */ ================================================ FILE: src/DRAMPower/DRAMPower/util/sub_bitset.h ================================================ #ifndef DRAMPOWER_UTIL_SUB_BITSET #define DRAMPOWER_UTIL_SUB_BITSET #include #include #include #include #include #include namespace DRAMPower::util { template class sub_bitset { public: using buffer_t = std::bitset; private: std::size_t width = 0; buffer_t buffer; buffer_t mask; public: explicit sub_bitset(std::size_t width) : width(width) , buffer(0) { assert(width <= max_width); if (width > max_width) { throw std::out_of_range("Width exceeds maximum width"); } makeMask(); } explicit sub_bitset(std::size_t width, uint64_t value) : width(width) , buffer(0) { assert(width <= max_width); if (width > max_width) { throw std::out_of_range("Width exceeds maximum width"); } uint64_t accumulator = 1; for (std::size_t bit_index = 0; bit_index < width; ++bit_index) { buffer.set(bit_index, value & accumulator); accumulator <<= 1; } makeMask(); } sub_bitset() = default; // width is 0 public: sub_bitset(const sub_bitset&) = default; sub_bitset(sub_bitset&&) noexcept = default; sub_bitset& operator=(const sub_bitset&) = default; sub_bitset& operator=(sub_bitset&&) noexcept = default; public: inline void makeMask() { mask.reset(); for (std::size_t i = 0; i < width; ++i) { mask.set(i); } } inline std::size_t size() const { return width; }; inline std::size_t count() const { return buffer.count(); } public: inline void set(std::size_t n, bool value) { assert(n < width); if (n >= width) { throw std::out_of_range("Index out of range"); } buffer.set(n, value); } inline void set() { buffer.set(); buffer &= mask; } inline void reset() { buffer.reset(); } inline void flip() { buffer.flip(); buffer &= mask; } inline void flip(std::size_t n) { assert(n < width); if (n >= width) { throw std::out_of_range("Index out of range"); } buffer.flip(n); } public: inline auto operator[](std::size_t n) { assert(n < width); if (n >= width) { throw std::out_of_range("Index out of range"); } return buffer.test(n); } inline auto operator[](std::size_t n) const { assert(n < width); if (n >= width) { throw std::out_of_range("Index out of range"); } return buffer.test(n); } public: template bool operator==(const sub_bitset& rhs) const { if (this->size() != rhs.size()) { return false; } return buffer == rhs.buffer; } template bool operator!=(const sub_bitset& rhs) const { return !(*this == rhs); } bool operator==(unsigned long rhs) const { return *this == sub_bitset { this->size(), rhs }; } bool operator!=(unsigned long rhs) const { return !(*this == rhs); } public: // bitset operations sub_bitset operator~() const { auto result = *this; result.buffer = ~buffer; result.buffer &= mask; return result; } template auto& operator^=(const sub_bitset& rhs) { assert(this->size() == rhs.size()); buffer ^= rhs.buffer; buffer &= mask; return *this; } template auto& operator&=(const sub_bitset& rhs) { assert(this->size() == rhs.size()); buffer &= rhs.buffer; return *this; } template auto& operator|=(const sub_bitset& rhs) { assert(this->size() == rhs.size()); buffer |= rhs.buffer; buffer &= mask; return *this; } public: }; template inline sub_bitset operator^(sub_bitset lhs, const sub_bitset& rhs) { return lhs ^= rhs; }; template inline sub_bitset operator&(sub_bitset lhs, const sub_bitset& rhs) { return lhs &= rhs; }; template inline sub_bitset operator|(sub_bitset lhs, const sub_bitset& rhs) { return lhs |= rhs; }; } // namespace DRAMPower::util #endif /* DRAMPOWER_UTIL_SUB_BITSET */ ================================================ FILE: src/cli/CMakeLists.txt ================================================ if (DRAMPOWER_BUILD_CLI) add_subdirectory(main) endif() if (DRAMPOWER_BUILD_CLI OR DRAMPOWER_BUILD_BENCHMARKS) add_subdirectory(lib) endif() ================================================ FILE: src/cli/lib/CMakeLists.txt ================================================ ############################### ### cli_lib ### ############################### find_package(spdlog REQUIRED) add_library(cli_lib DRAMPower/cli/run.cpp DRAMPower/cli/util.cpp ) target_include_directories(cli_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_features(cli_lib PUBLIC cxx_std_17) set_target_properties(cli_lib PROPERTIES CXX_EXTENSIONS OFF) set_target_properties(cli_lib PROPERTIES CXX_STANDARD_REQUIRED ON) target_link_libraries(cli_lib PUBLIC DRAMPower::DRAMPower DRAMUtils::DRAMUtils spdlog::spdlog ) target_compile_definitions(cli_lib PRIVATE DRAMPOWER_VERSION_STRING="${DRAMPOWER_VERSION_STRING}") add_library(DRAMPower::cli_lib ALIAS cli_lib) ================================================ FILE: src/cli/lib/DRAMPower/cli/config.h ================================================ #ifndef CLI_CONFIG_H #define CLI_CONFIG_H #include #include #include namespace DRAMPower::DRAMPowerCLI::config { struct CLIConfig { bool useToggleRate; DRAMPower::config::SimConfig simconfig; }; NLOHMANN_JSONIFY_ALL_THINGS(CLIConfig, useToggleRate, simconfig) } // namespace DRAMPower::DRAMPowerCLI::config #endif /* CLI_CONFIG_H */ ================================================ FILE: src/cli/lib/DRAMPower/cli/csv.hpp ================================================ #pragma once /* CSV for C++, version 2.1.3 https://github.com/vincentlaucsb/csv-parser MIT License Copyright (c) 2017-2020 Vincent La 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 CSV_HPP #define CSV_HPP /** @file * @brief Defines functionality needed for basic CSV parsing */ #include #include #include #include #include #include #include #include #include #include /* Copyright 2017 https://github.com/mandreyel * * 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 MIO_MMAP_HEADER #define MIO_MMAP_HEADER // #include "mio/page.hpp" /* Copyright 2017 https://github.com/mandreyel * * 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 MIO_PAGE_HEADER #define MIO_PAGE_HEADER #ifdef _WIN32 # include #else # include #endif namespace mio { /** * This is used by `basic_mmap` to determine whether to create a read-only or * a read-write memory mapping. */ enum class access_mode { read, write }; /** * Determines the operating system's page allocation granularity. * * On the first call to this function, it invokes the operating system specific syscall * to determine the page size, caches the value, and returns it. Any subsequent call to * this function serves the cached value, so no further syscalls are made. */ inline size_t page_size() { static const size_t page_size = [] { #ifdef _WIN32 SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); return SystemInfo.dwAllocationGranularity; #else return sysconf(_SC_PAGE_SIZE); #endif }(); return page_size; } /** * Alligns `offset` to the operating's system page size such that it subtracts the * difference until the nearest page boundary before `offset`, or does nothing if * `offset` is already page aligned. */ inline size_t make_offset_page_aligned(size_t offset) noexcept { const size_t page_size_ = page_size(); // Use integer division to round down to the nearest page alignment. return offset / page_size_ * page_size_; } } // namespace mio #endif // MIO_PAGE_HEADER #include #include #include #include #ifdef _WIN32 # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif // WIN32_LEAN_AND_MEAN # include #else // ifdef _WIN32 # define INVALID_HANDLE_VALUE -1 #endif // ifdef _WIN32 namespace mio { // This value may be provided as the `length` parameter to the constructor or // `map`, in which case a memory mapping of the entire file is created. enum { map_entire_file = 0 }; #ifdef _WIN32 using file_handle_type = HANDLE; #else using file_handle_type = int; #endif // This value represents an invalid file handle type. This can be used to // determine whether `basic_mmap::file_handle` is valid, for example. const static file_handle_type invalid_handle = INVALID_HANDLE_VALUE; template struct basic_mmap { using value_type = ByteT; using size_type = size_t; using reference = value_type&; using const_reference = const value_type&; using pointer = value_type*; using const_pointer = const value_type*; using difference_type = std::ptrdiff_t; using iterator = pointer; using const_iterator = const_pointer; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; using iterator_category = std::random_access_iterator_tag; using handle_type = file_handle_type; static_assert(sizeof(ByteT) == sizeof(char), "ByteT must be the same size as char."); private: // Points to the first requested byte, and not to the actual start of the mapping. pointer data_ = nullptr; // Length--in bytes--requested by user (which may not be the length of the // full mapping) and the length of the full mapping. size_type length_ = 0; size_type mapped_length_ = 0; // Letting user map a file using both an existing file handle and a path // introcudes some complexity (see `is_handle_internal_`). // On POSIX, we only need a file handle to create a mapping, while on // Windows systems the file handle is necessary to retrieve a file mapping // handle, but any subsequent operations on the mapped region must be done // through the latter. handle_type file_handle_ = INVALID_HANDLE_VALUE; #ifdef _WIN32 handle_type file_mapping_handle_ = INVALID_HANDLE_VALUE; #endif // Letting user map a file using both an existing file handle and a path // introcudes some complexity in that we must not close the file handle if // user provided it, but we must close it if we obtained it using the // provided path. For this reason, this flag is used to determine when to // close `file_handle_`. bool is_handle_internal_; public: /** * The default constructed mmap object is in a non-mapped state, that is, * any operation that attempts to access nonexistent underlying data will * result in undefined behaviour/segmentation faults. */ basic_mmap() = default; #ifdef __cpp_exceptions /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ template basic_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(path, offset, length, error); if(error) { throw std::system_error(error); } } /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ basic_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(handle, offset, length, error); if(error) { throw std::system_error(error); } } #endif // __cpp_exceptions /** * `basic_mmap` has single-ownership semantics, so transferring ownership * may only be accomplished by moving the object. */ basic_mmap(const basic_mmap&) = delete; basic_mmap(basic_mmap&&); basic_mmap& operator=(const basic_mmap&) = delete; basic_mmap& operator=(basic_mmap&&); /** * If this is a read-write mapping, the destructor invokes sync. Regardless * of the access mode, unmap is invoked as a final step. */ ~basic_mmap(); /** * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, * however, a mapped region of a file gets its own handle, which is returned by * 'mapping_handle'. */ handle_type file_handle() const noexcept { return file_handle_; } handle_type mapping_handle() const noexcept; /** Returns whether a valid memory mapping has been created. */ bool is_open() const noexcept { return file_handle_ != invalid_handle; } /** * Returns true if no mapping was established, that is, conceptually the * same as though the length that was mapped was 0. This function is * provided so that this class has Container semantics. */ bool empty() const noexcept { return length() == 0; } /** Returns true if a mapping was established. */ bool is_mapped() const noexcept; /** * `size` and `length` both return the logical length, i.e. the number of bytes * user requested to be mapped, while `mapped_length` returns the actual number of * bytes that were mapped which is a multiple of the underlying operating system's * page allocation granularity. */ size_type size() const noexcept { return length(); } size_type length() const noexcept { return length_; } size_type mapped_length() const noexcept { return mapped_length_; } /** Returns the offset relative to the start of the mapping. */ size_type mapping_offset() const noexcept { return mapped_length_ - length_; } /** * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping * exists. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > pointer data() noexcept { return data_; } const_pointer data() const noexcept { return data_; } /** * Returns an iterator to the first requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > iterator begin() noexcept { return data(); } const_iterator begin() const noexcept { return data(); } const_iterator cbegin() const noexcept { return data(); } /** * Returns an iterator one past the last requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > iterator end() noexcept { return data() + length(); } const_iterator end() const noexcept { return data() + length(); } const_iterator cend() const noexcept { return data() + length(); } /** * Returns a reverse iterator to the last memory mapped byte, if a valid * memory mapping exists, otherwise this function call is undefined * behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); } /** * Returns a reverse iterator past the first mapped byte, if a valid memory * mapping exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rend() noexcept { return reverse_iterator(begin()); } const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); } /** * Returns a reference to the `i`th byte from the first requested byte (as returned * by `data`). If this is invoked when no valid memory mapping has been created * prior to this call, undefined behaviour ensues. */ reference operator[](const size_type i) noexcept { return data_[i]; } const_reference operator[](const size_type i) const noexcept { return data_[i]; } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ template void map(const String& path, const size_type offset, const size_type length, std::error_code& error); /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * The entire file is mapped. */ template void map(const String& path, std::error_code& error) { map(path, 0, map_entire_file, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is * unsuccesful, the reason is reported via `error` and the object remains in * a state as if this function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ void map(const handle_type handle, const size_type offset, const size_type length, std::error_code& error); /** * Establishes a memory mapping with AccessMode. If the mapping is * unsuccesful, the reason is reported via `error` and the object remains in * a state as if this function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * The entire file is mapped. */ void map(const handle_type handle, std::error_code& error) { map(handle, 0, map_entire_file, error); } /** * If a valid memory mapping has been created prior to this call, this call * instructs the kernel to unmap the memory region and disassociate this object * from the file. * * The file handle associated with the file that is mapped is only closed if the * mapping was created using a file path. If, on the other hand, an existing * file handle was used to create the mapping, the file handle is not closed. */ void unmap(); void swap(basic_mmap& other); /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ template typename std::enable_if::type sync(std::error_code& error); /** * All operators compare the address of the first byte and size of the two mapped * regions. */ private: template< access_mode A = AccessMode, typename = typename std::enable_if::type > pointer get_mapping_start() noexcept { return !data() ? nullptr : data() - mapping_offset(); } const_pointer get_mapping_start() const noexcept { return !data() ? nullptr : data() - mapping_offset(); } /** * The destructor syncs changes to disk if `AccessMode` is `write`, but not * if it's `read`, but since the destructor cannot be templated, we need to * do SFINAE in a dedicated function, where one syncs and the other is a noop. */ template typename std::enable_if::type conditional_sync(); template typename std::enable_if::type conditional_sync(); }; template bool operator==(const basic_mmap& a, const basic_mmap& b); template bool operator!=(const basic_mmap& a, const basic_mmap& b); template bool operator<(const basic_mmap& a, const basic_mmap& b); template bool operator<=(const basic_mmap& a, const basic_mmap& b); template bool operator>(const basic_mmap& a, const basic_mmap& b); template bool operator>=(const basic_mmap& a, const basic_mmap& b); /** * This is the basis for all read-only mmap objects and should be preferred over * directly using `basic_mmap`. */ template using basic_mmap_source = basic_mmap; /** * This is the basis for all read-write mmap objects and should be preferred over * directly using `basic_mmap`. */ template using basic_mmap_sink = basic_mmap; /** * These aliases cover the most common use cases, both representing a raw byte stream * (either with a char or an unsigned char/uint8_t). */ using mmap_source = basic_mmap_source; using ummap_source = basic_mmap_source; using mmap_sink = basic_mmap_sink; using ummap_sink = basic_mmap_sink; /** * Convenience factory method that constructs a mapping for any `basic_mmap` or * `basic_mmap` type. */ template< typename MMap, typename MappingToken > MMap make_mmap(const MappingToken& token, int64_t offset, int64_t length, std::error_code& error) { MMap mmap; mmap.map(token, offset, length, error); return mmap; } /** * Convenience factory method. * * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`, * `std::filesystem::path`, `std::vector`, or similar), or a * `mmap_source::handle_type`. */ template mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset, mmap_source::size_type length, std::error_code& error) { return make_mmap(token, offset, length, error); } template mmap_source make_mmap_source(const MappingToken& token, std::error_code& error) { return make_mmap_source(token, 0, map_entire_file, error); } /** * Convenience factory method. * * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`, * `std::filesystem::path`, `std::vector`, or similar), or a * `mmap_sink::handle_type`. */ template mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset, mmap_sink::size_type length, std::error_code& error) { return make_mmap(token, offset, length, error); } template mmap_sink make_mmap_sink(const MappingToken& token, std::error_code& error) { return make_mmap_sink(token, 0, map_entire_file, error); } } // namespace mio // #include "detail/mmap.ipp" /* Copyright 2017 https://github.com/mandreyel * * 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 MIO_BASIC_MMAP_IMPL #define MIO_BASIC_MMAP_IMPL // #include "mio/mmap.hpp" // #include "mio/page.hpp" // #include "mio/detail/string_util.hpp" /* Copyright 2017 https://github.com/mandreyel * * 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 MIO_STRING_UTIL_HEADER #define MIO_STRING_UTIL_HEADER #include namespace mio { namespace detail { template< typename S, typename C = typename std::decay::type, typename = decltype(std::declval().data()), typename = typename std::enable_if< std::is_same::value #ifdef _WIN32 || std::is_same::value #endif >::type > struct char_type_helper { using type = typename C::value_type; }; template struct char_type { using type = typename char_type_helper::type; }; // TODO: can we avoid this brute force approach? template<> struct char_type { using type = char; }; template<> struct char_type { using type = char; }; template struct char_type { using type = char; }; template struct char_type { using type = char; }; #ifdef _WIN32 template<> struct char_type { using type = wchar_t; }; template<> struct char_type { using type = wchar_t; }; template struct char_type { using type = wchar_t; }; template struct char_type { using type = wchar_t; }; #endif // _WIN32 template struct is_c_str_helper { static constexpr bool value = std::is_same< CharT*, // TODO: I'm so sorry for this... Can this be made cleaner? typename std::add_pointer< typename std::remove_cv< typename std::remove_pointer< typename std::decay< S >::type >::type >::type >::type >::value; }; template struct is_c_str { static constexpr bool value = is_c_str_helper::value; }; #ifdef _WIN32 template struct is_c_wstr { static constexpr bool value = is_c_str_helper::value; }; #endif // _WIN32 template struct is_c_str_or_c_wstr { static constexpr bool value = is_c_str::value #ifdef _WIN32 || is_c_wstr::value #endif ; }; template< typename String, typename = decltype(std::declval().data()), typename = typename std::enable_if::value>::type > const typename char_type::type* c_str(const String& path) { return path.data(); } template< typename String, typename = decltype(std::declval().empty()), typename = typename std::enable_if::value>::type > bool empty(const String& path) { return path.empty(); } template< typename String, typename = typename std::enable_if::value>::type > const typename char_type::type* c_str(String path) { return path; } template< typename String, typename = typename std::enable_if::value>::type > bool empty(String path) { return !path || (*path == 0); } } // namespace detail } // namespace mio #endif // MIO_STRING_UTIL_HEADER #include #ifndef _WIN32 # include # include # include # include #endif namespace mio { namespace detail { #ifdef _WIN32 namespace win { /** Returns the 4 upper bytes of an 8-byte integer. */ inline DWORD int64_high(int64_t n) noexcept { return n >> 32; } /** Returns the 4 lower bytes of an 8-byte integer. */ inline DWORD int64_low(int64_t n) noexcept { return n & 0xffffffff; } template< typename String, typename = typename std::enable_if< std::is_same::type, char>::value >::type > file_handle_type open_file_helper(const String& path, const access_mode mode) { return ::CreateFileA(c_str(path), mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); } template typename std::enable_if< std::is_same::type, wchar_t>::value, file_handle_type >::type open_file_helper(const String& path, const access_mode mode) { return ::CreateFileW(c_str(path), mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); } } // win #endif // _WIN32 /** * Returns the last platform specific system error (errno on POSIX and * GetLastError on Win) as a `std::error_code`. */ inline std::error_code last_error() noexcept { std::error_code error; #ifdef _WIN32 error.assign(GetLastError(), std::system_category()); #else error.assign(errno, std::system_category()); #endif return error; } template file_handle_type open_file(const String& path, const access_mode mode, std::error_code& error) { error.clear(); if(detail::empty(path)) { error = std::make_error_code(std::errc::invalid_argument); return invalid_handle; } #ifdef _WIN32 const auto handle = win::open_file_helper(path, mode); #else // POSIX const auto handle = ::open(c_str(path), mode == access_mode::read ? O_RDONLY : O_RDWR); #endif if(handle == invalid_handle) { error = detail::last_error(); } return handle; } inline size_t query_file_size(file_handle_type handle, std::error_code& error) { error.clear(); #ifdef _WIN32 LARGE_INTEGER file_size; if(::GetFileSizeEx(handle, &file_size) == 0) { error = detail::last_error(); return 0; } return static_cast(file_size.QuadPart); #else // POSIX struct stat sbuf; if(::fstat(handle, &sbuf) == -1) { error = detail::last_error(); return 0; } return sbuf.st_size; #endif } struct mmap_context { char* data; int64_t length; int64_t mapped_length; #ifdef _WIN32 file_handle_type file_mapping_handle; #endif }; inline mmap_context memory_map(const file_handle_type file_handle, const int64_t offset, const int64_t length, const access_mode mode, std::error_code& error) { const int64_t aligned_offset = make_offset_page_aligned(offset); const int64_t length_to_map = offset - aligned_offset + length; #ifdef _WIN32 const int64_t max_file_size = offset + length; const auto file_mapping_handle = ::CreateFileMapping( file_handle, 0, mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE, win::int64_high(max_file_size), win::int64_low(max_file_size), 0); if(file_mapping_handle == invalid_handle) { error = detail::last_error(); return {}; } char* mapping_start = static_cast(::MapViewOfFile( file_mapping_handle, mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE, win::int64_high(aligned_offset), win::int64_low(aligned_offset), length_to_map)); if(mapping_start == nullptr) { // Close file handle if mapping it failed. ::CloseHandle(file_mapping_handle); error = detail::last_error(); return {}; } #else // POSIX char* mapping_start = static_cast(::mmap( 0, // Don't give hint as to where to map. length_to_map, mode == access_mode::read ? PROT_READ : PROT_WRITE, MAP_SHARED, file_handle, aligned_offset)); if(mapping_start == MAP_FAILED) { error = detail::last_error(); return {}; } #endif mmap_context ctx; ctx.data = mapping_start + offset - aligned_offset; ctx.length = length; ctx.mapped_length = length_to_map; #ifdef _WIN32 ctx.file_mapping_handle = file_mapping_handle; #endif return ctx; } } // namespace detail // -- basic_mmap -- template basic_mmap::~basic_mmap() { conditional_sync(); unmap(); } template basic_mmap::basic_mmap(basic_mmap&& other) : data_(std::move(other.data_)) , length_(std::move(other.length_)) , mapped_length_(std::move(other.mapped_length_)) , file_handle_(std::move(other.file_handle_)) #ifdef _WIN32 , file_mapping_handle_(std::move(other.file_mapping_handle_)) #endif , is_handle_internal_(std::move(other.is_handle_internal_)) { other.data_ = nullptr; other.length_ = other.mapped_length_ = 0; other.file_handle_ = invalid_handle; #ifdef _WIN32 other.file_mapping_handle_ = invalid_handle; #endif } template basic_mmap& basic_mmap::operator=(basic_mmap&& other) { if(this != &other) { // First the existing mapping needs to be removed. unmap(); data_ = std::move(other.data_); length_ = std::move(other.length_); mapped_length_ = std::move(other.mapped_length_); file_handle_ = std::move(other.file_handle_); #ifdef _WIN32 file_mapping_handle_ = std::move(other.file_mapping_handle_); #endif is_handle_internal_ = std::move(other.is_handle_internal_); // The moved from basic_mmap's fields need to be reset, because // otherwise other's destructor will unmap the same mapping that was // just moved into this. other.data_ = nullptr; other.length_ = other.mapped_length_ = 0; other.file_handle_ = invalid_handle; #ifdef _WIN32 other.file_mapping_handle_ = invalid_handle; #endif other.is_handle_internal_ = false; } return *this; } template typename basic_mmap::handle_type basic_mmap::mapping_handle() const noexcept { #ifdef _WIN32 return file_mapping_handle_; #else return file_handle_; #endif } template template void basic_mmap::map(const String& path, const size_type offset, const size_type length, std::error_code& error) { error.clear(); if(detail::empty(path)) { error = std::make_error_code(std::errc::invalid_argument); return; } const auto handle = detail::open_file(path, AccessMode, error); if(error) { return; } map(handle, offset, length, error); // This MUST be after the call to map, as that sets this to true. if(!error) { is_handle_internal_ = true; } } template void basic_mmap::map(const handle_type handle, const size_type offset, const size_type length, std::error_code& error) { error.clear(); if(handle == invalid_handle) { error = std::make_error_code(std::errc::bad_file_descriptor); return; } const auto file_size = detail::query_file_size(handle, error); if(error) { return; } if(offset + length > file_size) { error = std::make_error_code(std::errc::invalid_argument); return; } const auto ctx = detail::memory_map(handle, offset, length == map_entire_file ? (file_size - offset) : length, AccessMode, error); if(!error) { // We must unmap the previous mapping that may have existed prior to this call. // Note that this must only be invoked after a new mapping has been created in // order to provide the strong guarantee that, should the new mapping fail, the // `map` function leaves this instance in a state as though the function had // never been invoked. unmap(); file_handle_ = handle; is_handle_internal_ = false; data_ = reinterpret_cast(ctx.data); length_ = ctx.length; mapped_length_ = ctx.mapped_length; #ifdef _WIN32 file_mapping_handle_ = ctx.file_mapping_handle; #endif } } template template typename std::enable_if::type basic_mmap::sync(std::error_code& error) { error.clear(); if(!is_open()) { error = std::make_error_code(std::errc::bad_file_descriptor); return; } if(data()) { #ifdef _WIN32 if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0 || ::FlushFileBuffers(file_handle_) == 0) #else // POSIX if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0) #endif { error = detail::last_error(); return; } } #ifdef _WIN32 if(::FlushFileBuffers(file_handle_) == 0) { error = detail::last_error(); } #endif } template void basic_mmap::unmap() { if(!is_open()) { return; } // TODO do we care about errors here? #ifdef _WIN32 if(is_mapped()) { ::UnmapViewOfFile(get_mapping_start()); ::CloseHandle(file_mapping_handle_); } #else // POSIX if(data_) { ::munmap(const_cast(get_mapping_start()), mapped_length_); } #endif // If `file_handle_` was obtained by our opening it (when map is called with // a path, rather than an existing file handle), we need to close it, // otherwise it must not be closed as it may still be used outside this // instance. if(is_handle_internal_) { #ifdef _WIN32 ::CloseHandle(file_handle_); #else // POSIX ::close(file_handle_); #endif } // Reset fields to their default values. data_ = nullptr; length_ = mapped_length_ = 0; file_handle_ = invalid_handle; #ifdef _WIN32 file_mapping_handle_ = invalid_handle; #endif } template bool basic_mmap::is_mapped() const noexcept { #ifdef _WIN32 return file_mapping_handle_ != invalid_handle; #else // POSIX return is_open(); #endif } template void basic_mmap::swap(basic_mmap& other) { if(this != &other) { using std::swap; swap(data_, other.data_); swap(file_handle_, other.file_handle_); #ifdef _WIN32 swap(file_mapping_handle_, other.file_mapping_handle_); #endif swap(length_, other.length_); swap(mapped_length_, other.mapped_length_); swap(is_handle_internal_, other.is_handle_internal_); } } template template typename std::enable_if::type basic_mmap::conditional_sync() { // This is invoked from the destructor, so not much we can do about // failures here. std::error_code ec; sync(ec); } template template typename std::enable_if::type basic_mmap::conditional_sync() { // noop } template bool operator==(const basic_mmap& a, const basic_mmap& b) { return a.data() == b.data() && a.size() == b.size(); } template bool operator!=(const basic_mmap& a, const basic_mmap& b) { return !(a == b); } template bool operator<(const basic_mmap& a, const basic_mmap& b) { if(a.data() == b.data()) { return a.size() < b.size(); } return a.data() < b.data(); } template bool operator<=(const basic_mmap& a, const basic_mmap& b) { return !(a > b); } template bool operator>(const basic_mmap& a, const basic_mmap& b) { if(a.data() == b.data()) { return a.size() > b.size(); } return a.data() > b.data(); } template bool operator>=(const basic_mmap& a, const basic_mmap& b) { return !(a < b); } } // namespace mio #endif // MIO_BASIC_MMAP_IMPL #endif // MIO_MMAP_HEADER /* Copyright 2017 https://github.com/mandreyel * * 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 MIO_PAGE_HEADER #define MIO_PAGE_HEADER #ifdef _WIN32 # include #else # include #endif namespace mio { /** * This is used by `basic_mmap` to determine whether to create a read-only or * a read-write memory mapping. */ enum class access_mode { read, write }; /** * Determines the operating system's page allocation granularity. * * On the first call to this function, it invokes the operating system specific syscall * to determine the page size, caches the value, and returns it. Any subsequent call to * this function serves the cached value, so no further syscalls are made. */ inline size_t page_size() { static const size_t page_size = [] { #ifdef _WIN32 SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); return SystemInfo.dwAllocationGranularity; #else return sysconf(_SC_PAGE_SIZE); #endif }(); return page_size; } /** * Alligns `offset` to the operating's system page size such that it subtracts the * difference until the nearest page boundary before `offset`, or does nothing if * `offset` is already page aligned. */ inline size_t make_offset_page_aligned(size_t offset) noexcept { const size_t page_size_ = page_size(); // Use integer division to round down to the nearest page alignment. return offset / page_size_ * page_size_; } } // namespace mio #endif // MIO_PAGE_HEADER /* Copyright 2017 https://github.com/mandreyel * * 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 MIO_SHARED_MMAP_HEADER #define MIO_SHARED_MMAP_HEADER // #include "mio/mmap.hpp" #include // std::error_code #include // std::shared_ptr namespace mio { /** * Exposes (nearly) the same interface as `basic_mmap`, but endowes it with * `std::shared_ptr` semantics. * * This is not the default behaviour of `basic_mmap` to avoid allocating on the heap if * shared semantics are not required. */ template< access_mode AccessMode, typename ByteT > class basic_shared_mmap { using impl_type = basic_mmap; std::shared_ptr pimpl_; public: using value_type = typename impl_type::value_type; using size_type = typename impl_type::size_type; using reference = typename impl_type::reference; using const_reference = typename impl_type::const_reference; using pointer = typename impl_type::pointer; using const_pointer = typename impl_type::const_pointer; using difference_type = typename impl_type::difference_type; using iterator = typename impl_type::iterator; using const_iterator = typename impl_type::const_iterator; using reverse_iterator = typename impl_type::reverse_iterator; using const_reverse_iterator = typename impl_type::const_reverse_iterator; using iterator_category = typename impl_type::iterator_category; using handle_type = typename impl_type::handle_type; using mmap_type = impl_type; basic_shared_mmap() = default; basic_shared_mmap(const basic_shared_mmap&) = default; basic_shared_mmap& operator=(const basic_shared_mmap&) = default; basic_shared_mmap(basic_shared_mmap&&) = default; basic_shared_mmap& operator=(basic_shared_mmap&&) = default; /** Takes ownership of an existing mmap object. */ basic_shared_mmap(mmap_type&& mmap) : pimpl_(std::make_shared(std::move(mmap))) {} /** Takes ownership of an existing mmap object. */ basic_shared_mmap& operator=(mmap_type&& mmap) { pimpl_ = std::make_shared(std::move(mmap)); return *this; } /** Initializes this object with an already established shared mmap. */ basic_shared_mmap(std::shared_ptr mmap) : pimpl_(std::move(mmap)) {} /** Initializes this object with an already established shared mmap. */ basic_shared_mmap& operator=(std::shared_ptr mmap) { pimpl_ = std::move(mmap); return *this; } #ifdef __cpp_exceptions /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ template basic_shared_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(path, offset, length, error); if(error) { throw std::system_error(error); } } /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ basic_shared_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(handle, offset, length, error); if(error) { throw std::system_error(error); } } #endif // __cpp_exceptions /** * If this is a read-write mapping and the last reference to the mapping, * the destructor invokes sync. Regardless of the access mode, unmap is * invoked as a final step. */ ~basic_shared_mmap() = default; /** Returns the underlying `std::shared_ptr` instance that holds the mmap. */ std::shared_ptr get_shared_ptr() { return pimpl_; } /** * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, * however, a mapped region of a file gets its own handle, which is returned by * 'mapping_handle'. */ handle_type file_handle() const noexcept { return pimpl_ ? pimpl_->file_handle() : invalid_handle; } handle_type mapping_handle() const noexcept { return pimpl_ ? pimpl_->mapping_handle() : invalid_handle; } /** Returns whether a valid memory mapping has been created. */ bool is_open() const noexcept { return pimpl_ && pimpl_->is_open(); } /** * Returns true if no mapping was established, that is, conceptually the * same as though the length that was mapped was 0. This function is * provided so that this class has Container semantics. */ bool empty() const noexcept { return !pimpl_ || pimpl_->empty(); } /** * `size` and `length` both return the logical length, i.e. the number of bytes * user requested to be mapped, while `mapped_length` returns the actual number of * bytes that were mapped which is a multiple of the underlying operating system's * page allocation granularity. */ size_type size() const noexcept { return pimpl_ ? pimpl_->length() : 0; } size_type length() const noexcept { return pimpl_ ? pimpl_->length() : 0; } size_type mapped_length() const noexcept { return pimpl_ ? pimpl_->mapped_length() : 0; } /** * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping * exists. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > pointer data() noexcept { return pimpl_->data(); } const_pointer data() const noexcept { return pimpl_ ? pimpl_->data() : nullptr; } /** * Returns an iterator to the first requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ iterator begin() noexcept { return pimpl_->begin(); } const_iterator begin() const noexcept { return pimpl_->begin(); } const_iterator cbegin() const noexcept { return pimpl_->cbegin(); } /** * Returns an iterator one past the last requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > iterator end() noexcept { return pimpl_->end(); } const_iterator end() const noexcept { return pimpl_->end(); } const_iterator cend() const noexcept { return pimpl_->cend(); } /** * Returns a reverse iterator to the last memory mapped byte, if a valid * memory mapping exists, otherwise this function call is undefined * behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rbegin() noexcept { return pimpl_->rbegin(); } const_reverse_iterator rbegin() const noexcept { return pimpl_->rbegin(); } const_reverse_iterator crbegin() const noexcept { return pimpl_->crbegin(); } /** * Returns a reverse iterator past the first mapped byte, if a valid memory * mapping exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rend() noexcept { return pimpl_->rend(); } const_reverse_iterator rend() const noexcept { return pimpl_->rend(); } const_reverse_iterator crend() const noexcept { return pimpl_->crend(); } /** * Returns a reference to the `i`th byte from the first requested byte (as returned * by `data`). If this is invoked when no valid memory mapping has been created * prior to this call, undefined behaviour ensues. */ reference operator[](const size_type i) noexcept { return (*pimpl_)[i]; } const_reference operator[](const size_type i) const noexcept { return (*pimpl_)[i]; } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ template void map(const String& path, const size_type offset, const size_type length, std::error_code& error) { map_impl(path, offset, length, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * The entire file is mapped. */ template void map(const String& path, std::error_code& error) { map_impl(path, 0, map_entire_file, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ void map(const handle_type handle, const size_type offset, const size_type length, std::error_code& error) { map_impl(handle, offset, length, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * The entire file is mapped. */ void map(const handle_type handle, std::error_code& error) { map_impl(handle, 0, map_entire_file, error); } /** * If a valid memory mapping has been created prior to this call, this call * instructs the kernel to unmap the memory region and disassociate this object * from the file. * * The file handle associated with the file that is mapped is only closed if the * mapping was created using a file path. If, on the other hand, an existing * file handle was used to create the mapping, the file handle is not closed. */ void unmap() { if(pimpl_) pimpl_->unmap(); } void swap(basic_shared_mmap& other) { pimpl_.swap(other.pimpl_); } /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > void sync(std::error_code& error) { if(pimpl_) pimpl_->sync(error); } /** All operators compare the underlying `basic_mmap`'s addresses. */ friend bool operator==(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ == b.pimpl_; } friend bool operator!=(const basic_shared_mmap& a, const basic_shared_mmap& b) { return !(a == b); } friend bool operator<(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ < b.pimpl_; } friend bool operator<=(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ <= b.pimpl_; } friend bool operator>(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ > b.pimpl_; } friend bool operator>=(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ >= b.pimpl_; } private: template void map_impl(const MappingToken& token, const size_type offset, const size_type length, std::error_code& error) { if(!pimpl_) { mmap_type mmap = make_mmap(token, offset, length, error); if(error) { return; } pimpl_ = std::make_shared(std::move(mmap)); } else { pimpl_->map(token, offset, length, error); } } }; /** * This is the basis for all read-only mmap objects and should be preferred over * directly using basic_shared_mmap. */ template using basic_shared_mmap_source = basic_shared_mmap; /** * This is the basis for all read-write mmap objects and should be preferred over * directly using basic_shared_mmap. */ template using basic_shared_mmap_sink = basic_shared_mmap; /** * These aliases cover the most common use cases, both representing a raw byte stream * (either with a char or an unsigned char/uint8_t). */ using shared_mmap_source = basic_shared_mmap_source; using shared_ummap_source = basic_shared_mmap_source; using shared_mmap_sink = basic_shared_mmap_sink; using shared_ummap_sink = basic_shared_mmap_sink; } // namespace mio #endif // MIO_SHARED_MMAP_HEADER /** @file * @brief Contains the main CSV parsing algorithm and various utility functions */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** @file * A standalone header file containing shared code */ #include #include #include #include #include #if defined(_WIN32) # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # include # undef max # undef min #elif defined(__linux__) # include #endif /** Helper macro which should be #defined as "inline" * in the single header version */ #define CSV_INLINE inline #include // Copyright 2017-2019 by Martin Moene // // string-view lite, a C++17-like string_view for C++98 and later. // For more information see https://github.com/martinmoene/string-view-lite // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef NONSTD_SV_LITE_H_INCLUDED #define NONSTD_SV_LITE_H_INCLUDED #define string_view_lite_MAJOR 1 #define string_view_lite_MINOR 1 #define string_view_lite_PATCH 0 #define string_view_lite_VERSION nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH) #define nssv_STRINGIFY( x ) nssv_STRINGIFY_( x ) #define nssv_STRINGIFY_( x ) #x // string-view lite configuration: #define nssv_STRING_VIEW_DEFAULT 0 #define nssv_STRING_VIEW_NONSTD 1 #define nssv_STRING_VIEW_STD 2 #if !defined( nssv_CONFIG_SELECT_STRING_VIEW ) # define nssv_CONFIG_SELECT_STRING_VIEW ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD ) #endif #if defined( nssv_CONFIG_SELECT_STD_STRING_VIEW ) || defined( nssv_CONFIG_SELECT_NONSTD_STRING_VIEW ) # error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_... #endif #ifndef nssv_CONFIG_STD_SV_OPERATOR # define nssv_CONFIG_STD_SV_OPERATOR 0 #endif #ifndef nssv_CONFIG_USR_SV_OPERATOR # define nssv_CONFIG_USR_SV_OPERATOR 1 #endif #ifdef nssv_CONFIG_CONVERSION_STD_STRING # define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING # define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING #endif #ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS # define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1 #endif #ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS # define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1 #endif // Control presence of exception handling (try and auto discover): #ifndef nssv_CONFIG_NO_EXCEPTIONS # if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) # define nssv_CONFIG_NO_EXCEPTIONS 0 # else # define nssv_CONFIG_NO_EXCEPTIONS 1 # endif #endif // C++ language version detection (C++20 is speculative): // Note: VC14.0/1900 (VS2015) lacks too much from C++14. #ifndef nssv_CPLUSPLUS # if defined(_MSVC_LANG ) && !defined(__clang__) # define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) # else # define nssv_CPLUSPLUS __cplusplus # endif #endif #define nssv_CPP98_OR_GREATER ( nssv_CPLUSPLUS >= 199711L ) #define nssv_CPP11_OR_GREATER ( nssv_CPLUSPLUS >= 201103L ) #define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L ) #define nssv_CPP14_OR_GREATER ( nssv_CPLUSPLUS >= 201402L ) #define nssv_CPP17_OR_GREATER ( nssv_CPLUSPLUS >= 201703L ) #define nssv_CPP20_OR_GREATER ( nssv_CPLUSPLUS >= 202000L ) // use C++17 std::string_view if available and requested: #if nssv_CPP17_OR_GREATER && defined(__has_include ) # if __has_include( ) # define nssv_HAVE_STD_STRING_VIEW 1 # else # define nssv_HAVE_STD_STRING_VIEW 0 # endif #else # define nssv_HAVE_STD_STRING_VIEW 0 #endif #define nssv_USES_STD_STRING_VIEW ( (nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW) ) #define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW ) #define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH // // Use C++17 std::string_view: // #if nssv_USES_STD_STRING_VIEW #include // Extensions for std::string: #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS namespace nonstd { template< class CharT, class Traits, class Allocator = std::allocator > std::basic_string to_string( std::basic_string_view v, Allocator const & a = Allocator() ) { return std::basic_string( v.begin(), v.end(), a ); } template< class CharT, class Traits, class Allocator > std::basic_string_view to_string_view( std::basic_string const & s ) { return std::basic_string_view( s.data(), s.size() ); } // Literal operators sv and _sv: #if nssv_CONFIG_STD_SV_OPERATOR using namespace std::literals::string_view_literals; #endif #if nssv_CONFIG_USR_SV_OPERATOR inline namespace literals { inline namespace string_view_literals { constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept // (1) { return std::string_view{ str, len }; } constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept // (2) { return std::u16string_view{ str, len }; } constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept // (3) { return std::u32string_view{ str, len }; } constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept // (4) { return std::wstring_view{ str, len }; } }} // namespace literals::string_view_literals #endif // nssv_CONFIG_USR_SV_OPERATOR } // namespace nonstd #endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS namespace nonstd { using std::string_view; using std::wstring_view; using std::u16string_view; using std::u32string_view; using std::basic_string_view; // literal "sv" and "_sv", see above using std::operator==; using std::operator!=; using std::operator<; using std::operator<=; using std::operator>; using std::operator>=; using std::operator<<; } // namespace nonstd #else // nssv_HAVE_STD_STRING_VIEW // // Before C++17: use string_view lite: // // Compiler versions: // // MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0) // MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002) // MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003) // MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) // MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) // MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) // MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) // MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) // MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) // MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017) #if defined(_MSC_VER ) && !defined(__clang__) # define nssv_COMPILER_MSVC_VER (_MSC_VER ) # define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) #else # define nssv_COMPILER_MSVC_VER 0 # define nssv_COMPILER_MSVC_VERSION 0 #endif #define nssv_COMPILER_VERSION( major, minor, patch ) (10 * ( 10 * major + minor) + patch) #if defined(__clang__) # define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) #else # define nssv_COMPILER_CLANG_VERSION 0 #endif #if defined(__GNUC__) && !defined(__clang__) # define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #else # define nssv_COMPILER_GNUC_VERSION 0 #endif // half-open range [lo..hi): #define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) // Presence of language and library features: #ifdef _HAS_CPP0X # define nssv_HAS_CPP0X _HAS_CPP0X #else # define nssv_HAS_CPP0X 0 #endif // Unless defined otherwise below, consider VC14 as C++11 for variant-lite: #if nssv_COMPILER_MSVC_VER >= 1900 # undef nssv_CPP11_OR_GREATER # define nssv_CPP11_OR_GREATER 1 #endif #define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500) #define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600) #define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700) #define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800) #define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900) #define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910) #define nssv_CPP14_000 (nssv_CPP14_OR_GREATER) #define nssv_CPP17_000 (nssv_CPP17_OR_GREATER) // Presence of C++11 language features: #define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140 #define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140 #define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140 #define nssv_HAVE_NOEXCEPT nssv_CPP11_140 #define nssv_HAVE_NULLPTR nssv_CPP11_100 #define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140 #define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140 #define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140 #define nssv_HAVE_WCHAR16_T nssv_CPP11_100 #define nssv_HAVE_WCHAR32_T nssv_CPP11_100 #if ! ( ( nssv_CPP11 && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) ) # define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140 #endif // Presence of C++14 language features: #define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000 // Presence of C++17 language features: #define nssv_HAVE_NODISCARD nssv_CPP17_000 // Presence of C++ library features: #define nssv_HAVE_STD_HASH nssv_CPP11_120 // C++ feature usage: #if nssv_HAVE_CONSTEXPR_11 # define nssv_constexpr constexpr #else # define nssv_constexpr /*constexpr*/ #endif #if nssv_HAVE_CONSTEXPR_14 # define nssv_constexpr14 constexpr #else # define nssv_constexpr14 /*constexpr*/ #endif #if nssv_HAVE_EXPLICIT_CONVERSION # define nssv_explicit explicit #else # define nssv_explicit /*explicit*/ #endif #if nssv_HAVE_INLINE_NAMESPACE # define nssv_inline_ns inline #else # define nssv_inline_ns /*inline*/ #endif #if nssv_HAVE_NOEXCEPT # define nssv_noexcept noexcept #else # define nssv_noexcept /*noexcept*/ #endif //#if nssv_HAVE_REF_QUALIFIER //# define nssv_ref_qual & //# define nssv_refref_qual && //#else //# define nssv_ref_qual /*&*/ //# define nssv_refref_qual /*&&*/ //#endif #if nssv_HAVE_NULLPTR # define nssv_nullptr nullptr #else # define nssv_nullptr NULL #endif #if nssv_HAVE_NODISCARD # define nssv_nodiscard [[nodiscard]] #else # define nssv_nodiscard /*[[nodiscard]]*/ #endif // Additional includes: #include #include #include #include #include #include // std::char_traits<> #if ! nssv_CONFIG_NO_EXCEPTIONS # include #endif #if nssv_CPP11_OR_GREATER # include #endif // Clang, GNUC, MSVC warning suppression macros: #if defined(__clang__) # pragma clang diagnostic ignored "-Wreserved-user-defined-literal" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wuser-defined-literals" #elif defined(__GNUC__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wliteral-suffix" #endif // __clang__ #if nssv_COMPILER_MSVC_VERSION >= 140 # define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]] # define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress: code) ) # define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes)) #else # define nssv_SUPPRESS_MSGSL_WARNING(expr) # define nssv_SUPPRESS_MSVC_WARNING(code, descr) # define nssv_DISABLE_MSVC_WARNINGS(codes) #endif #if defined(__clang__) # define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop") #elif defined(__GNUC__) # define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop") #elif nssv_COMPILER_MSVC_VERSION >= 140 # define nssv_RESTORE_WARNINGS() __pragma(warning(pop )) #else # define nssv_RESTORE_WARNINGS() #endif // Suppress the following MSVC (GSL) warnings: // - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not // start with an underscore are reserved // - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; // use brace initialization, gsl::narrow_cast or gsl::narow // - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 ) //nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" ) //nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix ) namespace nonstd { namespace sv_lite { template < class CharT, class Traits = std::char_traits > class basic_string_view; // // basic_string_view: // template < class CharT, class Traits /* = std::char_traits */ > class basic_string_view { public: // Member types: typedef Traits traits_type; typedef CharT value_type; typedef CharT * pointer; typedef CharT const * const_pointer; typedef CharT & reference; typedef CharT const & const_reference; typedef const_pointer iterator; typedef const_pointer const_iterator; typedef std::reverse_iterator< const_iterator > reverse_iterator; typedef std::reverse_iterator< const_iterator > const_reverse_iterator; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; // 24.4.2.1 Construction and assignment: nssv_constexpr basic_string_view() nssv_noexcept : data_( nssv_nullptr ) , size_( 0 ) {} #if nssv_CPP11_OR_GREATER nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept = default; #else nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept : data_( other.data_) , size_( other.size_) {} #endif nssv_constexpr basic_string_view( CharT const * s, size_type count ) : data_( s ) , size_( count ) {} nssv_constexpr basic_string_view( CharT const * s) : data_( s ) , size_( Traits::length(s) ) {} // Assignment: #if nssv_CPP11_OR_GREATER nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept = default; #else nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept { data_ = other.data_; size_ = other.size_; return *this; } #endif // 24.4.2.2 Iterator support: nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; } nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; } nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); } nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); } nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator( end() ); } nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator( begin() ); } nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); } nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); } // 24.4.2.3 Capacity: nssv_constexpr size_type size() const nssv_noexcept { return size_; } nssv_constexpr size_type length() const nssv_noexcept { return size_; } nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits< size_type >::max)(); } // since C++20 nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept { return 0 == size_; } // 24.4.2.4 Element access: nssv_constexpr const_reference operator[]( size_type pos ) const { return data_at( pos ); } nssv_constexpr14 const_reference at( size_type pos ) const { #if nssv_CONFIG_NO_EXCEPTIONS assert( pos < size() ); #else if ( pos >= size() ) { throw std::out_of_range("nonst::string_view::at()"); } #endif return data_at( pos ); } nssv_constexpr const_reference front() const { return data_at( 0 ); } nssv_constexpr const_reference back() const { return data_at( size() - 1 ); } nssv_constexpr const_pointer data() const nssv_noexcept { return data_; } // 24.4.2.5 Modifiers: nssv_constexpr14 void remove_prefix( size_type n ) { assert( n <= size() ); data_ += n; size_ -= n; } nssv_constexpr14 void remove_suffix( size_type n ) { assert( n <= size() ); size_ -= n; } nssv_constexpr14 void swap( basic_string_view & other ) nssv_noexcept { using std::swap; swap( data_, other.data_ ); swap( size_, other.size_ ); } // 24.4.2.6 String operations: size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const { #if nssv_CONFIG_NO_EXCEPTIONS assert( pos <= size() ); #else if ( pos > size() ) { throw std::out_of_range("nonst::string_view::copy()"); } #endif const size_type rlen = (std::min)( n, size() - pos ); (void) Traits::copy( dest, data() + pos, rlen ); return rlen; } nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const { #if nssv_CONFIG_NO_EXCEPTIONS assert( pos <= size() ); #else if ( pos > size() ) { throw std::out_of_range("nonst::string_view::substr()"); } #endif return basic_string_view( data() + pos, (std::min)( n, size() - pos ) ); } // compare(), 6x: nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1) { if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) ) return result; return size() == other.size() ? 0 : size() < other.size() ? -1 : 1; } nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other ) const // (2) { return substr( pos1, n1 ).compare( other ); } nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other, size_type pos2, size_type n2 ) const // (3) { return substr( pos1, n1 ).compare( other.substr( pos2, n2 ) ); } nssv_constexpr int compare( CharT const * s ) const // (4) { return compare( basic_string_view( s ) ); } nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s ) const // (5) { return substr( pos1, n1 ).compare( basic_string_view( s ) ); } nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s, size_type n2 ) const // (6) { return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) ); } // 24.4.2.7 Searching: // starts_with(), 3x, since C++20: nssv_constexpr bool starts_with( basic_string_view v ) const nssv_noexcept // (1) { return size() >= v.size() && compare( 0, v.size(), v ) == 0; } nssv_constexpr bool starts_with( CharT c ) const nssv_noexcept // (2) { return starts_with( basic_string_view( &c, 1 ) ); } nssv_constexpr bool starts_with( CharT const * s ) const // (3) { return starts_with( basic_string_view( s ) ); } // ends_with(), 3x, since C++20: nssv_constexpr bool ends_with( basic_string_view v ) const nssv_noexcept // (1) { return size() >= v.size() && compare( size() - v.size(), npos, v ) == 0; } nssv_constexpr bool ends_with( CharT c ) const nssv_noexcept // (2) { return ends_with( basic_string_view( &c, 1 ) ); } nssv_constexpr bool ends_with( CharT const * s ) const // (3) { return ends_with( basic_string_view( s ) ); } // find(), 4x: nssv_constexpr14 size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) { return assert( v.size() == 0 || v.data() != nssv_nullptr ) , pos >= size() ? npos : to_pos( std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) ); } nssv_constexpr14 size_type find( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) { return find( basic_string_view( &c, 1 ), pos ); } nssv_constexpr14 size_type find( CharT const * s, size_type pos, size_type n ) const // (3) { return find( basic_string_view( s, n ), pos ); } nssv_constexpr14 size_type find( CharT const * s, size_type pos = 0 ) const // (4) { return find( basic_string_view( s ), pos ); } // rfind(), 4x: nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) { if ( size() < v.size() ) return npos; if ( v.empty() ) return (std::min)( size(), pos ); const_iterator last = cbegin() + (std::min)( size() - v.size(), pos ) + v.size(); const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq ); return result != last ? size_type( result - cbegin() ) : npos; } nssv_constexpr14 size_type rfind( CharT c, size_type pos = npos ) const nssv_noexcept // (2) { return rfind( basic_string_view( &c, 1 ), pos ); } nssv_constexpr14 size_type rfind( CharT const * s, size_type pos, size_type n ) const // (3) { return rfind( basic_string_view( s, n ), pos ); } nssv_constexpr14 size_type rfind( CharT const * s, size_type pos = npos ) const // (4) { return rfind( basic_string_view( s ), pos ); } // find_first_of(), 4x: nssv_constexpr size_type find_first_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) { return pos >= size() ? npos : to_pos( std::find_first_of( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) ); } nssv_constexpr size_type find_first_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) { return find_first_of( basic_string_view( &c, 1 ), pos ); } nssv_constexpr size_type find_first_of( CharT const * s, size_type pos, size_type n ) const // (3) { return find_first_of( basic_string_view( s, n ), pos ); } nssv_constexpr size_type find_first_of( CharT const * s, size_type pos = 0 ) const // (4) { return find_first_of( basic_string_view( s ), pos ); } // find_last_of(), 4x: nssv_constexpr size_type find_last_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) { return empty() ? npos : pos >= size() ? find_last_of( v, size() - 1 ) : to_pos( std::find_first_of( const_reverse_iterator( cbegin() + pos + 1 ), crend(), v.cbegin(), v.cend(), Traits::eq ) ); } nssv_constexpr size_type find_last_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2) { return find_last_of( basic_string_view( &c, 1 ), pos ); } nssv_constexpr size_type find_last_of( CharT const * s, size_type pos, size_type count ) const // (3) { return find_last_of( basic_string_view( s, count ), pos ); } nssv_constexpr size_type find_last_of( CharT const * s, size_type pos = npos ) const // (4) { return find_last_of( basic_string_view( s ), pos ); } // find_first_not_of(), 4x: nssv_constexpr size_type find_first_not_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) { return pos >= size() ? npos : to_pos( std::find_if( cbegin() + pos, cend(), not_in_view( v ) ) ); } nssv_constexpr size_type find_first_not_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) { return find_first_not_of( basic_string_view( &c, 1 ), pos ); } nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos, size_type count ) const // (3) { return find_first_not_of( basic_string_view( s, count ), pos ); } nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos = 0 ) const // (4) { return find_first_not_of( basic_string_view( s ), pos ); } // find_last_not_of(), 4x: nssv_constexpr size_type find_last_not_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) { return empty() ? npos : pos >= size() ? find_last_not_of( v, size() - 1 ) : to_pos( std::find_if( const_reverse_iterator( cbegin() + pos + 1 ), crend(), not_in_view( v ) ) ); } nssv_constexpr size_type find_last_not_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2) { return find_last_not_of( basic_string_view( &c, 1 ), pos ); } nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos, size_type count ) const // (3) { return find_last_not_of( basic_string_view( s, count ), pos ); } nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos = npos ) const // (4) { return find_last_not_of( basic_string_view( s ), pos ); } // Constants: #if nssv_CPP17_OR_GREATER static nssv_constexpr size_type npos = size_type(-1); #elif nssv_CPP11_OR_GREATER enum : size_type { npos = size_type(-1) }; #else enum { npos = size_type(-1) }; #endif private: struct not_in_view { const basic_string_view v; nssv_constexpr not_in_view( basic_string_view v ) : v( v ) {} nssv_constexpr bool operator()( CharT c ) const { return npos == v.find_first_of( c ); } }; nssv_constexpr size_type to_pos( const_iterator it ) const { return it == cend() ? npos : size_type( it - cbegin() ); } nssv_constexpr size_type to_pos( const_reverse_iterator it ) const { return it == crend() ? npos : size_type( crend() - it - 1 ); } nssv_constexpr const_reference data_at( size_type pos ) const { #if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 ) return data_[pos]; #else return assert( pos < size() ), data_[pos]; #endif } private: const_pointer data_; size_type size_; public: #if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS template< class Allocator > basic_string_view( std::basic_string const & s ) nssv_noexcept : data_( s.data() ) , size_( s.size() ) {} #if nssv_HAVE_EXPLICIT_CONVERSION template< class Allocator > explicit operator std::basic_string() const { return to_string( Allocator() ); } #endif // nssv_HAVE_EXPLICIT_CONVERSION #if nssv_CPP11_OR_GREATER template< class Allocator = std::allocator > std::basic_string to_string( Allocator const & a = Allocator() ) const { return std::basic_string( begin(), end(), a ); } #else std::basic_string to_string() const { return std::basic_string( begin(), end() ); } template< class Allocator > std::basic_string to_string( Allocator const & a ) const { return std::basic_string( begin(), end(), a ); } #endif // nssv_CPP11_OR_GREATER #endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS }; // // Non-member functions: // // 24.4.3 Non-member comparison functions: // lexicographically compare two string views (function template): template< class CharT, class Traits > nssv_constexpr bool operator== ( basic_string_view lhs, basic_string_view rhs ) nssv_noexcept { return lhs.compare( rhs ) == 0 ; } template< class CharT, class Traits > nssv_constexpr bool operator!= ( basic_string_view lhs, basic_string_view rhs ) nssv_noexcept { return lhs.compare( rhs ) != 0 ; } template< class CharT, class Traits > nssv_constexpr bool operator< ( basic_string_view lhs, basic_string_view rhs ) nssv_noexcept { return lhs.compare( rhs ) < 0 ; } template< class CharT, class Traits > nssv_constexpr bool operator<= ( basic_string_view lhs, basic_string_view rhs ) nssv_noexcept { return lhs.compare( rhs ) <= 0 ; } template< class CharT, class Traits > nssv_constexpr bool operator> ( basic_string_view lhs, basic_string_view rhs ) nssv_noexcept { return lhs.compare( rhs ) > 0 ; } template< class CharT, class Traits > nssv_constexpr bool operator>= ( basic_string_view lhs, basic_string_view rhs ) nssv_noexcept { return lhs.compare( rhs ) >= 0 ; } // Let S be basic_string_view, and sv be an instance of S. // Implementations shall provide sufficient additional overloads marked // constexpr and noexcept so that an object t with an implicit conversion // to S can be compared according to Table 67. #if nssv_CPP11_OR_GREATER && ! nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 ) #define nssv_BASIC_STRING_VIEW_I(T,U) typename std::decay< basic_string_view >::type #if nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 140, 150 ) # define nssv_MSVC_ORDER(x) , int=x #else # define nssv_MSVC_ORDER(x) /*, int=x*/ #endif // == template< class CharT, class Traits nssv_MSVC_ORDER(1) > nssv_constexpr bool operator==( basic_string_view lhs, nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs ) nssv_noexcept { return lhs.compare( rhs ) == 0; } template< class CharT, class Traits nssv_MSVC_ORDER(2) > nssv_constexpr bool operator==( nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, basic_string_view rhs ) nssv_noexcept { return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } // != template< class CharT, class Traits nssv_MSVC_ORDER(1) > nssv_constexpr bool operator!= ( basic_string_view < CharT, Traits > lhs, nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept { return lhs.size() != rhs.size() || lhs.compare( rhs ) != 0 ; } template< class CharT, class Traits nssv_MSVC_ORDER(2) > nssv_constexpr bool operator!= ( nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, basic_string_view < CharT, Traits > rhs ) nssv_noexcept { return lhs.compare( rhs ) != 0 ; } // < template< class CharT, class Traits nssv_MSVC_ORDER(1) > nssv_constexpr bool operator< ( basic_string_view < CharT, Traits > lhs, nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept { return lhs.compare( rhs ) < 0 ; } template< class CharT, class Traits nssv_MSVC_ORDER(2) > nssv_constexpr bool operator< ( nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, basic_string_view < CharT, Traits > rhs ) nssv_noexcept { return lhs.compare( rhs ) < 0 ; } // <= template< class CharT, class Traits nssv_MSVC_ORDER(1) > nssv_constexpr bool operator<= ( basic_string_view < CharT, Traits > lhs, nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept { return lhs.compare( rhs ) <= 0 ; } template< class CharT, class Traits nssv_MSVC_ORDER(2) > nssv_constexpr bool operator<= ( nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, basic_string_view < CharT, Traits > rhs ) nssv_noexcept { return lhs.compare( rhs ) <= 0 ; } // > template< class CharT, class Traits nssv_MSVC_ORDER(1) > nssv_constexpr bool operator> ( basic_string_view < CharT, Traits > lhs, nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept { return lhs.compare( rhs ) > 0 ; } template< class CharT, class Traits nssv_MSVC_ORDER(2) > nssv_constexpr bool operator> ( nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, basic_string_view < CharT, Traits > rhs ) nssv_noexcept { return lhs.compare( rhs ) > 0 ; } // >= template< class CharT, class Traits nssv_MSVC_ORDER(1) > nssv_constexpr bool operator>= ( basic_string_view < CharT, Traits > lhs, nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept { return lhs.compare( rhs ) >= 0 ; } template< class CharT, class Traits nssv_MSVC_ORDER(2) > nssv_constexpr bool operator>= ( nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, basic_string_view < CharT, Traits > rhs ) nssv_noexcept { return lhs.compare( rhs ) >= 0 ; } #undef nssv_MSVC_ORDER #undef nssv_BASIC_STRING_VIEW_I #endif // nssv_CPP11_OR_GREATER // 24.4.4 Inserters and extractors: namespace detail { template< class Stream > void write_padding( Stream & os, std::streamsize n ) { for ( std::streamsize i = 0; i < n; ++i ) os.rdbuf()->sputc( os.fill() ); } template< class Stream, class View > Stream & write_to_stream( Stream & os, View const & sv ) { typename Stream::sentry sentry( os ); if ( !os ) return os; const std::streamsize length = static_cast( sv.length() ); // Whether, and how, to pad: const bool pad = ( length < os.width() ); const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right; if ( left_pad ) write_padding( os, os.width() - length ); // Write span characters: os.rdbuf()->sputn( sv.begin(), length ); if ( pad && !left_pad ) write_padding( os, os.width() - length ); // Reset output stream width: os.width( 0 ); return os; } } // namespace detail template< class CharT, class Traits > std::basic_ostream & operator<<( std::basic_ostream& os, basic_string_view sv ) { return detail::write_to_stream( os, sv ); } // Several typedefs for common character types are provided: typedef basic_string_view string_view; typedef basic_string_view wstring_view; #if nssv_HAVE_WCHAR16_T typedef basic_string_view u16string_view; typedef basic_string_view u32string_view; #endif }} // namespace nonstd::sv_lite // // 24.4.6 Suffix for basic_string_view literals: // #if nssv_HAVE_USER_DEFINED_LITERALS namespace nonstd { nssv_inline_ns namespace literals { nssv_inline_ns namespace string_view_literals { #if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept // (1) { return nonstd::sv_lite::string_view{ str, len }; } nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept // (2) { return nonstd::sv_lite::u16string_view{ str, len }; } nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept // (3) { return nonstd::sv_lite::u32string_view{ str, len }; } nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) { return nonstd::sv_lite::wstring_view{ str, len }; } #endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS #if nssv_CONFIG_USR_SV_OPERATOR nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept // (1) { return nonstd::sv_lite::string_view{ str, len }; } nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept // (2) { return nonstd::sv_lite::u16string_view{ str, len }; } nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept // (3) { return nonstd::sv_lite::u32string_view{ str, len }; } nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) { return nonstd::sv_lite::wstring_view{ str, len }; } #endif // nssv_CONFIG_USR_SV_OPERATOR }}} // namespace nonstd::literals::string_view_literals #endif // // Extensions for std::string: // #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS namespace nonstd { namespace sv_lite { // Exclude MSVC 14 (19.00): it yields ambiguous to_string(): #if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140 template< class CharT, class Traits, class Allocator = std::allocator > std::basic_string to_string( basic_string_view v, Allocator const & a = Allocator() ) { return std::basic_string( v.begin(), v.end(), a ); } #else template< class CharT, class Traits > std::basic_string to_string( basic_string_view v ) { return std::basic_string( v.begin(), v.end() ); } template< class CharT, class Traits, class Allocator > std::basic_string to_string( basic_string_view v, Allocator const & a ) { return std::basic_string( v.begin(), v.end(), a ); } #endif // nssv_CPP11_OR_GREATER template< class CharT, class Traits, class Allocator > basic_string_view to_string_view( std::basic_string const & s ) { return basic_string_view( s.data(), s.size() ); } }} // namespace nonstd::sv_lite #endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS // // make types and algorithms available in namespace nonstd: // namespace nonstd { using sv_lite::basic_string_view; using sv_lite::string_view; using sv_lite::wstring_view; #if nssv_HAVE_WCHAR16_T using sv_lite::u16string_view; #endif #if nssv_HAVE_WCHAR32_T using sv_lite::u32string_view; #endif // literal "sv" using sv_lite::operator==; using sv_lite::operator!=; using sv_lite::operator<; using sv_lite::operator<=; using sv_lite::operator>; using sv_lite::operator>=; using sv_lite::operator<<; #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS using sv_lite::to_string; using sv_lite::to_string_view; #endif } // namespace nonstd // 24.4.5 Hash support (C++11): // Note: The hash value of a string view object is equal to the hash value of // the corresponding string object. #if nssv_HAVE_STD_HASH #include namespace std { template<> struct hash< nonstd::string_view > { public: std::size_t operator()( nonstd::string_view v ) const nssv_noexcept { return std::hash()( std::string( v.data(), v.size() ) ); } }; template<> struct hash< nonstd::wstring_view > { public: std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept { return std::hash()( std::wstring( v.data(), v.size() ) ); } }; template<> struct hash< nonstd::u16string_view > { public: std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept { return std::hash()( std::u16string( v.data(), v.size() ) ); } }; template<> struct hash< nonstd::u32string_view > { public: std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept { return std::hash()( std::u32string( v.data(), v.size() ) ); } }; } // namespace std #endif // nssv_HAVE_STD_HASH nssv_RESTORE_WARNINGS() #endif // nssv_HAVE_STD_STRING_VIEW #endif // NONSTD_SV_LITE_H_INCLUDED // If there is another version of Hedley, then the newer one // takes precedence. // See: https://github.com/nemequ/hedley /* Hedley - https://nemequ.github.io/hedley * Created by Evan Nemerson * * To the extent possible under law, the author(s) have dedicated all * copyright and related and neighboring rights to this software to * the public domain worldwide. This software is distributed without * any warranty. * * For details, see . * SPDX-License-Identifier: CC0-1.0 */ #if !defined(HEDLEY_VERSION) || (HEDLEY_VERSION < 9) #if defined(HEDLEY_VERSION) # undef HEDLEY_VERSION #endif #define HEDLEY_VERSION 9 #if defined(HEDLEY_STRINGIFY_EX) # undef HEDLEY_STRINGIFY_EX #endif #define HEDLEY_STRINGIFY_EX(x) #x #if defined(HEDLEY_STRINGIFY) # undef HEDLEY_STRINGIFY #endif #define HEDLEY_STRINGIFY(x) HEDLEY_STRINGIFY_EX(x) #if defined(HEDLEY_CONCAT_EX) # undef HEDLEY_CONCAT_EX #endif #define HEDLEY_CONCAT_EX(a,b) a##b #if defined(HEDLEY_CONCAT) # undef HEDLEY_CONCAT #endif #define HEDLEY_CONCAT(a,b) HEDLEY_CONCAT_EX(a,b) #if defined(HEDLEY_VERSION_ENCODE) # undef HEDLEY_VERSION_ENCODE #endif #define HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) #if defined(HEDLEY_VERSION_DECODE_MAJOR) # undef HEDLEY_VERSION_DECODE_MAJOR #endif #define HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) #if defined(HEDLEY_VERSION_DECODE_MINOR) # undef HEDLEY_VERSION_DECODE_MINOR #endif #define HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) #if defined(HEDLEY_VERSION_DECODE_REVISION) # undef HEDLEY_VERSION_DECODE_REVISION #endif #define HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) #if defined(HEDLEY_GNUC_VERSION) # undef HEDLEY_GNUC_VERSION #endif #if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) # define HEDLEY_GNUC_VERSION HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #elif defined(__GNUC__) # define HEDLEY_GNUC_VERSION HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) #endif #if defined(HEDLEY_GNUC_VERSION_CHECK) # undef HEDLEY_GNUC_VERSION_CHECK #endif #if defined(HEDLEY_GNUC_VERSION) # define HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (HEDLEY_GNUC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_MSVC_VERSION) # undef HEDLEY_MSVC_VERSION #endif #if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) # define HEDLEY_MSVC_VERSION HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) #elif defined(_MSC_FULL_VER) # define HEDLEY_MSVC_VERSION HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) #elif defined(_MSC_VER) # define HEDLEY_MSVC_VERSION HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) #endif #if defined(HEDLEY_MSVC_VERSION_CHECK) # undef HEDLEY_MSVC_VERSION_CHECK #endif #if !defined(_MSC_VER) # define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) #elif defined(_MSC_VER) && (_MSC_VER >= 1400) # define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) #elif defined(_MSC_VER) && (_MSC_VER >= 1200) # define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) #else # define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) #endif #if defined(HEDLEY_INTEL_VERSION) # undef HEDLEY_INTEL_VERSION #endif #if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) # define HEDLEY_INTEL_VERSION HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) #elif defined(__INTEL_COMPILER) # define HEDLEY_INTEL_VERSION HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) #endif #if defined(HEDLEY_INTEL_VERSION_CHECK) # undef HEDLEY_INTEL_VERSION_CHECK #endif #if defined(HEDLEY_INTEL_VERSION) # define HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (HEDLEY_INTEL_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_PGI_VERSION) # undef HEDLEY_PGI_VERSION #endif #if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) # define HEDLEY_PGI_VERSION HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) #endif #if defined(HEDLEY_PGI_VERSION_CHECK) # undef HEDLEY_PGI_VERSION_CHECK #endif #if defined(HEDLEY_PGI_VERSION) # define HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (HEDLEY_PGI_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_SUNPRO_VERSION) # undef HEDLEY_SUNPRO_VERSION #endif #if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) # define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) #elif defined(__SUNPRO_C) # define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) #elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) # define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) #elif defined(__SUNPRO_CC) # define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) #endif #if defined(HEDLEY_SUNPRO_VERSION_CHECK) # undef HEDLEY_SUNPRO_VERSION_CHECK #endif #if defined(HEDLEY_SUNPRO_VERSION) # define HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (HEDLEY_SUNPRO_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_EMSCRIPTEN_VERSION) # undef HEDLEY_EMSCRIPTEN_VERSION #endif #if defined(__EMSCRIPTEN__) # define HEDLEY_EMSCRIPTEN_VERSION HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) #endif #if defined(HEDLEY_EMSCRIPTEN_VERSION_CHECK) # undef HEDLEY_EMSCRIPTEN_VERSION_CHECK #endif #if defined(HEDLEY_EMSCRIPTEN_VERSION) # define HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (HEDLEY_EMSCRIPTEN_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_ARM_VERSION) # undef HEDLEY_ARM_VERSION #endif #if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) # define HEDLEY_ARM_VERSION HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) #elif defined(__CC_ARM) && defined(__ARMCC_VERSION) # define HEDLEY_ARM_VERSION HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) #endif #if defined(HEDLEY_ARM_VERSION_CHECK) # undef HEDLEY_ARM_VERSION_CHECK #endif #if defined(HEDLEY_ARM_VERSION) # define HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (HEDLEY_ARM_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_IBM_VERSION) # undef HEDLEY_IBM_VERSION #endif #if defined(__ibmxl__) # define HEDLEY_IBM_VERSION HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) #elif defined(__xlC__) && defined(__xlC_ver__) # define HEDLEY_IBM_VERSION HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) #elif defined(__xlC__) # define HEDLEY_IBM_VERSION HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) #endif #if defined(HEDLEY_IBM_VERSION_CHECK) # undef HEDLEY_IBM_VERSION_CHECK #endif #if defined(HEDLEY_IBM_VERSION) # define HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (HEDLEY_IBM_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_TI_VERSION) # undef HEDLEY_TI_VERSION #endif #if defined(__TI_COMPILER_VERSION__) # define HEDLEY_TI_VERSION HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif #if defined(HEDLEY_TI_VERSION_CHECK) # undef HEDLEY_TI_VERSION_CHECK #endif #if defined(HEDLEY_TI_VERSION) # define HEDLEY_TI_VERSION_CHECK(major,minor,patch) (HEDLEY_TI_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_CRAY_VERSION) # undef HEDLEY_CRAY_VERSION #endif #if defined(_CRAYC) # if defined(_RELEASE_PATCHLEVEL) # define HEDLEY_CRAY_VERSION HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) # else # define HEDLEY_CRAY_VERSION HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) # endif #endif #if defined(HEDLEY_CRAY_VERSION_CHECK) # undef HEDLEY_CRAY_VERSION_CHECK #endif #if defined(HEDLEY_CRAY_VERSION) # define HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (HEDLEY_CRAY_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_IAR_VERSION) # undef HEDLEY_IAR_VERSION #endif #if defined(__IAR_SYSTEMS_ICC__) # if __VER__ > 1000 # define HEDLEY_IAR_VERSION HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) # else # define HEDLEY_IAR_VERSION HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0) # endif #endif #if defined(HEDLEY_IAR_VERSION_CHECK) # undef HEDLEY_IAR_VERSION_CHECK #endif #if defined(HEDLEY_IAR_VERSION) # define HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (HEDLEY_IAR_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_TINYC_VERSION) # undef HEDLEY_TINYC_VERSION #endif #if defined(__TINYC__) # define HEDLEY_TINYC_VERSION HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) #endif #if defined(HEDLEY_TINYC_VERSION_CHECK) # undef HEDLEY_TINYC_VERSION_CHECK #endif #if defined(HEDLEY_TINYC_VERSION) # define HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (HEDLEY_TINYC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_DMC_VERSION) # undef HEDLEY_DMC_VERSION #endif #if defined(__DMC__) # define HEDLEY_DMC_VERSION HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) #endif #if defined(HEDLEY_DMC_VERSION_CHECK) # undef HEDLEY_DMC_VERSION_CHECK #endif #if defined(HEDLEY_DMC_VERSION) # define HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (HEDLEY_DMC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_COMPCERT_VERSION) # undef HEDLEY_COMPCERT_VERSION #endif #if defined(__COMPCERT_VERSION__) # define HEDLEY_COMPCERT_VERSION HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) #endif #if defined(HEDLEY_COMPCERT_VERSION_CHECK) # undef HEDLEY_COMPCERT_VERSION_CHECK #endif #if defined(HEDLEY_COMPCERT_VERSION) # define HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (HEDLEY_COMPCERT_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_PELLES_VERSION) # undef HEDLEY_PELLES_VERSION #endif #if defined(__POCC__) # define HEDLEY_PELLES_VERSION HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) #endif #if defined(HEDLEY_PELLES_VERSION_CHECK) # undef HEDLEY_PELLES_VERSION_CHECK #endif #if defined(HEDLEY_PELLES_VERSION) # define HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (HEDLEY_PELLES_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_GCC_VERSION) # undef HEDLEY_GCC_VERSION #endif #if \ defined(HEDLEY_GNUC_VERSION) && \ !defined(__clang__) && \ !defined(HEDLEY_INTEL_VERSION) && \ !defined(HEDLEY_PGI_VERSION) && \ !defined(HEDLEY_ARM_VERSION) && \ !defined(HEDLEY_TI_VERSION) && \ !defined(__COMPCERT__) # define HEDLEY_GCC_VERSION HEDLEY_GNUC_VERSION #endif #if defined(HEDLEY_GCC_VERSION_CHECK) # undef HEDLEY_GCC_VERSION_CHECK #endif #if defined(HEDLEY_GCC_VERSION) # define HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (HEDLEY_GCC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_HAS_ATTRIBUTE) # undef HEDLEY_HAS_ATTRIBUTE #endif #if defined(__has_attribute) # define HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) #else # define HEDLEY_HAS_ATTRIBUTE(attribute) (0) #endif #if defined(HEDLEY_GNUC_HAS_ATTRIBUTE) # undef HEDLEY_GNUC_HAS_ATTRIBUTE #endif #if defined(__has_attribute) # define HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) #else # define HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_GCC_HAS_ATTRIBUTE) # undef HEDLEY_GCC_HAS_ATTRIBUTE #endif #if defined(__has_attribute) # define HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) #else # define HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_HAS_CPP_ATTRIBUTE) # undef HEDLEY_HAS_CPP_ATTRIBUTE #endif #if defined(__has_cpp_attribute) && defined(__cplusplus) # define HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) #else # define HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) #endif #if defined(HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) # undef HEDLEY_GNUC_HAS_CPP_ATTRIBUTE #endif #if defined(__has_cpp_attribute) && defined(__cplusplus) # define HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) #else # define HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_GCC_HAS_CPP_ATTRIBUTE) # undef HEDLEY_GCC_HAS_CPP_ATTRIBUTE #endif #if defined(__has_cpp_attribute) && defined(__cplusplus) # define HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) #else # define HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_HAS_BUILTIN) # undef HEDLEY_HAS_BUILTIN #endif #if defined(__has_builtin) # define HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) #else # define HEDLEY_HAS_BUILTIN(builtin) (0) #endif #if defined(HEDLEY_GNUC_HAS_BUILTIN) # undef HEDLEY_GNUC_HAS_BUILTIN #endif #if defined(__has_builtin) # define HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) #else # define HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_GCC_HAS_BUILTIN) # undef HEDLEY_GCC_HAS_BUILTIN #endif #if defined(__has_builtin) # define HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) #else # define HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_HAS_FEATURE) # undef HEDLEY_HAS_FEATURE #endif #if defined(__has_feature) # define HEDLEY_HAS_FEATURE(feature) __has_feature(feature) #else # define HEDLEY_HAS_FEATURE(feature) (0) #endif #if defined(HEDLEY_GNUC_HAS_FEATURE) # undef HEDLEY_GNUC_HAS_FEATURE #endif #if defined(__has_feature) # define HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) #else # define HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_GCC_HAS_FEATURE) # undef HEDLEY_GCC_HAS_FEATURE #endif #if defined(__has_feature) # define HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) #else # define HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_HAS_EXTENSION) # undef HEDLEY_HAS_EXTENSION #endif #if defined(__has_extension) # define HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) #else # define HEDLEY_HAS_EXTENSION(extension) (0) #endif #if defined(HEDLEY_GNUC_HAS_EXTENSION) # undef HEDLEY_GNUC_HAS_EXTENSION #endif #if defined(__has_extension) # define HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) #else # define HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_GCC_HAS_EXTENSION) # undef HEDLEY_GCC_HAS_EXTENSION #endif #if defined(__has_extension) # define HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) #else # define HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_HAS_DECLSPEC_ATTRIBUTE) # undef HEDLEY_HAS_DECLSPEC_ATTRIBUTE #endif #if defined(__has_declspec_attribute) # define HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) #else # define HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) #endif #if defined(HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) # undef HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE #endif #if defined(__has_declspec_attribute) # define HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) #else # define HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) # undef HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE #endif #if defined(__has_declspec_attribute) # define HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) #else # define HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_HAS_WARNING) # undef HEDLEY_HAS_WARNING #endif #if defined(__has_warning) # define HEDLEY_HAS_WARNING(warning) __has_warning(warning) #else # define HEDLEY_HAS_WARNING(warning) (0) #endif #if defined(HEDLEY_GNUC_HAS_WARNING) # undef HEDLEY_GNUC_HAS_WARNING #endif #if defined(__has_warning) # define HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) #else # define HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_GCC_HAS_WARNING) # undef HEDLEY_GCC_HAS_WARNING #endif #if defined(__has_warning) # define HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) #else # define HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if \ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ defined(__clang__) || \ HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_TI_VERSION_CHECK(6,0,0) || \ HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ (HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) # define HEDLEY_PRAGMA(value) _Pragma(#value) #elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) # define HEDLEY_PRAGMA(value) __pragma(value) #else # define HEDLEY_PRAGMA(value) #endif #if defined(HEDLEY_DIAGNOSTIC_PUSH) # undef HEDLEY_DIAGNOSTIC_PUSH #endif #if defined(HEDLEY_DIAGNOSTIC_POP) # undef HEDLEY_DIAGNOSTIC_POP #endif #if defined(__clang__) # define HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") # define HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") #elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") # define HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") #elif HEDLEY_GCC_VERSION_CHECK(4,6,0) # define HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") # define HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") #elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) # define HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) # define HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) #elif HEDLEY_ARM_VERSION_CHECK(5,6,0) # define HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") # define HEDLEY_DIAGNOSTIC_POP _Pragma("pop") #elif HEDLEY_TI_VERSION_CHECK(8,1,0) # define HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") # define HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") #elif HEDLEY_PELLES_VERSION_CHECK(2,90,0) # define HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") # define HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") #else # define HEDLEY_DIAGNOSTIC_PUSH # define HEDLEY_DIAGNOSTIC_POP #endif #if defined(HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) # undef HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED #endif #if HEDLEY_HAS_WARNING("-Wdeprecated-declarations") # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") #elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") #elif HEDLEY_PGI_VERSION_CHECK(17,10,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") #elif HEDLEY_GCC_VERSION_CHECK(4,3,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") #elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) #elif HEDLEY_TI_VERSION_CHECK(8,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") #elif HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") #elif HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") #elif HEDLEY_IAR_VERSION_CHECK(8,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") #elif HEDLEY_PELLES_VERSION_CHECK(2,90,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") #else # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED #endif #if defined(HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) # undef HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS #endif #if HEDLEY_HAS_WARNING("-Wunknown-pragmas") # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") #elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") #elif HEDLEY_PGI_VERSION_CHECK(17,10,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") #elif HEDLEY_GCC_VERSION_CHECK(4,3,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") #elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) #elif HEDLEY_TI_VERSION_CHECK(8,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") #elif HEDLEY_IAR_VERSION_CHECK(8,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") #else # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS #endif #if defined(HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) # undef HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL #endif #if HEDLEY_HAS_WARNING("-Wcast-qual") # define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") #elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") #elif HEDLEY_GCC_VERSION_CHECK(3,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") #else # define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL #endif #if defined(HEDLEY_DEPRECATED) # undef HEDLEY_DEPRECATED #endif #if defined(HEDLEY_DEPRECATED_FOR) # undef HEDLEY_DEPRECATED_FOR #endif #if defined(__cplusplus) && (__cplusplus >= 201402L) # define HEDLEY_DEPRECATED(since) [[deprecated("Since " #since)]] # define HEDLEY_DEPRECATED_FOR(since, replacement) [[deprecated("Since " #since "; use " #replacement)]] #elif \ HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) || \ HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ HEDLEY_TI_VERSION_CHECK(8,3,0) # define HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) # define HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) #elif \ HEDLEY_HAS_ATTRIBUTE(deprecated) || \ HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_TI_VERSION_CHECK(8,0,0) || \ (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) # define HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) # define HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) #elif HEDLEY_MSVC_VERSION_CHECK(14,0,0) # define HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) # define HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) #elif \ HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ HEDLEY_PELLES_VERSION_CHECK(6,50,0) # define HEDLEY_DEPRECATED(since) _declspec(deprecated) # define HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) #elif HEDLEY_IAR_VERSION_CHECK(8,0,0) # define HEDLEY_DEPRECATED(since) _Pragma("deprecated") # define HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") #else # define HEDLEY_DEPRECATED(since) # define HEDLEY_DEPRECATED_FOR(since, replacement) #endif #if defined(HEDLEY_UNAVAILABLE) # undef HEDLEY_UNAVAILABLE #endif #if \ HEDLEY_HAS_ATTRIBUTE(warning) || \ HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) #else # define HEDLEY_UNAVAILABLE(available_since) #endif #if defined(HEDLEY_WARN_UNUSED_RESULT) # undef HEDLEY_WARN_UNUSED_RESULT #endif #if defined(__cplusplus) && (__cplusplus >= 201703L) # define HEDLEY_WARN_UNUSED_RESULT [[nodiscard]] #elif \ HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_TI_VERSION_CHECK(8,0,0) || \ (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ (HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ HEDLEY_PGI_VERSION_CHECK(17,10,0) # define HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) #elif defined(_Check_return_) /* SAL */ # define HEDLEY_WARN_UNUSED_RESULT _Check_return_ #else # define HEDLEY_WARN_UNUSED_RESULT #endif #if defined(HEDLEY_SENTINEL) # undef HEDLEY_SENTINEL #endif #if \ HEDLEY_HAS_ATTRIBUTE(sentinel) || \ HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_ARM_VERSION_CHECK(5,4,0) # define HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) #else # define HEDLEY_SENTINEL(position) #endif #if defined(HEDLEY_NO_RETURN) # undef HEDLEY_NO_RETURN #endif #if HEDLEY_IAR_VERSION_CHECK(8,0,0) # define HEDLEY_NO_RETURN __noreturn #elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_NO_RETURN __attribute__((__noreturn__)) #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L # define HEDLEY_NO_RETURN _Noreturn #elif defined(__cplusplus) && (__cplusplus >= 201103L) # define HEDLEY_NO_RETURN [[noreturn]] #elif \ HEDLEY_HAS_ATTRIBUTE(noreturn) || \ HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_TI_VERSION_CHECK(18,0,0) || \ (HEDLEY_TI_VERSION_CHECK(17,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) # define HEDLEY_NO_RETURN __attribute__((__noreturn__)) #elif HEDLEY_MSVC_VERSION_CHECK(13,10,0) # define HEDLEY_NO_RETURN __declspec(noreturn) #elif HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) # define HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") #elif HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) # define HEDLEY_NO_RETURN __attribute((noreturn)) #elif HEDLEY_PELLES_VERSION_CHECK(9,0,0) # define HEDLEY_NO_RETURN __declspec(noreturn) #else # define HEDLEY_NO_RETURN #endif #if defined(HEDLEY_UNREACHABLE) # undef HEDLEY_UNREACHABLE #endif #if defined(HEDLEY_UNREACHABLE_RETURN) # undef HEDLEY_UNREACHABLE_RETURN #endif #if \ (HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(HEDLEY_ARM_VERSION))) || \ HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_IBM_VERSION_CHECK(13,1,5) # define HEDLEY_UNREACHABLE() __builtin_unreachable() #elif HEDLEY_MSVC_VERSION_CHECK(13,10,0) # define HEDLEY_UNREACHABLE() __assume(0) #elif HEDLEY_TI_VERSION_CHECK(6,0,0) # if defined(__cplusplus) # define HEDLEY_UNREACHABLE() std::_nassert(0) # else # define HEDLEY_UNREACHABLE() _nassert(0) # endif # define HEDLEY_UNREACHABLE_RETURN(value) return value #elif defined(EXIT_FAILURE) # define HEDLEY_UNREACHABLE() abort() #else # define HEDLEY_UNREACHABLE() # define HEDLEY_UNREACHABLE_RETURN(value) return value #endif #if !defined(HEDLEY_UNREACHABLE_RETURN) # define HEDLEY_UNREACHABLE_RETURN(value) HEDLEY_UNREACHABLE() #endif #if defined(HEDLEY_ASSUME) # undef HEDLEY_ASSUME #endif #if \ HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_ASSUME(expr) __assume(expr) #elif HEDLEY_HAS_BUILTIN(__builtin_assume) # define HEDLEY_ASSUME(expr) __builtin_assume(expr) #elif HEDLEY_TI_VERSION_CHECK(6,0,0) # if defined(__cplusplus) # define HEDLEY_ASSUME(expr) std::_nassert(expr) # else # define HEDLEY_ASSUME(expr) _nassert(expr) # endif #elif \ (HEDLEY_HAS_BUILTIN(__builtin_unreachable) && !defined(HEDLEY_ARM_VERSION)) || \ HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_IBM_VERSION_CHECK(13,1,5) # define HEDLEY_ASSUME(expr) ((void) ((expr) ? 1 : (__builtin_unreachable(), 1))) #else # define HEDLEY_ASSUME(expr) ((void) (expr)) #endif HEDLEY_DIAGNOSTIC_PUSH #if \ HEDLEY_HAS_WARNING("-Wvariadic-macros") || \ HEDLEY_GCC_VERSION_CHECK(4,0,0) # if defined(__clang__) # pragma clang diagnostic ignored "-Wvariadic-macros" # elif defined(HEDLEY_GCC_VERSION) # pragma GCC diagnostic ignored "-Wvariadic-macros" # endif #endif #if defined(HEDLEY_NON_NULL) # undef HEDLEY_NON_NULL #endif #if \ HEDLEY_HAS_ATTRIBUTE(nonnull) || \ HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) # define HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) #else # define HEDLEY_NON_NULL(...) #endif HEDLEY_DIAGNOSTIC_POP #if defined(HEDLEY_PRINTF_FORMAT) # undef HEDLEY_PRINTF_FORMAT #endif #if defined(__MINGW32__) && HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) # define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) #elif defined(__MINGW32__) && HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) # define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) #elif \ HEDLEY_HAS_ATTRIBUTE(format) || \ HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_TI_VERSION_CHECK(8,0,0) || \ (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) # define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) #elif HEDLEY_PELLES_VERSION_CHECK(6,0,0) # define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) #else # define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) #endif #if defined(HEDLEY_CONSTEXPR) # undef HEDLEY_CONSTEXPR #endif #if defined(__cplusplus) # if __cplusplus >= 201103L # define HEDLEY_CONSTEXPR constexpr # endif #endif #if !defined(HEDLEY_CONSTEXPR) # define HEDLEY_CONSTEXPR #endif #if defined(HEDLEY_PREDICT) # undef HEDLEY_PREDICT #endif #if defined(HEDLEY_LIKELY) # undef HEDLEY_LIKELY #endif #if defined(HEDLEY_UNLIKELY) # undef HEDLEY_UNLIKELY #endif #if defined(HEDLEY_UNPREDICTABLE) # undef HEDLEY_UNPREDICTABLE #endif #if HEDLEY_HAS_BUILTIN(__builtin_unpredictable) # define HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable(!!(expr)) #endif #if \ HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || \ HEDLEY_GCC_VERSION_CHECK(9,0,0) # define HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(expr, value, probability) # define HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1, probability) # define HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0, probability) # define HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) # define HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) # if !defined(HEDLEY_BUILTIN_UNPREDICTABLE) # define HEDLEY_BUILTIN_UNPREDICTABLE(expr) __builtin_expect_with_probability(!!(expr), 1, 0.5) # endif #elif \ HEDLEY_HAS_BUILTIN(__builtin_expect) || \ HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ (HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_TI_VERSION_CHECK(6,1,0) || \ HEDLEY_TINYC_VERSION_CHECK(0,9,27) # define HEDLEY_PREDICT(expr, expected, probability) \ (((probability) >= 0.9) ? __builtin_expect(!!(expr), (expected)) : (((void) (expected)), !!(expr))) # define HEDLEY_PREDICT_TRUE(expr, probability) \ (__extension__ ({ \ HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \ ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ })) # define HEDLEY_PREDICT_FALSE(expr, probability) \ (__extension__ ({ \ HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \ ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ })) # define HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) # define HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) #else # define HEDLEY_PREDICT(expr, expected, probability) (((void) (expected)), !!(expr)) # define HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) # define HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) # define HEDLEY_LIKELY(expr) (!!(expr)) # define HEDLEY_UNLIKELY(expr) (!!(expr)) #endif #if !defined(HEDLEY_UNPREDICTABLE) # define HEDLEY_UNPREDICTABLE(expr) HEDLEY_PREDICT(expr, 1, 0.5) #endif #if defined(HEDLEY_MALLOC) # undef HEDLEY_MALLOC #endif #if \ HEDLEY_HAS_ATTRIBUTE(malloc) || \ HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ HEDLEY_TI_VERSION_CHECK(8,0,0) || \ (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) # define HEDLEY_MALLOC __attribute__((__malloc__)) #elif HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) # define HEDLEY_MALLOC __declspec(restrict) #else # define HEDLEY_MALLOC #endif #if defined(HEDLEY_PURE) # undef HEDLEY_PURE #endif #if \ HEDLEY_HAS_ATTRIBUTE(pure) || \ HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_TI_VERSION_CHECK(8,0,0) || \ (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_PGI_VERSION_CHECK(17,10,0) # define HEDLEY_PURE __attribute__((__pure__)) #elif HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) # define HEDLEY_PURE _Pragma("FUNC_IS_PURE;") #else # define HEDLEY_PURE #endif #if defined(HEDLEY_CONST) # undef HEDLEY_CONST #endif #if \ HEDLEY_HAS_ATTRIBUTE(const) || \ HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_TI_VERSION_CHECK(8,0,0) || \ (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_PGI_VERSION_CHECK(17,10,0) # define HEDLEY_CONST __attribute__((__const__)) #else # define HEDLEY_CONST HEDLEY_PURE #endif #if defined(HEDLEY_RESTRICT) # undef HEDLEY_RESTRICT #endif #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) # define HEDLEY_RESTRICT restrict #elif \ HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ HEDLEY_TI_VERSION_CHECK(8,0,0) || \ (HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ defined(__clang__) # define HEDLEY_RESTRICT __restrict #elif HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) # define HEDLEY_RESTRICT _Restrict #else # define HEDLEY_RESTRICT #endif #if defined(HEDLEY_INLINE) # undef HEDLEY_INLINE #endif #if \ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ (defined(__cplusplus) && (__cplusplus >= 199711L)) # define HEDLEY_INLINE inline #elif \ defined(HEDLEY_GCC_VERSION) || \ HEDLEY_ARM_VERSION_CHECK(6,2,0) # define HEDLEY_INLINE __inline__ #elif \ HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_TI_VERSION_CHECK(8,0,0) # define HEDLEY_INLINE __inline #else # define HEDLEY_INLINE #endif #if defined(HEDLEY_ALWAYS_INLINE) # undef HEDLEY_ALWAYS_INLINE #endif #if \ HEDLEY_HAS_ATTRIBUTE(always_inline) || \ HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_TI_VERSION_CHECK(8,0,0) || \ (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) # define HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) HEDLEY_INLINE #elif HEDLEY_MSVC_VERSION_CHECK(12,0,0) # define HEDLEY_ALWAYS_INLINE __forceinline #elif HEDLEY_TI_VERSION_CHECK(7,0,0) && defined(__cplusplus) # define HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") #elif HEDLEY_IAR_VERSION_CHECK(8,0,0) # define HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") #else # define HEDLEY_ALWAYS_INLINE HEDLEY_INLINE #endif #if defined(HEDLEY_NEVER_INLINE) # undef HEDLEY_NEVER_INLINE #endif #if \ HEDLEY_HAS_ATTRIBUTE(noinline) || \ HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_TI_VERSION_CHECK(8,0,0) || \ (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) # define HEDLEY_NEVER_INLINE __attribute__((__noinline__)) #elif HEDLEY_MSVC_VERSION_CHECK(13,10,0) # define HEDLEY_NEVER_INLINE __declspec(noinline) #elif HEDLEY_PGI_VERSION_CHECK(10,2,0) # define HEDLEY_NEVER_INLINE _Pragma("noinline") #elif HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) # define HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") #elif HEDLEY_IAR_VERSION_CHECK(8,0,0) # define HEDLEY_NEVER_INLINE _Pragma("inline=never") #elif HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) # define HEDLEY_NEVER_INLINE __attribute((noinline)) #elif HEDLEY_PELLES_VERSION_CHECK(9,0,0) # define HEDLEY_NEVER_INLINE __declspec(noinline) #else # define HEDLEY_NEVER_INLINE #endif #if defined(HEDLEY_PRIVATE) # undef HEDLEY_PRIVATE #endif #if defined(HEDLEY_PUBLIC) # undef HEDLEY_PUBLIC #endif #if defined(HEDLEY_IMPORT) # undef HEDLEY_IMPORT #endif #if defined(_WIN32) || defined(__CYGWIN__) # define HEDLEY_PRIVATE # define HEDLEY_PUBLIC __declspec(dllexport) # define HEDLEY_IMPORT __declspec(dllimport) #else # if \ HEDLEY_HAS_ATTRIBUTE(visibility) || \ HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ HEDLEY_TI_VERSION_CHECK(8,0,0) || \ (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_EABI__) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) # define HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) # define HEDLEY_PUBLIC __attribute__((__visibility__("default"))) # else # define HEDLEY_PRIVATE # define HEDLEY_PUBLIC # endif # define HEDLEY_IMPORT extern #endif #if defined(HEDLEY_NO_THROW) # undef HEDLEY_NO_THROW #endif #if \ HEDLEY_HAS_ATTRIBUTE(nothrow) || \ HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_NO_THROW __attribute__((__nothrow__)) #elif \ HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) # define HEDLEY_NO_THROW __declspec(nothrow) #else # define HEDLEY_NO_THROW #endif #if defined(HEDLEY_FALL_THROUGH) # undef HEDLEY_FALL_THROUGH #endif #if \ defined(__cplusplus) && \ (!defined(HEDLEY_SUNPRO_VERSION) || HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ !defined(HEDLEY_PGI_VERSION) # if \ (__cplusplus >= 201703L) || \ ((__cplusplus >= 201103L) && HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)) # define HEDLEY_FALL_THROUGH [[fallthrough]] # elif (__cplusplus >= 201103L) && HEDLEY_HAS_CPP_ATTRIBUTE(clang::fallthrough) # define HEDLEY_FALL_THROUGH [[clang::fallthrough]] # elif (__cplusplus >= 201103L) && HEDLEY_GCC_VERSION_CHECK(7,0,0) # define HEDLEY_FALL_THROUGH [[gnu::fallthrough]] # endif #endif #if !defined(HEDLEY_FALL_THROUGH) # if HEDLEY_GNUC_HAS_ATTRIBUTE(fallthrough,7,0,0) && !defined(HEDLEY_PGI_VERSION) # define HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) # elif defined(__fallthrough) /* SAL */ # define HEDLEY_FALL_THROUGH __fallthrough # else # define HEDLEY_FALL_THROUGH # endif #endif #if defined(HEDLEY_RETURNS_NON_NULL) # undef HEDLEY_RETURNS_NON_NULL #endif #if \ HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ HEDLEY_GCC_VERSION_CHECK(4,9,0) # define HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) #elif defined(_Ret_notnull_) /* SAL */ # define HEDLEY_RETURNS_NON_NULL _Ret_notnull_ #else # define HEDLEY_RETURNS_NON_NULL #endif #if defined(HEDLEY_ARRAY_PARAM) # undef HEDLEY_ARRAY_PARAM #endif #if \ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ !defined(__STDC_NO_VLA__) && \ !defined(__cplusplus) && \ !defined(HEDLEY_PGI_VERSION) && \ !defined(HEDLEY_TINYC_VERSION) # define HEDLEY_ARRAY_PARAM(name) (name) #else # define HEDLEY_ARRAY_PARAM(name) #endif #if defined(HEDLEY_IS_CONSTANT) # undef HEDLEY_IS_CONSTANT #endif #if defined(HEDLEY_REQUIRE_CONSTEXPR) # undef HEDLEY_REQUIRE_CONSTEXPR #endif /* Note the double-underscore. For internal use only; no API * guarantees! */ #if defined(HEDLEY__IS_CONSTEXPR) # undef HEDLEY__IS_CONSTEXPR #endif #if \ HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ HEDLEY_TI_VERSION_CHECK(6,1,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) || \ HEDLEY_CRAY_VERSION_CHECK(8,1,0) # define HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) #endif #if !defined(__cplusplus) # if \ HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ HEDLEY_TINYC_VERSION_CHECK(0,9,24) # if defined(__INTPTR_TYPE__) # define HEDLEY__IS_CONSTEXPR(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) # else # include # define HEDLEY__IS_CONSTEXPR(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) # endif # elif \ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && !defined(HEDLEY_SUNPRO_VERSION) && !defined(HEDLEY_PGI_VERSION)) || \ HEDLEY_HAS_EXTENSION(c_generic_selections) || \ HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ HEDLEY_ARM_VERSION_CHECK(5,3,0) # if defined(__INTPTR_TYPE__) # define HEDLEY__IS_CONSTEXPR(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) # else # include # define HEDLEY__IS_CONSTEXPR(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) # endif # elif \ defined(HEDLEY_GCC_VERSION) || \ defined(HEDLEY_INTEL_VERSION) || \ defined(HEDLEY_TINYC_VERSION) || \ defined(HEDLEY_TI_VERSION) || \ defined(__clang__) # define HEDLEY__IS_CONSTEXPR(expr) ( \ sizeof(void) != \ sizeof(*( \ 1 ? \ ((void*) ((expr) * 0L) ) : \ ((struct { char v[sizeof(void) * 2]; } *) 1) \ ) \ ) \ ) # endif #endif #if defined(HEDLEY__IS_CONSTEXPR) # if !defined(HEDLEY_IS_CONSTANT) # define HEDLEY_IS_CONSTANT(expr) HEDLEY__IS_CONSTEXPR(expr) # endif # define HEDLEY_REQUIRE_CONSTEXPR(expr) (HEDLEY__IS_CONSTEXPR(expr) ? (expr) : (-1)) #else # if !defined(HEDLEY_IS_CONSTANT) # define HEDLEY_IS_CONSTANT(expr) (0) # endif # define HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) #endif #if defined(HEDLEY_BEGIN_C_DECLS) # undef HEDLEY_BEGIN_C_DECLS #endif #if defined(HEDLEY_END_C_DECLS) # undef HEDLEY_END_C_DECLS #endif #if defined(HEDLEY_C_DECL) # undef HEDLEY_C_DECL #endif #if defined(__cplusplus) # define HEDLEY_BEGIN_C_DECLS extern "C" { # define HEDLEY_END_C_DECLS } # define HEDLEY_C_DECL extern "C" #else # define HEDLEY_BEGIN_C_DECLS # define HEDLEY_END_C_DECLS # define HEDLEY_C_DECL #endif #if defined(HEDLEY_STATIC_ASSERT) # undef HEDLEY_STATIC_ASSERT #endif #if \ !defined(__cplusplus) && ( \ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ HEDLEY_HAS_FEATURE(c_static_assert) || \ HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ defined(_Static_assert) \ ) # define HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) #elif \ (defined(__cplusplus) && (__cplusplus >= 201703L)) || \ HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ (defined(__cplusplus) && HEDLEY_TI_VERSION_CHECK(8,3,0)) # define HEDLEY_STATIC_ASSERT(expr, message) static_assert(expr, message) #elif defined(__cplusplus) && (__cplusplus >= 201103L) # define HEDLEY_STATIC_ASSERT(expr, message) static_assert(expr) #else # define HEDLEY_STATIC_ASSERT(expr, message) #endif #if defined(HEDLEY_CONST_CAST) # undef HEDLEY_CONST_CAST #endif #if defined(__cplusplus) # define HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) #elif \ HEDLEY_HAS_WARNING("-Wcast-qual") || \ HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ HEDLEY_DIAGNOSTIC_PUSH \ HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ ((T) (expr)); \ HEDLEY_DIAGNOSTIC_POP \ })) #else # define HEDLEY_CONST_CAST(T, expr) ((T) (expr)) #endif #if defined(HEDLEY_REINTERPRET_CAST) # undef HEDLEY_REINTERPRET_CAST #endif #if defined(__cplusplus) # define HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) #else # define HEDLEY_REINTERPRET_CAST(T, expr) (*((T*) &(expr))) #endif #if defined(HEDLEY_STATIC_CAST) # undef HEDLEY_STATIC_CAST #endif #if defined(__cplusplus) # define HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) #else # define HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) #endif #if defined(HEDLEY_CPP_CAST) # undef HEDLEY_CPP_CAST #endif #if defined(__cplusplus) # define HEDLEY_CPP_CAST(T, expr) static_cast(expr) #else # define HEDLEY_CPP_CAST(T, expr) (expr) #endif #if defined(HEDLEY_MESSAGE) # undef HEDLEY_MESSAGE #endif #if HEDLEY_HAS_WARNING("-Wunknown-pragmas") # define HEDLEY_MESSAGE(msg) \ HEDLEY_DIAGNOSTIC_PUSH \ HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ HEDLEY_PRAGMA(message msg) \ HEDLEY_DIAGNOSTIC_POP #elif \ HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(message msg) #elif HEDLEY_CRAY_VERSION_CHECK(5,0,0) # define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(_CRI message msg) #elif HEDLEY_IAR_VERSION_CHECK(8,0,0) # define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(message(msg)) #elif HEDLEY_PELLES_VERSION_CHECK(2,0,0) # define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(message(msg)) #else # define HEDLEY_MESSAGE(msg) #endif #if defined(HEDLEY_WARNING) # undef HEDLEY_WARNING #endif #if HEDLEY_HAS_WARNING("-Wunknown-pragmas") # define HEDLEY_WARNING(msg) \ HEDLEY_DIAGNOSTIC_PUSH \ HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ HEDLEY_PRAGMA(clang warning msg) \ HEDLEY_DIAGNOSTIC_POP #elif \ HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ HEDLEY_PGI_VERSION_CHECK(18,4,0) # define HEDLEY_WARNING(msg) HEDLEY_PRAGMA(GCC warning msg) #elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) # define HEDLEY_WARNING(msg) HEDLEY_PRAGMA(message(msg)) #else # define HEDLEY_WARNING(msg) HEDLEY_MESSAGE(msg) #endif #if defined(HEDLEY_REQUIRE_MSG) # undef HEDLEY_REQUIRE_MSG #endif #if HEDLEY_HAS_ATTRIBUTE(diagnose_if) # if HEDLEY_HAS_WARNING("-Wgcc-compat") # define HEDLEY_REQUIRE_MSG(expr, msg) \ HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ __attribute__((__diagnose_if__(!(expr), msg, "error"))) \ HEDLEY_DIAGNOSTIC_POP # else # define HEDLEY_REQUIRE_MSG(expr, msg) __attribute__((__diagnose_if__(!(expr), msg, "error"))) # endif #else # define HEDLEY_REQUIRE_MSG(expr, msg) #endif #if defined(HEDLEY_REQUIRE) # undef HEDLEY_REQUIRE #endif #define HEDLEY_REQUIRE(expr) HEDLEY_REQUIRE_MSG(expr, #expr) #if defined(HEDLEY_FLAGS) # undef HEDLEY_FLAGS #endif #if HEDLEY_HAS_ATTRIBUTE(flag_enum) # define HEDLEY_FLAGS __attribute__((__flag_enum__)) #endif #if defined(HEDLEY_FLAGS_CAST) # undef HEDLEY_FLAGS_CAST #endif #if HEDLEY_INTEL_VERSION_CHECK(19,0,0) # define HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("warning(disable:188)") \ ((T) (expr)); \ HEDLEY_DIAGNOSTIC_POP \ })) #else # define HEDLEY_FLAGS_CAST(T, expr) HEDLEY_STATIC_CAST(T, expr) #endif /* Remaining macros are deprecated. */ #if defined(HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) # undef HEDLEY_GCC_NOT_CLANG_VERSION_CHECK #endif #if defined(__clang__) # define HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) #else # define HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_CLANG_HAS_ATTRIBUTE) # undef HEDLEY_CLANG_HAS_ATTRIBUTE #endif #define HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) HEDLEY_HAS_ATTRIBUTE(attribute) #if defined(HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) # undef HEDLEY_CLANG_HAS_CPP_ATTRIBUTE #endif #define HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) HEDLEY_HAS_CPP_ATTRIBUTE(attribute) #if defined(HEDLEY_CLANG_HAS_BUILTIN) # undef HEDLEY_CLANG_HAS_BUILTIN #endif #define HEDLEY_CLANG_HAS_BUILTIN(builtin) HEDLEY_HAS_BUILTIN(builtin) #if defined(HEDLEY_CLANG_HAS_FEATURE) # undef HEDLEY_CLANG_HAS_FEATURE #endif #define HEDLEY_CLANG_HAS_FEATURE(feature) HEDLEY_HAS_FEATURE(feature) #if defined(HEDLEY_CLANG_HAS_EXTENSION) # undef HEDLEY_CLANG_HAS_EXTENSION #endif #define HEDLEY_CLANG_HAS_EXTENSION(extension) HEDLEY_HAS_EXTENSION(extension) #if defined(HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) # undef HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE #endif #define HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) #if defined(HEDLEY_CLANG_HAS_WARNING) # undef HEDLEY_CLANG_HAS_WARNING #endif #define HEDLEY_CLANG_HAS_WARNING(warning) HEDLEY_HAS_WARNING(warning) #endif /* !defined(HEDLEY_VERSION) || (HEDLEY_VERSION < X) */ namespace csv { #ifdef _MSC_VER #pragma region Compatibility Macros #endif /** * @def IF_CONSTEXPR * Expands to `if constexpr` in C++17 and `if` otherwise * * @def CONSTEXPR_VALUE * Expands to `constexpr` in C++17 and `const` otherwise. * Mainly used for global variables. * * @def CONSTEXPR * Expands to `constexpr` in decent compilers and `inline` otherwise. * Intended for functions and methods. */ #define STATIC_ASSERT(x) static_assert(x, "Assertion failed") #if CMAKE_CXX_STANDARD == 17 || __cplusplus >= 201703L #define CSV_HAS_CXX17 #endif #if CMAKE_CXX_STANDARD >= 14 || __cplusplus >= 201402L #define CSV_HAS_CXX14 #endif #ifdef CSV_HAS_CXX17 #include /** @typedef string_view * The string_view class used by this library. */ using string_view = std::string_view; #else /** @typedef string_view * The string_view class used by this library. */ using string_view = nonstd::string_view; #endif #ifdef CSV_HAS_CXX17 #define IF_CONSTEXPR if constexpr #define CONSTEXPR_VALUE constexpr #define CONSTEXPR_17 constexpr #else #define IF_CONSTEXPR if #define CONSTEXPR_VALUE const #define CONSTEXPR_17 inline #endif #ifdef CSV_HAS_CXX14 template using enable_if_t = std::enable_if_t; #define CONSTEXPR_14 constexpr #define CONSTEXPR_VALUE_14 constexpr #else template using enable_if_t = typename std::enable_if::type; #define CONSTEXPR_14 inline #define CONSTEXPR_VALUE_14 const #endif // Resolves g++ bug with regard to constexpr methods // See: https://stackoverflow.com/questions/36489369/constexpr-non-static-member-function-with-non-constexpr-constructor-gcc-clang-d #if defined __GNUC__ && !defined __clang__ #if (__GNUC__ >= 7 &&__GNUC_MINOR__ >= 2) || (__GNUC__ >= 8) #define CONSTEXPR constexpr #endif #else #ifdef CSV_HAS_CXX17 #define CONSTEXPR constexpr #endif #endif #ifndef CONSTEXPR #define CONSTEXPR inline #endif #ifdef _MSC_VER #pragma endregion #endif namespace internals { // PAGE_SIZE macro could be already defined by the host system. #if defined(PAGE_SIZE) #undef PAGE_SIZE #endif // Get operating system specific details #if defined(_WIN32) inline int getpagesize() { _SYSTEM_INFO sys_info = {}; GetSystemInfo(&sys_info); return std::max(sys_info.dwPageSize, sys_info.dwAllocationGranularity); } const int PAGE_SIZE = getpagesize(); #elif defined(__linux__) const int PAGE_SIZE = getpagesize(); #else /** Size of a memory page in bytes. Used by * csv::internals::CSVFieldArray when allocating blocks. */ const int PAGE_SIZE = 4096; #endif /** For functions that lazy load a large CSV, this determines how * many bytes are read at a time */ constexpr size_t ITERATION_CHUNK_SIZE = 10000000; // 10MB template inline bool is_equal(T a, T b, T epsilon = 0.001) { /** Returns true if two floating point values are about the same */ static_assert(std::is_floating_point::value, "T must be a floating point type."); return std::abs(a - b) < epsilon; } /** @typedef ParseFlags * An enum used for describing the significance of each character * with respect to CSV parsing * * @see quote_escape_flag */ enum class ParseFlags { QUOTE_ESCAPE_QUOTE = 0, /**< A quote inside or terminating a quote_escaped field */ QUOTE = 2 | 1, /**< Characters which may signify a quote escape */ NOT_SPECIAL = 4, /**< Characters with no special meaning or escaped delimiters and newlines */ DELIMITER = 4 | 2, /**< Characters which signify a new field */ NEWLINE = 4 | 2 | 1 /**< Characters which signify a new row */ }; /** Transform the ParseFlags given the context of whether or not the current * field is quote escaped */ constexpr ParseFlags quote_escape_flag(ParseFlags flag, bool quote_escape) noexcept { return (ParseFlags)((int)flag & ~((int)ParseFlags::QUOTE * quote_escape)); } // Assumed to be true by parsing functions: allows for testing // if an item is DELIMITER or NEWLINE with a >= statement STATIC_ASSERT(ParseFlags::DELIMITER < ParseFlags::NEWLINE); /** Optimizations for reducing branching in parsing loop * * Idea: The meaning of all non-quote characters changes depending * on whether or not the parser is in a quote-escaped mode (0 or 1) */ STATIC_ASSERT(quote_escape_flag(ParseFlags::NOT_SPECIAL, false) == ParseFlags::NOT_SPECIAL); STATIC_ASSERT(quote_escape_flag(ParseFlags::QUOTE, false) == ParseFlags::QUOTE); STATIC_ASSERT(quote_escape_flag(ParseFlags::DELIMITER, false) == ParseFlags::DELIMITER); STATIC_ASSERT(quote_escape_flag(ParseFlags::NEWLINE, false) == ParseFlags::NEWLINE); STATIC_ASSERT(quote_escape_flag(ParseFlags::NOT_SPECIAL, true) == ParseFlags::NOT_SPECIAL); STATIC_ASSERT(quote_escape_flag(ParseFlags::QUOTE, true) == ParseFlags::QUOTE_ESCAPE_QUOTE); STATIC_ASSERT(quote_escape_flag(ParseFlags::DELIMITER, true) == ParseFlags::NOT_SPECIAL); STATIC_ASSERT(quote_escape_flag(ParseFlags::NEWLINE, true) == ParseFlags::NOT_SPECIAL); /** An array which maps ASCII chars to a parsing flag */ using ParseFlagMap = std::array; /** An array which maps ASCII chars to a flag indicating if it is whitespace */ using WhitespaceMap = std::array; } /** Integer indicating a requested column wasn't found. */ constexpr int CSV_NOT_FOUND = -1; } namespace csv { namespace internals { struct ColNames; using ColNamesPtr = std::shared_ptr; /** @struct ColNames * A data structure for handling column name information. * * These are created by CSVReader and passed (via smart pointer) * to CSVRow objects it creates, thus * allowing for indexing by column name. */ struct ColNames { public: ColNames() = default; ColNames(const std::vector& names) { set_col_names(names); } std::vector get_col_names() const; void set_col_names(const std::vector&); int index_of(csv::string_view) const; bool empty() const noexcept { return this->col_names.empty(); } size_t size() const noexcept; private: std::vector col_names; std::unordered_map col_pos; }; } } /** @file * Defines an object used to store CSV format settings */ #include #include #include #include namespace csv { namespace internals { class IBasicCSVParser; } class CSVReader; /** Determines how to handle rows that are shorter or longer than the majority */ enum class VariableColumnPolicy { THROW = -1, IGNORE_ROW = 0, KEEP = 1 }; /** Stores the inferred format of a CSV file. */ struct CSVGuessResult { char delim; int header_row; }; /** Stores information about how to parse a CSV file. * Can be used to construct a csv::CSVReader. */ class CSVFormat { public: /** Settings for parsing a RFC 4180 CSV file */ CSVFormat() = default; /** Sets the delimiter of the CSV file * * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap */ CSVFormat& delimiter(char delim); /** Sets a list of potential delimiters * * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap * @param[in] delim An array of possible delimiters to try parsing the CSV with */ CSVFormat& delimiter(const std::vector & delim); /** Sets the whitespace characters to be trimmed * * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap * @param[in] ws An array of whitespace characters that should be trimmed */ CSVFormat& trim(const std::vector & ws); /** Sets the quote character * * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap */ CSVFormat& quote(char quote); /** Sets the column names. * * @note Unsets any values set by header_row() */ CSVFormat& column_names(const std::vector& names); /** Sets the header row * * @note Unsets any values set by column_names() */ CSVFormat& header_row(int row); /** Tells the parser that this CSV has no header row * * @note Equivalent to `header_row(-1)` * */ CSVFormat& no_header() { this->header_row(-1); return *this; } /** Turn quoting on or off */ CSVFormat& quote(bool use_quote) { this->no_quote = !use_quote; return *this; } /** Tells the parser how to handle columns of a different length than the others */ CONSTEXPR_14 CSVFormat& variable_columns(VariableColumnPolicy policy = VariableColumnPolicy::IGNORE_ROW) { this->variable_column_policy = policy; return *this; } /** Tells the parser how to handle columns of a different length than the others */ CONSTEXPR_14 CSVFormat& variable_columns(bool policy) { this->variable_column_policy = (VariableColumnPolicy)policy; return *this; } #ifndef DOXYGEN_SHOULD_SKIP_THIS char get_delim() const { // This error should never be received by end users. if (this->possible_delimiters.size() > 1) { throw std::runtime_error("There is more than one possible delimiter."); } return this->possible_delimiters.at(0); } CONSTEXPR bool is_quoting_enabled() const { return !this->no_quote; } CONSTEXPR char get_quote_char() const { return this->quote_char; } CONSTEXPR int get_header() const { return this->header; } std::vector get_possible_delims() const { return this->possible_delimiters; } std::vector get_trim_chars() const { return this->trim_chars; } CONSTEXPR VariableColumnPolicy get_variable_column_policy() const { return this->variable_column_policy; } #endif /** CSVFormat for guessing the delimiter */ CSV_INLINE static CSVFormat guess_csv() { CSVFormat format; format.delimiter({ ',', '|', '\t', ';', '^' }) .quote('"') .header_row(0); return format; } bool guess_delim() { return this->possible_delimiters.size() > 1; } friend CSVReader; friend internals::IBasicCSVParser; private: /**< Throws an error if delimiters and trim characters overlap */ void assert_no_char_overlap(); /**< Set of possible delimiters */ std::vector possible_delimiters = { ',' }; /**< Set of whitespace characters to trim */ std::vector trim_chars = {}; /**< Row number with columns (ignored if col_names is non-empty) */ int header = 0; /**< Whether or not to use quoting */ bool no_quote = false; /**< Quote character */ char quote_char = '"'; /**< Should be left empty unless file doesn't include header */ std::vector col_names = {}; /**< Allow variable length columns? */ VariableColumnPolicy variable_column_policy = VariableColumnPolicy::IGNORE_ROW; }; } /** @file * Defines the data type used for storing information about a CSV row */ #include #include #include // For CSVField #include // For CSVField #include #include #include #include #include /** @file * @brief Implements data type parsing functionality */ #include #include #include #include namespace csv { /** Enumerates the different CSV field types that are * recognized by this library * * @note Overflowing integers will be stored and classified as doubles. * @note Unlike previous releases, integer enums here are platform agnostic. */ enum class DataType { UNKNOWN = -1, CSV_NULL, /**< Empty string */ CSV_STRING, /**< Non-numeric string */ CSV_INT8, /**< 8-bit integer */ CSV_INT16, /**< 16-bit integer (short on MSVC/GCC) */ CSV_INT32, /**< 32-bit integer (int on MSVC/GCC) */ CSV_INT64, /**< 64-bit integer (long long on MSVC/GCC) */ CSV_DOUBLE /**< Floating point value */ }; static_assert(DataType::CSV_STRING < DataType::CSV_INT8, "String type should come before numeric types."); static_assert(DataType::CSV_INT8 < DataType::CSV_INT64, "Smaller integer types should come before larger integer types."); static_assert(DataType::CSV_INT64 < DataType::CSV_DOUBLE, "Integer types should come before floating point value types."); namespace internals { /** Compute 10 to the power of n */ template HEDLEY_CONST CONSTEXPR_14 long double pow10(const T& n) noexcept { long double multiplicand = n > 0 ? 10 : 0.1, ret = 1; // Make all numbers positive T iterations = n > 0 ? n : -n; for (T i = 0; i < iterations; i++) { ret *= multiplicand; } return ret; } /** Compute 10 to the power of n */ template<> HEDLEY_CONST CONSTEXPR_14 long double pow10(const unsigned& n) noexcept { long double multiplicand = n > 0 ? 10 : 0.1, ret = 1; for (unsigned i = 0; i < n; i++) { ret *= multiplicand; } return ret; } #ifndef DOXYGEN_SHOULD_SKIP_THIS /** Private site-indexed array mapping byte sizes to an integer size enum */ constexpr DataType int_type_arr[8] = { DataType::CSV_INT8, // 1 DataType::CSV_INT16, // 2 DataType::UNKNOWN, DataType::CSV_INT32, // 4 DataType::UNKNOWN, DataType::UNKNOWN, DataType::UNKNOWN, DataType::CSV_INT64 // 8 }; template inline DataType type_num() { static_assert(std::is_integral::value, "T should be an integral type."); static_assert(sizeof(T) <= 8, "Byte size must be no greater than 8."); return int_type_arr[sizeof(T) - 1]; } template<> inline DataType type_num() { return DataType::CSV_DOUBLE; } template<> inline DataType type_num() { return DataType::CSV_DOUBLE; } template<> inline DataType type_num() { return DataType::CSV_DOUBLE; } template<> inline DataType type_num() { return DataType::CSV_NULL; } template<> inline DataType type_num() { return DataType::CSV_STRING; } CONSTEXPR_14 DataType data_type(csv::string_view in, long double* const out = nullptr); #endif /** Given a byte size, return the largest number than can be stored in * an integer of that size * * Note: Provides a platform-agnostic way of mapping names like "long int" to * byte sizes */ template CONSTEXPR_14 long double get_int_max() { static_assert(Bytes == 1 || Bytes == 2 || Bytes == 4 || Bytes == 8, "Bytes must be a power of 2 below 8."); IF_CONSTEXPR (sizeof(signed char) == Bytes) { return (long double)std::numeric_limits::max(); } IF_CONSTEXPR (sizeof(short) == Bytes) { return (long double)std::numeric_limits::max(); } IF_CONSTEXPR (sizeof(int) == Bytes) { return (long double)std::numeric_limits::max(); } IF_CONSTEXPR (sizeof(long int) == Bytes) { return (long double)std::numeric_limits::max(); } IF_CONSTEXPR (sizeof(long long int) == Bytes) { return (long double)std::numeric_limits::max(); } HEDLEY_UNREACHABLE(); } /** Given a byte size, return the largest number than can be stored in * an unsigned integer of that size */ template CONSTEXPR_14 long double get_uint_max() { static_assert(Bytes == 1 || Bytes == 2 || Bytes == 4 || Bytes == 8, "Bytes must be a power of 2 below 8."); IF_CONSTEXPR(sizeof(unsigned char) == Bytes) { return (long double)std::numeric_limits::max(); } IF_CONSTEXPR(sizeof(unsigned short) == Bytes) { return (long double)std::numeric_limits::max(); } IF_CONSTEXPR(sizeof(unsigned int) == Bytes) { return (long double)std::numeric_limits::max(); } IF_CONSTEXPR(sizeof(unsigned long int) == Bytes) { return (long double)std::numeric_limits::max(); } IF_CONSTEXPR(sizeof(unsigned long long int) == Bytes) { return (long double)std::numeric_limits::max(); } HEDLEY_UNREACHABLE(); } /** Largest number that can be stored in a 8-bit integer */ CONSTEXPR_VALUE_14 long double CSV_INT8_MAX = get_int_max<1>(); /** Largest number that can be stored in a 16-bit integer */ CONSTEXPR_VALUE_14 long double CSV_INT16_MAX = get_int_max<2>(); /** Largest number that can be stored in a 32-bit integer */ CONSTEXPR_VALUE_14 long double CSV_INT32_MAX = get_int_max<4>(); /** Largest number that can be stored in a 64-bit integer */ CONSTEXPR_VALUE_14 long double CSV_INT64_MAX = get_int_max<8>(); /** Largest number that can be stored in a 8-bit ungisned integer */ CONSTEXPR_VALUE_14 long double CSV_UINT8_MAX = get_uint_max<1>(); /** Largest number that can be stored in a 16-bit unsigned integer */ CONSTEXPR_VALUE_14 long double CSV_UINT16_MAX = get_uint_max<2>(); /** Largest number that can be stored in a 32-bit unsigned integer */ CONSTEXPR_VALUE_14 long double CSV_UINT32_MAX = get_uint_max<4>(); /** Largest number that can be stored in a 64-bit unsigned integer */ CONSTEXPR_VALUE_14 long double CSV_UINT64_MAX = get_uint_max<8>(); /** Given a pointer to the start of what is start of * the exponential part of a number written (possibly) in scientific notation * parse the exponent */ HEDLEY_PRIVATE CONSTEXPR_14 DataType _process_potential_exponential( csv::string_view exponential_part, const long double& coeff, long double * const out) { long double exponent = 0; auto result = data_type(exponential_part, &exponent); // Exponents in scientific notation should not be decimal numbers if (result >= DataType::CSV_INT8 && result < DataType::CSV_DOUBLE) { if (out) *out = coeff * pow10(exponent); return DataType::CSV_DOUBLE; } return DataType::CSV_STRING; } /** Given the absolute value of an integer, determine what numeric type * it fits in */ HEDLEY_PRIVATE HEDLEY_PURE CONSTEXPR_14 DataType _determine_integral_type(const long double& number) noexcept { // We can assume number is always non-negative assert(number >= 0); if (number <= internals::CSV_INT8_MAX) return DataType::CSV_INT8; else if (number <= internals::CSV_INT16_MAX) return DataType::CSV_INT16; else if (number <= internals::CSV_INT32_MAX) return DataType::CSV_INT32; else if (number <= internals::CSV_INT64_MAX) return DataType::CSV_INT64; else // Conversion to long long will cause an overflow return DataType::CSV_DOUBLE; } /** Distinguishes numeric from other text values. Used by various * type casting functions, like csv_parser::CSVReader::read_row() * * #### Rules * - Leading and trailing whitespace ("padding") ignored * - A string of just whitespace is NULL * * @param[in] in String value to be examined * @param[out] out Pointer to long double where results of numeric parsing * get stored */ CONSTEXPR_14 DataType data_type(csv::string_view in, long double* const out) { // Empty string --> NULL if (in.size() == 0) return DataType::CSV_NULL; bool ws_allowed = true, neg_allowed = true, dot_allowed = true, digit_allowed = true, has_digit = false, prob_float = false; unsigned places_after_decimal = 0; long double integral_part = 0, decimal_part = 0; for (size_t i = 0, ilen = in.size(); i < ilen; i++) { const char& current = in[i]; switch (current) { case ' ': if (!ws_allowed) { if (isdigit(in[i - 1])) { digit_allowed = false; ws_allowed = true; } else { // Ex: '510 123 4567' return DataType::CSV_STRING; } } break; case '-': if (!neg_allowed) { // Ex: '510-123-4567' return DataType::CSV_STRING; } neg_allowed = false; break; case '.': if (!dot_allowed) { return DataType::CSV_STRING; } dot_allowed = false; prob_float = true; break; case 'e': case 'E': // Process scientific notation if (prob_float || (i && i + 1 < ilen && isdigit(in[i - 1]))) { size_t exponent_start_idx = i + 1; prob_float = true; // Strip out plus sign if (in[i + 1] == '+') { exponent_start_idx++; } return _process_potential_exponential( in.substr(exponent_start_idx), neg_allowed ? integral_part + decimal_part : -(integral_part + decimal_part), out ); } return DataType::CSV_STRING; break; default: short digit = static_cast(current - '0'); if (digit >= 0 && digit <= 9) { // Process digit has_digit = true; if (!digit_allowed) return DataType::CSV_STRING; else if (ws_allowed) // Ex: '510 456' ws_allowed = false; // Build current number if (prob_float) decimal_part += digit / pow10(++places_after_decimal); else integral_part = (integral_part * 10) + digit; } else { return DataType::CSV_STRING; } } } // No non-numeric/non-whitespace characters found if (has_digit) { long double number = integral_part + decimal_part; if (out) { *out = neg_allowed ? number : -number; } return prob_float ? DataType::CSV_DOUBLE : _determine_integral_type(number); } // Just whitespace return DataType::CSV_NULL; } } } namespace csv { namespace internals { class IBasicCSVParser; static const std::string ERROR_NAN = "Not a number."; static const std::string ERROR_OVERFLOW = "Overflow error."; static const std::string ERROR_FLOAT_TO_INT = "Attempted to convert a floating point value to an integral type."; static const std::string ERROR_NEG_TO_UNSIGNED = "Negative numbers cannot be converted to unsigned types."; std::string json_escape_string(csv::string_view s) noexcept; /** A barebones class used for describing CSV fields */ struct RawCSVField { RawCSVField() = default; RawCSVField(size_t _start, size_t _length, bool _double_quote = false) { start = _start; length = _length; has_double_quote = _double_quote; } /** The start of the field, relative to the beginning of the row */ size_t start; /** The length of the row, ignoring quote escape characters */ size_t length; /** Whether or not the field contains an escaped quote */ bool has_double_quote; }; /** A class used for efficiently storing RawCSVField objects and expanding as necessary * * @par Implementation * This data structure stores RawCSVField in continguous blocks. When more capacity * is needed, a new block is allocated, but previous data stays put. * * @par Thread Safety * This class may be safely read from multiple threads and written to from one, * as long as the writing thread does not actively touch fields which are being * read. */ class CSVFieldList { public: /** Construct a CSVFieldList which allocates blocks of a certain size */ CSVFieldList(size_t single_buffer_capacity = (size_t)(internals::PAGE_SIZE / sizeof(RawCSVField))) : _single_buffer_capacity(single_buffer_capacity) { this->allocate(); } // No copy constructor CSVFieldList(const CSVFieldList& other) = delete; // CSVFieldArrays may be moved CSVFieldList(CSVFieldList&& other) : _single_buffer_capacity(other._single_buffer_capacity) { buffers = std::move(other.buffers); _current_buffer_size = other._current_buffer_size; _back = other._back; } ~CSVFieldList() { for (auto& buffer : buffers) delete[] buffer; } template void emplace_back(Args&&... args) { if (this->_current_buffer_size == this->_single_buffer_capacity) { this->allocate(); } *(_back++) = RawCSVField(std::forward(args)...); _current_buffer_size++; } size_t size() const noexcept { return this->_current_buffer_size + ((this->buffers.size() - 1) * this->_single_buffer_capacity); } RawCSVField& operator[](size_t n) const; private: const size_t _single_buffer_capacity; std::vector buffers = {}; /** Number of items in the current buffer */ size_t _current_buffer_size = 0; /** Pointer to the current empty field */ RawCSVField* _back = nullptr; /** Allocate a new page of memory */ void allocate(); }; /** A class for storing raw CSV data and associated metadata */ struct RawCSVData { std::shared_ptr _data = nullptr; csv::string_view data = ""; internals::CSVFieldList fields; std::unordered_set has_double_quotes = {}; // TODO: Consider replacing with a more thread-safe structure std::unordered_map double_quote_fields = {}; internals::ColNamesPtr col_names = nullptr; internals::ParseFlagMap parse_flags; internals::WhitespaceMap ws_flags; }; using RawCSVDataPtr = std::shared_ptr; } /** * @class CSVField * @brief Data type representing individual CSV values. * CSVFields can be obtained by using CSVRow::operator[] */ class CSVField { public: /** Constructs a CSVField from a string_view */ constexpr explicit CSVField(csv::string_view _sv) noexcept : sv(_sv) { }; operator std::string() const { return std::string(" ") + std::string(this->sv); } /** Returns the value casted to the requested type, performing type checking before. * * \par Valid options for T * - std::string or csv::string_view * - signed integral types (signed char, short, int, long int, long long int) * - floating point types (float, double, long double) * - unsigned integers are not supported at this time, but may be in a later release * * \par Invalid conversions * - Converting non-numeric values to any numeric type * - Converting floating point values to integers * - Converting a large integer to a smaller type that will not hold it * * @note This method is capable of parsing scientific E-notation. * See [this page](md_docs_source_scientific_notation.html) * for more details. * * @throws std::runtime_error Thrown if an invalid conversion is performed. * * @warning Currently, conversions to floating point types are not * checked for loss of precision * * @warning Any string_views returned are only guaranteed to be valid * if the parent CSVRow is still alive. If you are concerned * about object lifetimes, then grab a std::string or a * numeric value. * */ template T get() { IF_CONSTEXPR(std::is_arithmetic::value) { // Note: this->type() also converts the CSV value to float if (this->type() <= DataType::CSV_STRING) { throw std::runtime_error(internals::ERROR_NAN); } } IF_CONSTEXPR(std::is_integral::value) { // Note: this->is_float() also converts the CSV value to float if (this->is_float()) { throw std::runtime_error(internals::ERROR_FLOAT_TO_INT); } IF_CONSTEXPR(std::is_unsigned::value) { if (this->value < 0) { throw std::runtime_error(internals::ERROR_NEG_TO_UNSIGNED); } } } // Allow fallthrough from previous if branch IF_CONSTEXPR(!std::is_floating_point::value) { IF_CONSTEXPR(std::is_unsigned::value) { // Quick hack to perform correct unsigned integer boundary checks if (this->value > internals::get_uint_max()) { throw std::runtime_error(internals::ERROR_OVERFLOW); } } else if (internals::type_num() < this->_type) { throw std::runtime_error(internals::ERROR_OVERFLOW); } } return static_cast(this->value); } /** Parse a hexadecimal value, returning false if the value is not hex. */ bool try_parse_hex(int& parsedValue); /** Compares the contents of this field to a numeric value. If this * field does not contain a numeric value, then all comparisons return * false. * * @note Floating point values are considered equal if they are within * `0.000001` of each other. * * @warning Multiple numeric comparisons involving the same field can * be done more efficiently by calling the CSVField::get<>() method. * * @sa csv::CSVField::operator==(const char * other) * @sa csv::CSVField::operator==(csv::string_view other) */ template CONSTEXPR_14 bool operator==(T other) const noexcept { static_assert(std::is_arithmetic::value, "T should be a numeric value."); if (this->_type != DataType::UNKNOWN) { if (this->_type == DataType::CSV_STRING) { return false; } return internals::is_equal(value, static_cast(other), 0.000001L); } long double out = 0; if (internals::data_type(this->sv, &out) == DataType::CSV_STRING) { return false; } return internals::is_equal(out, static_cast(other), 0.000001L); } /** Return a string view over the field's contents */ CONSTEXPR csv::string_view get_sv() const noexcept { return this->sv; } /** Returns true if field is an empty string or string of whitespace characters */ CONSTEXPR_14 bool is_null() noexcept { return type() == DataType::CSV_NULL; } /** Returns true if field is a non-numeric, non-empty string */ CONSTEXPR_14 bool is_str() noexcept { return type() == DataType::CSV_STRING; } /** Returns true if field is an integer or float */ CONSTEXPR_14 bool is_num() noexcept { return type() >= DataType::CSV_INT8; } /** Returns true if field is an integer */ CONSTEXPR_14 bool is_int() noexcept { return (type() >= DataType::CSV_INT8) && (type() <= DataType::CSV_INT64); } /** Returns true if field is a floating point value */ CONSTEXPR_14 bool is_float() noexcept { return type() == DataType::CSV_DOUBLE; }; /** Return the type of the underlying CSV data */ CONSTEXPR_14 DataType type() noexcept { this->get_value(); return _type; } private: long double value = 0; /**< Cached numeric value */ csv::string_view sv = ""; /**< A pointer to this field's text */ DataType _type = DataType::UNKNOWN; /**< Cached data type value */ CONSTEXPR_14 void get_value() noexcept { /* Check to see if value has been cached previously, if not * evaluate it */ if ((int)_type < 0) { this->_type = internals::data_type(this->sv, &this->value); } } }; /** Data structure for representing CSV rows */ class CSVRow { public: friend internals::IBasicCSVParser; CSVRow() = default; /** Construct a CSVRow from a RawCSVDataPtr */ CSVRow(internals::RawCSVDataPtr _data) : data(_data) {} CSVRow(internals::RawCSVDataPtr _data, size_t _data_start, size_t _field_bounds) : data(_data), data_start(_data_start), fields_start(_field_bounds) {} /** Indicates whether row is empty or not */ CONSTEXPR bool empty() const noexcept { return this->size() == 0; } /** Return the number of fields in this row */ CONSTEXPR size_t size() const noexcept { return row_length; } /** @name Value Retrieval */ ///@{ CSVField operator[](size_t n) const; CSVField operator[](const std::string&) const; std::string to_json(const std::vector& subset = {}) const; std::string to_json_array(const std::vector& subset = {}) const; /** Retrieve this row's associated column names */ std::vector get_col_names() const { return this->data->col_names->get_col_names(); } /** Convert this CSVRow into a vector of strings. * **Note**: This is a less efficient method of * accessing data than using the [] operator. */ operator std::vector() const; ///@} /** A random access iterator over the contents of a CSV row. * Each iterator points to a CSVField. */ class iterator { public: #ifndef DOXYGEN_SHOULD_SKIP_THIS using value_type = CSVField; using difference_type = int; // Using CSVField * as pointer type causes segfaults in MSVC debug builds // but using shared_ptr as pointer type won't compile in g++ #ifdef _MSC_BUILD using pointer = std::shared_ptr; #else using pointer = CSVField * ; #endif using reference = CSVField & ; using iterator_category = std::random_access_iterator_tag; #endif iterator(const CSVRow*, int i); reference operator*() const; pointer operator->() const; iterator operator++(int); iterator& operator++(); iterator operator--(int); iterator& operator--(); iterator operator+(difference_type n) const; iterator operator-(difference_type n) const; /** Two iterators are equal if they point to the same field */ CONSTEXPR bool operator==(const iterator& other) const noexcept { return this->i == other.i; }; CONSTEXPR bool operator!=(const iterator& other) const noexcept { return !operator==(other); } #ifndef NDEBUG friend CSVRow; #endif private: const CSVRow * daddy = nullptr; // Pointer to parent std::shared_ptr field = nullptr; // Current field pointed at int i = 0; // Index of current field }; /** A reverse iterator over the contents of a CSVRow. */ using reverse_iterator = std::reverse_iterator; /** @name Iterators * @brief Each iterator points to a CSVField object. */ ///@{ iterator begin() const; iterator end() const noexcept; reverse_iterator rbegin() const noexcept; reverse_iterator rend() const; ///@} private: /** Retrieve a string view corresponding to the specified index */ csv::string_view get_field(size_t index) const; internals::RawCSVDataPtr data; /** Where in RawCSVData.data we start */ size_t data_start = 0; /** Where in the RawCSVDataPtr.fields array we start */ size_t fields_start = 0; /** How many columns this row spans */ size_t row_length = 0; }; #ifdef _MSC_VER #pragma region CSVField::get Specializations #endif /** Retrieve this field's original string */ template<> inline std::string CSVField::get() { return std::string(this->sv); } /** Retrieve a view over this field's string * * @warning This string_view is only guaranteed to be valid as long as this * CSVRow is still alive. */ template<> CONSTEXPR_14 csv::string_view CSVField::get() { return this->sv; } /** Retrieve this field's value as a long double */ template<> CONSTEXPR_14 long double CSVField::get() { if (!is_num()) throw std::runtime_error(internals::ERROR_NAN); return this->value; } #ifdef _MSC_VER #pragma endregion CSVField::get Specializations #endif /** Compares the contents of this field to a string */ template<> CONSTEXPR bool CSVField::operator==(const char * other) const noexcept { return this->sv == other; } /** Compares the contents of this field to a string */ template<> CONSTEXPR bool CSVField::operator==(csv::string_view other) const noexcept { return this->sv == other; } } inline std::ostream& operator << (std::ostream& os, csv::CSVField const& value) { os << std::string(value); return os; } namespace csv { namespace internals { /** Create a vector v where each index i corresponds to the * ASCII number for a character and, v[i + 128] labels it according to * the CSVReader::ParseFlags enum */ HEDLEY_CONST CONSTEXPR_17 ParseFlagMap make_parse_flags(char delimiter) { std::array ret = {}; for (int i = -128; i < 128; i++) { const int arr_idx = i + 128; char ch = char(i); if (ch == delimiter) ret[arr_idx] = ParseFlags::DELIMITER; else if (ch == '\r' || ch == '\n') ret[arr_idx] = ParseFlags::NEWLINE; else ret[arr_idx] = ParseFlags::NOT_SPECIAL; } return ret; } /** Create a vector v where each index i corresponds to the * ASCII number for a character and, v[i + 128] labels it according to * the CSVReader::ParseFlags enum */ HEDLEY_CONST CONSTEXPR_17 ParseFlagMap make_parse_flags(char delimiter, char quote_char) { std::array ret = make_parse_flags(delimiter); ret[(size_t)quote_char + 128] = ParseFlags::QUOTE; return ret; } /** Create a vector v where each index i corresponds to the * ASCII number for a character c and, v[i + 128] is true if * c is a whitespace character */ HEDLEY_CONST CONSTEXPR_17 WhitespaceMap make_ws_flags(const char* ws_chars, size_t n_chars) { std::array ret = {}; for (int i = -128; i < 128; i++) { const int arr_idx = i + 128; char ch = char(i); ret[arr_idx] = false; for (size_t j = 0; j < n_chars; j++) { if (ws_chars[j] == ch) { ret[arr_idx] = true; } } } return ret; } inline WhitespaceMap make_ws_flags(const std::vector& flags) { return make_ws_flags(flags.data(), flags.size()); } CSV_INLINE size_t get_file_size(csv::string_view filename); CSV_INLINE std::string get_csv_head(csv::string_view filename); /** Read the first 500KB of a CSV file */ CSV_INLINE std::string get_csv_head(csv::string_view filename, size_t file_size); /** A std::deque wrapper which allows multiple read and write threads to concurrently * access it along with providing read threads the ability to wait for the deque * to become populated */ template class ThreadSafeDeque { public: ThreadSafeDeque(size_t notify_size = 100) : _notify_size(notify_size) {}; ThreadSafeDeque(const ThreadSafeDeque& other) { this->data = other.data; this->_notify_size = other._notify_size; } ThreadSafeDeque(const std::deque& source) : ThreadSafeDeque() { this->data = source; } void clear() noexcept { this->data.clear(); } bool empty() const noexcept { return this->data.empty(); } T& front() noexcept { return this->data.front(); } T& operator[](size_t n) { return this->data[n]; } void push_back(T&& item) { std::lock_guard lock{ this->_lock }; this->data.push_back(std::move(item)); if (this->size() >= _notify_size) { this->_cond.notify_all(); } } T pop_front() noexcept { std::lock_guard lock{ this->_lock }; T item = std::move(data.front()); data.pop_front(); return item; } size_t size() const noexcept { return this->data.size(); } /** Returns true if a thread is actively pushing items to this deque */ constexpr bool is_waitable() const noexcept { return this->_is_waitable; } /** Wait for an item to become available */ void wait() { if (!is_waitable()) { return; } std::unique_lock lock{ this->_lock }; this->_cond.wait(lock, [this] { return this->size() >= _notify_size || !this->is_waitable(); }); lock.unlock(); } typename std::deque::iterator begin() noexcept { return this->data.begin(); } typename std::deque::iterator end() noexcept { return this->data.end(); } /** Tell listeners that this deque is actively being pushed to */ void notify_all() { std::unique_lock lock{ this->_lock }; this->_is_waitable = true; this->_cond.notify_all(); } /** Tell all listeners to stop */ void kill_all() { std::unique_lock lock{ this->_lock }; this->_is_waitable = false; this->_cond.notify_all(); } private: bool _is_waitable = false; size_t _notify_size; std::mutex _lock; std::condition_variable _cond; std::deque data; }; constexpr const int UNINITIALIZED_FIELD = -1; } /** Standard type for storing collection of rows */ using RowCollection = internals::ThreadSafeDeque; namespace internals { /** Abstract base class which provides CSV parsing logic. * * Concrete implementations may customize this logic across * different input sources, such as memory mapped files, stringstreams, * etc... */ class IBasicCSVParser { public: IBasicCSVParser() = default; IBasicCSVParser(const CSVFormat&, const ColNamesPtr&); IBasicCSVParser(const ParseFlagMap& parse_flags, const WhitespaceMap& ws_flags ) : _parse_flags(parse_flags), _ws_flags(ws_flags) {}; virtual ~IBasicCSVParser() {} /** Whether or not we have reached the end of source */ bool eof() { return this->_eof; } /** Parse the next block of data */ virtual void next(size_t bytes) = 0; /** Indicate the last block of data has been parsed */ void end_feed(); CONSTEXPR_17 ParseFlags parse_flag(const char ch) const noexcept { return _parse_flags.data()[ch + 128]; } CONSTEXPR_17 ParseFlags compound_parse_flag(const char ch) const noexcept { return quote_escape_flag(parse_flag(ch), this->quote_escape); } /** Whether or not this CSV has a UTF-8 byte order mark */ CONSTEXPR bool utf8_bom() const { return this->_utf8_bom; } void set_output(RowCollection& rows) { this->_records = &rows; } protected: /** @name Current Parser State */ ///@{ CSVRow current_row; RawCSVDataPtr data_ptr = nullptr; ColNamesPtr _col_names = nullptr; CSVFieldList* fields = nullptr; int field_start = UNINITIALIZED_FIELD; size_t field_length = 0; /** An array where the (i + 128)th slot gives the ParseFlags for ASCII character i */ ParseFlagMap _parse_flags; ///@} /** @name Current Stream/File State */ ///@{ bool _eof = false; /** The size of the incoming CSV */ size_t source_size = 0; ///@} /** Whether or not source needs to be read in chunks */ CONSTEXPR bool no_chunk() const { return this->source_size < ITERATION_CHUNK_SIZE; } /** Parse the current chunk of data * * * @returns How many character were read that are part of complete rows */ size_t parse(); /** Create a new RawCSVDataPtr for a new chunk of data */ void reset_data_ptr(); private: /** An array where the (i + 128)th slot determines whether ASCII character i should * be trimmed */ WhitespaceMap _ws_flags; bool quote_escape = false; bool field_has_double_quote = false; /** Where we are in the current data block */ size_t data_pos = 0; /** Whether or not an attempt to find Unicode BOM has been made */ bool unicode_bom_scan = false; bool _utf8_bom = false; /** Where complete rows should be pushed to */ RowCollection* _records = nullptr; CONSTEXPR_17 bool ws_flag(const char ch) const noexcept { return _ws_flags.data()[ch + 128]; } size_t& current_row_start() { return this->current_row.data_start; } void parse_field() noexcept; /** Finish parsing the current field */ void push_field(); /** Finish parsing the current row */ void push_row(); /** Handle possible Unicode byte order mark */ void trim_utf8_bom(); }; /** A class for parsing CSV data from a `std::stringstream` * or an `std::ifstream` */ template class StreamParser: public IBasicCSVParser { using RowCollection = ThreadSafeDeque; public: StreamParser(TStream& source, const CSVFormat& format, const ColNamesPtr& col_names = nullptr ) : IBasicCSVParser(format, col_names), _source(std::move(source)) {}; StreamParser( TStream& source, internals::ParseFlagMap parse_flags, internals::WhitespaceMap ws_flags) : IBasicCSVParser(parse_flags, ws_flags), _source(std::move(source)) {}; ~StreamParser() {} void next(size_t bytes = ITERATION_CHUNK_SIZE) override { if (this->eof()) return; this->reset_data_ptr(); this->data_ptr->_data = std::make_shared(); if (source_size == 0) { const auto start = _source.tellg(); _source.seekg(0, std::ios::end); const auto end = _source.tellg(); _source.seekg(0, std::ios::beg); source_size = end - start; } // Read data into buffer size_t length = std::min(source_size - stream_pos, bytes); std::unique_ptr buff(new char[length]); _source.seekg(stream_pos, std::ios::beg); _source.read(buff.get(), length); stream_pos = _source.tellg(); ((std::string*)(this->data_ptr->_data.get()))->assign(buff.get(), length); // Create string_view this->data_ptr->data = *((std::string*)this->data_ptr->_data.get()); // Parse this->current_row = CSVRow(this->data_ptr); size_t remainder = this->parse(); if (stream_pos == source_size || no_chunk()) { this->_eof = true; this->end_feed(); } else { this->stream_pos -= (length - remainder); } } private: TStream _source; size_t stream_pos = 0; }; /** Parser for memory-mapped files * * @par Implementation * This class constructs moving windows over a file to avoid * creating massive memory maps which may require more RAM * than the user has available. It contains logic to automatically * re-align each memory map to the beginning of a CSV row. * */ class MmapParser : public IBasicCSVParser { public: MmapParser(csv::string_view filename, const CSVFormat& format, const ColNamesPtr& col_names = nullptr ) : IBasicCSVParser(format, col_names) { this->_filename = filename.data(); this->source_size = get_file_size(filename); }; ~MmapParser() {} void next(size_t bytes) override; private: std::string _filename; size_t mmap_pos = 0; }; } } /** The all encompassing namespace */ namespace csv { /** Stuff that is generally not of interest to end-users */ namespace internals { std::string format_row(const std::vector& row, csv::string_view delim = ", "); std::vector _get_col_names( csv::string_view head, const CSVFormat format = CSVFormat::guess_csv()); struct GuessScore { double score; size_t header; }; CSV_INLINE GuessScore calculate_score(csv::string_view head, CSVFormat format); CSVGuessResult _guess_format(csv::string_view head, const std::vector& delims = { ',', '|', '\t', ';', '^', '~' }); } std::vector get_col_names( csv::string_view filename, const CSVFormat format = CSVFormat::guess_csv()); /** Guess the delimiter used by a delimiter-separated values file */ CSVGuessResult guess_format(csv::string_view filename, const std::vector& delims = { ',', '|', '\t', ';', '^', '~' }); /** @class CSVReader * @brief Main class for parsing CSVs from files and in-memory sources * * All rows are compared to the column names for length consistency * - By default, rows that are too short or too long are dropped * - Custom behavior can be defined by overriding bad_row_handler in a subclass */ class CSVReader { public: /** * An input iterator capable of handling large files. * @note Created by CSVReader::begin() and CSVReader::end(). * * @par Iterating over a file * @snippet tests/test_csv_iterator.cpp CSVReader Iterator 1 * * @par Using with `` library * @snippet tests/test_csv_iterator.cpp CSVReader Iterator 2 */ class iterator { public: #ifndef DOXYGEN_SHOULD_SKIP_THIS using value_type = CSVRow; using difference_type = std::ptrdiff_t; using pointer = CSVRow * ; using reference = CSVRow & ; using iterator_category = std::input_iterator_tag; #endif iterator() = default; iterator(CSVReader* reader) : daddy(reader) {}; iterator(CSVReader*, CSVRow&&); /** Access the CSVRow held by the iterator */ CONSTEXPR_14 reference operator*() { return this->row; } /** Return a pointer to the CSVRow the iterator has stopped at */ CONSTEXPR_14 pointer operator->() { return &(this->row); } iterator& operator++(); /**< Pre-increment iterator */ iterator operator++(int); /**< Post-increment ierator */ iterator& operator--(); /** Returns true if iterators were constructed from the same CSVReader * and point to the same row */ CONSTEXPR bool operator==(const iterator& other) const noexcept { return (this->daddy == other.daddy) && (this->i == other.i); } CONSTEXPR bool operator!=(const iterator& other) const noexcept { return !operator==(other); } private: CSVReader * daddy = nullptr; // Pointer to parent CSVRow row; // Current row size_t i = 0; // Index of current row }; /** @name Constructors * Constructors for iterating over large files and parsing in-memory sources. */ ///@{ CSVReader(csv::string_view filename, CSVFormat format = CSVFormat::guess_csv()); /** Allows parsing stream sources such as `std::stringstream` or `std::ifstream` * * @tparam TStream An input stream deriving from `std::istream` * @note Currently this constructor requires special CSV dialects to be manually * specified. */ template::value, int> = 0> CSVReader(TStream& source, CSVFormat format = CSVFormat()) : _format(format) { using Parser = internals::StreamParser; if (!format.col_names.empty()) this->set_col_names(format.col_names); this->parser = std::unique_ptr( new Parser(source, format, col_names)); // For C++11 this->initial_read(); } ///@} CSVReader(const CSVReader&) = delete; // No copy constructor CSVReader(CSVReader&&) = default; // Move constructor CSVReader& operator=(const CSVReader&) = delete; // No copy assignment CSVReader& operator=(CSVReader&& other) = default; ~CSVReader() { if (this->read_csv_worker.joinable()) { this->read_csv_worker.join(); } } /** @name Retrieving CSV Rows */ ///@{ bool read_row(CSVRow &row); iterator begin(); HEDLEY_CONST iterator end() const noexcept; /** Returns true if we have reached end of file */ bool eof() const noexcept { return this->parser->eof(); }; ///@} /** @name CSV Metadata */ ///@{ CSVFormat get_format() const; std::vector get_col_names() const; int index_of(csv::string_view col_name) const; ///@} /** @name CSV Metadata: Attributes */ ///@{ /** Whether or not the file or stream contains valid CSV rows, * not including the header. * * @note Gives an accurate answer regardless of when it is called. * */ CONSTEXPR bool empty() const noexcept { return this->n_rows() == 0; } /** Retrieves the number of rows that have been read so far */ CONSTEXPR size_t n_rows() const noexcept { return this->_n_rows; } /** Whether or not CSV was prefixed with a UTF-8 bom */ bool utf8_bom() const noexcept { return this->parser->utf8_bom(); } ///@} protected: /** * \defgroup csv_internal CSV Parser Internals * @brief Internals of CSVReader. Only maintainers and those looking to * extend the parser should read this. * @{ */ /** Sets this reader's column names and associated data */ void set_col_names(const std::vector&); /** @name CSV Settings **/ ///@{ CSVFormat _format; ///@} /** @name Parser State */ ///@{ /** Pointer to a object containing column information */ internals::ColNamesPtr col_names = std::make_shared(); /** Helper class which actually does the parsing */ std::unique_ptr parser = nullptr; /** Queue of parsed CSV rows */ std::unique_ptr records{new RowCollection(100)}; size_t n_cols = 0; /**< The number of columns in this CSV */ size_t _n_rows = 0; /**< How many rows (minus header) have been read so far */ /** @name Multi-Threaded File Reading Functions */ ///@{ bool read_csv(size_t bytes = internals::ITERATION_CHUNK_SIZE); ///@} /**@}*/ private: /** Whether or not rows before header were trimmed */ bool header_trimmed = false; /** @name Multi-Threaded File Reading: Flags and State */ ///@{ std::thread read_csv_worker; /**< Worker thread for read_csv() */ ///@} /** Read initial chunk to get metadata */ void initial_read() { this->read_csv_worker = std::thread(&CSVReader::read_csv, this, internals::ITERATION_CHUNK_SIZE); this->read_csv_worker.join(); } void trim_header(); }; } /** @file * Calculates statistics from CSV files */ #include #include #include namespace csv { /** Class for calculating statistics from CSV files and in-memory sources * * **Example** * \include programs/csv_stats.cpp * */ class CSVStat { public: using FreqCount = std::unordered_map; using TypeCount = std::unordered_map; std::vector get_mean() const; std::vector get_variance() const; std::vector get_mins() const; std::vector get_maxes() const; std::vector get_counts() const; std::vector get_dtypes() const; std::vector get_col_names() const { return this->reader.get_col_names(); } CSVStat(csv::string_view filename, CSVFormat format = CSVFormat::guess_csv()); CSVStat(std::stringstream& source, CSVFormat format = CSVFormat()); private: // An array of rolling averages // Each index corresponds to the rolling mean for the column at said index std::vector rolling_means; std::vector rolling_vars; std::vector mins; std::vector maxes; std::vector counts; std::vector dtypes; std::vector n; // Statistic calculators void variance(const long double&, const size_t&); void count(CSVField&, const size_t&); void min_max(const long double&, const size_t&); void dtype(CSVField&, const size_t&); void calc(); void calc_chunk(); void calc_worker(const size_t&); CSVReader reader; std::deque records = {}; }; } #include #include #include namespace csv { /** Returned by get_file_info() */ struct CSVFileInfo { std::string filename; /**< Filename */ std::vector col_names; /**< CSV column names */ char delim; /**< Delimiting character */ size_t n_rows; /**< Number of rows in a file */ size_t n_cols; /**< Number of columns in a CSV */ }; /** @name Shorthand Parsing Functions * @brief Convienience functions for parsing small strings */ ///@{ CSVReader operator ""_csv(const char*, size_t); CSVReader operator ""_csv_no_header(const char*, size_t); CSVReader parse(csv::string_view in, CSVFormat format = CSVFormat()); CSVReader parse_no_header(csv::string_view in); ///@} /** @name Utility Functions */ ///@{ std::unordered_map csv_data_types(const std::string&); CSVFileInfo get_file_info(const std::string& filename); int get_col_pos(csv::string_view filename, csv::string_view col_name, const CSVFormat& format = CSVFormat::guess_csv()); ///@} } /** @file * A standalone header file for writing delimiter-separated files */ #include #include #include #include #include #include namespace csv { namespace internals { static int DECIMAL_PLACES = 5; /** to_string() for unsigned integers */ template::value, int> = 0> inline std::string to_string(T value) { std::string digits_reverse = ""; if (value == 0) return "0"; while (value > 0) { digits_reverse += (char)('0' + (value % 10)); value /= 10; } return std::string(digits_reverse.rbegin(), digits_reverse.rend()); } /** to_string() for signed integers */ template< typename T, csv::enable_if_t::value && std::is_signed::value, int> = 0 > inline std::string to_string(T value) { if (value >= 0) return to_string((size_t)value); return "-" + to_string((size_t)(value * -1)); } /** to_string() for floating point numbers */ template< typename T, csv::enable_if_t::value, int> = 0 > inline std::string to_string(T value) { std::string result; T integral_part; T fractional_part = std::abs(std::modf(value, &integral_part)); integral_part = std::abs(integral_part); // Integral part if (value < 0) result = "-"; if (integral_part == 0) { result = "0"; } else { for (int n_digits = (int)(std::log(integral_part) / std::log(10)); n_digits + 1 > 0; n_digits --) { int digit = (int)(std::fmod(integral_part, pow10(n_digits + 1)) / pow10(n_digits)); result += (char)('0' + digit); } } // Decimal part result += "."; if (fractional_part > 0) { fractional_part *= (T)(pow10(DECIMAL_PLACES)); for (int n_digits = DECIMAL_PLACES; n_digits > 0; n_digits--) { int digit = (int)(std::fmod(fractional_part, pow10(n_digits)) / pow10(n_digits - 1)); result += (char)('0' + digit); } } else { result += "0"; } return result; } } /** Sets how many places after the decimal will be written for floating point numbers * * @param precision Number of decimal places */ inline static void set_decimal_places(int precision) { internals::DECIMAL_PLACES = precision; } /** @name CSV Writing */ ///@{ /** * Class for writing delimiter separated values files * * To write formatted strings, one should * -# Initialize a DelimWriter with respect to some output stream * -# Call write_row() on std::vectors of unformatted text * * @tparam OutputStream The output stream, e.g. `std::ofstream`, `std::stringstream` * @tparam Delim The delimiter character * @tparam Quote The quote character * @tparam Flush True: flush after every writing function, * false: you need to flush explicitly if needed. * In both cases the destructor will flush. * * @par Hint * Use the aliases csv::CSVWriter to write CSV * formatted strings and csv::TSVWriter * to write tab separated strings * * @par Example w/ std::vector, std::deque, std::list * @snippet test_write_csv.cpp CSV Writer Example * * @par Example w/ std::tuple * @snippet test_write_csv.cpp CSV Writer Tuple Example */ template class DelimWriter { public: /** Construct a DelimWriter over the specified output stream * * @param _out Stream to write to * @param _quote_minimal Limit field quoting to only when necessary */ DelimWriter(OutputStream& _out, bool _quote_minimal = true) : out(_out), quote_minimal(_quote_minimal) {}; /** Construct a DelimWriter over the file * * @param[out] filename File to write to */ DelimWriter(const std::string& filename) : DelimWriter(std::ifstream(filename)) {}; /** Destructor will flush remaining data * */ ~DelimWriter() { out.flush(); } /** Format a sequence of strings and write to CSV according to RFC 4180 * * @warning This does not check to make sure row lengths are consistent * * @param[in] record Sequence of strings to be formatted * * @return The current DelimWriter instance (allowing for operator chaining) */ template DelimWriter& operator<<(const std::array& record) { for (size_t i = 0; i < Size; i++) { out << csv_escape(record[i]); if (i + 1 != Size) out << Delim; } end_out(); return *this; } /** @copydoc operator<< */ template DelimWriter& operator<<(const std::tuple& record) { this->write_tuple<0, T...>(record); return *this; } /** * @tparam T A container such as std::vector, std::deque, or std::list * * @copydoc operator<< */ template< typename T, typename Alloc, template class Container, // Avoid conflicting with tuples with two elements csv::enable_if_t::value, int> = 0 > DelimWriter& operator<<(const Container& record) { const size_t ilen = record.size(); size_t i = 0; for (const auto& field : record) { out << csv_escape(field); if (i + 1 != ilen) out << Delim; i++; } end_out(); return *this; } /** Flushes the written data * */ void flush() { out.flush(); } private: template< typename T, csv::enable_if_t< !std::is_convertible::value && !std::is_convertible::value , int> = 0 > std::string csv_escape(T in) { return internals::to_string(in); } template< typename T, csv::enable_if_t< std::is_convertible::value || std::is_convertible::value , int> = 0 > std::string csv_escape(T in) { IF_CONSTEXPR(std::is_convertible::value) { return _csv_escape(in); } return _csv_escape(std::string(in)); } std::string _csv_escape(csv::string_view in) { /** Format a string to be RFC 4180-compliant * @param[in] in String to be CSV-formatted * @param[out] quote_minimal Only quote fields if necessary. * If False, everything is quoted. */ // Do we need a quote escape bool quote_escape = false; for (auto ch : in) { if (ch == Quote || ch == Delim || ch == '\r' || ch == '\n') { quote_escape = true; break; } } if (!quote_escape) { if (quote_minimal) return std::string(in); else { std::string ret(1, Quote); ret += in.data(); ret += Quote; return ret; } } // Start initial quote escape sequence std::string ret(1, Quote); for (auto ch: in) { if (ch == Quote) ret += std::string(2, Quote); else ret += ch; } // Finish off quote escape ret += Quote; return ret; } /** Recurisve template for writing std::tuples */ template typename std::enable_if::type write_tuple(const std::tuple& record) { out << csv_escape(std::get(record)); IF_CONSTEXPR (Index + 1 < sizeof...(T)) out << Delim; this->write_tuple(record); } /** Base case for writing std::tuples */ template typename std::enable_if::type write_tuple(const std::tuple& record) { (void)record; end_out(); } /** Ends a line in 'out' and flushes, if Flush is true.*/ void end_out() { out << '\n'; IF_CONSTEXPR(Flush) out.flush(); } OutputStream & out; bool quote_minimal; }; /** An alias for csv::DelimWriter for writing standard CSV files * * @sa csv::DelimWriter::operator<<() * * @note Use `csv::make_csv_writer()` to in instatiate this class over * an actual output stream. */ template using CSVWriter = DelimWriter; /** Class for writing tab-separated values files * * @sa csv::DelimWriter::write_row() * @sa csv::DelimWriter::operator<<() * * @note Use `csv::make_tsv_writer()` to in instatiate this class over * an actual output stream. */ template using TSVWriter = DelimWriter; /** Return a csv::CSVWriter over the output stream */ template inline CSVWriter make_csv_writer(OutputStream& out, bool quote_minimal=true) { return CSVWriter(out, quote_minimal); } /** Return a buffered csv::CSVWriter over the output stream (does not auto flush) */ template inline CSVWriter make_csv_writer_buffered(OutputStream& out, bool quote_minimal=true) { return CSVWriter(out, quote_minimal); } /** Return a csv::TSVWriter over the output stream */ template inline TSVWriter make_tsv_writer(OutputStream& out, bool quote_minimal=true) { return TSVWriter(out, quote_minimal); } /** Return a buffered csv::TSVWriter over the output stream (does not auto flush) */ template inline TSVWriter make_tsv_writer_buffered(OutputStream& out, bool quote_minimal=true) { return TSVWriter(out, quote_minimal); } ///@} } namespace csv { namespace internals { CSV_INLINE size_t get_file_size(csv::string_view filename) { std::ifstream infile(std::string(filename), std::ios::binary); const auto start = infile.tellg(); infile.seekg(0, std::ios::end); const auto end = infile.tellg(); return end - start; } CSV_INLINE std::string get_csv_head(csv::string_view filename) { return get_csv_head(filename, get_file_size(filename)); } CSV_INLINE std::string get_csv_head(csv::string_view filename, size_t file_size) { const size_t bytes = 500000; std::error_code error; size_t length = std::min((size_t)file_size, bytes); auto mmap = mio::make_mmap_source(std::string(filename), 0, length, error); if (error) { throw std::runtime_error("Cannot open file " + std::string(filename)); } return std::string(mmap.begin(), mmap.end()); } #ifdef _MSC_VER #pragma region IBasicCVParser #endif CSV_INLINE IBasicCSVParser::IBasicCSVParser( const CSVFormat& format, const ColNamesPtr& col_names ) : _col_names(col_names) { if (format.no_quote) { _parse_flags = internals::make_parse_flags(format.get_delim()); } else { _parse_flags = internals::make_parse_flags(format.get_delim(), format.quote_char); } _ws_flags = internals::make_ws_flags( format.trim_chars.data(), format.trim_chars.size() ); } CSV_INLINE void IBasicCSVParser::end_feed() { using internals::ParseFlags; bool empty_last_field = this->data_ptr && this->data_ptr->_data && !this->data_ptr->data.empty() && parse_flag(this->data_ptr->data.back()) == ParseFlags::DELIMITER; // Push field if (this->field_length > 0 || empty_last_field) { this->push_field(); } // Push row if (this->current_row.size() > 0) this->push_row(); } CSV_INLINE void IBasicCSVParser::parse_field() noexcept { using internals::ParseFlags; auto& in = this->data_ptr->data; // Trim off leading whitespace while (data_pos < in.size() && ws_flag(in[data_pos])) data_pos++; if (field_start == UNINITIALIZED_FIELD) field_start = (int)(data_pos - current_row_start()); // Optimization: Since NOT_SPECIAL characters tend to occur in contiguous // sequences, use the loop below to avoid having to go through the outer // switch statement as much as possible while (data_pos < in.size() && compound_parse_flag(in[data_pos]) == ParseFlags::NOT_SPECIAL) data_pos++; field_length = data_pos - (field_start + current_row_start()); // Trim off trailing whitespace, this->field_length constraint matters // when field is entirely whitespace for (size_t j = data_pos - 1; ws_flag(in[j]) && this->field_length > 0; j--) this->field_length--; } CSV_INLINE void IBasicCSVParser::push_field() { // Update if (field_has_double_quote) { fields->emplace_back( field_start == UNINITIALIZED_FIELD ? 0 : (unsigned int)field_start, field_length, true ); field_has_double_quote = false; } else { fields->emplace_back( field_start == UNINITIALIZED_FIELD ? 0 : (unsigned int)field_start, field_length ); } current_row.row_length++; // Reset field state field_start = UNINITIALIZED_FIELD; field_length = 0; } /** @return The number of characters parsed that belong to complete rows */ CSV_INLINE size_t IBasicCSVParser::parse() { using internals::ParseFlags; this->quote_escape = false; this->data_pos = 0; this->current_row_start() = 0; this->trim_utf8_bom(); auto& in = this->data_ptr->data; while (this->data_pos < in.size()) { switch (compound_parse_flag(in[this->data_pos])) { case ParseFlags::DELIMITER: this->push_field(); this->data_pos++; break; case ParseFlags::NEWLINE: this->data_pos++; // Catches CRLF (or LFLF) if (this->data_pos < in.size() && parse_flag(in[this->data_pos]) == ParseFlags::NEWLINE) this->data_pos++; // End of record -> Write record this->push_field(); this->push_row(); // Reset this->current_row = CSVRow(data_ptr, this->data_pos, fields->size()); break; case ParseFlags::NOT_SPECIAL: this->parse_field(); break; case ParseFlags::QUOTE_ESCAPE_QUOTE: if (data_pos + 1 == in.size()) return this->current_row_start(); else if (data_pos + 1 < in.size()) { auto next_ch = parse_flag(in[data_pos + 1]); if (next_ch >= ParseFlags::DELIMITER) { quote_escape = false; data_pos++; break; } else if (next_ch == ParseFlags::QUOTE) { // Case: Escaped quote data_pos += 2; this->field_length += 2; this->field_has_double_quote = true; break; } } // Case: Unescaped single quote => not strictly valid but we'll keep it this->field_length++; data_pos++; break; default: // Quote (currently not quote escaped) if (this->field_length == 0) { quote_escape = true; data_pos++; if (field_start == UNINITIALIZED_FIELD && data_pos < in.size() && !ws_flag(in[data_pos])) field_start = (int)(data_pos - current_row_start()); break; } // Case: Unescaped quote this->field_length++; data_pos++; break; } } return this->current_row_start(); } CSV_INLINE void IBasicCSVParser::push_row() { current_row.row_length = fields->size() - current_row.fields_start; this->_records->push_back(std::move(current_row)); } CSV_INLINE void IBasicCSVParser::reset_data_ptr() { this->data_ptr = std::make_shared(); this->data_ptr->parse_flags = this->_parse_flags; this->data_ptr->col_names = this->_col_names; this->fields = &(this->data_ptr->fields); } CSV_INLINE void IBasicCSVParser::trim_utf8_bom() { auto& data = this->data_ptr->data; if (!this->unicode_bom_scan && data.size() >= 3) { if (data[0] == '\xEF' && data[1] == '\xBB' && data[2] == '\xBF') { this->data_pos += 3; // Remove BOM from input string this->_utf8_bom = true; } this->unicode_bom_scan = true; } } #ifdef _MSC_VER #pragma endregion #endif #ifdef _MSC_VER #pragma region Specializations #endif CSV_INLINE void MmapParser::next(size_t bytes = ITERATION_CHUNK_SIZE) { // Reset parser state this->field_start = UNINITIALIZED_FIELD; this->field_length = 0; this->reset_data_ptr(); // Create memory map size_t length = std::min(this->source_size - this->mmap_pos, bytes); std::error_code error; this->data_ptr->_data = std::make_shared>(mio::make_mmap_source(this->_filename, this->mmap_pos, length, error)); this->mmap_pos += length; if (error) throw error; auto mmap_ptr = (mio::basic_mmap_source*)(this->data_ptr->_data.get()); // Create string view this->data_ptr->data = csv::string_view(mmap_ptr->data(), mmap_ptr->length()); // Parse this->current_row = CSVRow(this->data_ptr); size_t remainder = this->parse(); if (this->mmap_pos == this->source_size || no_chunk()) { this->_eof = true; this->end_feed(); } this->mmap_pos -= (length - remainder); } #ifdef _MSC_VER #pragma endregion #endif } } namespace csv { namespace internals { CSV_INLINE std::vector ColNames::get_col_names() const { return this->col_names; } CSV_INLINE void ColNames::set_col_names(const std::vector& cnames) { this->col_names = cnames; for (size_t i = 0; i < cnames.size(); i++) { this->col_pos[cnames[i]] = i; } } CSV_INLINE int ColNames::index_of(csv::string_view col_name) const { auto pos = this->col_pos.find(col_name.data()); if (pos != this->col_pos.end()) return (int)pos->second; return CSV_NOT_FOUND; } CSV_INLINE size_t ColNames::size() const noexcept { return this->col_names.size(); } } } /** @file * Defines an object used to store CSV format settings */ #include #include namespace csv { CSV_INLINE CSVFormat& CSVFormat::delimiter(char delim) { this->possible_delimiters = { delim }; this->assert_no_char_overlap(); return *this; } CSV_INLINE CSVFormat& CSVFormat::delimiter(const std::vector & delim) { this->possible_delimiters = delim; this->assert_no_char_overlap(); return *this; } CSV_INLINE CSVFormat& CSVFormat::quote(char quote) { this->no_quote = false; this->quote_char = quote; this->assert_no_char_overlap(); return *this; } CSV_INLINE CSVFormat& CSVFormat::trim(const std::vector & chars) { this->trim_chars = chars; this->assert_no_char_overlap(); return *this; } CSV_INLINE CSVFormat& CSVFormat::column_names(const std::vector& names) { this->col_names = names; this->header = -1; return *this; } CSV_INLINE CSVFormat& CSVFormat::header_row(int row) { if (row < 0) this->variable_column_policy = VariableColumnPolicy::KEEP; this->header = row; this->col_names = {}; return *this; } CSV_INLINE void CSVFormat::assert_no_char_overlap() { auto delims = std::set( this->possible_delimiters.begin(), this->possible_delimiters.end()), trims = std::set( this->trim_chars.begin(), this->trim_chars.end()); // Stores intersection of possible delimiters and trim characters std::vector intersection = {}; // Find which characters overlap, if any std::set_intersection( delims.begin(), delims.end(), trims.begin(), trims.end(), std::back_inserter(intersection)); // Make sure quote character is not contained in possible delimiters // or whitespace characters if (delims.find(this->quote_char) != delims.end() || trims.find(this->quote_char) != trims.end()) { intersection.push_back(this->quote_char); } if (!intersection.empty()) { std::string err_msg = "There should be no overlap between the quote character, " "the set of possible delimiters " "and the set of whitespace characters. Offending characters: "; // Create a pretty error message with the list of overlapping // characters for (size_t i = 0; i < intersection.size(); i++) { err_msg += "'"; err_msg += intersection[i]; err_msg += "'"; if (i + 1 < intersection.size()) err_msg += ", "; } throw std::runtime_error(err_msg + '.'); } } } /** @file * @brief Defines functionality needed for basic CSV parsing */ namespace csv { namespace internals { CSV_INLINE std::string format_row(const std::vector& row, csv::string_view delim) { /** Print a CSV row */ std::stringstream ret; for (size_t i = 0; i < row.size(); i++) { ret << row[i]; if (i + 1 < row.size()) ret << delim; else ret << '\n'; } ret.flush(); return ret.str(); } /** Return a CSV's column names * * @param[in] filename Path to CSV file * @param[in] format Format of the CSV file * */ CSV_INLINE std::vector _get_col_names(csv::string_view head, CSVFormat format) { // Parse the CSV auto trim_chars = format.get_trim_chars(); std::stringstream source(head.data()); RowCollection rows; StreamParser parser(source, format); parser.set_output(rows); parser.next(); return CSVRow(std::move(rows[format.get_header()])); } CSV_INLINE GuessScore calculate_score(csv::string_view head, CSVFormat format) { // Frequency counter of row length std::unordered_map row_tally = { { 0, 0 } }; // Map row lengths to row num where they first occurred std::unordered_map row_when = { { 0, 0 } }; // Parse the CSV std::stringstream source(head.data()); RowCollection rows; StreamParser parser(source, format); parser.set_output(rows); parser.next(); for (size_t i = 0; i < rows.size(); i++) { auto& row = rows[i]; // Ignore zero-length rows if (row.size() > 0) { if (row_tally.find(row.size()) != row_tally.end()) { row_tally[row.size()]++; } else { row_tally[row.size()] = 1; row_when[row.size()] = i; } } } double final_score = 0; size_t header_row = 0; // Final score is equal to the largest // row size times rows of that size for (auto& pair : row_tally) { auto row_size = pair.first; auto row_count = pair.second; double score = (double)(row_size * row_count); if (score > final_score) { final_score = score; header_row = row_when[row_size]; } } return { final_score, header_row }; } /** Guess the delimiter used by a delimiter-separated values file */ CSV_INLINE CSVGuessResult _guess_format(csv::string_view head, const std::vector& delims) { /** For each delimiter, find out which row length was most common. * The delimiter with the longest mode row length wins. * Then, the line number of the header row is the first row with * the mode row length. */ CSVFormat format; size_t max_score = 0, header = 0; char current_delim = delims[0]; for (char cand_delim : delims) { auto result = calculate_score(head, format.delimiter(cand_delim)); if ((size_t)result.score > max_score) { max_score = (size_t)result.score; current_delim = cand_delim; header = result.header; } } return { current_delim, (int)header }; } } /** Return a CSV's column names * * @param[in] filename Path to CSV file * @param[in] format Format of the CSV file * */ CSV_INLINE std::vector get_col_names(csv::string_view filename, CSVFormat format) { auto head = internals::get_csv_head(filename); /** Guess delimiter and header row */ if (format.guess_delim()) { auto guess_result = guess_format(filename, format.get_possible_delims()); format.delimiter(guess_result.delim).header_row(guess_result.header_row); } return internals::_get_col_names(head, format); } /** Guess the delimiter used by a delimiter-separated values file */ CSV_INLINE CSVGuessResult guess_format(csv::string_view filename, const std::vector& delims) { auto head = internals::get_csv_head(filename); return internals::_guess_format(head, delims); } /** Reads an arbitrarily large CSV file using memory-mapped IO. * * **Details:** Reads the first block of a CSV file synchronously to get information * such as column names and delimiting character. * * @param[in] filename Path to CSV file * @param[in] format Format of the CSV file * * \snippet tests/test_read_csv.cpp CSVField Example * */ CSV_INLINE CSVReader::CSVReader(csv::string_view filename, CSVFormat format) : _format(format) { auto head = internals::get_csv_head(filename); using Parser = internals::MmapParser; /** Guess delimiter and header row */ if (format.guess_delim()) { auto guess_result = internals::_guess_format(head, format.possible_delimiters); format.delimiter(guess_result.delim); format.header = guess_result.header_row; this->_format = format; } if (!format.col_names.empty()) this->set_col_names(format.col_names); this->parser = std::unique_ptr(new Parser(filename, format, this->col_names)); // For C++11 this->initial_read(); } /** Return the format of the original raw CSV */ CSV_INLINE CSVFormat CSVReader::get_format() const { CSVFormat new_format = this->_format; // Since users are normally not allowed to set // column names and header row simulatenously, // we will set the backing variables directly here new_format.col_names = this->col_names->get_col_names(); new_format.header = this->_format.header; return new_format; } /** Return the CSV's column names as a vector of strings. */ CSV_INLINE std::vector CSVReader::get_col_names() const { if (this->col_names) { return this->col_names->get_col_names(); } return std::vector(); } /** Return the index of the column name if found or * csv::CSV_NOT_FOUND otherwise. */ CSV_INLINE int CSVReader::index_of(csv::string_view col_name) const { auto _col_names = this->get_col_names(); for (size_t i = 0; i < _col_names.size(); i++) if (_col_names[i] == col_name) return (int)i; return CSV_NOT_FOUND; } CSV_INLINE void CSVReader::trim_header() { if (!this->header_trimmed) { for (int i = 0; i <= this->_format.header && !this->records->empty(); i++) { if (i == this->_format.header && this->col_names->empty()) { this->set_col_names(this->records->pop_front()); } else { this->records->pop_front(); } } this->header_trimmed = true; } } /** * @param[in] names Column names */ CSV_INLINE void CSVReader::set_col_names(const std::vector& names) { this->col_names->set_col_names(names); this->n_cols = names.size(); } /** * Read a chunk of CSV data. * * @note This method is meant to be run on its own thread. Only one `read_csv()` thread * should be active at a time. * * @param[in] bytes Number of bytes to read. * * @see CSVReader::read_csv_worker * @see CSVReader::read_row() */ CSV_INLINE bool CSVReader::read_csv(size_t bytes) { // Tell read_row() to listen for CSV rows this->records->notify_all(); this->parser->set_output(*this->records); this->parser->next(bytes); if (!this->header_trimmed) { this->trim_header(); } // Tell read_row() to stop waiting this->records->kill_all(); return true; } /** * Retrieve rows as CSVRow objects, returning true if more rows are available. * * @par Performance Notes * - Reads chunks of data that are csv::internals::ITERATION_CHUNK_SIZE bytes large at a time * - For performance details, read the documentation for CSVRow and CSVField. * * @param[out] row The variable where the parsed row will be stored * @see CSVRow, CSVField * * **Example:** * \snippet tests/test_read_csv.cpp CSVField Example * */ CSV_INLINE bool CSVReader::read_row(CSVRow &row) { while (true) { if (this->records->empty()) { if (this->records->is_waitable()) // Reading thread is currently active => wait for it to populate records this->records->wait(); else if (this->parser->eof()) // End of file and no more records return false; else { // Reading thread is not active => start another one if (this->read_csv_worker.joinable()) this->read_csv_worker.join(); this->read_csv_worker = std::thread(&CSVReader::read_csv, this, internals::ITERATION_CHUNK_SIZE); } } else if (this->records->front().size() != this->n_cols && this->_format.variable_column_policy != VariableColumnPolicy::KEEP) { auto errored_row = this->records->pop_front(); if (this->_format.variable_column_policy == VariableColumnPolicy::THROW) { if (errored_row.size() < this->n_cols) throw std::runtime_error("Line too short " + internals::format_row(errored_row)); throw std::runtime_error("Line too long " + internals::format_row(errored_row)); } } else { row = this->records->pop_front(); this->_n_rows++; return true; } } return false; } } /** @file * Defines an input iterator for csv::CSVReader */ namespace csv { /** Return an iterator to the first row in the reader */ CSV_INLINE CSVReader::iterator CSVReader::begin() { if (this->records->empty()) { this->read_csv_worker = std::thread(&CSVReader::read_csv, this, internals::ITERATION_CHUNK_SIZE); this->read_csv_worker.join(); // Still empty => return end iterator if (this->records->empty()) return this->end(); } CSVReader::iterator ret(this, this->records->pop_front()); return ret; } /** A placeholder for the imaginary past the end row in a CSV. * Attempting to deference this will lead to bad things. */ CSV_INLINE HEDLEY_CONST CSVReader::iterator CSVReader::end() const noexcept { return CSVReader::iterator(); } ///////////////////////// // CSVReader::iterator // ///////////////////////// CSV_INLINE CSVReader::iterator::iterator(CSVReader* _daddy, CSVRow&& _row) : daddy(_daddy) { row = std::move(_row); } /** Advance the iterator by one row. If this CSVReader has an * associated file, then the iterator will lazily pull more data from * that file until the end of file is reached. * * @note This iterator does **not** block the thread responsible for parsing CSV. * */ CSV_INLINE CSVReader::iterator& CSVReader::iterator::operator++() { if (!daddy->read_row(this->row)) { this->daddy = nullptr; // this == end() } return *this; } /** Post-increment iterator */ CSV_INLINE CSVReader::iterator CSVReader::iterator::operator++(int) { auto temp = *this; if (!daddy->read_row(this->row)) { this->daddy = nullptr; // this == end() } return temp; } } /** @file * Defines the data type used for storing information about a CSV row */ #include #include namespace csv { namespace internals { CSV_INLINE RawCSVField& CSVFieldList::operator[](size_t n) const { const size_t page_no = n / _single_buffer_capacity; const size_t buffer_idx = (page_no < 1) ? n : n % _single_buffer_capacity; return this->buffers[page_no][buffer_idx]; } CSV_INLINE void CSVFieldList::allocate() { RawCSVField * buffer = new RawCSVField[_single_buffer_capacity]; buffers.push_back(buffer); _current_buffer_size = 0; _back = &(buffers.back()[0]); } } /** Return a CSVField object corrsponding to the nth value in the row. * * @note This method performs bounds checking, and will throw an * `std::runtime_error` if n is invalid. * * @complexity * Constant, by calling csv::CSVRow::get_csv::string_view() * */ CSV_INLINE CSVField CSVRow::operator[](size_t n) const { return CSVField(this->get_field(n)); } /** Retrieve a value by its associated column name. If the column * specified can't be round, a runtime error is thrown. * * @complexity * Constant. This calls the other CSVRow::operator[]() after * converting column names into indices using a hash table. * * @param[in] col_name The column to look for */ CSV_INLINE CSVField CSVRow::operator[](const std::string& col_name) const { auto & col_names = this->data->col_names; auto col_pos = col_names->index_of(col_name); if (col_pos > -1) { return this->operator[](col_pos); } throw std::runtime_error("Can't find a column named " + col_name); } CSV_INLINE CSVRow::operator std::vector() const { std::vector ret; for (size_t i = 0; i < size(); i++) ret.push_back(std::string(this->get_field(i))); return ret; } CSV_INLINE csv::string_view CSVRow::get_field(size_t index) const { using internals::ParseFlags; if (index >= this->size()) throw std::runtime_error("Index out of bounds."); const size_t field_index = this->fields_start + index; auto& field = this->data->fields[field_index]; auto field_str = csv::string_view(this->data->data).substr(this->data_start + field.start); if (field.has_double_quote) { auto& value = this->data->double_quote_fields[field_index]; if (value.empty()) { bool prev_ch_quote = false; for (size_t i = 0; i < field.length; i++) { if (this->data->parse_flags[field_str[i] + 128] == ParseFlags::QUOTE) { if (prev_ch_quote) { prev_ch_quote = false; continue; } else { prev_ch_quote = true; } } value += field_str[i]; } } return csv::string_view(value); } return field_str.substr(0, field.length); } CSV_INLINE bool CSVField::try_parse_hex(int& parsedValue) { size_t start = 0, end = 0; // Trim out whitespace chars for (; start < this->sv.size() && this->sv[start] == ' '; start++); for (end = start; end < this->sv.size() && this->sv[end] != ' '; end++); unsigned long long int value = 0; size_t digits = (end - start); size_t base16_exponent = digits - 1; if (digits == 0) return false; for (const auto& ch : this->sv.substr(start, digits)) { int digit = 0; switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': digit = static_cast(ch - '0'); break; case 'a': case 'A': digit = 10; break; case 'b': case 'B': digit = 11; break; case 'c': case 'C': digit = 12; break; case 'd': case 'D': digit = 13; break; case 'e': case 'E': digit = 14; break; case 'f': case 'F': digit = 15; break; default: return false; } value += digit * pow(16, base16_exponent); base16_exponent--; } parsedValue = value; return true; } #ifdef _MSC_VER #pragma region CSVRow Iterator #endif /** Return an iterator pointing to the first field. */ CSV_INLINE CSVRow::iterator CSVRow::begin() const { return CSVRow::iterator(this, 0); } /** Return an iterator pointing to just after the end of the CSVRow. * * @warning Attempting to dereference the end iterator results * in dereferencing a null pointer. */ CSV_INLINE CSVRow::iterator CSVRow::end() const noexcept { return CSVRow::iterator(this, (int)this->size()); } CSV_INLINE CSVRow::reverse_iterator CSVRow::rbegin() const noexcept { return std::reverse_iterator(this->end()); } CSV_INLINE CSVRow::reverse_iterator CSVRow::rend() const { return std::reverse_iterator(this->begin()); } CSV_INLINE HEDLEY_NON_NULL(2) CSVRow::iterator::iterator(const CSVRow* _reader, int _i) : daddy(_reader), i(_i) { if (_i < (int)this->daddy->size()) this->field = std::make_shared( this->daddy->operator[](_i)); else this->field = nullptr; } CSV_INLINE CSVRow::iterator::reference CSVRow::iterator::operator*() const { return *(this->field.get()); } CSV_INLINE CSVRow::iterator::pointer CSVRow::iterator::operator->() const { // Using CSVField * as pointer type causes segfaults in MSVC debug builds #ifdef _MSC_BUILD return this->field; #else return this->field.get(); #endif } CSV_INLINE CSVRow::iterator& CSVRow::iterator::operator++() { // Pre-increment operator this->i++; if (this->i < (int)this->daddy->size()) this->field = std::make_shared( this->daddy->operator[](i)); else // Reached the end of row this->field = nullptr; return *this; } CSV_INLINE CSVRow::iterator CSVRow::iterator::operator++(int) { // Post-increment operator auto temp = *this; this->operator++(); return temp; } CSV_INLINE CSVRow::iterator& CSVRow::iterator::operator--() { // Pre-decrement operator this->i--; this->field = std::make_shared( this->daddy->operator[](this->i)); return *this; } CSV_INLINE CSVRow::iterator CSVRow::iterator::operator--(int) { // Post-decrement operator auto temp = *this; this->operator--(); return temp; } CSV_INLINE CSVRow::iterator CSVRow::iterator::operator+(difference_type n) const { // Allows for iterator arithmetic return CSVRow::iterator(this->daddy, i + (int)n); } CSV_INLINE CSVRow::iterator CSVRow::iterator::operator-(difference_type n) const { // Allows for iterator arithmetic return CSVRow::iterator::operator+(-n); } #ifdef _MSC_VER #pragma endregion CSVRow Iterator #endif } /** @file * Implements JSON serialization abilities */ namespace csv { /* The implementations for json_extra_space() and json_escape_string() were modified from source code for JSON for Modern C++. The respective license is below: The code is licensed under the [MIT License](http://opensource.org/licenses/MIT): Copyright © 2013-2015 Niels Lohmann. 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. */ namespace internals { /*! @brief calculates the extra space to escape a JSON string @param[in] s the string to escape @return the number of characters required to escape string @a s @complexity Linear in the length of string @a s. */ static std::size_t json_extra_space(csv::string_view& s) noexcept { std::size_t result = 0; for (const auto& c : s) { switch (c) { case '"': case '\\': case '\b': case '\f': case '\n': case '\r': case '\t': { // from c (1 byte) to \x (2 bytes) result += 1; break; } default: { if (c >= 0x00 && c <= 0x1f) { // from c (1 byte) to \uxxxx (6 bytes) result += 5; } break; } } } return result; } CSV_INLINE std::string json_escape_string(csv::string_view s) noexcept { const auto space = json_extra_space(s); if (space == 0) { return std::string(s); } // create a result string of necessary size std::string result(s.size() + space, '\\'); std::size_t pos = 0; for (const auto& c : s) { switch (c) { // quotation mark (0x22) case '"': { result[pos + 1] = '"'; pos += 2; break; } // reverse solidus (0x5c) case '\\': { // nothing to change pos += 2; break; } // backspace (0x08) case '\b': { result[pos + 1] = 'b'; pos += 2; break; } // formfeed (0x0c) case '\f': { result[pos + 1] = 'f'; pos += 2; break; } // newline (0x0a) case '\n': { result[pos + 1] = 'n'; pos += 2; break; } // carriage return (0x0d) case '\r': { result[pos + 1] = 'r'; pos += 2; break; } // horizontal tab (0x09) case '\t': { result[pos + 1] = 't'; pos += 2; break; } default: { if (c >= 0x00 && c <= 0x1f) { // print character c as \uxxxx sprintf(&result[pos + 1], "u%04x", int(c)); pos += 6; // overwrite trailing null character result[pos] = '\\'; } else { // all other characters are added as-is result[pos++] = c; } break; } } } return result; } } /** Convert a CSV row to a JSON object, i.e. * `{"col1":"value1","col2":"value2"}` * * @note All strings are properly escaped. Numeric values are not quoted. * @param[in] subset A subset of columns to contain in the JSON. * Leave empty for original columns. */ CSV_INLINE std::string CSVRow::to_json(const std::vector& subset) const { std::vector col_names = subset; if (subset.empty()) { col_names = this->data ? this->get_col_names() : std::vector({}); } const size_t _n_cols = col_names.size(); std::string ret = "{"; for (size_t i = 0; i < _n_cols; i++) { auto& col = col_names[i]; auto field = this->operator[](col); // TODO: Possible performance enhancements by caching escaped column names ret += '"' + internals::json_escape_string(col) + "\":"; // Add quotes around strings but not numbers if (field.is_num()) ret += internals::json_escape_string(field.get()); else ret += '"' + internals::json_escape_string(field.get()) + '"'; // Do not add comma after last string if (i + 1 < _n_cols) ret += ','; } ret += '}'; return ret; } /** Convert a CSV row to a JSON array, i.e. * `["value1","value2",...]` * * @note All strings are properly escaped. Numeric values are not quoted. * @param[in] subset A subset of columns to contain in the JSON. * Leave empty for all columns. */ CSV_INLINE std::string CSVRow::to_json_array(const std::vector& subset) const { std::vector col_names = subset; if (subset.empty()) col_names = this->data ? this->get_col_names() : std::vector({}); const size_t _n_cols = col_names.size(); std::string ret = "["; for (size_t i = 0; i < _n_cols; i++) { auto field = this->operator[](col_names[i]); // Add quotes around strings but not numbers if (field.is_num()) ret += internals::json_escape_string(field.get()); else ret += '"' + internals::json_escape_string(field.get()) + '"'; // Do not add comma after last string if (i + 1 < _n_cols) ret += ','; } ret += ']'; return ret; } } /** @file * Calculates statistics from CSV files */ #include namespace csv { /** Calculate statistics for an arbitrarily large file. When this constructor * is called, CSVStat will process the entire file iteratively. Once finished, * methods like get_mean(), get_counts(), etc... can be used to retrieve statistics. */ CSV_INLINE CSVStat::CSVStat(csv::string_view filename, CSVFormat format) : reader(filename, format) { this->calc(); } /** Calculate statistics for a CSV stored in a std::stringstream */ CSV_INLINE CSVStat::CSVStat(std::stringstream& stream, CSVFormat format) : reader(stream, format) { this->calc(); } /** Return current means */ CSV_INLINE std::vector CSVStat::get_mean() const { std::vector ret; for (size_t i = 0; i < this->get_col_names().size(); i++) { ret.push_back(this->rolling_means[i]); } return ret; } /** Return current variances */ CSV_INLINE std::vector CSVStat::get_variance() const { std::vector ret; for (size_t i = 0; i < this->get_col_names().size(); i++) { ret.push_back(this->rolling_vars[i]/(this->n[i] - 1)); } return ret; } /** Return current mins */ CSV_INLINE std::vector CSVStat::get_mins() const { std::vector ret; for (size_t i = 0; i < this->get_col_names().size(); i++) { ret.push_back(this->mins[i]); } return ret; } /** Return current maxes */ CSV_INLINE std::vector CSVStat::get_maxes() const { std::vector ret; for (size_t i = 0; i < this->get_col_names().size(); i++) { ret.push_back(this->maxes[i]); } return ret; } /** Get counts for each column */ CSV_INLINE std::vector CSVStat::get_counts() const { std::vector ret; for (size_t i = 0; i < this->get_col_names().size(); i++) { ret.push_back(this->counts[i]); } return ret; } /** Get data type counts for each column */ CSV_INLINE std::vector CSVStat::get_dtypes() const { std::vector ret; for (size_t i = 0; i < this->get_col_names().size(); i++) { ret.push_back(this->dtypes[i]); } return ret; } CSV_INLINE void CSVStat::calc_chunk() { /** Only create stats counters the first time **/ if (dtypes.empty()) { /** Go through all records and calculate specified statistics */ for (size_t i = 0; i < this->get_col_names().size(); i++) { dtypes.push_back({}); counts.push_back({}); rolling_means.push_back(0); rolling_vars.push_back(0); mins.push_back(NAN); maxes.push_back(NAN); n.push_back(0); } } // Start threads std::vector pool; for (size_t i = 0; i < this->get_col_names().size(); i++) pool.push_back(std::thread(&CSVStat::calc_worker, this, i)); // Block until done for (auto& th : pool) th.join(); this->records.clear(); } CSV_INLINE void CSVStat::calc() { constexpr size_t CALC_CHUNK_SIZE = 5000; for (auto& row : reader) { this->records.push_back(std::move(row)); /** Chunk rows */ if (this->records.size() == CALC_CHUNK_SIZE) { calc_chunk(); } } if (!this->records.empty()) { calc_chunk(); } } CSV_INLINE void CSVStat::calc_worker(const size_t &i) { /** Worker thread for CSVStat::calc() which calculates statistics for one column. * * @param[in] i Column index */ auto current_record = this->records.begin(); for (size_t processed = 0; current_record != this->records.end(); processed++) { if (current_record->size() == this->get_col_names().size()) { auto current_field = (*current_record)[i]; // Optimization: Don't count() if there's too many distinct values in the first 1000 rows if (processed < 1000 || this->counts[i].size() <= 500) this->count(current_field, i); this->dtype(current_field, i); // Numeric Stuff if (current_field.is_num()) { long double x_n = current_field.get(); // This actually calculates mean AND variance this->variance(x_n, i); this->min_max(x_n, i); } } else if (this->reader.get_format().get_variable_column_policy() == VariableColumnPolicy::THROW) { throw std::runtime_error("Line has different length than the others " + internals::format_row(*current_record)); } ++current_record; } } CSV_INLINE void CSVStat::dtype(CSVField& data, const size_t &i) { /** Given a record update the type counter * @param[in] record Data observation * @param[out] i The column index that should be updated */ auto type = data.type(); if (this->dtypes[i].find(type) != this->dtypes[i].end()) { // Increment count this->dtypes[i][type]++; } else { // Initialize count this->dtypes[i].insert(std::make_pair(type, 1)); } } CSV_INLINE void CSVStat::count(CSVField& data, const size_t &i) { /** Given a record update the frequency counter * @param[in] record Data observation * @param[out] i The column index that should be updated */ auto item = data.get(); if (this->counts[i].find(item) != this->counts[i].end()) { // Increment count this->counts[i][item]++; } else { // Initialize count this->counts[i].insert(std::make_pair(item, 1)); } } CSV_INLINE void CSVStat::min_max(const long double &x_n, const size_t &i) { /** Update current minimum and maximum * @param[in] x_n Data observation * @param[out] i The column index that should be updated */ if (std::isnan(this->mins[i])) this->mins[i] = x_n; if (std::isnan(this->maxes[i])) this->maxes[i] = x_n; if (x_n < this->mins[i]) this->mins[i] = x_n; else if (x_n > this->maxes[i]) this->maxes[i] = x_n; } CSV_INLINE void CSVStat::variance(const long double &x_n, const size_t &i) { /** Given a record update rolling mean and variance for all columns * using Welford's Algorithm * @param[in] x_n Data observation * @param[out] i The column index that should be updated */ long double& current_rolling_mean = this->rolling_means[i]; long double& current_rolling_var = this->rolling_vars[i]; long double& current_n = this->n[i]; long double delta; long double delta2; current_n++; if (current_n == 1) { current_rolling_mean = x_n; } else { delta = x_n - current_rolling_mean; current_rolling_mean += delta/current_n; delta2 = x_n - current_rolling_mean; current_rolling_var += delta*delta2; } } /** Useful for uploading CSV files to SQL databases. * * Return a data type for each column such that every value in a column can be * converted to the corresponding data type without data loss. * @param[in] filename The CSV file * * \return A mapping of column names to csv::DataType enums */ CSV_INLINE std::unordered_map csv_data_types(const std::string& filename) { CSVStat stat(filename); std::unordered_map csv_dtypes; auto col_names = stat.get_col_names(); auto temp = stat.get_dtypes(); for (size_t i = 0; i < stat.get_col_names().size(); i++) { auto& col = temp[i]; auto& col_name = col_names[i]; if (col[DataType::CSV_STRING]) csv_dtypes[col_name] = DataType::CSV_STRING; else if (col[DataType::CSV_INT64]) csv_dtypes[col_name] = DataType::CSV_INT64; else if (col[DataType::CSV_INT32]) csv_dtypes[col_name] = DataType::CSV_INT32; else if (col[DataType::CSV_INT16]) csv_dtypes[col_name] = DataType::CSV_INT16; else if (col[DataType::CSV_INT8]) csv_dtypes[col_name] = DataType::CSV_INT8; else csv_dtypes[col_name] = DataType::CSV_DOUBLE; } return csv_dtypes; } } #include #include namespace csv { /** Shorthand function for parsing an in-memory CSV string * * @return A collection of CSVRow objects * * @par Example * @snippet tests/test_read_csv.cpp Parse Example */ CSV_INLINE CSVReader parse(csv::string_view in, CSVFormat format) { std::stringstream stream(in.data()); return CSVReader(stream, format); } /** Parses a CSV string with no headers * * @return A collection of CSVRow objects */ CSV_INLINE CSVReader parse_no_header(csv::string_view in) { CSVFormat format; format.header_row(-1); return parse(in, format); } /** Parse a RFC 4180 CSV string, returning a collection * of CSVRow objects * * @par Example * @snippet tests/test_read_csv.cpp Escaped Comma * */ CSV_INLINE CSVReader operator ""_csv(const char* in, size_t n) { return parse(csv::string_view(in, n)); } /** A shorthand for csv::parse_no_header() */ CSV_INLINE CSVReader operator ""_csv_no_header(const char* in, size_t n) { return parse_no_header(csv::string_view(in, n)); } /** * Find the position of a column in a CSV file or CSV_NOT_FOUND otherwise * * @param[in] filename Path to CSV file * @param[in] col_name Column whose position we should resolve * @param[in] format Format of the CSV file */ CSV_INLINE int get_col_pos( csv::string_view filename, csv::string_view col_name, const CSVFormat& format) { CSVReader reader(filename, format); return reader.index_of(col_name); } /** Get basic information about a CSV file * @include programs/csv_info.cpp */ CSV_INLINE CSVFileInfo get_file_info(const std::string& filename) { CSVReader reader(filename); CSVFormat format = reader.get_format(); for (auto it = reader.begin(); it != reader.end(); ++it); CSVFileInfo info = { filename, reader.get_col_names(), format.get_delim(), reader.n_rows(), reader.get_col_names().size() }; return info; } } #endif ================================================ FILE: src/cli/lib/DRAMPower/cli/run.cpp ================================================ #include "run.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "csv.hpp" #include "util.hpp" #include "config.h" namespace DRAMPower::DRAMPowerCLI { using namespace DRAMPower; std::unique_ptr> getMemory(const std::string_view &data, const DRAMPower::config::SimConfig& simconfig) { try { std::unique_ptr> result = nullptr; // Get memspec auto memspec = DRAMUtils::parse_memspec_from_file(std::filesystem::path(data)); if (!memspec) { return result; } // Get ddr std::visit( [&result, &simconfig] (auto&& arg) { using T = std::decay_t; if constexpr (std::is_same_v) { MemSpecDDR4 ddr (static_cast(arg)); result = std::make_unique(ddr, simconfig); } else if constexpr (std::is_same_v) { MemSpecDDR5 ddr (static_cast(arg)); result = std::make_unique(ddr, simconfig); } else if constexpr (std::is_same_v) { MemSpecLPDDR4 ddr (static_cast(arg)); result = std::make_unique(ddr, simconfig); } else if constexpr (std::is_same_v) { MemSpecLPDDR5 ddr (static_cast(arg)); result = std::make_unique(ddr, simconfig); } }, memspec->getVariant()); return result; } catch(const std::exception& e) { return nullptr; } } bool parse_command_list(std::string_view csv_file, std::vector>> &commandList) { // Read csv file csv::CSVFormat format; format.no_header(); format.trim({ ' ', '\t' }); csv::CSVReader reader{ csv_file, format }; // loop variables uint64_t rowcounter = 0; std::size_t rowidx, size, rank_id, bank_group_id, bank_id, row_id, column_id = 0; timestamp_t timestamp = 0; csv::string_view cmdType; std::unordered_map::iterator cmdit; CmdType cmd; // Parse csv file // timestamp, command, rank, bank_group, bank, row, column, [data] constexpr std::size_t MINCSVSIZE = 7; for ( csv::CSVRow& row : reader ) { rowidx = 0; // Read csv row if ( row.size() < MINCSVSIZE ) { return false; } timestamp = row[rowidx++].get(); cmdType = row[rowidx++].get_sv(); rank_id = row[rowidx++].get(); bank_group_id = row[rowidx++].get(); bank_id = row[rowidx++].get(); row_id = row[rowidx++].get(); column_id = row[rowidx++].get(); // Get command cmd = DRAMPower::CmdTypeUtil::from_string(cmdType); // Get data if needed if ( DRAMPower::CmdTypeUtil::needs_data(cmd) ) { if ( row.size() < MINCSVSIZE + 1 ) { return false; } // uint64_t length = 0; csv::string_view data = row[rowidx++].get_sv(); std::unique_ptr arr; try { arr = util::hexStringToUint8Array(data, size); } catch (std::exception &e) { return false; } commandList.emplace_back(Command{ timestamp, cmd, { bank_id, bank_group_id, rank_id, row_id, column_id}, arr.get(), size * 8}, std::move(arr)); } else { commandList.emplace_back(Command{ timestamp, cmd, { bank_id, bank_group_id, rank_id, row_id, column_id} }, nullptr); } // Increment row counter ++rowcounter; } return true; }; bool jsonFileResult(const std::string &jsonfile, const std::unique_ptr> &ddr, const energy_t &core_energy, const interface_energy_info_t &interface_energy) { std::ofstream out; out = std::ofstream(jsonfile); if ( !out.is_open() ) { return false; } json_t j; size_t energy_offset = 0; DRAMPower::util::CLIArchitectureConfig cli_config = ddr->getCLIArchitectureConfig(); auto bankcount = cli_config.bankCount; auto rankcount = cli_config.rankCount; auto devicecount = cli_config.deviceCount; j["RankCount"] = rankcount; j["DeviceCount"] = devicecount; j["BankCount"] = bankcount; j["TotalEnergy"] = core_energy.total() + interface_energy.total(); // Energy object to json core_energy.to_json(j["CoreEnergy"]); interface_energy.to_json(j["InterfaceEnergy"]); // Validate array length if ( !j["CoreEnergy"][core_energy.get_Bank_energy_keyword()].is_array() || j["CoreEnergy"][core_energy.get_Bank_energy_keyword()].size() != rankcount * bankcount * devicecount ) { assert(false); // (should not happen) return false; } // Add rank,device,bank description for ( std::size_t r = 0; r < rankcount; r++ ) { for ( std::size_t d = 0; d < devicecount; d++ ) { energy_offset = r * bankcount * devicecount + d * bankcount; for ( std::size_t b = 0; b < bankcount; b++ ) { // Rank,Device,Bank -> bank_energy j["CoreEnergy"][core_energy.get_Bank_energy_keyword()].at(energy_offset + b)["Rank"] = r; j["CoreEnergy"][core_energy.get_Bank_energy_keyword()].at(energy_offset + b)["Device"] = d; j["CoreEnergy"][core_energy.get_Bank_energy_keyword()].at(energy_offset + b)["Bank"] = b; } } } out << j.dump(4) << std::endl; out.close(); return true; } bool stdoutResult(const std::unique_ptr> &ddr, const energy_t &core_energy, const interface_energy_info_t &interface_energy) { // Setup output format std::cout << std::defaultfloat << std::setprecision(3); // Print stats DRAMPower::util::CLIArchitectureConfig cli_config = ddr->getCLIArchitectureConfig(); auto bankcount = cli_config.bankCount; auto rankcount = cli_config.rankCount; auto devicecount = cli_config.deviceCount; size_t energy_offset = 0; // NOTE: ensure the same order of calculation in the interface spdlog::info("Rank,Device,Bank -> bank_energy:"); for ( std::size_t r = 0; r < rankcount; r++ ) { for ( std::size_t d = 0; d < devicecount; d++ ) { energy_offset = r * bankcount * devicecount + d * bankcount; for ( std::size_t b = 0; b < bankcount; b++ ) { // Rank,Device,Bank -> bank_energy spdlog::info("{},{},{} -> {}", r, d, b, core_energy.bank_energy[energy_offset + b] ); } } } spdlog::info("Cumulated bank energy with bg_act_shared -> {}", core_energy.aggregated_bank_energy()); spdlog::info("Shared energy -> {}", core_energy); spdlog::info("Interface Energy:\n{}", interface_energy); spdlog::info("Total Energy -> {}", core_energy.total() + interface_energy.total()); return true; } bool getConfig(const std::string &configfile, config::CLIConfig &config) { try { std::ifstream file(configfile); if (!file.is_open()) { return false; } json_t json_obj = json_t::parse(file, nullptr, false, true); config = json_obj; if (!config.useToggleRate) { config.simconfig.toggleRateDefinition = std::nullopt; } if (config.useToggleRate && !config.simconfig.toggleRateDefinition.has_value()) { spdlog::error("Provide a toggleRateDefinition for a simulation with toggling rates"); return false; } } catch (std::exception&) { return false; } return true; } bool makeResult(std::optional jsonfile, const std::unique_ptr> &ddr) { energy_t core_energy = ddr->calcCoreEnergy(ddr->getLastCommandTime()); interface_energy_info_t interface_energy = ddr->calcInterfaceEnergy(ddr->getLastCommandTime()); if(jsonfile) { return jsonFileResult(*jsonfile, std::move(ddr), core_energy, interface_energy); } else { return stdoutResult(std::move(ddr), core_energy, interface_energy); } } bool runCommands(std::unique_ptr> &ddr, const std::vector>> &commandList) { try { for (auto &command : commandList ) { ddr->doCommand(command.first); } } catch (std::exception &e) { return false; } return true; } } // namespace DRAMPower::DRAMPowerCLI ================================================ FILE: src/cli/lib/DRAMPower/cli/run.hpp ================================================ #ifndef LIB_DRAMPOWERCLI_RUN_H #define LIB_DRAMPOWERCLI_RUN_H #include #include #include #include #include #include #include #include #include #include "config.h" namespace DRAMPower::DRAMPowerCLI { std::unique_ptr> getMemory(const std::string_view &data, const DRAMPower::config::SimConfig& simconfig); bool parse_command_list(std::string_view csv_file, std::vector>> &commandList); bool makeResult(std::optional jsonfile, const std::unique_ptr> &ddr); bool jsonFileResult(const std::string &jsonfile, const std::unique_ptr> &ddr, const energy_t &core_energy, const interface_energy_info_t &interface_energy); bool stdoutResult(const std::unique_ptr> &ddr, const energy_t &core_energy, const interface_energy_info_t &interface_energy); bool getConfig(const std::string &configfile, config::CLIConfig &config); bool runCommands(std::unique_ptr> &ddr, const std::vector>> &commandList); } // namespace DRAMPower::DRAMPowerCLI #endif /* LIB_DRAMPOWERCLI_RUN_H */ ================================================ FILE: src/cli/lib/DRAMPower/cli/util.cpp ================================================ #include "util.hpp" namespace DRAMPower::DRAMPowerCLI { std::unique_ptr util::hexStringToUint8Array(const csv::string_view data, size_t &size) { // Check if the string has valid length if ( ( data.length() % 2 ) != 0) { throw std::invalid_argument("Invalid hex string length"); } // String conversion std::string hexString; size = data.length() / 2; // Remove 0x or 0X prefix if present if (data.substr(0, 2) == "0x" || data.substr(0, 2) == "0X") { size--; hexString = data.substr(2); } else { hexString = data; } // Allocate memory for the array and fill it auto content = std::make_unique(size); for (size_t i = 0; i < size; i++) { content[i] = static_cast(std::stoi(hexString.substr(i * 2, 2), nullptr, 16)); } return content; } } // namespace DRAMPower::DRAMPowerCLI ================================================ FILE: src/cli/lib/DRAMPower/cli/util.hpp ================================================ #ifndef UTIL_HPP #define UTIL_HPP #include #include "csv.hpp" namespace DRAMPower::DRAMPowerCLI::util { // Util function to get the memory std::unique_ptr hexStringToUint8Array(const csv::string_view data, size_t &size); } // namespace DRAMPower::DRAMPowerCLI::util #endif ================================================ FILE: src/cli/main/CMakeLists.txt ================================================ ############################### ### cli ### ############################### find_package(spdlog REQUIRED) find_package(cli11 REQUIRED) add_executable(cli main.cpp validators.cpp ) target_compile_features(cli PUBLIC cxx_std_17) set_target_properties(cli PROPERTIES CXX_EXTENSIONS OFF) set_target_properties(cli PROPERTIES CXX_STANDARD_REQUIRED ON) target_link_libraries(cli PRIVATE DRAMPower::cli_lib DRAMPower::DRAMPower spdlog::spdlog CLI11::CLI11 ) target_compile_definitions(cli PRIVATE DRAMPOWER_VERSION_STRING="${DRAMPOWER_VERSION_STRING}") ================================================ FILE: src/cli/main/main.cpp ================================================ #include #include #include #include #include #include #include #include #include #include "validators.h" namespace cli11 = ::CLI; using namespace DRAMPower; int parseArgs(int argc, char *argv[], std::string &configfile, std::string &tracefile, std::string &memspec, std::optional &jsonfile) { // Application description cli11::App app{"DRAMPower v" DRAMPOWER_VERSION_STRING}; argv = app.ensure_utf8(argv); // Configfile app.add_option("-c,--config", configfile, "config") ->required(true) ->check(cli11::ExistingFile); // Tracefile app.add_option("-t,--trace", tracefile, "csv trace file") ->required(true) ->check(cli11::ExistingFile); // Memspec app.add_option("-m,--memspec", memspec, "json memspec file") ->required(true) ->check(cli11::ExistingFile); // JSON output file app.add_option("-j,--json", jsonfile, "json output file path") ->required(false) ->check(validators::EnsureFileExists); // Parse arguments try { app.parse(argc, argv); } catch(const cli11::ParseError &e) { return app.exit(e); } return 0; } int main(int argc, char *argv[]) { // Options std::string configfile; std::string tracefile; std::string memspec; std::optional jsonfile = std::nullopt; int res = parseArgs(argc, argv, configfile, tracefile, memspec, jsonfile); if(res != 0) { return res; } // Set spdlog pattern spdlog::set_pattern("%v"); // Read config DRAMPower::DRAMPowerCLI::config::CLIConfig config; if (!DRAMPower::DRAMPowerCLI::getConfig(configfile, config)) { spdlog::info("Invalid config file"); return 1; } // Parse command list (load command list in memory) std::vector>> commandList; if(!DRAMPower::DRAMPowerCLI::parse_command_list(tracefile, commandList)) { spdlog::error("Error while parsing command list. Exiting application"); return 1; } // Initialize memory / Create memory object std::unique_ptr> ddr = DRAMPower::DRAMPowerCLI::getMemory(std::string_view(memspec), config.simconfig); if (!ddr) { spdlog::error("Invalid memory specification"); return 1; } // Execute commands if(!DRAMPower::DRAMPowerCLI::runCommands(ddr, commandList)) { spdlog::error("Error while running commands. Exiting application"); return 1; } // Calculate energy and stats if(!DRAMPower::DRAMPowerCLI::makeResult(jsonfile, std::move(ddr))) { spdlog::error("Error while creating result. Exiting application"); return 1; } return 0; }; ================================================ FILE: src/cli/main/validators.cpp ================================================ #include "validators.h" #include #include #include #include namespace validators { struct ResolveSymlinkResult { enum class Type { FILE, DIRECTORY, // included for future default filename creation NOT_FOUND // File does not exist but a parent directory could exist }; Type type; std::string path; }; std::optional resolve_symlink(const std::string &filepath, const uint_fast8_t max_iter) { // Check if path exists and is a symlink if (!std::filesystem::is_symlink(filepath)) { // File does not exist or is not a symlink return std::nullopt; } // The path exists and is a symlink -> resolve the symlink chain std::filesystem::path resolved = filepath; for (uint_fast8_t iter = 0; iter < max_iter; ++iter) { resolved = std::filesystem::read_symlink(resolved); // Check if the provided path exists if (!std::filesystem::exists(resolved)) { // symlink resolved to a non-existing path return std::make_optional(ResolveSymlinkResult{ ResolveSymlinkResult::Type::NOT_FOUND, // type resolved.string() // path }); } else if (std::filesystem::is_regular_file(resolved)) { // symlink resolved to a file return std::make_optional(ResolveSymlinkResult{ ResolveSymlinkResult::Type::FILE, // type resolved.string() // path }); } else if (std::filesystem::is_directory(resolved)) { // symlink resolved to a non-file return std::make_optional(ResolveSymlinkResult{ ResolveSymlinkResult::Type::DIRECTORY, // type resolved.string() // path }); } else if (std::filesystem::is_symlink(resolved)) { // symlink resolved to another symlink // continue to next iteration } else { // symlink resolved to an unknown type return std::nullopt; } } // The symlink chain was not resolved in the maximum number of iterations return std::nullopt; } // This function assumes the filepath does not exist std::string createFileInParentDirectory(const std::filesystem::path& filepath) { // Check parent directory exists std::filesystem::path path = filepath.parent_path(); // No path directory provided -> create the file in current directory if (path.empty() && filepath.is_relative()) { path = std::filesystem::current_path(); } if (!std::filesystem::exists(path)) { // parent directory does not exist return std::string{"Parent directory does not exist"}; } else if (!std::filesystem::is_directory(path)) { // parent is not a directory. The path is invalid return std::string{"Invlid path"}; } // File does not exist and the parent directory exists // Try to create the file try { // Catch std::ofstream exceptions std::ofstream file{filepath}; if (!file.is_open()) { // Could not create the file // creation failed ofstream closes the file in destructor return std::string{"Could not create the file"}; } // explicitly close the file to ensure that the file is created and closed file.close(); if (!file.good()) { // Could not create the file return std::string{"Could not create the file"}; } } catch (const std::exception&) { // Could not create the file return std::string{"Could not create the file"}; } // File was created successfully return std::string{}; } EnsureFileExists_t::EnsureFileExists_t() { name_ = "EnsureFileExists"; non_modifying_ = false; // The validator modifies the input for a symlink func_ = [](std::string &filepath) { // Check if file exists if (std::filesystem::is_symlink(filepath)) { // resolve symlink chain for at most MAX_SYMLINK_RESOLVE_ITERATIONS iterations auto resolved = resolve_symlink(filepath, MAX_SYMLINK_RESOLVE_ITERATIONS); if (!resolved) { return std::string{"Could not resolve the symlink"}; } // resolved is valid if (resolved->type == ResolveSymlinkResult::Type::FILE) { // resolved to a file filepath = resolved->path; return std::string{}; } else if (resolved->type == ResolveSymlinkResult::Type::DIRECTORY) { // resolved to a directory return std::string{"Path is a directory"}; } else if (resolved->type == ResolveSymlinkResult::Type::NOT_FOUND) { // file not found but a parent directory could exist filepath = resolved->path; return createFileInParentDirectory(filepath); } } else if (std::filesystem::is_regular_file(filepath)) { // file exists return std::string{}; } else if(std::filesystem::exists(filepath)) { // the path exists but is not a file or a symlink return std::string{"Path is not a file"}; } // The filepath does not exist // Try to create the file in the parent directory return createFileInParentDirectory(filepath); }; } } // namespace validators ================================================ FILE: src/cli/main/validators.h ================================================ #ifndef CLI_VALIDATORS_H #define CLI_VALIDATORS_H #include #include namespace validators { #define MAX_SYMLINK_RESOLVE_ITERATIONS 10 struct EnsureFileExists_t : public CLI::Validator { EnsureFileExists_t(); }; const static EnsureFileExists_t EnsureFileExists; } // namespace validators #endif /* CLI_VALIDATORS_H */ ================================================ FILE: tests/CMakeLists.txt ================================================ include(GoogleTest) option(DRAMPOWER_USE_FETCH_CONTENT_GOOGLE_TEST "Enable FetchContent to provide Google Test" ${DRAMPOWER_USE_FETCH_CONTENT}) if (DRAMPOWER_USE_FETCH_CONTENT) if (DRAMPOWER_USE_FETCH_CONTENT_GOOGLE_TEST) FetchContent_Declare( GTest URL https://github.com/google/googletest/releases/download/v1.15.2/googletest-1.15.2.tar.gz OVERRIDE_FIND_PACKAGE ) # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(GTest) # TODO: Necessary target properties? set_target_properties(gmock PROPERTIES FOLDER lib) set_target_properties(gmock_main PROPERTIES FOLDER lib) set_target_properties(gtest PROPERTIES FOLDER lib) set_target_properties(gtest_main PROPERTIES FOLDER lib) endif() endif() find_package(GTest REQUIRED) add_subdirectory(tests_drampower) add_subdirectory(tests_misc) ================================================ FILE: tests/tests_drampower/CMakeLists.txt ================================================ ############################################### ### tests_drampower ### ############################################### add_executable(tests_drampower base/test_ddr_serialize.cpp base/test_ddr_base.cpp base/test_ddr_data.cpp base/test_pattern_pre_cycles.cpp core/DDR4/ddr4_multidevice_tests.cpp core/DDR4/ddr4_multirank_tests.cpp core/DDR4/ddr4_test_pattern_0.cpp core/DDR4/ddr4_test_pattern_1.cpp core/DDR4/ddr4_test_pattern_2.cpp core/DDR4/ddr4_test_pattern_3.cpp core/DDR4/ddr4_test_pattern_4.cpp core/DDR4/ddr4_test_pattern_5.cpp core/DDR4/ddr4_test_pattern_6.cpp core/DDR4/ddr4_test_pattern_7.cpp core/DDR4/ddr4_test_pattern_8.cpp core/DDR4/ddr4_test_pattern_9.cpp core/DDR4/ddr4_test_pattern_10.cpp core/DDR4/ddr4_test_pattern_11.cpp core/DDR4/ddr4_test_pattern_12.cpp core/DDR4/ddr4_test_pattern_13.cpp core/DDR4/ddr4_test_pattern_14.cpp core/DDR4/ddr4_test_pattern_15.cpp core/DDR5/ddr5_multidevice_tests.cpp core/DDR5/ddr5_multirank_tests.cpp core/DDR5/ddr5_test_pattern_0.cpp core/DDR5/ddr5_test_pattern_1.cpp core/DDR5/ddr5_test_pattern_2.cpp core/DDR5/ddr5_test_pattern_3.cpp core/DDR5/ddr5_test_pattern_4.cpp core/DDR5/ddr5_test_pattern_5.cpp core/DDR5/ddr5_test_pattern_6.cpp core/DDR5/ddr5_test_pattern_7.cpp core/DDR5/ddr5_test_pattern_8.cpp core/DDR5/ddr5_test_pattern_9.cpp core/DDR5/ddr5_test_pattern_10.cpp core/DDR5/ddr5_test_pattern_11.cpp core/DDR5/ddr5_test_pattern_12.cpp core/DDR5/ddr5_test_pattern_13.cpp core/DDR5/ddr5_test_pattern_14.cpp core/DDR5/ddr5_test_pattern_15.cpp core/DDR5/ddr5_test_pattern_16.cpp core/DDR5/ddr5_test_pattern_17.cpp core/DDR5/ddr5_test_pattern_18.cpp core/LPDDR4/lpddr4_multidevice_tests.cpp core/LPDDR4/lpddr4_multirank_tests.cpp core/LPDDR4/lpddr4_test_pattern_0.cpp core/LPDDR4/lpddr4_test_pattern_1.cpp core/LPDDR4/lpddr4_test_pattern_2.cpp core/LPDDR4/lpddr4_test_pattern_3.cpp core/LPDDR4/lpddr4_test_pattern_4.cpp core/LPDDR4/lpddr4_test_pattern_5.cpp core/LPDDR4/lpddr4_test_pattern_6.cpp core/LPDDR4/lpddr4_test_pattern_7.cpp core/LPDDR4/lpddr4_test_pattern_8.cpp core/LPDDR4/lpddr4_test_pattern_9.cpp core/LPDDR4/lpddr4_test_pattern_10.cpp core/LPDDR4/lpddr4_test_pattern_11.cpp core/LPDDR4/lpddr4_test_pattern_12.cpp core/LPDDR4/lpddr4_test_pattern_13.cpp core/LPDDR4/lpddr4_test_pattern_14.cpp core/LPDDR4/lpddr4_test_pattern_15.cpp core/LPDDR4/lpddr4_test_pattern_16.cpp core/LPDDR4/lpddr4_test_pattern_17.cpp core/LPDDR4/lpddr4_test_pattern_18.cpp core/LPDDR5/lpddr5_multidevice_tests.cpp core/LPDDR5/lpddr5_multirank_tests.cpp core/LPDDR5/lpddr5_test_pattern_0.cpp core/LPDDR5/lpddr5_test_pattern_1.cpp core/LPDDR5/lpddr5_test_pattern_2.cpp core/LPDDR5/lpddr5_test_pattern_3.cpp core/LPDDR5/lpddr5_test_pattern_4.cpp core/LPDDR5/lpddr5_test_pattern_5.cpp core/LPDDR5/lpddr5_test_pattern_6.cpp core/LPDDR5/lpddr5_test_pattern_7.cpp core/LPDDR5/lpddr5_test_pattern_8.cpp core/LPDDR5/lpddr5_test_pattern_9.cpp core/LPDDR5/lpddr5_test_pattern_10.cpp core/LPDDR5/lpddr5_test_pattern_11.cpp core/LPDDR5/lpddr5_test_pattern_12.cpp core/LPDDR5/lpddr5_test_pattern_13.cpp core/LPDDR5/lpddr5_test_pattern_14.cpp core/LPDDR5/lpddr5_test_pattern_15.cpp core/LPDDR5/lpddr5_test_pattern_16.cpp core/LPDDR5/lpddr5_test_pattern_17.cpp core/LPDDR5/lpddr5_test_pattern_18.cpp core/LPDDR5/lpddr5_test_pattern_19.cpp core/LPDDR5/lpddr5_test_pattern_20.cpp core/LPDDR5/lpddr5_test_pattern_21.cpp interface/test_interface_ddr4.cpp interface/test_interface_ddr5.cpp interface/test_interface_lpddr4.cpp interface/test_interface_lpddr5.cpp interface/test_togglingrate_ddr4.cpp interface/test_togglingrate_ddr5.cpp interface/test_togglingrate_lpddr4.cpp interface/test_togglingrate_lpddr5.cpp interface/test_dbi_ddr4.cpp interface/test_dbi_lpddr4.cpp interface/test_dbi_lpddr5.cpp ) set_target_properties(tests_drampower PROPERTIES FOLDER tests/drampower) target_compile_definitions(tests_drampower PUBLIC TEST_RESOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/resources/") target_include_directories(tests_drampower PUBLIC ${PROJECT_SOURCE_DIR}) target_link_libraries(tests_drampower DRAMPower::DRAMPower gtest gtest_main ) gtest_discover_tests(tests_drampower WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) ================================================ FILE: tests/tests_drampower/base/test_ddr_base.cpp ================================================ #include #include #include "DRAMPower/Types.h" #include "DRAMPower/command/Command.h" #include "DRAMPower/data/stats.h" #include "DRAMPower/dram/dram_base.h" #include "DRAMPower/util/cli_architecture_config.h" #include #include using namespace DRAMPower; class test_ddr : public dram_base { public: energy_t calcCoreEnergyStats(const SimulationStats&) const override { return energy_t(1); }; interface_energy_info_t calcInterfaceEnergyStats(const SimulationStats&) const override { return interface_energy_info_t(); }; SimulationStats getWindowStats(timestamp_t) override { return {}; }; util::CLIArchitectureConfig getCLIArchitectureConfig() override { return util::CLIArchitectureConfig{}; }; bool isSerializable() const override { return false; } test_ddr() = default; private: void serialize_impl(std::ostream&) const override {} void deserialize_impl(std::istream&) override {} void doCoreCommandImpl(const Command& command) override { implicitCommandHandler.processImplicitCommandQueue(command.timestamp, last_command_time); __doCoreCommand(command); } void doInterfaceCommandImpl(const Command& command) override { implicitCommandHandler.processImplicitCommandQueue(command.timestamp, last_command_time); __doInterfaceCommand(command); } void __doInterfaceCommand(const Command&) { return; } void __doCoreCommand(const Command& command) { auto next_timestamp = command.timestamp; switch (command.type) { case CmdType::ACT: break; case CmdType::PRE: next_timestamp += 10; implicitCommandHandler.addImplicitCommand(next_timestamp, [this, next_timestamp]() { execution_order.push_back(next_timestamp); }); break; case CmdType::PREA: next_timestamp += 1; implicitCommandHandler.addImplicitCommand(next_timestamp, [this, next_timestamp]() { execution_order.push_back(next_timestamp); }); break; default: throw std::runtime_error("Invalid type"); } execution_order.push_back(command.timestamp); last_command_time = std::max(last_command_time, command.timestamp); } timestamp_t getLastCommandTime_impl() const override { return last_command_time; } timestamp_t last_command_time; public: std::vector execution_order; ImplicitCommandHandler<> implicitCommandHandler; }; class DDR_Base_Test : public ::testing::Test { protected: // Test variables std::unique_ptr ddr; virtual void SetUp() { ddr = std::make_unique(); } virtual void TearDown() { } }; TEST_F(DDR_Base_Test, DoCommand) { this->ddr->doCoreCommand({ 10, CmdType::ACT, { 1, 0, 0 } }); ASSERT_EQ(ddr->execution_order.size(), 1); ASSERT_EQ(ddr->execution_order[0], 10); } TEST_F(DDR_Base_Test, ImplicitCommand) { this->ddr->doCoreCommand({ 10, CmdType::PRE, { 1, 0, 0 } }); this->ddr->doCoreCommand({ 15, CmdType::PREA, { 1, 0, 0 } }); this->ddr->doCoreCommand({ 50, CmdType::ACT, { 1, 0, 0 } }); ASSERT_EQ(ddr->execution_order.size(), 5); ASSERT_EQ(ddr->execution_order[0], 10); ASSERT_EQ(ddr->execution_order[1], 15); ASSERT_EQ(ddr->execution_order[2], 16); ASSERT_EQ(ddr->execution_order[3], 20); ASSERT_EQ(ddr->execution_order[4], 50); } ================================================ FILE: tests/tests_drampower/base/test_ddr_data.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include "DRAMPower/dram/dram_base.h" #include using namespace DRAMPower; template struct CommandPattern { constexpr static auto type() { return Type; }; constexpr static auto pattern() { return Pattern; }; }; template struct pattern_helper { constexpr static uint64_t get(CmdType type) { if (Head::type() == type) return Head::pattern(); return pattern_helper::get(type); }; }; template struct pattern_helper { constexpr static uint64_t get(CmdType type) { if (Head::type() == type) return Head::pattern(); return 0x00; }; }; template struct CommandPatternMap { constexpr static uint64_t getPattern(CmdType type) { return pattern_helper::get(type); }; }; class DramPowerDataTest : public ::testing::Test { protected: virtual void SetUp() { } virtual void TearDown() { } }; TEST_F(DramPowerDataTest, Test_1) { Command command{ 10, CmdType::ACT, {} }; using TestPatterMap = CommandPatternMap< CommandPattern, CommandPattern >; ASSERT_EQ(TestPatterMap::getPattern(CmdType::ACT), 0x1001); ASSERT_EQ(TestPatterMap::getPattern(CmdType::PRE), 0x1011); ASSERT_EQ(TestPatterMap::getPattern(CmdType::NOP), 0x0000); }; ================================================ FILE: tests/tests_drampower/base/test_ddr_serialize.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include "DRAMPower/simconfig/simconfig.h" #include "DRAMUtils/config/toggling_rate.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace DRAMPower; template class DramPowerTest_DDR_Serialize : public ::testing::Test { protected: using Pattern_t = std::vector; using PatternList_t = std::vector; static constexpr uint8_t wr_data[] = { 0x00, 0xFF, 0x01, 0x10, 0x00, 0xFF, 0x00, 0xFF }; static constexpr uint8_t rd_data[] = { 0x00, 0xFF, 0x01, 0x10, 0x00, 0xFF, 0x00, 0xFF }; // Test pattern PatternList_t testPattern = { { { 0, CmdType::ACT, { 0, 0, 0 }}, Command{15, CmdType::WR, TargetCoordinate{ 0, 0, 0, 0, 0, }, wr_data, sizeof(wr_data) * 8}, // TODO: for sz_bits 0 not working Command{25, CmdType::RD, TargetCoordinate{ 0, 0, 0, 0, 0, }, rd_data, sizeof(wr_data) * 8}, { 35, CmdType::PRE, { 0, 0, 0 }}, { 45, CmdType::REFA, { 0, 0, 0 }}, { 80, CmdType::END_OF_SIMULATION }, }, { { 0, CmdType::ACT, { 0, 0, 0 }}, Command{15, CmdType::WR, TargetCoordinate{ 0, 0, 0, 0, 0, }, nullptr}, Command{25, CmdType::RD, TargetCoordinate{ 0, 0, 0, 0, 0, }, nullptr}, { 35, CmdType::PRE, { 0, 0, 0 }}, { 45, CmdType::REFA, { 0, 0, 0 }}, { 80, CmdType::END_OF_SIMULATION }, } }; // Test variables std::unique_ptr ddr1; std::unique_ptr ddr2; std::unique_ptr memSpec; virtual void getPath(std::filesystem::path& path) const = 0; virtual void SetUp() { std::filesystem::path path; getPath(path); auto data = DRAMUtils::parse_memspec_from_file(path); memSpec = std::make_unique(MemSpec::from_memspec(*data)); } void createStandard(std::optional trd) { if (!trd.has_value()) { ddr1 = std::make_unique(*memSpec); ddr2 = std::make_unique(*memSpec); } else { ddr1 = std::make_unique(*memSpec, config::SimConfig{trd.value()}); ddr2 = std::make_unique(*memSpec, config::SimConfig{trd.value()}); } } virtual void TearDown() { } }; class DramPowerTest_DDR4_Serialize : public DramPowerTest_DDR_Serialize { protected: void getPath(std::filesystem::path& path) const override { path = std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"; } }; class DramPowerTest_DDR5_Serialize : public DramPowerTest_DDR_Serialize { protected: void getPath(std::filesystem::path& path) const override { path = std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"; } }; class DramPowerTest_LPDDR4_Serialize : public DramPowerTest_DDR_Serialize { protected: void getPath(std::filesystem::path& path) const override { path = std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"; } }; class DramPowerTest_LPDDR5_Serialize : public DramPowerTest_DDR_Serialize { protected: void getPath(std::filesystem::path& path) const override { path = std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"; } }; template void compareStats(const std::vector& testPattern, std::unique_ptr& ddr1, std::unique_ptr& ddr2) { assert(testPattern.size() == 6); // Enable dbi if possible ddr1->getExtensionManager().template withExtension([](DRAMPower::extensions::DBI& dbi) { dbi.enable(0, false); }); // ACT ddr1->doCoreCommand(testPattern[0]); ddr1->doInterfaceCommand(testPattern[0]); // WR ddr1->doCoreCommand(testPattern[1]); ddr1->doInterfaceCommand(testPattern[1]); // Serialize and deserialize auto streamout = std::ostringstream(); ddr1->serialize(streamout); auto streamin = std::istringstream(streamout.str()); ddr2->deserialize(streamin); // RD ddr1->doCoreCommand(testPattern[2]); ddr1->doInterfaceCommand(testPattern[2]); ddr2->doCoreCommand(testPattern[2]); ddr2->doInterfaceCommand(testPattern[2]); // PRE ddr1->doCoreCommand(testPattern[3]); ddr1->doInterfaceCommand(testPattern[3]); ddr2->doCoreCommand(testPattern[3]); ddr2->doInterfaceCommand(testPattern[3]); // REFA ddr1->doCoreCommand(testPattern[4]); ddr1->doInterfaceCommand(testPattern[4]); ddr2->doCoreCommand(testPattern[4]); ddr2->doInterfaceCommand(testPattern[4]); // EOS ddr1->doCoreCommand(testPattern[5]); ddr1->doInterfaceCommand(testPattern[5]); ddr2->doCoreCommand(testPattern[5]); ddr2->doInterfaceCommand(testPattern[5]); auto stats1 = ddr1->getStats(); auto stats2 = ddr2->getStats(); // Compare the stats of both DDR4 instances are equal ASSERT_EQ(stats1, stats2); } TEST_F(DramPowerTest_DDR4_Serialize, Test0){ createStandard(std::nullopt); compareStats(testPattern.at(0), ddr1, ddr2); } TEST_F(DramPowerTest_DDR5_Serialize, Test0){ createStandard(std::nullopt); compareStats(testPattern.at(0), ddr1, ddr2); } TEST_F(DramPowerTest_LPDDR4_Serialize, Test0){ createStandard(std::nullopt); compareStats(testPattern.at(0), ddr1, ddr2); } TEST_F(DramPowerTest_LPDDR5_Serialize, Test0){ createStandard(std::nullopt); compareStats(testPattern.at(0), ddr1, ddr2); } TEST_F(DramPowerTest_DDR4_Serialize, Test1){ createStandard(DRAMUtils::Config::ToggleRateDefinition { 0.6, 0.4, 0.3, 0.2, TogglingRateIdlePattern::L, TogglingRateIdlePattern::L, }); compareStats(testPattern.at(1), ddr1, ddr2); } TEST_F(DramPowerTest_DDR5_Serialize, Test1){ createStandard(DRAMUtils::Config::ToggleRateDefinition { 0.6, 0.4, 0.3, 0.2, TogglingRateIdlePattern::L, TogglingRateIdlePattern::L, }); compareStats(testPattern.at(1), ddr1, ddr2); } TEST_F(DramPowerTest_LPDDR4_Serialize, Test1){ createStandard(DRAMUtils::Config::ToggleRateDefinition { 0.6, 0.4, 0.3, 0.2, TogglingRateIdlePattern::L, TogglingRateIdlePattern::L, }); compareStats(testPattern.at(1), ddr1, ddr2); } TEST_F(DramPowerTest_LPDDR5_Serialize, Test1){ createStandard(DRAMUtils::Config::ToggleRateDefinition { 0.6, 0.4, 0.3, 0.2, TogglingRateIdlePattern::L, TogglingRateIdlePattern::L, }); compareStats(testPattern.at(1), ddr1, ddr2); } ================================================ FILE: tests/tests_drampower/base/test_pattern_pre_cycles.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_Pre_Cycles : public ::testing::Test { protected: // Test pattern // TODO invalid state transitions std::vector testPattern = { // Timestamp, Cmd, { Bank, BG, Rank} { 0, CmdType::ACT, { 0, 0, 0 } }, { 30, CmdType::PDEA, { 0, 0, 0 } }, { 40, CmdType::PDXA, { 0, 0, 0 } }, { 70, CmdType::PRE, { 0, 0, 0 } }, { 80, CmdType::ACT, { 1, 0, 0 } }, { 90, CmdType::PRE, { 1, 0, 0 } }, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); auto memSpec = DRAMPower::MemSpecDDR4::from_memspec(*data); memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 2; memSpec.numberOfBankGroups = 2; //memSpec.banksPerGroup = 4; memSpec.memTimingSpec.tRAS = 10; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tRCD = 20; memSpec.memTimingSpec.tRFC = 10; //memSpec.memTimingSpec.tRFCPB = 25; //memSpec.memTimingSpec.tRFCsb_slr = 25; memSpec.memTimingSpec.tWR = 20; memSpec.memTimingSpec.tRP = 20; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1; //memSpec.memTimingSpec.tREFI = 1; memSpec.memPowerSpec[0].vXX = 1; memSpec.memPowerSpec[0].iXX0 = 64; memSpec.memPowerSpec[0].iXX2N = 8; memSpec.memPowerSpec[0].iXX2P = 6; memSpec.memPowerSpec[0].iXX3N = 32; memSpec.memPowerSpec[0].iXX3P = 20; memSpec.memPowerSpec[0].iXX4R = 72; memSpec.memPowerSpec[0].iXX4W = 72; //memSpec.memPowerSpec[0].iXX5C = 28; //memSpec.memPowerSpec[0].iXX5PB_B = 30; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_Pre_Cycles, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 60); ASSERT_EQ(stats.bank[1].cycles.act, 10); // Check per-bank PRE count ASSERT_EQ(stats.bank[0].cycles.pre, 30); ASSERT_EQ(stats.bank[1].cycles.pre, 80); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 70); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); } TEST_F(DramPowerTest_Pre_Cycles, Test_Detailed) { SimulationStats window; auto iterate_to_timestamp = [this, command = testPattern.begin()](timestamp_t timestamp) mutable { while (command != this->testPattern.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } return this->ddr->getWindowStats(timestamp); }; // Cycle 5 window = iterate_to_timestamp(5); ASSERT_EQ(window.rank_total[0].cycles.act, 5); ASSERT_EQ(window.rank_total[0].cycles.pre, 0); ASSERT_EQ(window.bank[0].cycles.act, 5); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 5); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 10 window = iterate_to_timestamp(10); ASSERT_EQ(window.rank_total[0].cycles.act, 10); ASSERT_EQ(window.rank_total[0].cycles.pre, 0); ASSERT_EQ(window.bank[0].cycles.act, 10); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 30 window = iterate_to_timestamp(30); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 0); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 35 window = iterate_to_timestamp(35); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 0); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 5); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 40 window = iterate_to_timestamp(40); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 0); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); } ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_multidevice_tests.cpp ================================================ /* Note This test is based on ddr4_test_pattern_4.cpp and assumes the correctness of the test. Calculation for 5 devices: E_act: 358.99038461538458 E_pre: 415.38461538461542 E_bg_act: 1703.6538461538462 E_bg_act_shared: 1690.3846153846155 E_bg_pre: 623.07692307692309 E_RD: 1307.0769230769231 Total: 4408.1826923076924 E_act * 5: 1794.9519230769229 E_bg_act_shared * 5: 8451.9230769230775 E_pre * 5: 2076.9230769230771 E_bg_act * 5: 8518.269230769231 E_bg_pre * 5: 3115.38461538461545 E_RD * 5: 6535.3846153846155 Total * 5: 22040.913461538462 */ #include #include #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_MultiDevice : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 40, CmdType::RD, { 0, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 70, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; uint64_t numberOfDevices; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); memSpec->numberOfDevices = 5; numberOfDevices = memSpec->numberOfDevices; ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_MultiDevice, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 2); else if(b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); }; // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 50); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 50); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 30); else ASSERT_EQ(stats.bank[b].cycles.act, 0); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 20); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 40); else ASSERT_EQ(stats.bank[b].cycles.pre, 70); } } TEST_F(DramPowerTest_DDR4_MultiDevice, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(energy.bank_energy.size(), numberOfDevices * memSpec->numberOfBanks); // Validate every device has the same bank energy for(size_t i = 0; i < numberOfDevices; i++){ for(size_t j = 0; j < memSpec->numberOfBanks; j++){ ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_act, energy.bank_energy[j].E_act); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_pre, energy.bank_energy[j].E_pre); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_RD, energy.bank_energy[j].E_RD); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_bg_act, energy.bank_energy[j].E_bg_act); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_bg_pre, energy.bank_energy[j].E_bg_pre); } } ASSERT_EQ(std::round(total_energy.E_act*1e12), 1795); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 2077); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 6535); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 8518); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 8452); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 3115); ASSERT_EQ(std::round(total_energy.total()*1e12), 22041); } ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_multirank_tests.cpp ================================================ #include #include #include #include "DRAMPower/command/Command.h" #include "DRAMPower/standards/ddr4/DDR4.h" #include "DRAMPower/memspec/MemSpecDDR4.h" #include #include #include using DRAMPower::CmdType; using DRAMPower::Command; using DRAMPower::DDR4; using DRAMPower::MemSpecDDR4; using DRAMPower::SimulationStats; #define SZ_BITS(x) sizeof(x)*8 static constexpr uint8_t wr_data[] = { 0, 0, 0, 0, 0, 0, 0, 255 }; static constexpr uint8_t rd_data[] = { 0, 0, 0, 0, 0, 0, 0, 1 }; class DDR4_MultirankTests : public ::testing::Test { public: virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); spec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); spec->numberOfRanks = 2; ddr = std::make_unique(*spec); } void runCommands(const std::vector &commands) { for (const Command &command : commands) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); } } inline size_t bankIndex(int bank, int rank) { return rank * spec->numberOfBanks + bank; } std::unique_ptr spec; std::unique_ptr ddr; }; TEST_F(DDR4_MultirankTests, Pattern_1) { runCommands({ {0, CmdType::ACT, {1, 0, 0}}, {2, CmdType::ACT, {1, 0, 1}}, // rank 1 {4, CmdType::WR, {1, 0, 0, 0, 4}, wr_data, SZ_BITS(wr_data)}, {6, CmdType::RD, {1, 0, 1, 0, 4}, rd_data, SZ_BITS(rd_data)}, // rank 1 {10, CmdType::PRE, {1, 0, 1}}, // rank 1 {15, CmdType::PRE, {1, 0, 0}}, {20, CmdType::END_OF_SIMULATION} }); SimulationStats stats = ddr->getStats(); EXPECT_EQ(stats.bank.size(), spec->numberOfBanks * spec->numberOfRanks); EXPECT_EQ(stats.bank[bankIndex(1, 0)].cycles.act, 15); EXPECT_EQ(stats.bank[bankIndex(1, 1)].cycles.act, 8); EXPECT_EQ(stats.bank[bankIndex(1, 0)].cycles.pre, 5); EXPECT_EQ(stats.bank[bankIndex(1, 1)].cycles.pre, 12); EXPECT_EQ(stats.rank_total[0].cycles.act, 15); EXPECT_EQ(stats.rank_total[1].cycles.act, 8); } TEST_F(DDR4_MultirankTests, Pattern_2) { // TODO error no read data runCommands({ {0, CmdType::ACT, {0, 0, 0}}, {5, CmdType::ACT, {0, 0, 1}}, // r1 {15, CmdType::RDA, {0, 0, 0}, rd_data, SZ_BITS(rd_data)}, {20, CmdType::ACT, {3, 0, 1}}, // r1 {35, CmdType::RD, {3, 0, 1}, rd_data, SZ_BITS(rd_data)}, // r1 {40, CmdType::RD, {0, 0, 0}, rd_data, SZ_BITS(rd_data)}, {50, CmdType::PREA, {0, 0, 0}}, {55, CmdType::PREA, {0, 0, 1}}, // r1 {65, CmdType::REFA, {0, 0, 0}}, {70, CmdType::REFA, {0, 0, 1}}, // r1 {100, CmdType::END_OF_SIMULATION}, }); SimulationStats stats = ddr->getStats(); EXPECT_EQ(stats.bank.size(), spec->numberOfBanks * spec->numberOfRanks); EXPECT_EQ(stats.bank[bankIndex(0, 0)].cycles.act, 45); EXPECT_EQ(stats.bank[bankIndex(0, 1)].cycles.act, 75); EXPECT_EQ(stats.bank[bankIndex(3, 1)].cycles.act, 60); EXPECT_EQ(stats.bank[bankIndex(0, 0)].cycles.pre, 55); EXPECT_EQ(stats.bank[bankIndex(0, 1)].cycles.pre, 25); EXPECT_EQ(stats.bank[bankIndex(3, 1)].cycles.pre, 40); EXPECT_EQ(stats.rank_total[0].cycles.act, 45); EXPECT_EQ(stats.rank_total[1].cycles.act, 75); } ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_0.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_0 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::PRE, { 0, 0, 0 }}, { 15, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; std::unique_ptr memSpec; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_0, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); //TODO remove } auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 15); ASSERT_EQ(stats.rank_total[0].cycles.pre, 0); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 15); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 0); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 15); } TEST_F(DramPowerTest_DDR4_0, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 179); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 208); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 510); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 507); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 0); ASSERT_EQ(std::round(total_energy.total()*1e12), 897); } ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_1.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_1 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 35, CmdType::PRE, { 0, 0, 0 }}, { 35, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; std::unique_ptr memSpec; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_1, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check bank command count: RD ASSERT_EQ(stats.bank[0].counter.reads, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.reads, 0); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.pre, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 35); ASSERT_EQ(stats.rank_total[0].cycles.pre, 0); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 35); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 0); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 35); } TEST_F(DramPowerTest_DDR4_1, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 179); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 208); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 436); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1189); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1183); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 0); ASSERT_EQ(std::round(total_energy.total()*1e12), 2012); } ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_10.cpp ================================================ #include #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_10 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { // Timestamp, Cmd, { Bank, BG, Rank} { 0, CmdType::ACT, { 0, 0, 0 } }, { 10, CmdType::ACT, { 1, 0, 0 } }, { 15, CmdType::PRE, { 0, 0, 0 } }, { 25, CmdType::ACT, { 3, 0, 0 } }, { 25, CmdType::PRE, { 1, 0, 0 } }, { 30, CmdType::ACT, { 0, 0, 0 } }, { 35, CmdType::ACT, { 2, 0, 0 } }, { 40, CmdType::PRE, { 1, 0, 0 } }, { 40, CmdType::PRE, { 3, 0, 0 } }, { 45, CmdType::PRE, { 0, 0, 0 } }, { 50, CmdType::PRE, { 2, 0, 0 } }, { 85, CmdType::ACT, { 7, 0, 0 } }, { 100, CmdType::PRE, { 7, 0, 0 } }, { 120, CmdType::ACT, { 6, 0, 0 } }, { 125, CmdType::END_OF_SIMULATION}, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); auto memSpec = DRAMPower::MemSpecDDR4::from_memspec(*data); memSpec.numberOfRanks = 1; memSpec.numberOfDevices = 1; memSpec.numberOfBanks = 8; memSpec.numberOfBankGroups = 1; memSpec.burstLength = 8; memSpec.dataRate = 2; memSpec.memTimingSpec.tCK = 1; memSpec.memTimingSpec.tRAS = 10; memSpec.memTimingSpec.tRP = 10; memSpec.memPowerSpec[0].vXX = 1; memSpec.memPowerSpec[0].iXX0 = 64; memSpec.memPowerSpec[0].iXX2N = 8; memSpec.memPowerSpec[0].iXX3N = 32; memSpec.memPowerSpec[0].iXX4R = 0; memSpec.memPowerSpec[0].iXX4W = 0; memSpec.memPowerSpec[0].iXX5X = 0; memSpec.memPowerSpec[0].iXX6N = 0; memSpec.memPowerSpec[0].iXX2P = 0; memSpec.memPowerSpec[0].iXX3P = 0; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iXX0; memSpec.bwParams.bwPowerFactRho = 0.333333333; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_10, Pattern1) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; // Inspect first rank const auto & rank_1 = internal::DDR4TestAccessor.getRanks(ddr->getCore()).at(0); auto stats = ddr->getStats(); // Check global count ASSERT_EQ(stats.rank_total[0].cycles.act, 70); ASSERT_EQ(stats.rank_total[0].cycles.pre, 55); // Check per-bank ACT count ASSERT_EQ(stats.bank[0].cycles.act, 30); ASSERT_EQ(stats.bank[1].cycles.act, 15); ASSERT_EQ(stats.bank[2].cycles.act, 15); ASSERT_EQ(stats.bank[3].cycles.act, 15); ASSERT_EQ(stats.bank[7].cycles.act, 15); ASSERT_EQ(stats.bank[6].cycles.act, 5); ASSERT_EQ(stats.bank[4].cycles.act, 0); ASSERT_EQ(stats.bank[5].cycles.act, 0); // Check per-bank PRE count ASSERT_EQ(stats.bank[0].cycles.pre, 95); ASSERT_EQ(stats.bank[1].cycles.pre, 110); ASSERT_EQ(stats.bank[2].cycles.pre, 110); ASSERT_EQ(stats.bank[3].cycles.pre, 110); ASSERT_EQ(stats.bank[7].cycles.pre, 110); ASSERT_EQ(stats.bank[6].cycles.pre, 120); ASSERT_EQ(stats.bank[4].cycles.pre, 125); ASSERT_EQ(stats.bank[5].cycles.pre, 125); // Check global command count ASSERT_EQ(rank_1.commandCounter.get(CmdType::ACT), 7); ASSERT_EQ(rank_1.commandCounter.get(CmdType::PRE), 7); // per-bank ACT command count ASSERT_EQ(stats.bank[0].counter.act, 2); ASSERT_EQ(stats.bank[1].counter.act, 1); ASSERT_EQ(stats.bank[2].counter.act, 1); ASSERT_EQ(stats.bank[3].counter.act, 1); ASSERT_EQ(stats.bank[6].counter.act, 1); ASSERT_EQ(stats.bank[7].counter.act, 1); ASSERT_EQ(stats.bank[4].counter.act, 0); ASSERT_EQ(stats.bank[5].counter.act, 0); // per-bank PRE command count ASSERT_EQ(stats.bank[0].counter.pre, 2); ASSERT_EQ(stats.bank[1].counter.pre, 1); ASSERT_EQ(stats.bank[2].counter.pre, 1); ASSERT_EQ(stats.bank[3].counter.pre, 1); ASSERT_EQ(stats.bank[7].counter.pre, 1); ASSERT_EQ(stats.bank[6].counter.pre, 0); ASSERT_EQ(stats.bank[4].counter.pre, 0); ASSERT_EQ(stats.bank[5].counter.pre, 0); } TEST_F(DramPowerTest_DDR4_10, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ((int)total_energy.E_act, 3220); ASSERT_EQ((int)energy.bank_energy[0].E_act, 920); ASSERT_EQ((int)energy.bank_energy[1].E_act, 460); ASSERT_EQ((int)energy.bank_energy[2].E_act, 460); ASSERT_EQ((int)energy.bank_energy[3].E_act, 460); ASSERT_EQ((int)energy.bank_energy[4].E_act, 0); ASSERT_EQ((int)energy.bank_energy[5].E_act, 0); ASSERT_EQ((int)energy.bank_energy[6].E_act, 460); ASSERT_EQ((int)energy.bank_energy[7].E_act, 460); ASSERT_EQ((int)total_energy.E_pre, 3360); ASSERT_EQ((int)energy.bank_energy[0].E_pre, 1120); ASSERT_EQ((int)energy.bank_energy[1].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[2].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[3].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[4].E_pre, 0); ASSERT_EQ((int)energy.bank_energy[5].E_pre, 0); ASSERT_EQ((int)energy.bank_energy[6].E_pre, 0); ASSERT_EQ((int)energy.bank_energy[7].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[0].E_bg_act, 60); ASSERT_EQ((int)energy.bank_energy[1].E_bg_act, 30); ASSERT_EQ((int)energy.bank_energy[2].E_bg_act, 30); ASSERT_EQ((int)energy.bank_energy[3].E_bg_act, 30); ASSERT_EQ((int)energy.bank_energy[4].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[5].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[6].E_bg_act, 10); ASSERT_EQ((int)energy.bank_energy[7].E_bg_act, 30); ASSERT_EQ(std::round(energy.E_bg_act_shared), 1120); ASSERT_EQ(std::round(total_energy.E_bg_act), 1310); ASSERT_EQ((int)total_energy.E_bg_pre, 440); ASSERT_EQ((int)energy.bank_energy[0].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[1].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[2].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[3].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[4].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[5].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[6].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[7].E_bg_pre, 55); }; ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_11.cpp ================================================ #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_11 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0} }, { 5, CmdType::ACT, { 1, 0, 0} }, { 10, CmdType::ACT, { 2, 0, 0} }, { 15, CmdType::ACT, { 3, 0, 0} }, { 20, CmdType::ACT, { 4, 0, 0} }, { 25, CmdType::ACT, { 5, 0, 0} }, { 30, CmdType::ACT, { 6, 0, 0} }, { 35, CmdType::ACT, { 7, 0, 0} }, { 35, CmdType::RD, { 0, 0, 0} }, { 40, CmdType::RD, { 0, 0, 0} }, { 40, CmdType::RD, { 1, 0, 0} }, { 45, CmdType::RD, { 0, 0, 0} }, { 45, CmdType::RD, { 2, 0, 0} }, { 50, CmdType::RD, { 3, 0, 0} }, { 55, CmdType::RD, { 4, 0, 0} }, { 60, CmdType::RD, { 5, 0, 0} }, { 65, CmdType::RD, { 6, 0, 0} }, { 70, CmdType::RD, { 7, 0, 0} }, { 75, CmdType::RD, { 7, 0, 0} }, { 80, CmdType::RD, { 7, 0, 0} }, { 85, CmdType::WR, { 7, 0, 0} }, { 90, CmdType::WR, { 6, 0, 0} }, { 95, CmdType::WR, { 5, 0, 0} }, { 100, CmdType::WR, { 4, 0, 0} }, { 105, CmdType::WR, { 3, 0, 0} }, { 110, CmdType::WR, { 2, 0, 0} }, { 110, CmdType::WR, { 0, 0, 0} }, { 115, CmdType::WR, { 1, 0, 0} }, { 115, CmdType::RD, { 0, 0, 0} }, { 120, CmdType::WR, { 0, 0, 0} }, { 120, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); auto memSpec = DRAMPower::MemSpecDDR4::from_memspec(*data); memSpec.numberOfRanks = 1; memSpec.numberOfDevices = 1; memSpec.numberOfBanks = 8; memSpec.numberOfBankGroups = 1; memSpec.memTimingSpec.tCK = 1; memSpec.memPowerSpec[0].vXX = 1; memSpec.memPowerSpec[0].iXX0 = 64; memSpec.memPowerSpec[0].iXX3N = 32; memSpec.memPowerSpec[0].iXX4R = 72; memSpec.memPowerSpec[0].iXX4W = 96; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iXX0; memSpec.burstLength = 16; memSpec.dataRate = 2; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_11, Pattern_2) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; // Inspect first rank const Rank & rank_1 = internal::DDR4TestAccessor.getRanks(ddr->getCore()).at(0); // Check global command count ASSERT_EQ(rank_1.commandCounter.get(CmdType::RD), 13); ASSERT_EQ(rank_1.commandCounter.get(CmdType::WR), 9); // Check bank RD command count ASSERT_EQ(rank_1.banks[0].counter.reads, 4); ASSERT_EQ(rank_1.banks[1].counter.reads, 1); ASSERT_EQ(rank_1.banks[2].counter.reads, 1); ASSERT_EQ(rank_1.banks[3].counter.reads, 1); ASSERT_EQ(rank_1.banks[4].counter.reads, 1); ASSERT_EQ(rank_1.banks[5].counter.reads, 1); ASSERT_EQ(rank_1.banks[6].counter.reads, 1); ASSERT_EQ(rank_1.banks[7].counter.reads, 3); // Check bank WR command count ASSERT_EQ(rank_1.banks[0].counter.writes, 2); ASSERT_EQ(rank_1.banks[1].counter.writes, 1); ASSERT_EQ(rank_1.banks[2].counter.writes, 1); ASSERT_EQ(rank_1.banks[3].counter.writes, 1); ASSERT_EQ(rank_1.banks[4].counter.writes, 1); ASSERT_EQ(rank_1.banks[5].counter.writes, 1); ASSERT_EQ(rank_1.banks[6].counter.writes, 1); ASSERT_EQ(rank_1.banks[7].counter.writes, 1); } TEST_F(DramPowerTest_DDR4_11, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ((int)total_energy.E_RD, 4160); ASSERT_EQ((int)energy.bank_energy[0].E_RD, 1280); ASSERT_EQ((int)energy.bank_energy[1].E_RD, 320); ASSERT_EQ((int)energy.bank_energy[2].E_RD, 320); ASSERT_EQ((int)energy.bank_energy[3].E_RD, 320); ASSERT_EQ((int)energy.bank_energy[4].E_RD, 320); ASSERT_EQ((int)energy.bank_energy[5].E_RD, 320); ASSERT_EQ((int)energy.bank_energy[6].E_RD, 320); ASSERT_EQ((int)energy.bank_energy[7].E_RD, 960); ASSERT_EQ((int)total_energy.E_WR, 4608); ASSERT_EQ((int)energy.bank_energy[0].E_WR, 1024); ASSERT_EQ((int)energy.bank_energy[1].E_WR, 512); ASSERT_EQ((int)energy.bank_energy[2].E_WR, 512); ASSERT_EQ((int)energy.bank_energy[3].E_WR, 512); ASSERT_EQ((int)energy.bank_energy[4].E_WR, 512); ASSERT_EQ((int)energy.bank_energy[5].E_WR, 512); ASSERT_EQ((int)energy.bank_energy[6].E_WR, 512); ASSERT_EQ((int)energy.bank_energy[7].E_WR, 512); } ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_12.cpp ================================================ #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_12 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { // Timestamp, Cmd, { Bank, BG, Rank} { 0, CmdType::ACT, { 0, 0, 0 } }, { 10, CmdType::RDA, { 0, 0, 0 } }, { 15, CmdType::ACT, { 1, 0, 0 } }, { 15, CmdType::ACT, { 2, 0, 0 } }, { 25, CmdType::WRA, { 1, 0, 0 } }, { 30, CmdType::PRE, { 0, 0, 0 } }, { 35, CmdType::RDA, { 2, 0, 0 } }, { 50, CmdType::ACT, { 0, 0, 0 } }, { 50, CmdType::ACT, { 2, 0, 0 } }, { 60, CmdType::WRA, { 0, 0, 0 } }, { 60, CmdType::RDA, { 2, 0, 0 } }, { 75, CmdType::ACT, { 1, 0, 0 } }, { 80, CmdType::ACT, { 0, 0, 0 } }, { 85, CmdType::WRA, { 0, 0, 0 } }, { 95, CmdType::RDA, { 1, 0, 0 } }, { 125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); auto memSpec = DRAMPower::MemSpecDDR4::from_memspec(*data); memSpec.bitWidth = 16; memSpec.numberOfRanks = 1; memSpec.numberOfDevices = 1; memSpec.numberOfBanks = 8; memSpec.numberOfBankGroups = 1; memSpec.memTimingSpec.tAL = 0; memSpec.memTimingSpec.tRAS = 20; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tWR = 12; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1; memSpec.memTimingSpec.tRP = 10; memSpec.memPowerSpec[0].vXX = 1; memSpec.memPowerSpec[0].iXX0 = 64; memSpec.memPowerSpec[0].iXX2N = 8; memSpec.memPowerSpec[0].iXX3N = 32; memSpec.memPowerSpec[0].iXX4R = 72; memSpec.memPowerSpec[0].iXX4W = 96; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iXX0; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; memSpec.memTimingSpec.tBurst = memSpec.burstLength/memSpec.dataRate; memSpec.prechargeOffsetRD = memSpec.memTimingSpec.tAL + memSpec.memTimingSpec.tRTP; memSpec.prechargeOffsetWR = memSpec.memTimingSpec.tBurst + memSpec.memTimingSpec.tWL + memSpec.memTimingSpec.tWR; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_12, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; const Rank & rank_1 = internal::DDR4TestAccessor.getRanks(ddr->getCore()).at(0); auto stats = ddr->getStats(); // Check global command count ASSERT_EQ(rank_1.commandCounter.get(CmdType::ACT), 7); ASSERT_EQ(rank_1.commandCounter.get(CmdType::PRE), 1); ASSERT_EQ(rank_1.commandCounter.get(CmdType::RDA), 4); ASSERT_EQ(rank_1.commandCounter.get(CmdType::WRA), 3); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 3); ASSERT_EQ(stats.bank[1].counter.act, 2); ASSERT_EQ(stats.bank[2].counter.act, 2); ASSERT_EQ(stats.bank[3].counter.act, 0); ASSERT_EQ(stats.bank[4].counter.act, 0); ASSERT_EQ(stats.bank[5].counter.act, 0); ASSERT_EQ(stats.bank[6].counter.act, 0); ASSERT_EQ(stats.bank[7].counter.act, 0); // Check bank command count: RDA ASSERT_EQ(stats.bank[0].counter.readAuto, 1); ASSERT_EQ(stats.bank[1].counter.readAuto, 1); ASSERT_EQ(stats.bank[2].counter.readAuto, 2); ASSERT_EQ(stats.bank[3].counter.readAuto, 0); ASSERT_EQ(stats.bank[4].counter.readAuto, 0); ASSERT_EQ(stats.bank[5].counter.readAuto, 0); ASSERT_EQ(stats.bank[6].counter.readAuto, 0); ASSERT_EQ(stats.bank[7].counter.readAuto, 0); // Check bank command count: WRA ASSERT_EQ(stats.bank[0].counter.writeAuto, 2); ASSERT_EQ(stats.bank[1].counter.writeAuto, 1); ASSERT_EQ(stats.bank[2].counter.writeAuto, 0); ASSERT_EQ(stats.bank[3].counter.writeAuto, 0); ASSERT_EQ(stats.bank[4].counter.writeAuto, 0); ASSERT_EQ(stats.bank[5].counter.writeAuto, 0); ASSERT_EQ(stats.bank[6].counter.writeAuto, 0); ASSERT_EQ(stats.bank[7].counter.writeAuto, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 100); ASSERT_EQ(stats.rank_total[0].cycles.pre, 25); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 75); ASSERT_EQ(stats.bank[1].cycles.act, 60); ASSERT_EQ(stats.bank[2].cycles.act, 50); ASSERT_EQ(stats.bank[3].cycles.act, 0); ASSERT_EQ(stats.bank[4].cycles.act, 0); ASSERT_EQ(stats.bank[5].cycles.act, 0); ASSERT_EQ(stats.bank[6].cycles.act, 0); ASSERT_EQ(stats.bank[7].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 50); ASSERT_EQ(stats.bank[1].cycles.pre, 65); ASSERT_EQ(stats.bank[2].cycles.pre, 75); ASSERT_EQ(stats.bank[3].cycles.pre, 125); ASSERT_EQ(stats.bank[4].cycles.pre, 125); ASSERT_EQ(stats.bank[5].cycles.pre, 125); ASSERT_EQ(stats.bank[6].cycles.pre, 125); ASSERT_EQ(stats.bank[7].cycles.pre, 125); } TEST_F(DramPowerTest_DDR4_12, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act), 1970); ASSERT_EQ(std::round(energy.E_bg_act_shared), 1600); ASSERT_EQ((int)energy.bank_energy[0].E_bg_act, 150); ASSERT_EQ((int)energy.bank_energy[1].E_bg_act, 120); ASSERT_EQ((int)energy.bank_energy[2].E_bg_act, 100); ASSERT_EQ((int)energy.bank_energy[3].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[4].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[5].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[6].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[7].E_bg_act, 0); ASSERT_EQ((int)total_energy.E_bg_pre, 200); ASSERT_EQ((int)energy.bank_energy[0].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[1].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[2].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[3].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[4].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[5].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[6].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[7].E_bg_pre, 25); ASSERT_EQ((int)total_energy.E_RDA, 1280); ASSERT_EQ((int)energy.bank_energy[0].E_RDA, 320); ASSERT_EQ((int)energy.bank_energy[1].E_RDA, 320); ASSERT_EQ((int)energy.bank_energy[2].E_RDA, 640); ASSERT_EQ((int)energy.bank_energy[3].E_RDA, 0); ASSERT_EQ((int)energy.bank_energy[4].E_RDA, 0); ASSERT_EQ((int)energy.bank_energy[5].E_RDA, 0); ASSERT_EQ((int)energy.bank_energy[6].E_RDA, 0); ASSERT_EQ((int)energy.bank_energy[7].E_RDA, 0); ASSERT_EQ((int)total_energy.E_WRA, 1536); ASSERT_EQ((int)energy.bank_energy[0].E_WRA, 1024); ASSERT_EQ((int)energy.bank_energy[1].E_WRA, 512); ASSERT_EQ((int)energy.bank_energy[2].E_WRA, 0); ASSERT_EQ((int)energy.bank_energy[3].E_WRA, 0); ASSERT_EQ((int)energy.bank_energy[4].E_WRA, 0); ASSERT_EQ((int)energy.bank_energy[5].E_WRA, 0); ASSERT_EQ((int)energy.bank_energy[6].E_WRA, 0); ASSERT_EQ((int)energy.bank_energy[7].E_WRA, 0); ASSERT_EQ((int)total_energy.E_pre_RDA, 2240); ASSERT_EQ((int)energy.bank_energy[0].E_pre_RDA, 560); ASSERT_EQ((int)energy.bank_energy[1].E_pre_RDA, 560); ASSERT_EQ((int)energy.bank_energy[2].E_pre_RDA, 1120); ASSERT_EQ((int)energy.bank_energy[3].E_pre_RDA, 0); ASSERT_EQ((int)energy.bank_energy[4].E_pre_RDA, 0); ASSERT_EQ((int)energy.bank_energy[5].E_pre_RDA, 0); ASSERT_EQ((int)energy.bank_energy[6].E_pre_RDA, 0); ASSERT_EQ((int)energy.bank_energy[7].E_pre_RDA, 0); ASSERT_EQ((int)total_energy.E_pre_WRA, 1120+560); ASSERT_EQ((int)energy.bank_energy[0].E_pre_WRA, 1120); ASSERT_EQ((int)energy.bank_energy[1].E_pre_WRA, 560); ASSERT_EQ((int)energy.bank_energy[2].E_pre_WRA, 0); ASSERT_EQ((int)energy.bank_energy[3].E_pre_WRA, 0); ASSERT_EQ((int)energy.bank_energy[4].E_pre_WRA, 0); ASSERT_EQ((int)energy.bank_energy[5].E_pre_WRA, 0); ASSERT_EQ((int)energy.bank_energy[6].E_pre_WRA, 0); ASSERT_EQ((int)energy.bank_energy[7].E_pre_WRA, 0); }; ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_13.cpp ================================================ #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_13 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 5, CmdType::ACT, { 1, 0, 0 }}, { 10, CmdType::ACT, { 2, 0, 0 }}, { 15, CmdType::PRE, { 0, 0, 0 }}, { 20, CmdType::PREA, { 0, 0, 0 }}, { 25, CmdType::PRE, { 1, 0, 0 }}, { 30, CmdType::PREA, { 0, 0, 0 }}, { 40, CmdType::ACT, { 0, 0, 0 }}, { 40, CmdType::ACT, { 3, 0, 0 }}, { 50, CmdType::PRE, { 3, 0, 0 }}, { 125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); auto memSpec = DRAMPower::MemSpecDDR4::from_memspec(*data); memSpec.numberOfRanks = 1; memSpec.numberOfDevices = 1; memSpec.numberOfBanks = 8; memSpec.numberOfBankGroups = 1; memSpec.memTimingSpec.tRAS = 20; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tWR = 20; memSpec.memTimingSpec.tRAS = 20; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tWR = 12; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1; memSpec.memTimingSpec.tRP = 10; memSpec.memPowerSpec[0].vXX = 1; memSpec.memPowerSpec[0].iXX0 = 64; memSpec.memPowerSpec[0].iXX2N = 8; memSpec.memPowerSpec[0].iXX3N = 32; memSpec.memPowerSpec[0].iXX4R = 72; memSpec.memPowerSpec[0].iXX4W = 72; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iXX0; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_13, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 2); ASSERT_EQ(stats.bank[1].counter.act, 1); ASSERT_EQ(stats.bank[2].counter.act, 1); ASSERT_EQ(stats.bank[3].counter.act, 1); ASSERT_EQ(stats.bank[4].counter.act, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 105); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 100); ASSERT_EQ(stats.bank[1].cycles.act, 15); ASSERT_EQ(stats.bank[2].cycles.act, 10); ASSERT_EQ(stats.bank[3].cycles.act, 10); ASSERT_EQ(stats.bank[4].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 25); ASSERT_EQ(stats.bank[1].cycles.pre, 110); ASSERT_EQ(stats.bank[2].cycles.pre, 115); ASSERT_EQ(stats.bank[3].cycles.pre, 115); ASSERT_EQ(stats.bank[4].cycles.pre, 125); } TEST_F(DramPowerTest_DDR4_13, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ((int)total_energy.E_act, 2300*2); ASSERT_EQ((int)energy.bank_energy[0].E_act, 920*2); ASSERT_EQ((int)energy.bank_energy[1].E_act, 460*2); ASSERT_EQ((int)energy.bank_energy[2].E_act, 460*2); ASSERT_EQ((int)energy.bank_energy[3].E_act, 460*2); ASSERT_EQ((int)energy.bank_energy[4].E_act, 0); ASSERT_EQ((int)energy.bank_energy[5].E_act, 0); ASSERT_EQ((int)energy.bank_energy[6].E_act, 0); ASSERT_EQ((int)energy.bank_energy[7].E_act, 0); ASSERT_EQ((int)total_energy.E_pre, 2240); ASSERT_EQ((int)energy.bank_energy[0].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[1].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[2].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[3].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[4].E_pre, 0); ASSERT_EQ((int)energy.bank_energy[5].E_pre, 0); ASSERT_EQ((int)energy.bank_energy[6].E_pre, 0); ASSERT_EQ((int)energy.bank_energy[7].E_pre, 0); ASSERT_EQ(std::round(total_energy.E_bg_act), 1950); ASSERT_EQ(std::round(energy.E_bg_act_shared), 1680); ASSERT_EQ((int)energy.bank_energy[0].E_bg_act, 200); ASSERT_EQ((int)energy.bank_energy[1].E_bg_act, 30); ASSERT_EQ((int)energy.bank_energy[2].E_bg_act, 20); ASSERT_EQ((int)energy.bank_energy[3].E_bg_act, 20); ASSERT_EQ((int)energy.bank_energy[4].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[5].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[6].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[7].E_bg_act, 0); ASSERT_EQ((int)total_energy.E_bg_pre, 160); ASSERT_EQ((int)energy.bank_energy[0].E_bg_pre, 20); ASSERT_EQ((int)energy.bank_energy[1].E_bg_pre, 20); ASSERT_EQ((int)energy.bank_energy[2].E_bg_pre, 20); ASSERT_EQ((int)energy.bank_energy[3].E_bg_pre, 20); ASSERT_EQ((int)energy.bank_energy[4].E_bg_pre, 20); ASSERT_EQ((int)energy.bank_energy[5].E_bg_pre, 20); ASSERT_EQ((int)energy.bank_energy[6].E_bg_pre, 20); ASSERT_EQ((int)energy.bank_energy[7].E_bg_pre, 20); } ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_14.cpp ================================================ #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_14 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, {0, 0, 0} }, { 20, CmdType::PDEA }, { 30, CmdType::PDXA }, { 40, CmdType::PRE, {0, 0, 0} }, { 60, CmdType::PDEP }, { 65, CmdType::PDXP }, { 75, CmdType::REFA }, {100, CmdType::PDEP }, {125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); auto memSpec = DRAMPower::MemSpecDDR4::from_memspec(*data); memSpec.numberOfRanks = 1; memSpec.numberOfDevices = 1; memSpec.numberOfBanks = 8; memSpec.numberOfBankGroups = 2; memSpec.memTimingSpec.tRAS = 10; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tRCD = 20; memSpec.memTimingSpec.tRFC = 25; memSpec.memTimingSpec.tWR = 20; memSpec.memTimingSpec.tRP = 20; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1; memSpec.memPowerSpec[0].vXX = 1; memSpec.memPowerSpec[0].iXX0 = 64; memSpec.memPowerSpec[0].iXX2N = 8; memSpec.memPowerSpec[0].iXX2P = 6; memSpec.memPowerSpec[0].iXX3N = 32; memSpec.memPowerSpec[0].iXX3P = 20; memSpec.memPowerSpec[0].iXX4R = 72; memSpec.memPowerSpec[0].iXX4W = 72; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iXX0; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_14, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.activeTime(), 55); ASSERT_EQ(stats.rank_total[0].cycles.act, 55); ASSERT_EQ(stats.rank_total[0].cycles.pre, 30); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 30); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 55); ASSERT_EQ(stats.bank[0].cycles.activeTime(), 55); ASSERT_EQ(stats.bank[1].cycles.act, 25); ASSERT_EQ(stats.bank[1].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[2].cycles.act, 25); ASSERT_EQ(stats.bank[2].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[3].cycles.act, 25); ASSERT_EQ(stats.bank[3].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[4].cycles.act, 25); ASSERT_EQ(stats.bank[4].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[5].cycles.act, 25); ASSERT_EQ(stats.bank[5].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[6].cycles.act, 25); ASSERT_EQ(stats.bank[6].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[7].cycles.act, 25); ASSERT_EQ(stats.bank[7].cycles.activeTime(), 25); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 30); ASSERT_EQ(stats.bank[1].cycles.pre, 60); ASSERT_EQ(stats.bank[2].cycles.pre, 60); ASSERT_EQ(stats.bank[3].cycles.pre, 60); ASSERT_EQ(stats.bank[4].cycles.pre, 60); ASSERT_EQ(stats.bank[5].cycles.pre, 60); ASSERT_EQ(stats.bank[6].cycles.pre, 60); ASSERT_EQ(stats.bank[7].cycles.pre, 60); } TEST_F(DramPowerTest_DDR4_14, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act), 1340); ASSERT_EQ(std::round(energy.E_bg_act_shared), 880); ASSERT_EQ((int)energy.bank_energy[0].E_bg_act, 110); ASSERT_EQ((int)energy.bank_energy[1].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[2].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[3].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[4].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[5].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[6].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[7].E_bg_act, 50); ASSERT_EQ((int)total_energy.E_bg_pre, 240); ASSERT_EQ((int)energy.bank_energy[0].E_bg_pre, 30); ASSERT_EQ((int)energy.bank_energy[1].E_bg_pre, 30); ASSERT_EQ((int)energy.bank_energy[2].E_bg_pre, 30); ASSERT_EQ((int)energy.bank_energy[3].E_bg_pre, 30); ASSERT_EQ((int)energy.bank_energy[4].E_bg_pre, 30); ASSERT_EQ((int)energy.bank_energy[5].E_bg_pre, 30); ASSERT_EQ((int)energy.bank_energy[6].E_bg_pre, 30); ASSERT_EQ((int)energy.bank_energy[7].E_bg_pre, 30); ASSERT_EQ((int)energy.E_PDNA, 200); ASSERT_EQ((int)energy.E_PDNP, 180); }; ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_15.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_15 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0,0,0} }, { 5, CmdType::PDEA }, { 30, CmdType::PDXA }, { 40, CmdType::PRE, {0,0,0} }, { 45, CmdType::PDEP }, { 65, CmdType::PDXP }, { 75, CmdType::REFA }, { 85, CmdType::PDEP }, { 90, CmdType::PDXP }, {125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); auto memSpec = DRAMPower::MemSpecDDR4::from_memspec(*data); memSpec.bitWidth = 16; memSpec.numberOfRanks = 1; memSpec.numberOfDevices = 1; memSpec.numberOfBanks = 8; memSpec.numberOfBankGroups = 2; memSpec.memTimingSpec.tRAS = 10; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tRCD = 20; memSpec.memTimingSpec.tRFC = 25; memSpec.memTimingSpec.tWR = 20; memSpec.memTimingSpec.tRP = 20; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1; memSpec.memPowerSpec[0].vXX = 1; memSpec.memPowerSpec[0].iXX0 = 64; memSpec.memPowerSpec[0].iXX2N = 8; memSpec.memPowerSpec[0].iXX2P = 6; memSpec.memPowerSpec[0].iXX3N = 32; memSpec.memPowerSpec[0].iXX3P = 20; memSpec.memPowerSpec[0].iXX4R = 72; memSpec.memPowerSpec[0].iXX4W = 72; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iXX0; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_15, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 55); ASSERT_EQ(stats.rank_total[0].cycles.pre, 55); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 5); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 55); ASSERT_EQ(stats.bank[1].cycles.act, 25); ASSERT_EQ(stats.bank[2].cycles.act, 25); ASSERT_EQ(stats.bank[3].cycles.act, 25); ASSERT_EQ(stats.bank[4].cycles.act, 25); ASSERT_EQ(stats.bank[5].cycles.act, 25); ASSERT_EQ(stats.bank[6].cycles.act, 25); ASSERT_EQ(stats.bank[7].cycles.act, 25); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 55); ASSERT_EQ(stats.bank[1].cycles.pre, 85); ASSERT_EQ(stats.bank[2].cycles.pre, 85); ASSERT_EQ(stats.bank[3].cycles.pre, 85); ASSERT_EQ(stats.bank[4].cycles.pre, 85); ASSERT_EQ(stats.bank[5].cycles.pre, 85); ASSERT_EQ(stats.bank[6].cycles.pre, 85); ASSERT_EQ(stats.bank[7].cycles.pre, 85); } TEST_F(DramPowerTest_DDR4_15, CalcWindow) { SimulationStats window; auto iterate_to_timestamp = [this](auto & command, timestamp_t timestamp) { while (command != this->testPattern.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } return this->ddr->getWindowStats(timestamp); }; auto command = testPattern.begin(); // Cycle 5 window = iterate_to_timestamp(command, 5); ASSERT_EQ(window.rank_total[0].cycles.act, 5); ASSERT_EQ(window.bank[0].cycles.act, 5); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 5); // Cycle 10 window = iterate_to_timestamp(command, 10); ASSERT_EQ(window.rank_total[0].cycles.act, 10); ASSERT_EQ(window.bank[0].cycles.act, 10); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 10); // Cycle 15 window = iterate_to_timestamp(command, 15); ASSERT_EQ(window.rank_total[0].cycles.act, 15); ASSERT_EQ(window.bank[0].cycles.act, 15); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 15); // Cycle 20 window = iterate_to_timestamp(command, 20); ASSERT_EQ(window.rank_total[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 20); // Cycle 25 window = iterate_to_timestamp(command, 25); ASSERT_EQ(window.rank_total[0].cycles.act, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 5); ASSERT_EQ(window.bank[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 20); // Cycle 30 window = iterate_to_timestamp(command, 30); ASSERT_EQ(window.rank_total[0].cycles.act, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 20); // Cycle 35 window = iterate_to_timestamp(command, 35); ASSERT_EQ(window.rank_total[0].cycles.act, 25); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 25); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 25); // Cycle 40 window = iterate_to_timestamp(command, 40); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 30); // Cycle 45 window = iterate_to_timestamp(command, 45); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 5); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 35); // Cycle 50 window = iterate_to_timestamp(command, 50); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 10); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 40); // Cycle 55 window = iterate_to_timestamp(command, 55); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 15); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 15); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 45); // Cycle 60 window = iterate_to_timestamp(command, 60); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 20); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 50); // Cycle 65 window = iterate_to_timestamp(command, 65); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 20); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 50); // Cycle 70 window = iterate_to_timestamp(command, 70); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 25); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 25); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 55); // Cycle 75 window = iterate_to_timestamp(command, 75); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 80 window = iterate_to_timestamp(command, 80); ASSERT_EQ(window.rank_total[0].cycles.act, 35); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 35); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 5); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 85 window = iterate_to_timestamp(command, 85); ASSERT_EQ(window.rank_total[0].cycles.act, 40); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 40); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 10); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 90 window = iterate_to_timestamp(command, 90); ASSERT_EQ(window.rank_total[0].cycles.act, 45); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 45); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 15); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 95 window = iterate_to_timestamp(command, 95); ASSERT_EQ(window.rank_total[0].cycles.act, 50); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 50); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 20); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 100 window = iterate_to_timestamp(command, 100); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 105 window = iterate_to_timestamp(command, 105); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 35); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 35); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 65); // Cycle 110 window = iterate_to_timestamp(command, 110); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 40); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 40); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 70); // Cycle 115 window = iterate_to_timestamp(command, 115); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 45); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 45); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 75); // Cycle 120 window = iterate_to_timestamp(command, 120); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 50); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 50); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 80); // Cycle 125 window = iterate_to_timestamp(command, 125); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 55); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 55); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 85); }; TEST_F(DramPowerTest_DDR4_15, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act), 1340); ASSERT_EQ(std::round(energy.E_bg_act_shared), 880); ASSERT_EQ((int)energy.bank_energy[0].E_bg_act, 110); ASSERT_EQ((int)energy.bank_energy[1].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[2].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[3].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[4].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[5].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[6].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[7].E_bg_act, 50); ASSERT_EQ((int)total_energy.E_bg_pre, 440); ASSERT_EQ((int)energy.bank_energy[0].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[1].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[2].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[3].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[4].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[5].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[6].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[7].E_bg_pre, 55); ASSERT_EQ((int)energy.E_PDNA, 200); ASSERT_EQ((int)energy.E_PDNP, 30); }; ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_2.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_2 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 35, CmdType::PRE, { 0, 0, 0 }}, { 50, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; std::unique_ptr memSpec; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_2, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check bank command count: RD ASSERT_EQ(stats.bank[0].counter.reads, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.reads, 0); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.pre, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 35); ASSERT_EQ(stats.rank_total[0].cycles.pre, 15); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 35); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 15); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 50); } TEST_F(DramPowerTest_DDR4_2, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 179); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 208); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 436); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1189); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1183); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 467); ASSERT_EQ(std::round(total_energy.total()*1e12), 2479); } ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_3.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_3 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::PRE, { 0, 0, 0 }}, { 45, CmdType::PRE, { 3, 0, 0 }}, { 50, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; std::unique_ptr memSpec; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_3, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); } // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 45); ASSERT_EQ(stats.rank_total[0].cycles.pre, 5); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 35); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 25); else ASSERT_EQ(stats.bank[b].cycles.act, 0); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 15); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 25); else ASSERT_EQ(stats.bank[b].cycles.pre, 50); } } TEST_F(DramPowerTest_DDR4_3, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 359); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 436); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1531); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1521); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 156); ASSERT_EQ(std::round(total_energy.total()*1e12), 2897); } ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_4.cpp ================================================ #include #include #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_4 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 40, CmdType::RD, { 0, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 70, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; std::unique_ptr memSpec; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_4, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 2); else if(b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); }; // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 50); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 50); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 30); else ASSERT_EQ(stats.bank[b].cycles.act, 0); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 20); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 40); else ASSERT_EQ(stats.bank[b].cycles.pre, 70); } } TEST_F(DramPowerTest_DDR4_4, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 359); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 1307); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1704); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1690); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 623); ASSERT_EQ(std::round(total_energy.total()*1e12), 4408); } ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_5.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_5 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 40, CmdType::RD, { 0, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 65, CmdType::REFA, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; std::unique_ptr memSpec; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_5, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 2); else if( b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); }; // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check bank command count: REFA for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ ASSERT_EQ(stats.bank[b].counter.refAllBank, 1); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 75); ASSERT_EQ(stats.rank_total[0].cycles.pre, 25); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 75); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 55); else ASSERT_EQ(stats.bank[b].cycles.act, 25); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 25); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 45); else ASSERT_EQ(stats.bank[b].cycles.pre, 75); } } TEST_F(DramPowerTest_DDR4_5, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 359); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 1307); ASSERT_EQ(std::round(total_energy.E_ref_AB*1e12), 1812); ASSERT_EQ(std::round(total_energy.E_ref_SB*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 2615); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 2536); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 779); ASSERT_EQ(std::round(total_energy.total()*1e12), 7287); } ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_6.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_6 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RDA, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 65, CmdType::REFA, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; std::unique_ptr memSpec; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_6, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); } for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.readAuto, 1); else ASSERT_EQ(stats.bank[b].counter.readAuto, 0); } // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check bank command count: REFA for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ ASSERT_EQ(stats.bank[b].counter.refAllBank, 1); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 75); ASSERT_EQ(stats.rank_total[0].cycles.pre, 25); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 45); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 55); else ASSERT_EQ(stats.bank[b].cycles.act, 25); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 55); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 45); else ASSERT_EQ(stats.bank[b].cycles.pre, 75); } } TEST_F(DramPowerTest_DDR4_6, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 359); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 436); ASSERT_EQ(std::round(total_energy.E_RDA*1e12), 436); ASSERT_EQ(std::round(total_energy.E_ref_AB*1e12), 1812); ASSERT_EQ(std::round(total_energy.E_ref_SB*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 2610); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 2536); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 779); ASSERT_EQ(std::round(total_energy.total()*1e12), 6846); } ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_7.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_7 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::SREFEN, { 0, 0, 0 }}, { 40, CmdType::SREFEX, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; std::unique_ptr memSpec; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_7, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check bank command count: REFA for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.refAllBank, 1); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 25); ASSERT_EQ(stats.rank_total[0].cycles.pre, 60); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 15); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 25); // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 60); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 15); } TEST_F(DramPowerTest_DDR4_7, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 0); ASSERT_EQ(std::round(total_energy.E_ref_AB*1e12), 1812); ASSERT_EQ(std::round(energy.E_sref*1e12), 280); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 912); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 845); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 1869); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref)*1e12), 4872); } ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_8.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_8 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { //{ 0, CmdType::ACT, { 0, 0, 0 }}, { 0, CmdType::PDEA, { 0, 0, 0 }}, // Keep in mind earliest power down (entry 13 cycles) { 30, CmdType::PDXA, { 0, 0, 0 }}, //{ 30, CmdType::PRE, { 0, 0, 0 }}, { 45, CmdType::PDEP, { 0, 0, 0 }}, { 70, CmdType::PDXP, { 0, 0, 0 }}, { 85, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; std::unique_ptr memSpec; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_8, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 0); ASSERT_EQ(stats.rank_total[0].cycles.pre, 30); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 30); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 25); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 0); // TODO pre for banks 2-16 invalid // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 30); // Check bank specific PDNA cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownAct, 30); // Check bank specific PDNP cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownPre, 25); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 0); } TEST_F(DramPowerTest_DDR4_8, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 0); ASSERT_EQ(std::round(energy.E_sref*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 623); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 392); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 935); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref + energy.E_PDNA + energy.E_PDNP)*1e12), 1950); } ================================================ FILE: tests/tests_drampower/core/DDR4/ddr4_test_pattern_9.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR4_9 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 5, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::ACT, { 5, 0, 0 }}, { 20, CmdType::RD, { 0, 0, 0 }}, { 30, CmdType::PDEA, { 0, 0, 0 }}, { 50, CmdType::PDXA, { 0, 0, 0 }}, { 55, CmdType::RD, { 5, 0, 0 }}, { 60, CmdType::RD, { 0, 0, 0 }}, { 70, CmdType::PREA, { 0, 0, 0 }}, { 80, CmdType::PDEP, { 0, 0, 0 }}, { 95, CmdType::PDXP, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; std::unique_ptr memSpec; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR4_9, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 45); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 20); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 15); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats .bank[b].cycles.act, 45); }else if (b == 5){ ASSERT_EQ(stats .bank[b].cycles.act, 35); }else{ ASSERT_EQ(stats .bank[b].cycles.act, 0); } } // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats.bank[b].cycles.pre, 20); }else if (b == 5){ ASSERT_EQ(stats.bank[b].cycles.pre, 30); }else{ ASSERT_EQ(stats.bank[b].cycles.pre, 65); } } // Check bank specific PDNA cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownAct, 20); // Check bank specific PDNP cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownPre, 15); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 0); } TEST_F(DramPowerTest_DDR4_9, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 359); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 1307); ASSERT_EQ(std::round(energy.E_sref*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 415); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 235); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1535); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1521); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 623); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref + energy.E_PDNA + energy.E_PDNP)*1e12), 4890); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_multidevice_tests.cpp ================================================ /* Note This test is based on ddr5_test_pattern_4.cpp and assumes the correctness of the test. Calculation for 5 devices: E_act: 358.99038461538458 E_pre: 415.38461538461542 E_RD: 1307.0769230769231 E_bg_act: 1703.6538461538462 E_bg_act_shared: 1690.3846153846155 E_bg_pre: 623.07692307692309 Total: 4408.1826923076924 E_act * 5: 1794.9519230769229 E_pre * 5: 2076.9230769230771 E_RD * 5: 6535.3846153846155 E_bg_act * 5: 8518.269230769231 E_bg_act_shared * 5: 8451.9230769230775 E_bg_pre * 5: 3115.38461538461545 Total * 5: 22040.913461538462 */ #include #include #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_MultiDevice : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 40, CmdType::RD, { 0, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 70, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; uint64_t numberOfDevices; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); memSpec->numberOfDevices = 5; numberOfDevices = memSpec->numberOfDevices; ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_MultiDevice, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 2); else if(b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); }; // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 50); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 50); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 30); else ASSERT_EQ(stats.bank[b].cycles.act, 0); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 20); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 40); else ASSERT_EQ(stats.bank[b].cycles.pre, 70); } } TEST_F(DramPowerTest_DDR5_MultiDevice, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(energy.bank_energy.size(), numberOfDevices * memSpec->numberOfBanks); // Validate every device has the same bank energy for(size_t i = 0; i < numberOfDevices; i++){ for(size_t j = 0; j < memSpec->numberOfBanks; j++){ ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_act, energy.bank_energy[j].E_act); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_pre, energy.bank_energy[j].E_pre); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_RD, energy.bank_energy[j].E_RD); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_bg_act, energy.bank_energy[j].E_bg_act); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_bg_pre, energy.bank_energy[j].E_bg_pre); } } ASSERT_EQ(std::round(total_energy.E_act*1e12), 1795); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 2077); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 6535); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 8518); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 8452); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 3115); ASSERT_EQ(std::round(total_energy.total()*1e12), 22041); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_multirank_tests.cpp ================================================ #include #include #include #include #include #include #include "DRAMPower/command/Command.h" #include "DRAMPower/standards/ddr5/DDR5.h" #include "DRAMPower/memspec/MemSpecDDR5.h" using DRAMPower::CmdType; using DRAMPower::Command; using DRAMPower::DDR5; using DRAMPower::MemSpecDDR5; using DRAMPower::SimulationStats; using DRAMPower::TargetCoordinate; #define SZ_BITS(x) sizeof(x)*8 static constexpr uint8_t wr_data[] = { 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, }; static constexpr uint8_t rd_data[] = { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; class DDR5_MultirankTests : public ::testing::Test { public: void SetUp() { initSpec(); ddr = std::make_unique(*spec); } void initSpec() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); spec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); spec->numberOfRanks = 2; } void runCommands(const std::vector &commands) { for (const Command &command : commands) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); } } inline size_t bankIndex(int bank, int rank) { return rank * spec->numberOfBanks + bank; } std::unique_ptr spec; std::unique_ptr ddr; }; TEST_F(DDR5_MultirankTests, Pattern_1) { runCommands({ {0, CmdType::ACT, {1, 0, 0}}, {2, CmdType::ACT, {1, 0, 1}}, // rank 1 {4, CmdType::WR, {1, 0, 0, 0, 4}, wr_data, SZ_BITS(wr_data)}, {6, CmdType::RD, {1, 0, 1, 0, 4}, rd_data, SZ_BITS(rd_data)}, // rank 1 {10, CmdType::PRE, {1, 0, 1}}, // rank 1 {15, CmdType::PRE, {1, 0, 0}}, {20, CmdType::END_OF_SIMULATION} }); SimulationStats stats = ddr->getStats(); EXPECT_EQ(stats.bank.size(), spec->numberOfBanks * spec->numberOfRanks); EXPECT_EQ(stats.bank[bankIndex(1, 0)].cycles.act, 15); EXPECT_EQ(stats.bank[bankIndex(1, 1)].cycles.act, 8); EXPECT_EQ(stats.bank[bankIndex(1, 0)].cycles.pre, 5); EXPECT_EQ(stats.bank[bankIndex(1, 1)].cycles.pre, 12); EXPECT_EQ(stats.rank_total[0].cycles.act, 15); EXPECT_EQ(stats.rank_total[1].cycles.act, 8); } TEST_F(DDR5_MultirankTests, Pattern_2) { runCommands({ // TODO invalid state traversal {0, CmdType::ACT, TargetCoordinate{0, 0, 0}}, // r0 {5, CmdType::ACT, TargetCoordinate{0, 0, 1}}, // r1 {15, CmdType::RDA, TargetCoordinate{0, 0, 0, 0, 4}, rd_data, SZ_BITS(rd_data)}, // r0 {22, CmdType::ACT, TargetCoordinate{3, 0, 0}}, // r0 {35, CmdType::RD, TargetCoordinate{0, 0, 1, 0, 0}, rd_data, SZ_BITS(rd_data)}, // r1 {55, CmdType::RD, TargetCoordinate{3, 0, 0, 0, 3}, rd_data, SZ_BITS(rd_data)}, // r0 {60, CmdType::PREA, TargetCoordinate{0, 0, 0}}, // r0 {65, CmdType::PREA, TargetCoordinate{0, 0, 1}}, // r1 {75, CmdType::REFA, TargetCoordinate{0, 0, 0}}, // r0 {80, CmdType::REFA, TargetCoordinate{0, 0, 1}}, // r1 {130, CmdType::END_OF_SIMULATION}, }); // t = 0 ACT -> Start activate // r0, b0 // t = 5 ACT -> Start activate // r1, b0 // t = 15 RDA -> delayed pre after 5 cycles // r0, b0 // t = 20 RDA (implicit) -> End activate // r0, b0 // t = 22 ACT -> Start activate // r0, b3 // t = 35 RD -> Read // r1, b0 // t = 55 RD -> Read // r0, b3 // t = 60 PREA -> End activate // r0, b0, b3 // t = 65 PREA -> End activate // r1, b0 // t = 75 REFA -> Start activate // r0, b0, b3 // t = 80 REFA -> Start activate // r1, b0 // t = 100 REFA (implicit) -> End activate // r0, b0, b3 // t = 105 REFA (implicit) -> End activate // r1, b0 // t = 130 END_OF_SIMULATION SimulationStats stats = ddr->getStats(); EXPECT_EQ(stats.bank.size(), spec->numberOfBanks * spec->numberOfRanks); EXPECT_EQ(stats.bank[bankIndex(0, 0)].cycles.act, 45); EXPECT_EQ(stats.bank[bankIndex(3, 0)].cycles.act, 63); EXPECT_EQ(stats.bank[bankIndex(0, 1)].cycles.act, 85); EXPECT_EQ(stats.bank[bankIndex(0, 0)].cycles.pre, 130 - 45); EXPECT_EQ(stats.bank[bankIndex(3, 0)].cycles.pre, 130 - 63); EXPECT_EQ(stats.bank[bankIndex(0, 1)].cycles.pre, 130 - 85); EXPECT_EQ(stats.rank_total[0].cycles.act, 83); EXPECT_EQ(stats.rank_total[1].cycles.act, 85); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_0.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_0 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::PRE, { 0, 0, 0 }}, { 15, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_0, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 15); ASSERT_EQ(stats.rank_total[0].cycles.pre, 0); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 15); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 0); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 15); } TEST_F(DramPowerTest_DDR5_0, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 179); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 208); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 510); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 507); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 0); ASSERT_EQ(std::round(total_energy.total()*1e12), 897); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_1.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_1 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 35, CmdType::PRE, { 0, 0, 0 }}, { 35, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_1, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check bank command count: RD ASSERT_EQ(stats.bank[0].counter.reads, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.reads, 0); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.pre, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 35); ASSERT_EQ(stats.rank_total[0].cycles.pre, 0); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 35); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 0); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 35); } TEST_F(DramPowerTest_DDR5_1, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 179); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 208); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 436); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1189); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1183); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 0); ASSERT_EQ(std::round(total_energy.total()*1e12), 2012); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_10.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_10 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 11, 0, 0 }}, { 55, CmdType::PREA, { 0, 0, 0 }}, { 70, CmdType::REFSB, { 0, 0, 0 }}, { 75, CmdType::REFSB, { 11, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_10, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 80); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 0); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats.bank[b].cycles.act, 75); }else if (b == 3 || b == 8){ ASSERT_EQ(stats.bank[b].cycles.act, 20); }else if (b == 11){ ASSERT_EQ(stats.bank[b].cycles.act, 55); }else{ ASSERT_EQ(stats.bank[b].cycles.act, 0); } } // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats.bank[b].cycles.pre, 25); }else if (b == 3 || b == 8){ ASSERT_EQ(stats.bank[b].cycles.pre, 80); }else if (b == 11){ ASSERT_EQ(stats.bank[b].cycles.pre, 45); }else{ ASSERT_EQ(stats.bank[b].cycles.pre, 100); } } // Check bank specific PDNA cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownAct, 0); // Check bank specific PDNP cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownPre, 0); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 0); } TEST_F(DramPowerTest_DDR5_10, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 359); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 436); ASSERT_EQ(std::round(energy.E_sref*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 2733); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 2705); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 623); ASSERT_EQ(std::round(total_energy.E_ref_SB*1e12), 2696); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref + energy.E_PDNA + energy.E_PDNP)*1e12), 7262); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_11.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_11 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 11, 0, 0 }}, { 25, CmdType::ACT, { 3, 0, 0 }}, { 40, CmdType::WR, { 11, 0, 0 }}, { 45, CmdType::RD, { 3, 0, 0 }}, { 55, CmdType::PRESB, { 0, 0, 0 }}, { 65, CmdType::PRESB, { 11, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_11, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 65); ASSERT_EQ(stats.rank_total[0].cycles.pre, 35); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 0); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats.bank[b].cycles.act, 55); }else if (b == 3){ ASSERT_EQ(stats.bank[b].cycles.act, 40); }else if (b == 11){ ASSERT_EQ(stats.bank[b].cycles.act, 45); }else{ ASSERT_EQ(stats.bank[b].cycles.act, 0); } } // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats.bank[b].cycles.pre, 45); }else if (b == 3){ ASSERT_EQ(stats.bank[b].cycles.pre, 60); }else if (b == 11){ ASSERT_EQ(stats.bank[b].cycles.pre, 55); }else{ ASSERT_EQ(stats.bank[b].cycles.pre, 100); } } // Check bank specific PDNA cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownAct, 0); // Check bank specific PDNP cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownPre, 0); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 0); } TEST_F(DramPowerTest_DDR5_11, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 538); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 623); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 871); ASSERT_EQ(std::round(total_energy.E_WR*1e12), 353); ASSERT_EQ(std::round(energy.E_sref*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 2221); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 2198); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 1090); ASSERT_EQ(std::round(total_energy.E_ref_SB*1e12), 0); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref + energy.E_PDNA + energy.E_PDNP)*1e12), 5697); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_12.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_12 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { // Timestamp, Cmd, { Bank, BG, Rank} { 0, CmdType::ACT, { 0, 0, 0 } }, { 10, CmdType::ACT, { 1, 0, 0 } }, { 15, CmdType::PRE, { 0, 0, 0 } }, { 25, CmdType::ACT, { 3, 0, 0 } }, { 25, CmdType::PRE, { 1, 0, 0 } }, { 30, CmdType::ACT, { 0, 0, 0 } }, { 35, CmdType::ACT, { 2, 0, 0 } }, { 40, CmdType::PRE, { 1, 0, 0 } }, { 40, CmdType::PRE, { 3, 0, 0 } }, { 45, CmdType::PRE, { 0, 0, 0 } }, { 50, CmdType::PRE, { 2, 0, 0 } }, { 85, CmdType::ACT, { 7, 0, 0 } }, { 100, CmdType::PRE, { 7, 0, 0 } }, { 120, CmdType::ACT, { 6, 0, 0 } }, { 125, CmdType::END_OF_SIMULATION}, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); auto memSpec = DRAMPower::MemSpecDDR5::from_memspec(*data); memSpec.bitWidth = 16; memSpec.numberOfDevices = 1; memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 8; memSpec.banksPerGroup = 8; memSpec.numberOfBankGroups = 1; memSpec.burstLength = 8; memSpec.dataRate = 2; memSpec.memTimingSpec.tCK = 1; memSpec.memTimingSpec.tRAS = 10; memSpec.memTimingSpec.tRP = 10; memSpec.memPowerSpec[0].vXX = 1; memSpec.memPowerSpec[0].iXX0 = 64; memSpec.memPowerSpec[0].iXX3N = 32; memSpec.memPowerSpec[0].iXX2N = 8; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iXX0; memSpec.bwParams.bwPowerFactRho = 0.333333333; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_12, Pattern1) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; // Inspect first rank const auto & rank_1 = internal::DDR5TestAccessor.getRanks(ddr->getCore()).at(0); auto stats = ddr->getStats(); // Check global count ASSERT_EQ(stats.rank_total[0].cycles.act, 70); ASSERT_EQ(stats.rank_total[0].cycles.pre, 55); // Check per-bank ACT count ASSERT_EQ(stats.bank[0].cycles.act, 30); ASSERT_EQ(stats.bank[1].cycles.act, 15); ASSERT_EQ(stats.bank[2].cycles.act, 15); ASSERT_EQ(stats.bank[3].cycles.act, 15); ASSERT_EQ(stats.bank[7].cycles.act, 15); ASSERT_EQ(stats.bank[6].cycles.act, 5); ASSERT_EQ(stats.bank[4].cycles.act, 0); ASSERT_EQ(stats.bank[5].cycles.act, 0); // Check per-bank PRE count ASSERT_EQ(stats.bank[0].cycles.pre, 95); ASSERT_EQ(stats.bank[1].cycles.pre, 110); ASSERT_EQ(stats.bank[2].cycles.pre, 110); ASSERT_EQ(stats.bank[3].cycles.pre, 110); ASSERT_EQ(stats.bank[7].cycles.pre, 110); ASSERT_EQ(stats.bank[6].cycles.pre, 120); ASSERT_EQ(stats.bank[4].cycles.pre, 125); ASSERT_EQ(stats.bank[5].cycles.pre, 125); // Check global command count ASSERT_EQ(rank_1.commandCounter.get(CmdType::ACT), 7); ASSERT_EQ(rank_1.commandCounter.get(CmdType::PRE), 7); // per-bank ACT command count ASSERT_EQ(stats.bank[0].counter.act, 2); ASSERT_EQ(stats.bank[1].counter.act, 1); ASSERT_EQ(stats.bank[2].counter.act, 1); ASSERT_EQ(stats.bank[3].counter.act, 1); ASSERT_EQ(stats.bank[6].counter.act, 1); ASSERT_EQ(stats.bank[7].counter.act, 1); ASSERT_EQ(stats.bank[4].counter.act, 0); ASSERT_EQ(stats.bank[5].counter.act, 0); // per-bank PRE command count ASSERT_EQ(stats.bank[0].counter.pre, 2); ASSERT_EQ(stats.bank[1].counter.pre, 1); ASSERT_EQ(stats.bank[2].counter.pre, 1); ASSERT_EQ(stats.bank[3].counter.pre, 1); ASSERT_EQ(stats.bank[7].counter.pre, 1); ASSERT_EQ(stats.bank[6].counter.pre, 0); ASSERT_EQ(stats.bank[4].counter.pre, 0); ASSERT_EQ(stats.bank[5].counter.pre, 0); } TEST_F(DramPowerTest_DDR5_12, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ((int)total_energy.E_act, 3220); ASSERT_EQ((int)energy.bank_energy[0].E_act, 920); ASSERT_EQ((int)energy.bank_energy[1].E_act, 460); ASSERT_EQ((int)energy.bank_energy[2].E_act, 460); ASSERT_EQ((int)energy.bank_energy[3].E_act, 460); ASSERT_EQ((int)energy.bank_energy[4].E_act, 0); ASSERT_EQ((int)energy.bank_energy[5].E_act, 0); ASSERT_EQ((int)energy.bank_energy[6].E_act, 460); ASSERT_EQ((int)energy.bank_energy[7].E_act, 460); ASSERT_EQ((int)total_energy.E_pre, 3360); ASSERT_EQ((int)energy.bank_energy[0].E_pre, 1120); ASSERT_EQ((int)energy.bank_energy[1].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[2].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[3].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[4].E_pre, 0); ASSERT_EQ((int)energy.bank_energy[5].E_pre, 0); ASSERT_EQ((int)energy.bank_energy[6].E_pre, 0); ASSERT_EQ((int)energy.bank_energy[7].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[0].E_bg_act, 60); ASSERT_EQ((int)energy.bank_energy[1].E_bg_act, 30); ASSERT_EQ((int)energy.bank_energy[2].E_bg_act, 30); ASSERT_EQ((int)energy.bank_energy[3].E_bg_act, 30); ASSERT_EQ((int)energy.bank_energy[4].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[5].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[6].E_bg_act, 10); ASSERT_EQ((int)energy.bank_energy[7].E_bg_act, 30); ASSERT_EQ(std::round(energy.E_bg_act_shared), 1120); ASSERT_EQ(std::round(total_energy.E_bg_act), 1310); ASSERT_EQ((int)total_energy.E_bg_pre, 440); ASSERT_EQ((int)energy.bank_energy[0].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[1].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[2].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[3].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[4].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[5].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[6].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[7].E_bg_pre, 55); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_13.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_13 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0} }, { 5, CmdType::ACT, { 1, 0, 0} }, { 10, CmdType::ACT, { 2, 0, 0} }, { 15, CmdType::ACT, { 3, 0, 0} }, { 20, CmdType::ACT, { 4, 0, 0} }, { 25, CmdType::ACT, { 5, 0, 0} }, { 30, CmdType::ACT, { 6, 0, 0} }, { 35, CmdType::ACT, { 7, 0, 0} }, { 35, CmdType::RD, { 0, 0, 0} }, { 40, CmdType::RD, { 0, 0, 0} }, { 40, CmdType::RD, { 1, 0, 0} }, { 45, CmdType::RD, { 0, 0, 0} }, { 45, CmdType::RD, { 2, 0, 0} }, { 50, CmdType::RD, { 3, 0, 0} }, { 55, CmdType::RD, { 4, 0, 0} }, { 60, CmdType::RD, { 5, 0, 0} }, { 65, CmdType::RD, { 6, 0, 0} }, { 70, CmdType::RD, { 7, 0, 0} }, { 75, CmdType::RD, { 7, 0, 0} }, { 80, CmdType::RD, { 7, 0, 0} }, { 85, CmdType::WR, { 7, 0, 0} }, { 90, CmdType::WR, { 6, 0, 0} }, { 95, CmdType::WR, { 5, 0, 0} }, { 100, CmdType::WR, { 4, 0, 0} }, { 105, CmdType::WR, { 3, 0, 0} }, { 110, CmdType::WR, { 2, 0, 0} }, { 110, CmdType::WR, { 0, 0, 0} }, { 115, CmdType::WR, { 1, 0, 0} }, { 115, CmdType::RD, { 0, 0, 0} }, { 120, CmdType::WR, { 0, 0, 0} }, { 120, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); auto memSpec = DRAMPower::MemSpecDDR5::from_memspec(*data); memSpec.bitWidth = 16; memSpec.numberOfDevices = 1; memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 8; memSpec.banksPerGroup = 8; memSpec.numberOfBankGroups = 1; memSpec.memTimingSpec.tCK = 1; memSpec.memPowerSpec[0].vXX = 1; memSpec.memPowerSpec[0].iXX0 = 64; memSpec.memPowerSpec[0].iXX3N = 32; memSpec.memPowerSpec[0].iXX4R = 72; memSpec.memPowerSpec[0].iXX4W = 96; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iXX0; memSpec.burstLength = 16; memSpec.dataRate = 2; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_13, Pattern_2) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; // Inspect first rank const Rank & rank_1 = internal::DDR5TestAccessor.getRanks(ddr->getCore()).at(0); // Check global command count ASSERT_EQ(rank_1.commandCounter.get(CmdType::RD), 13); ASSERT_EQ(rank_1.commandCounter.get(CmdType::WR), 9); // Check bank RD command count ASSERT_EQ(rank_1.banks[0].counter.reads, 4); ASSERT_EQ(rank_1.banks[1].counter.reads, 1); ASSERT_EQ(rank_1.banks[2].counter.reads, 1); ASSERT_EQ(rank_1.banks[3].counter.reads, 1); ASSERT_EQ(rank_1.banks[4].counter.reads, 1); ASSERT_EQ(rank_1.banks[5].counter.reads, 1); ASSERT_EQ(rank_1.banks[6].counter.reads, 1); ASSERT_EQ(rank_1.banks[7].counter.reads, 3); // Check bank WR command count ASSERT_EQ(rank_1.banks[0].counter.writes, 2); ASSERT_EQ(rank_1.banks[1].counter.writes, 1); ASSERT_EQ(rank_1.banks[2].counter.writes, 1); ASSERT_EQ(rank_1.banks[3].counter.writes, 1); ASSERT_EQ(rank_1.banks[4].counter.writes, 1); ASSERT_EQ(rank_1.banks[5].counter.writes, 1); ASSERT_EQ(rank_1.banks[6].counter.writes, 1); ASSERT_EQ(rank_1.banks[7].counter.writes, 1); } TEST_F(DramPowerTest_DDR5_13, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ((int)total_energy.E_RD, 4160); ASSERT_EQ((int)energy.bank_energy[0].E_RD, 1280); ASSERT_EQ((int)energy.bank_energy[1].E_RD, 320); ASSERT_EQ((int)energy.bank_energy[2].E_RD, 320); ASSERT_EQ((int)energy.bank_energy[3].E_RD, 320); ASSERT_EQ((int)energy.bank_energy[4].E_RD, 320); ASSERT_EQ((int)energy.bank_energy[5].E_RD, 320); ASSERT_EQ((int)energy.bank_energy[6].E_RD, 320); ASSERT_EQ((int)energy.bank_energy[7].E_RD, 960); ASSERT_EQ((int)total_energy.E_WR, 4608); ASSERT_EQ((int)energy.bank_energy[0].E_WR, 1024); ASSERT_EQ((int)energy.bank_energy[1].E_WR, 512); ASSERT_EQ((int)energy.bank_energy[2].E_WR, 512); ASSERT_EQ((int)energy.bank_energy[3].E_WR, 512); ASSERT_EQ((int)energy.bank_energy[4].E_WR, 512); ASSERT_EQ((int)energy.bank_energy[5].E_WR, 512); ASSERT_EQ((int)energy.bank_energy[6].E_WR, 512); ASSERT_EQ((int)energy.bank_energy[7].E_WR, 512); }; ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_14.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_14 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { // Timestamp, Cmd, { Bank, BG, Rank} { 0, CmdType::ACT, { 0, 0, 0 } }, { 10, CmdType::RDA, { 0, 0, 0 } }, { 15, CmdType::ACT, { 1, 0, 0 } }, { 15, CmdType::ACT, { 2, 0, 0 } }, { 25, CmdType::WRA, { 1, 0, 0 } }, { 30, CmdType::PRE, { 0, 0, 0 } }, { 35, CmdType::RDA, { 2, 0, 0 } }, { 50, CmdType::ACT, { 0, 0, 0 } }, { 50, CmdType::ACT, { 2, 0, 0 } }, { 60, CmdType::WRA, { 0, 0, 0 } }, { 60, CmdType::RDA, { 2, 0, 0 } }, { 75, CmdType::ACT, { 1, 0, 0 } }, { 80, CmdType::ACT, { 0, 0, 0 } }, { 85, CmdType::WRA, { 0, 0, 0 } }, { 95, CmdType::RDA, { 1, 0, 0 } }, { 125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); auto memSpec = DRAMPower::MemSpecDDR5::from_memspec(*data); memSpec.bitWidth = 16; memSpec.numberOfDevices = 1; memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 8; memSpec.banksPerGroup = 8; memSpec.numberOfBankGroups = 1; memSpec.memTimingSpec.tRAS = 20; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tWR = 12; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1; memSpec.memTimingSpec.tRP = 10; memSpec.memPowerSpec[0].vXX = 1; memSpec.memPowerSpec[0].iXX0 = 64; memSpec.memPowerSpec[0].iXX2N = 8; memSpec.memPowerSpec[0].iXX3N = 32; memSpec.memPowerSpec[0].iXX4R = 72; memSpec.memPowerSpec[0].iXX4W = 96; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iXX0; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; memSpec.memTimingSpec.tBurst = memSpec.burstLength/memSpec.dataRate; memSpec.prechargeOffsetRD = memSpec.memTimingSpec.tRTP; memSpec.prechargeOffsetWR = memSpec.memTimingSpec.tBurst + memSpec.memTimingSpec.tWL + memSpec.memTimingSpec.tWR; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_14, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; const Rank & rank_1 = internal::DDR5TestAccessor.getRanks(ddr->getCore()).at(0); auto stats = ddr->getStats(); // Check global command count ASSERT_EQ(rank_1.commandCounter.get(CmdType::ACT), 7); ASSERT_EQ(rank_1.commandCounter.get(CmdType::PRE), 1); ASSERT_EQ(rank_1.commandCounter.get(CmdType::RDA), 4); ASSERT_EQ(rank_1.commandCounter.get(CmdType::WRA), 3); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 3); ASSERT_EQ(stats.bank[1].counter.act, 2); ASSERT_EQ(stats.bank[2].counter.act, 2); ASSERT_EQ(stats.bank[3].counter.act, 0); ASSERT_EQ(stats.bank[4].counter.act, 0); ASSERT_EQ(stats.bank[5].counter.act, 0); ASSERT_EQ(stats.bank[6].counter.act, 0); ASSERT_EQ(stats.bank[7].counter.act, 0); // Check bank command count: RDA ASSERT_EQ(stats.bank[0].counter.readAuto, 1); ASSERT_EQ(stats.bank[1].counter.readAuto, 1); ASSERT_EQ(stats.bank[2].counter.readAuto, 2); ASSERT_EQ(stats.bank[3].counter.readAuto, 0); ASSERT_EQ(stats.bank[4].counter.readAuto, 0); ASSERT_EQ(stats.bank[5].counter.readAuto, 0); ASSERT_EQ(stats.bank[6].counter.readAuto, 0); ASSERT_EQ(stats.bank[7].counter.readAuto, 0); // Check bank command count: WRA ASSERT_EQ(stats.bank[0].counter.writeAuto, 2); ASSERT_EQ(stats.bank[1].counter.writeAuto, 1); ASSERT_EQ(stats.bank[2].counter.writeAuto, 0); ASSERT_EQ(stats.bank[3].counter.writeAuto, 0); ASSERT_EQ(stats.bank[4].counter.writeAuto, 0); ASSERT_EQ(stats.bank[5].counter.writeAuto, 0); ASSERT_EQ(stats.bank[6].counter.writeAuto, 0); ASSERT_EQ(stats.bank[7].counter.writeAuto, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 100); ASSERT_EQ(stats.rank_total[0].cycles.pre, 25); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 75); ASSERT_EQ(stats.bank[1].cycles.act, 60); ASSERT_EQ(stats.bank[2].cycles.act, 50); ASSERT_EQ(stats.bank[3].cycles.act, 0); ASSERT_EQ(stats.bank[4].cycles.act, 0); ASSERT_EQ(stats.bank[5].cycles.act, 0); ASSERT_EQ(stats.bank[6].cycles.act, 0); ASSERT_EQ(stats.bank[7].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 50); ASSERT_EQ(stats.bank[1].cycles.pre, 65); ASSERT_EQ(stats.bank[2].cycles.pre, 75); ASSERT_EQ(stats.bank[3].cycles.pre, 125); ASSERT_EQ(stats.bank[4].cycles.pre, 125); ASSERT_EQ(stats.bank[5].cycles.pre, 125); ASSERT_EQ(stats.bank[6].cycles.pre, 125); ASSERT_EQ(stats.bank[7].cycles.pre, 125); } TEST_F(DramPowerTest_DDR5_14, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act), 1970); ASSERT_EQ(std::round(energy.E_bg_act_shared), 1600); ASSERT_EQ((int)energy.bank_energy[0].E_bg_act, 150); ASSERT_EQ((int)energy.bank_energy[1].E_bg_act, 120); ASSERT_EQ((int)energy.bank_energy[2].E_bg_act, 100); ASSERT_EQ((int)energy.bank_energy[3].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[4].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[5].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[6].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[7].E_bg_act, 0); ASSERT_EQ((int)total_energy.E_bg_pre, 200); ASSERT_EQ((int)energy.bank_energy[0].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[1].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[2].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[3].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[4].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[5].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[6].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[7].E_bg_pre, 25); ASSERT_EQ((int)total_energy.E_RDA, 1280); ASSERT_EQ((int)energy.bank_energy[0].E_RDA, 320); ASSERT_EQ((int)energy.bank_energy[1].E_RDA, 320); ASSERT_EQ((int)energy.bank_energy[2].E_RDA, 640); ASSERT_EQ((int)energy.bank_energy[3].E_RDA, 0); ASSERT_EQ((int)energy.bank_energy[4].E_RDA, 0); ASSERT_EQ((int)energy.bank_energy[5].E_RDA, 0); ASSERT_EQ((int)energy.bank_energy[6].E_RDA, 0); ASSERT_EQ((int)energy.bank_energy[7].E_RDA, 0); ASSERT_EQ((int)total_energy.E_WRA, 1536); ASSERT_EQ((int)energy.bank_energy[0].E_WRA, 1024); ASSERT_EQ((int)energy.bank_energy[1].E_WRA, 512); ASSERT_EQ((int)energy.bank_energy[2].E_WRA, 0); ASSERT_EQ((int)energy.bank_energy[3].E_WRA, 0); ASSERT_EQ((int)energy.bank_energy[4].E_WRA, 0); ASSERT_EQ((int)energy.bank_energy[5].E_WRA, 0); ASSERT_EQ((int)energy.bank_energy[6].E_WRA, 0); ASSERT_EQ((int)energy.bank_energy[7].E_WRA, 0); ASSERT_EQ((int)total_energy.E_pre_RDA, 2240); ASSERT_EQ((int)energy.bank_energy[0].E_pre_RDA, 560); ASSERT_EQ((int)energy.bank_energy[1].E_pre_RDA, 560); ASSERT_EQ((int)energy.bank_energy[2].E_pre_RDA, 1120); ASSERT_EQ((int)energy.bank_energy[3].E_pre_RDA, 0); ASSERT_EQ((int)energy.bank_energy[4].E_pre_RDA, 0); ASSERT_EQ((int)energy.bank_energy[5].E_pre_RDA, 0); ASSERT_EQ((int)energy.bank_energy[6].E_pre_RDA, 0); ASSERT_EQ((int)energy.bank_energy[7].E_pre_RDA, 0); ASSERT_EQ((int)total_energy.E_pre_WRA, 1120+560); ASSERT_EQ((int)energy.bank_energy[0].E_pre_WRA, 1120); ASSERT_EQ((int)energy.bank_energy[1].E_pre_WRA, 560); ASSERT_EQ((int)energy.bank_energy[2].E_pre_WRA, 0); ASSERT_EQ((int)energy.bank_energy[3].E_pre_WRA, 0); ASSERT_EQ((int)energy.bank_energy[4].E_pre_WRA, 0); ASSERT_EQ((int)energy.bank_energy[5].E_pre_WRA, 0); ASSERT_EQ((int)energy.bank_energy[6].E_pre_WRA, 0); ASSERT_EQ((int)energy.bank_energy[7].E_pre_WRA, 0); }; ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_15.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_15 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 5, CmdType::ACT, { 1, 0, 0 }}, { 10, CmdType::ACT, { 2, 0, 0 }}, { 15, CmdType::PRE, { 0, 0, 0 }}, { 20, CmdType::PREA, { 0, 0, 0 }}, { 25, CmdType::PRE, { 1, 0, 0 }}, { 30, CmdType::PREA, { 0, 0, 0 }}, { 40, CmdType::ACT, { 0, 0, 0 }}, { 40, CmdType::ACT, { 3, 0, 0 }}, { 50, CmdType::PRE, { 3, 0, 0 }}, { 125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); auto memSpec = DRAMPower::MemSpecDDR5::from_memspec(*data); memSpec.bitWidth = 16; memSpec.numberOfDevices = 1; memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 8; memSpec.banksPerGroup = 8; memSpec.numberOfBankGroups = 1; memSpec.memTimingSpec.tRAS = 20; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tWR = 20; memSpec.memTimingSpec.tRAS = 20; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tWR = 12; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1; memSpec.memTimingSpec.tRP = 10; memSpec.memPowerSpec[0].vXX = 1; memSpec.memPowerSpec[0].iXX0 = 64; memSpec.memPowerSpec[0].iXX2N = 8; memSpec.memPowerSpec[0].iXX3N = 32; memSpec.memPowerSpec[0].iXX4R = 72; memSpec.memPowerSpec[0].iXX4W = 72; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iXX0; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_15, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 2); ASSERT_EQ(stats.bank[1].counter.act, 1); ASSERT_EQ(stats.bank[2].counter.act, 1); ASSERT_EQ(stats.bank[3].counter.act, 1); ASSERT_EQ(stats.bank[4].counter.act, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 105); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 100); ASSERT_EQ(stats.bank[1].cycles.act, 15); ASSERT_EQ(stats.bank[2].cycles.act, 10); ASSERT_EQ(stats.bank[3].cycles.act, 10); ASSERT_EQ(stats.bank[4].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 25); ASSERT_EQ(stats.bank[1].cycles.pre, 110); ASSERT_EQ(stats.bank[2].cycles.pre, 115); ASSERT_EQ(stats.bank[3].cycles.pre, 115); ASSERT_EQ(stats.bank[4].cycles.pre, 125); } TEST_F(DramPowerTest_DDR5_15, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ((int)total_energy.E_act, 2300*2); ASSERT_EQ((int)energy.bank_energy[0].E_act, 920*2); ASSERT_EQ((int)energy.bank_energy[1].E_act, 460*2); ASSERT_EQ((int)energy.bank_energy[2].E_act, 460*2); ASSERT_EQ((int)energy.bank_energy[3].E_act, 460*2); ASSERT_EQ((int)energy.bank_energy[4].E_act, 0); ASSERT_EQ((int)energy.bank_energy[5].E_act, 0); ASSERT_EQ((int)energy.bank_energy[6].E_act, 0); ASSERT_EQ((int)energy.bank_energy[7].E_act, 0); ASSERT_EQ((int)total_energy.E_pre, 2240); ASSERT_EQ((int)energy.bank_energy[0].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[1].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[2].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[3].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[4].E_pre, 0); ASSERT_EQ((int)energy.bank_energy[5].E_pre, 0); ASSERT_EQ((int)energy.bank_energy[6].E_pre, 0); ASSERT_EQ((int)energy.bank_energy[7].E_pre, 0); ASSERT_EQ(std::round(total_energy.E_bg_act), 1950); ASSERT_EQ(std::round(energy.E_bg_act_shared), 1680); ASSERT_EQ((int)energy.bank_energy[0].E_bg_act, 200); ASSERT_EQ((int)energy.bank_energy[1].E_bg_act, 30); ASSERT_EQ((int)energy.bank_energy[2].E_bg_act, 20); ASSERT_EQ((int)energy.bank_energy[3].E_bg_act, 20); ASSERT_EQ((int)energy.bank_energy[4].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[5].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[6].E_bg_act, 0); ASSERT_EQ((int)energy.bank_energy[7].E_bg_act, 0); ASSERT_EQ((int)total_energy.E_bg_pre, 160); ASSERT_EQ((int)energy.bank_energy[0].E_bg_pre, 20); ASSERT_EQ((int)energy.bank_energy[1].E_bg_pre, 20); ASSERT_EQ((int)energy.bank_energy[2].E_bg_pre, 20); ASSERT_EQ((int)energy.bank_energy[3].E_bg_pre, 20); ASSERT_EQ((int)energy.bank_energy[4].E_bg_pre, 20); ASSERT_EQ((int)energy.bank_energy[5].E_bg_pre, 20); ASSERT_EQ((int)energy.bank_energy[6].E_bg_pre, 20); ASSERT_EQ((int)energy.bank_energy[7].E_bg_pre, 20); }; ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_16.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_16 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::REFSB, {0, 0, 0 }}, { 10, CmdType::REFSB, {1, 0, 0 }}, { 45, CmdType::REFSB, {2, 0, 0 }}, { 50, CmdType::ACT , {7, 0, 0 }}, { 55, CmdType::ACT , {3, 0, 0 }}, { 70, CmdType::PRE , {7, 0, 0 }}, { 80, CmdType::PRESB, {3, 0, 0 }}, { 95, CmdType::REFSB, {3, 0, 0 }}, {100, CmdType::REFSB, {0, 0, 0 }}, {125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); auto memSpec = DRAMPower::MemSpecDDR5::from_memspec(*data); memSpec.bitWidth = 16; memSpec.numberOfDevices = 1; memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 8; memSpec.numberOfBankGroups = 2; memSpec.banksPerGroup = 4; memSpec.memTimingSpec.tRAS = 10; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tRFCsb = 25; memSpec.memTimingSpec.tWR = 20; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1; memSpec.memTimingSpec.tRP = 10; memSpec.memPowerSpec[0].vXX = 1; memSpec.memPowerSpec[0].iXX0 = 64; memSpec.memPowerSpec[0].iXX2N = 8; memSpec.memPowerSpec[0].iXX3N = 32; memSpec.memPowerSpec[0].iXX4R = 72; memSpec.memPowerSpec[0].iXX4W = 72; memSpec.memPowerSpec[0].iXX5C = 28; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iXX0; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_16, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[3].counter.act, 1); ASSERT_EQ(stats.bank[7].counter.act, 1); // Check bank command count: PRE ASSERT_EQ(stats.bank[3].counter.pre, 1); ASSERT_EQ(stats.bank[7].counter.pre, 1); // Check bank command count: REFSB ASSERT_EQ(stats.bank[0].counter.refSameBank, 2); ASSERT_EQ(stats.bank[4].counter.refSameBank, 2); ASSERT_EQ(stats.bank[1].counter.refSameBank, 1); ASSERT_EQ(stats.bank[2].counter.refSameBank, 1); ASSERT_EQ(stats.bank[3].counter.refSameBank, 1); ASSERT_EQ(stats.bank[5].counter.refSameBank, 1); ASSERT_EQ(stats.bank[6].counter.refSameBank, 1); ASSERT_EQ(stats.bank[7].counter.refSameBank, 1); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 100); ASSERT_EQ(stats.rank_total[0].cycles.pre, 25); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 50); ASSERT_EQ(stats.bank[4].cycles.act, 50); ASSERT_EQ(stats.bank[3].cycles.act, 50); ASSERT_EQ(stats.bank[1].cycles.act, 25); ASSERT_EQ(stats.bank[2].cycles.act, 25); ASSERT_EQ(stats.bank[5].cycles.act, 25); ASSERT_EQ(stats.bank[6].cycles.act, 25); ASSERT_EQ(stats.bank[7].cycles.act, 45); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 75); ASSERT_EQ(stats.bank[3].cycles.pre, 75); ASSERT_EQ(stats.bank[4].cycles.pre, 75); ASSERT_EQ(stats.bank[1].cycles.pre, 100); ASSERT_EQ(stats.bank[2].cycles.pre, 100); ASSERT_EQ(stats.bank[5].cycles.pre, 100); ASSERT_EQ(stats.bank[6].cycles.pre, 100); ASSERT_EQ(stats.bank[7].cycles.pre, 80); } TEST_F(DramPowerTest_DDR5_16, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act), 2190); ASSERT_EQ(std::round(energy.E_bg_act_shared), 1600); ASSERT_EQ((int)energy.bank_energy[0].E_bg_act, 100); ASSERT_EQ((int)energy.bank_energy[1].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[2].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[3].E_bg_act, 100); ASSERT_EQ((int)energy.bank_energy[4].E_bg_act, 100); ASSERT_EQ((int)energy.bank_energy[5].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[6].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[7].E_bg_act, 90); ASSERT_EQ((int)total_energy.E_bg_pre, 200); ASSERT_EQ((int)energy.bank_energy[0].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[1].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[2].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[3].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[4].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[5].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[6].E_bg_pre, 25); ASSERT_EQ((int)energy.bank_energy[7].E_bg_pre, 25); ASSERT_EQ((int)total_energy.E_pre, 1120); ASSERT_EQ((int)energy.bank_energy[3].E_pre, 560); ASSERT_EQ((int)energy.bank_energy[7].E_pre, 560); ASSERT_EQ((int)total_energy.E_ref_SB, 1000); ASSERT_EQ((int)energy.bank_energy[0].E_ref_SB, 200); ASSERT_EQ((int)energy.bank_energy[1].E_ref_SB, 100); ASSERT_EQ((int)energy.bank_energy[2].E_ref_SB, 100); ASSERT_EQ((int)energy.bank_energy[3].E_ref_SB, 100); ASSERT_EQ((int)energy.bank_energy[4].E_ref_SB, 200); ASSERT_EQ((int)energy.bank_energy[5].E_ref_SB, 100); ASSERT_EQ((int)energy.bank_energy[6].E_ref_SB, 100); ASSERT_EQ((int)energy.bank_energy[7].E_ref_SB, 100); }; ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_17.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_17 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, {0, 0, 0} }, { 20, CmdType::PDEA }, { 30, CmdType::PDXA }, { 40, CmdType::PRE, {0, 0, 0} }, { 60, CmdType::PDEP }, { 65, CmdType::PDXP }, { 75, CmdType::REFA }, {100, CmdType::PDEP }, {125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); auto memSpec = DRAMPower::MemSpecDDR5::from_memspec(*data); memSpec.bitWidth = 16; memSpec.numberOfDevices = 1; memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 8; memSpec.numberOfBankGroups = 2; memSpec.banksPerGroup = 4; memSpec.memTimingSpec.tRAS = 10; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tRCD = 20; memSpec.memTimingSpec.tRFC = 25; memSpec.memTimingSpec.tWR = 20; memSpec.memTimingSpec.tRP = 20; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1; memSpec.memPowerSpec[0].vXX = 1; memSpec.memPowerSpec[0].iXX0 = 64; memSpec.memPowerSpec[0].iXX2N = 8; memSpec.memPowerSpec[0].iXX2P = 6; memSpec.memPowerSpec[0].iXX3N = 32; memSpec.memPowerSpec[0].iXX3P = 20; memSpec.memPowerSpec[0].iXX4R = 72; memSpec.memPowerSpec[0].iXX4W = 72; memSpec.memPowerSpec[0].iXX5C = 28; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iXX0; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_17, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.activeTime(), 55); ASSERT_EQ(stats.rank_total[0].cycles.act, 55); ASSERT_EQ(stats.rank_total[0].cycles.pre, 30); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 30); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 55); ASSERT_EQ(stats.bank[0].cycles.activeTime(), 55); ASSERT_EQ(stats.bank[1].cycles.act, 25); ASSERT_EQ(stats.bank[1].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[2].cycles.act, 25); ASSERT_EQ(stats.bank[2].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[3].cycles.act, 25); ASSERT_EQ(stats.bank[3].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[4].cycles.act, 25); ASSERT_EQ(stats.bank[4].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[5].cycles.act, 25); ASSERT_EQ(stats.bank[5].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[6].cycles.act, 25); ASSERT_EQ(stats.bank[6].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[7].cycles.act, 25); ASSERT_EQ(stats.bank[7].cycles.activeTime(), 25); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 30); ASSERT_EQ(stats.bank[1].cycles.pre, 60); ASSERT_EQ(stats.bank[2].cycles.pre, 60); ASSERT_EQ(stats.bank[3].cycles.pre, 60); ASSERT_EQ(stats.bank[4].cycles.pre, 60); ASSERT_EQ(stats.bank[5].cycles.pre, 60); ASSERT_EQ(stats.bank[6].cycles.pre, 60); ASSERT_EQ(stats.bank[7].cycles.pre, 60); } TEST_F(DramPowerTest_DDR5_17, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act), 1340); ASSERT_EQ(std::round(energy.E_bg_act_shared), 880); ASSERT_EQ((int)energy.bank_energy[0].E_bg_act, 110); ASSERT_EQ((int)energy.bank_energy[1].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[2].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[3].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[4].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[5].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[6].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[7].E_bg_act, 50); ASSERT_EQ((int)total_energy.E_bg_pre, 240); ASSERT_EQ((int)energy.bank_energy[0].E_bg_pre, 30); ASSERT_EQ((int)energy.bank_energy[1].E_bg_pre, 30); ASSERT_EQ((int)energy.bank_energy[2].E_bg_pre, 30); ASSERT_EQ((int)energy.bank_energy[3].E_bg_pre, 30); ASSERT_EQ((int)energy.bank_energy[4].E_bg_pre, 30); ASSERT_EQ((int)energy.bank_energy[5].E_bg_pre, 30); ASSERT_EQ((int)energy.bank_energy[6].E_bg_pre, 30); ASSERT_EQ((int)energy.bank_energy[7].E_bg_pre, 30); ASSERT_EQ((int)energy.E_PDNA, 200); ASSERT_EQ((int)energy.E_PDNP, 180); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_18.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_18 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0,0,0} }, { 5, CmdType::PDEA }, { 30, CmdType::PDXA }, { 40, CmdType::PRE, {0,0,0} }, { 45, CmdType::PDEP }, { 65, CmdType::PDXP }, { 75, CmdType::REFA }, { 85, CmdType::PDEP }, { 90, CmdType::PDXP }, {125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); auto memSpec = DRAMPower::MemSpecDDR5::from_memspec(*data); memSpec.bitWidth = 16; memSpec.numberOfDevices = 1; memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 8; memSpec.numberOfBankGroups = 2; memSpec.banksPerGroup = 4; memSpec.memTimingSpec.tRAS = 10; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tRCD = 20; memSpec.memTimingSpec.tRFC = 25; memSpec.memTimingSpec.tWR = 20; memSpec.memTimingSpec.tRP = 20; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1; memSpec.memPowerSpec[0].vXX = 1; memSpec.memPowerSpec[0].iXX0 = 64; memSpec.memPowerSpec[0].iXX2N = 8; memSpec.memPowerSpec[0].iXX2P = 6; memSpec.memPowerSpec[0].iXX3N = 32; memSpec.memPowerSpec[0].iXX3P = 20; memSpec.memPowerSpec[0].iXX4R = 72; memSpec.memPowerSpec[0].iXX4W = 72; memSpec.memPowerSpec[0].iXX5C = 28; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iXX0; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_18, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 55); ASSERT_EQ(stats.rank_total[0].cycles.pre, 55); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 5); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 55); ASSERT_EQ(stats.bank[1].cycles.act, 25); ASSERT_EQ(stats.bank[2].cycles.act, 25); ASSERT_EQ(stats.bank[3].cycles.act, 25); ASSERT_EQ(stats.bank[4].cycles.act, 25); ASSERT_EQ(stats.bank[5].cycles.act, 25); ASSERT_EQ(stats.bank[6].cycles.act, 25); ASSERT_EQ(stats.bank[7].cycles.act, 25); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 55); ASSERT_EQ(stats.bank[1].cycles.pre, 85); ASSERT_EQ(stats.bank[2].cycles.pre, 85); ASSERT_EQ(stats.bank[3].cycles.pre, 85); ASSERT_EQ(stats.bank[4].cycles.pre, 85); ASSERT_EQ(stats.bank[5].cycles.pre, 85); ASSERT_EQ(stats.bank[6].cycles.pre, 85); ASSERT_EQ(stats.bank[7].cycles.pre, 85); } TEST_F(DramPowerTest_DDR5_18, CalcWindow) { SimulationStats window; auto iterate_to_timestamp = [this](auto & command, timestamp_t timestamp) { while (command != this->testPattern.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } return this->ddr->getWindowStats(timestamp); }; auto command = testPattern.begin(); // Cycle 5 window = iterate_to_timestamp(command, 5); ASSERT_EQ(window.rank_total[0].cycles.act, 5); ASSERT_EQ(window.bank[0].cycles.act, 5); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 5); // Cycle 10 window = iterate_to_timestamp(command, 10); ASSERT_EQ(window.rank_total[0].cycles.act, 10); ASSERT_EQ(window.bank[0].cycles.act, 10); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 10); // Cycle 15 window = iterate_to_timestamp(command, 15); ASSERT_EQ(window.rank_total[0].cycles.act, 15); ASSERT_EQ(window.bank[0].cycles.act, 15); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 15); // Cycle 20 window = iterate_to_timestamp(command, 20); ASSERT_EQ(window.rank_total[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 20); // Cycle 25 window = iterate_to_timestamp(command, 25); ASSERT_EQ(window.rank_total[0].cycles.act, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 5); ASSERT_EQ(window.bank[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 20); // Cycle 30 window = iterate_to_timestamp(command, 30); ASSERT_EQ(window.rank_total[0].cycles.act, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 20); // Cycle 35 window = iterate_to_timestamp(command, 35); ASSERT_EQ(window.rank_total[0].cycles.act, 25); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 25); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 25); // Cycle 40 window = iterate_to_timestamp(command, 40); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 30); // Cycle 45 window = iterate_to_timestamp(command, 45); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 5); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 35); // Cycle 50 window = iterate_to_timestamp(command, 50); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 10); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 40); // Cycle 55 window = iterate_to_timestamp(command, 55); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 15); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 15); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 45); // Cycle 60 window = iterate_to_timestamp(command, 60); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 20); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 50); // Cycle 65 window = iterate_to_timestamp(command, 65); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 20); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 50); // Cycle 70 window = iterate_to_timestamp(command, 70); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 25); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 25); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 55); // Cycle 75 window = iterate_to_timestamp(command, 75); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 80 window = iterate_to_timestamp(command, 80); ASSERT_EQ(window.rank_total[0].cycles.act, 35); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 35); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 5); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 85 window = iterate_to_timestamp(command, 85); ASSERT_EQ(window.rank_total[0].cycles.act, 40); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 40); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 10); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 90 window = iterate_to_timestamp(command, 90); ASSERT_EQ(window.rank_total[0].cycles.act, 45); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 45); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 15); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 95 window = iterate_to_timestamp(command, 95); ASSERT_EQ(window.rank_total[0].cycles.act, 50); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 50); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 20); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 100 window = iterate_to_timestamp(command, 100); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 105 window = iterate_to_timestamp(command, 105); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 35); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 35); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 65); // Cycle 110 window = iterate_to_timestamp(command, 110); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 40); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 40); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 70); // Cycle 115 window = iterate_to_timestamp(command, 115); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 45); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 45); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 75); // Cycle 120 window = iterate_to_timestamp(command, 120); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 50); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 50); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 80); // Cycle 125 window = iterate_to_timestamp(command, 125); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 55); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 55); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 85); }; TEST_F(DramPowerTest_DDR5_18, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act), 1340); ASSERT_EQ(std::round(energy.E_bg_act_shared), 880); ASSERT_EQ((int)energy.bank_energy[0].E_bg_act, 110); ASSERT_EQ((int)energy.bank_energy[1].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[2].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[3].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[4].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[5].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[6].E_bg_act, 50); ASSERT_EQ((int)energy.bank_energy[7].E_bg_act, 50); ASSERT_EQ((int)total_energy.E_bg_pre, 440); ASSERT_EQ((int)energy.bank_energy[0].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[1].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[2].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[3].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[4].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[5].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[6].E_bg_pre, 55); ASSERT_EQ((int)energy.bank_energy[7].E_bg_pre, 55); ASSERT_EQ((int)energy.E_PDNA, 200); ASSERT_EQ((int)energy.E_PDNP, 30); }; ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_2.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_2 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 35, CmdType::PRE, { 0, 0, 0 }}, { 50, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_2, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check bank command count: RD ASSERT_EQ(stats.bank[0].counter.reads, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.reads, 0); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.pre, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 35); ASSERT_EQ(stats.rank_total[0].cycles.pre, 15); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 35); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 15); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 50); } TEST_F(DramPowerTest_DDR5_2, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 179); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 208); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 436); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1189); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1183); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 467); ASSERT_EQ(std::round(total_energy.total()*1e12), 2479); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_3.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_3 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::PRE, { 0, 0, 0 }}, { 45, CmdType::PRE, { 3, 0, 0 }}, { 50, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_3, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); }; // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 45); ASSERT_EQ(stats.rank_total[0].cycles.pre, 5); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 35); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 25); else ASSERT_EQ(stats.bank[b].cycles.act, 0); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 15); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 25); else ASSERT_EQ(stats.bank[b].cycles.pre, 50); } } TEST_F(DramPowerTest_DDR5_3, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 359); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 436); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1531); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1521); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 156); ASSERT_EQ(std::round(total_energy.total()*1e12), 2897); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_4.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_4 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 40, CmdType::RD, { 0, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 70, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_4, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 2); else if( b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); }; // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 50); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 50); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 30); else ASSERT_EQ(stats.bank[b].cycles.act, 0); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 20); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 40); else ASSERT_EQ(stats.bank[b].cycles.pre, 70); } } TEST_F(DramPowerTest_DDR5_4, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 359); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 1307); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1704); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1690); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 623); ASSERT_EQ(std::round(total_energy.total()*1e12), 4408); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_5.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_5 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 40, CmdType::RD, { 0, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 65, CmdType::REFA, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_5, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 2); else if( b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); }; // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check bank command count: REFA for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ ASSERT_EQ(stats.bank[b].counter.refAllBank, 1); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 75); ASSERT_EQ(stats.rank_total[0].cycles.pre, 25); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 75); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 55); else ASSERT_EQ(stats.bank[b].cycles.act, 25); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 25); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 45); else ASSERT_EQ(stats.bank[b].cycles.pre, 75); } } TEST_F(DramPowerTest_DDR5_5, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 359); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 1307); ASSERT_EQ(std::round(total_energy.E_ref_AB*1e12), 1812); ASSERT_EQ(std::round(total_energy.E_ref_SB*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 2615); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 2536); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 779); ASSERT_EQ(std::round(total_energy.total()*1e12), 7287); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_6.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_6 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RDA, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 65, CmdType::REFA, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_6, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); } for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.readAuto, 1); else ASSERT_EQ(stats.bank[b].counter.readAuto, 0); } // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check bank command count: REFA for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ ASSERT_EQ(stats.bank[b].counter.refAllBank, 1); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 75); ASSERT_EQ(stats.rank_total[0].cycles.pre, 25); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 45); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 55); else ASSERT_EQ(stats.bank[b].cycles.act, 25); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 55); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 45); else ASSERT_EQ(stats.bank[b].cycles.pre, 75); } } TEST_F(DramPowerTest_DDR5_6, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 359); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 436); ASSERT_EQ(std::round(total_energy.E_RDA*1e12), 436); ASSERT_EQ(std::round(total_energy.E_ref_AB*1e12), 1812); ASSERT_EQ(std::round(total_energy.E_ref_SB*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 2610); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 2536); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 779); ASSERT_EQ(std::round(total_energy.total()*1e12), 6846); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_7.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_7 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::SREFEN, { 0, 0, 0 }}, { 40, CmdType::SREFEX, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_7, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check bank command count: REFA for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.refAllBank, 1); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 25); ASSERT_EQ(stats.rank_total[0].cycles.pre, 60); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 15); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 25); // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 60); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 15); } TEST_F(DramPowerTest_DDR5_7, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 0); ASSERT_EQ(std::round(total_energy.E_ref_AB*1e12), 1812); ASSERT_EQ(std::round(energy.E_sref*1e12), 280); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 912); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 845); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 1869); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref)*1e12), 4873); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_8.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_8 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::PDEA, { 0, 0, 0 }}, { 30, CmdType::PDXA, { 0, 0, 0 }}, { 45, CmdType::PDEP, { 0, 0, 0 }}, { 70, CmdType::PDXP, { 0, 0, 0 }}, { 85, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_8, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 0); ASSERT_EQ(stats.rank_total[0].cycles.pre, 30); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 30); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 25); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 30); // Check bank specific PDNA cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownAct, 30); // Check bank specific PDNP cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownPre, 25); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 0); } TEST_F(DramPowerTest_DDR5_8, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 0); ASSERT_EQ(std::round(energy.E_sref*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 623); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 392); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 935); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref + energy.E_PDNA + energy.E_PDNP)*1e12), 1950); } ================================================ FILE: tests/tests_drampower/core/DDR5/ddr5_test_pattern_9.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_DDR5_9 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 5, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::ACT, { 5, 0, 0 }}, { 20, CmdType::RD, { 0, 0, 0 }}, { 30, CmdType::PDEA, { 0, 0, 0 }}, { 50, CmdType::PDXA, { 0, 0, 0 }}, { 55, CmdType::RD, { 5, 0, 0 }}, { 60, CmdType::RD, { 0, 0, 0 }}, { 70, CmdType::PREA, { 0, 0, 0 }}, { 80, CmdType::PDEP, { 0, 0, 0 }}, { 95, CmdType::PDXP, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_DDR5_9, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 45); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 20); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 15); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats .bank[b].cycles.act, 45); }else if (b == 5){ ASSERT_EQ(stats .bank[b].cycles.act, 35); }else{ ASSERT_EQ(stats .bank[b].cycles.act, 0); } } // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats.bank[b].cycles.pre, 20); }else if (b == 5){ ASSERT_EQ(stats.bank[b].cycles.pre, 30); }else{ ASSERT_EQ(stats.bank[b].cycles.pre, 65); } } // Check bank specific PDNA cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownAct, 20); // Check bank specific PDNP cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownPre, 15); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 0); } TEST_F(DramPowerTest_DDR5_9, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 359); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 1307); ASSERT_EQ(std::round(energy.E_sref*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 415); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 235); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1535); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1521); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 623); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref + energy.E_PDNA + energy.E_PDNP)*1e12), 4890); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_multidevice_tests.cpp ================================================ /* Note This test is based on lpddr4_test_pattern_4.cpp and assumes the correctness of the test. Calculation for 5 devices: E_act: 392.30769230769232 E_pre: 415.38461538461542 E_RD: 1356.9230769230769 E_bg_act: 1632.6923076923078 E_bg_act_shared: 1586.5384615384617 E_bg_pre: 623.07692307692309 Total: 4420.3846153846162 E_act * 5: 1961.5384615384616 E_pre * 5: 2076.9230769230771 E_RD * 5: 6784.6153846153845 E_bg_act * 5: 8163.461538461539 E_bg_act_shared * 5: 7932.6923076923085 E_bg_pre * 5: 3115.38461538461545 Total * 5: 22101.923076923081 */ #include #include #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_MultiDevice : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 40, CmdType::RD, { 0, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 70, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; uint64_t numberOfDevices; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); memSpec->numberOfDevices = 5; numberOfDevices = memSpec->numberOfDevices; ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_MultiDevice, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 2); else if(b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); }; // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 50); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 50); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 30); else ASSERT_EQ(stats.bank[b].cycles.act, 0); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 20); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 40); else ASSERT_EQ(stats.bank[b].cycles.pre, 70); } } TEST_F(DramPowerTest_LPDDR4_MultiDevice, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(energy.bank_energy.size(), numberOfDevices * memSpec->numberOfBanks); // Validate every device has the same bank energy for(size_t i = 0; i < numberOfDevices; i++){ for(size_t j = 0; j < memSpec->numberOfBanks; j++){ ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_act, energy.bank_energy[j].E_act); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_pre, energy.bank_energy[j].E_pre); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_RD, energy.bank_energy[j].E_RD); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_bg_act, energy.bank_energy[j].E_bg_act); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_bg_pre, energy.bank_energy[j].E_bg_pre); } } ASSERT_EQ(std::round(total_energy.E_act*1e12), 1962); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 2077); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 6785); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 8163); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 7933); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 3115); ASSERT_EQ(std::round(total_energy.total()*1e12), 22102); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_multirank_tests.cpp ================================================ #include #include #include #include #include #include #include "DRAMPower/command/Command.h" #include "DRAMPower/standards/lpddr4/LPDDR4.h" #include "DRAMPower/memspec/MemSpecLPDDR4.h" using DRAMPower::CmdType; using DRAMPower::Command; using DRAMPower::LPDDR4; using DRAMPower::MemSpecLPDDR4; using DRAMPower::SimulationStats; using DRAMPower::TargetCoordinate; #define SZ_BITS(x) sizeof(x)*8 static constexpr uint8_t wr_data[] = { 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, }; static constexpr uint8_t rd_data[] = { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; class LPDDR4_MultirankTests : public ::testing::Test { public: LPDDR4_MultirankTests() { initSpec(); ddr = std::make_unique(*spec); } void initSpec() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); spec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); spec->numberOfRanks = 2; } void runCommands(const std::vector &commands) { for (const Command &command : commands) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); } } inline size_t bankIndex(int bank, int rank) { return rank * spec->numberOfBanks + bank; } std::unique_ptr spec; std::unique_ptr ddr; }; TEST_F(LPDDR4_MultirankTests, Pattern_1) { runCommands({ {0, CmdType::ACT, {1, 0, 0}}, // rank 0 {4, CmdType::ACT, {1, 0, 1}}, // rank 1 {8, CmdType::WR, {1, 0, 0, 0, 4}, wr_data, SZ_BITS(wr_data)}, // rank 0 {24, CmdType::RD, {1, 0, 1, 0, 4}, rd_data, SZ_BITS(rd_data)}, // rank 1 {38, CmdType::PRE, {1, 0, 1}}, // rank 1 {40, CmdType::PRE, {1, 0, 0}}, // rank 0 {45, CmdType::END_OF_SIMULATION} }); // Act count for rank 0: act = 40 - 0 = 40 // Act count for rank 1: act = 38 - 4 = 34 // Pre count for rank 0: pre = 45 - 40 = 5 // Pre count for rank 1: pre = 45 - 34 = 11 SimulationStats stats = ddr->getStats(); EXPECT_EQ(stats.bank.size(), spec->numberOfBanks * spec->numberOfRanks); EXPECT_EQ(stats.bank[bankIndex(1, 0)].cycles.act, 40); EXPECT_EQ(stats.bank[bankIndex(1, 1)].cycles.act, 34); EXPECT_EQ(stats.bank[bankIndex(1, 0)].cycles.pre, 5); EXPECT_EQ(stats.bank[bankIndex(1, 1)].cycles.pre, 11); EXPECT_EQ(stats.rank_total[0].cycles.act, 40); EXPECT_EQ(stats.rank_total[1].cycles.act, 34); } TEST_F(LPDDR4_MultirankTests, Pattern_2) { runCommands({ {0, CmdType::ACT, TargetCoordinate{0, 0, 0}}, // r0 {5, CmdType::ACT, TargetCoordinate{0, 0, 1}}, // r1 {22, CmdType::ACT, TargetCoordinate{3, 0, 1}}, // r1 {35, CmdType::RD, TargetCoordinate{3, 0, 1, 0, 0}, rd_data, SZ_BITS(rd_data)}, // r1 {55, CmdType::RD, TargetCoordinate{0, 0, 0, 0, 3}, rd_data, SZ_BITS(rd_data)}, // r0 {60, CmdType::PREA, TargetCoordinate{0, 0, 0}}, // r0 {65, CmdType::PREA, TargetCoordinate{0, 0, 1}}, // r1 {75, CmdType::REFA, TargetCoordinate{0, 0, 0}}, // r0 {80, CmdType::REFA, TargetCoordinate{0, 0, 1}}, // r1 {130, CmdType::END_OF_SIMULATION}, }); // t = 0 ACT -> Start activate // r0, b0 // t = 5 ACT -> Start activate // r1, b0 // t = 22 ACT -> Start activate // r1, b3 // t = 35 RD -> Read // r1, b3 // t = 55 RD -> Read // r0, b0 // t = 60 PREA -> End activate // r0, b0 // t = 65 PREA -> End activate // r1, b0, b3 // t = 75 REFA -> Start activate // r0, b0 // t = 80 REFA -> Start activate // r1, b0, b3 // t = 100 REFA (implicit) -> End activate // r0, b0 // t = 105 REFA (implicit) -> End activate // r1, b0, b3 // t = 130 END_OF_SIMULATION SimulationStats stats = ddr->getStats(); EXPECT_EQ(stats.bank.size(), spec->numberOfBanks * spec->numberOfRanks); EXPECT_EQ(stats.bank[bankIndex(0, 0)].cycles.act, 85); EXPECT_EQ(stats.bank[bankIndex(0, 1)].cycles.act, 85); EXPECT_EQ(stats.bank[bankIndex(3, 1)].cycles.act, 68); EXPECT_EQ(stats.bank[bankIndex(0, 0)].cycles.pre, 130 - 85); EXPECT_EQ(stats.bank[bankIndex(0, 1)].cycles.pre, 130 - 85); EXPECT_EQ(stats.bank[bankIndex(3, 1)].cycles.pre, 130 - 68); EXPECT_EQ(stats.rank_total[0].cycles.act, 85); EXPECT_EQ(stats.rank_total[1].cycles.act, 85); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_0.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_0 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::PRE, { 0, 0, 0 }}, { 15, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_0, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 15); ASSERT_EQ(stats.rank_total[0].cycles.pre, 0); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 15); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 0); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 15); } TEST_F(DramPowerTest_LPDDR4_0, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 196); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 208); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 476); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 485); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 0); ASSERT_EQ(std::round(total_energy.total()*1e12), 888); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_1.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_1 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 35, CmdType::PRE, { 0, 0, 0 }}, { 35, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_1, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check bank command count: RD ASSERT_EQ(stats.bank[0].counter.reads, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.reads, 0); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.pre, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 35); ASSERT_EQ(stats.rank_total[0].cycles.pre, 0); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 35); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 0); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 35); } TEST_F(DramPowerTest_LPDDR4_1, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 196); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 208); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 452); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1131); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1111); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 0); ASSERT_EQ(std::round(total_energy.total()*1e12), 1987); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_10.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_10 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 5, CmdType::ACT, { 2, 0, 0 }}, { 15, CmdType::ACT, { 10, 0, 0 }}, { 30, CmdType::PREA, { 0, 0, 0 }}, { 40, CmdType::REFB, { 9, 0, 0 }}, { 50, CmdType::REFB, { 5, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_10, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 60); ASSERT_EQ(stats.rank_total[0].cycles.pre, 40); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 0); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats .bank[b].cycles.act, 30); }else if (b == 2){ ASSERT_EQ(stats .bank[b].cycles.act, 25); }else if(b == 10){ ASSERT_EQ(stats .bank[b].cycles.act, 15); }else if (b == 5 || b == 9){ ASSERT_EQ(stats .bank[b].cycles.act, 20); }else{ ASSERT_EQ(stats .bank[b].cycles.act, 0); } } // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats.bank[b].cycles.pre, 70); }else if (b == 2){ ASSERT_EQ(stats.bank[b].cycles.pre, 75); }else if (b== 10){ ASSERT_EQ(stats.bank[b].cycles.pre, 85); }else if (b == 5 || b == 9){ ASSERT_EQ(stats.bank[b].cycles.pre, 80); }else{ ASSERT_EQ(stats.bank[b].cycles.pre, 100); } } // Check bank specific PDNA cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownAct, 0); // Check bank specific PDNP cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownPre, 0); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 0); } TEST_F(DramPowerTest_LPDDR4_10, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 588); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 623); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 0); ASSERT_EQ(std::round(total_energy.E_ref_2B*1e12), 0); ASSERT_EQ(std::round(total_energy.E_ref_PB*1e12), 140); ASSERT_EQ(std::round(energy.E_sref*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1967); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1904); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 1246); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref + energy.E_PDNA + energy.E_PDNP)*1e12), 4565); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_11.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_11 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { // Timestamp, Cmd, { Bank, BG, Rank} { 0, CmdType::ACT, { 0, 0, 0 } }, { 10, CmdType::ACT, { 1, 0, 0 } }, { 15, CmdType::PRE, { 0, 0, 0 } }, { 25, CmdType::ACT, { 3, 0, 0 } }, { 25, CmdType::PRE, { 1, 0, 0 } }, { 30, CmdType::ACT, { 0, 0, 0 } }, { 35, CmdType::ACT, { 2, 0, 0 } }, { 40, CmdType::PRE, { 1, 0, 0 } }, { 40, CmdType::PRE, { 3, 0, 0 } }, { 45, CmdType::PRE, { 0, 0, 0 } }, { 50, CmdType::PRE, { 2, 0, 0 } }, { 85, CmdType::ACT, { 7, 0, 0 } }, { 100, CmdType::PRE, { 7, 0, 0 } }, { 120, CmdType::ACT, { 6, 0, 0 } }, { 125, CmdType::END_OF_SIMULATION}, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); auto memSpec = DRAMPower::MemSpecLPDDR4::from_memspec(*data); memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 8; memSpec.banksPerGroup = 8; memSpec.numberOfBankGroups = 1; memSpec.burstLength = 8; memSpec.dataRate = 2; memSpec.bitWidth = 16; memSpec.numberOfDevices = 1; memSpec.memTimingSpec.tRAS = 10; memSpec.memTimingSpec.tRP = 10; memSpec.memTimingSpec.tCK = 1e-9; memSpec.memPowerSpec[0].vDDX = 1; memSpec.memPowerSpec[0].iDD0X = 64e-3; memSpec.memPowerSpec[0].iDD3NX = 32e-3; memSpec.memPowerSpec[0].iDD2NX = 8e-3; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iDD0X; memSpec.bwParams.bwPowerFactRho = 0.333333333; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_11, Pattern1) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; // Inspect first rank const auto & rank_1 = internal::LPDDR4TestAccessor.getRanks(ddr->getCore()).at(0); auto stats = ddr->getStats(); // Check global count ASSERT_EQ(stats.rank_total[0].cycles.act, 70); ASSERT_EQ(stats.rank_total[0].cycles.pre, 55); // Check per-bank ACT count ASSERT_EQ(stats.bank[0].cycles.act, 30); ASSERT_EQ(stats.bank[1].cycles.act, 15); ASSERT_EQ(stats.bank[2].cycles.act, 15); ASSERT_EQ(stats.bank[3].cycles.act, 15); ASSERT_EQ(stats.bank[7].cycles.act, 15); ASSERT_EQ(stats.bank[6].cycles.act, 5); ASSERT_EQ(stats.bank[4].cycles.act, 0); ASSERT_EQ(stats.bank[5].cycles.act, 0); // Check per-bank PRE count ASSERT_EQ(stats.bank[0].cycles.pre, 95); ASSERT_EQ(stats.bank[1].cycles.pre, 110); ASSERT_EQ(stats.bank[2].cycles.pre, 110); ASSERT_EQ(stats.bank[3].cycles.pre, 110); ASSERT_EQ(stats.bank[7].cycles.pre, 110); ASSERT_EQ(stats.bank[6].cycles.pre, 120); ASSERT_EQ(stats.bank[4].cycles.pre, 125); ASSERT_EQ(stats.bank[5].cycles.pre, 125); // Check global command count ASSERT_EQ(rank_1.commandCounter.get(CmdType::ACT), 7); ASSERT_EQ(rank_1.commandCounter.get(CmdType::PRE), 7); // per-bank ACT command count ASSERT_EQ(stats.bank[0].counter.act, 2); ASSERT_EQ(stats.bank[1].counter.act, 1); ASSERT_EQ(stats.bank[2].counter.act, 1); ASSERT_EQ(stats.bank[3].counter.act, 1); ASSERT_EQ(stats.bank[6].counter.act, 1); ASSERT_EQ(stats.bank[7].counter.act, 1); ASSERT_EQ(stats.bank[4].counter.act, 0); ASSERT_EQ(stats.bank[5].counter.act, 0); // per-bank PRE command count ASSERT_EQ(stats.bank[0].counter.pre, 2); ASSERT_EQ(stats.bank[1].counter.pre, 1); ASSERT_EQ(stats.bank[2].counter.pre, 1); ASSERT_EQ(stats.bank[3].counter.pre, 1); ASSERT_EQ(stats.bank[7].counter.pre, 1); ASSERT_EQ(stats.bank[6].counter.pre, 0); ASSERT_EQ(stats.bank[4].counter.pre, 0); ASSERT_EQ(stats.bank[5].counter.pre, 0); } TEST_F(DramPowerTest_LPDDR4_11, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 2240); ASSERT_EQ(std::round(energy.bank_energy[0].E_act*1e12), 640); ASSERT_EQ(std::round(energy.bank_energy[1].E_act*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[2].E_act*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[3].E_act*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[4].E_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_act*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[7].E_act*1e12), 320); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 3360); ASSERT_EQ(std::round(energy.bank_energy[0].E_pre*1e12), 1120); ASSERT_EQ(std::round(energy.bank_energy[1].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[2].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[3].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[4].E_pre*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_pre*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_pre*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 480); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 240); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 240); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 240); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 80); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 240); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1120); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 2640); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 440); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 55); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_12.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_12 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0} }, { 5, CmdType::ACT, { 1, 0, 0} }, { 10, CmdType::ACT, { 2, 0, 0} }, { 15, CmdType::ACT, { 3, 0, 0} }, { 20, CmdType::ACT, { 4, 0, 0} }, { 25, CmdType::ACT, { 5, 0, 0} }, { 30, CmdType::ACT, { 6, 0, 0} }, { 35, CmdType::ACT, { 7, 0, 0} }, { 35, CmdType::RD, { 0, 0, 0} }, { 40, CmdType::RD, { 0, 0, 0} }, { 40, CmdType::RD, { 1, 0, 0} }, { 45, CmdType::RD, { 0, 0, 0} }, { 45, CmdType::RD, { 2, 0, 0} }, { 50, CmdType::RD, { 3, 0, 0} }, { 55, CmdType::RD, { 4, 0, 0} }, { 60, CmdType::RD, { 5, 0, 0} }, { 65, CmdType::RD, { 6, 0, 0} }, { 70, CmdType::RD, { 7, 0, 0} }, { 75, CmdType::RD, { 7, 0, 0} }, { 80, CmdType::RD, { 7, 0, 0} }, { 85, CmdType::WR, { 7, 0, 0} }, { 90, CmdType::WR, { 6, 0, 0} }, { 95, CmdType::WR, { 5, 0, 0} }, { 100, CmdType::WR, { 4, 0, 0} }, { 105, CmdType::WR, { 3, 0, 0} }, { 110, CmdType::WR, { 2, 0, 0} }, { 110, CmdType::WR, { 0, 0, 0} }, { 115, CmdType::WR, { 1, 0, 0} }, { 115, CmdType::RD, { 0, 0, 0} }, { 120, CmdType::WR, { 0, 0, 0} }, { 120, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); auto memSpec = DRAMPower::MemSpecLPDDR4::from_memspec(*data); memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 8; memSpec.banksPerGroup = 8; memSpec.numberOfBankGroups = 1; memSpec.bitWidth = 16; memSpec.numberOfDevices = 1; memSpec.memTimingSpec.tCK = 1e-9; memSpec.memPowerSpec[0].vDDX = 1; memSpec.memPowerSpec[0].iDD0X = 64e-3; memSpec.memPowerSpec[0].iDD3NX = 32e-3; memSpec.memPowerSpec[0].iDD4RX = 72e-3; memSpec.memPowerSpec[0].iDD4WX = 96e-3; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iDD0X; memSpec.bwParams.bwPowerFactRho = 1; memSpec.burstLength = 16; memSpec.dataRate = 2; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_12, Pattern_2) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; // Inspect first rank const Rank & rank_1 = internal::LPDDR4TestAccessor.getRanks(ddr->getCore()).at(0); // Check global command count ASSERT_EQ(rank_1.commandCounter.get(CmdType::RD), 13); ASSERT_EQ(rank_1.commandCounter.get(CmdType::WR), 9); // Check bank RD command count ASSERT_EQ(rank_1.banks[0].counter.reads, 4); ASSERT_EQ(rank_1.banks[1].counter.reads, 1); ASSERT_EQ(rank_1.banks[2].counter.reads, 1); ASSERT_EQ(rank_1.banks[3].counter.reads, 1); ASSERT_EQ(rank_1.banks[4].counter.reads, 1); ASSERT_EQ(rank_1.banks[5].counter.reads, 1); ASSERT_EQ(rank_1.banks[6].counter.reads, 1); ASSERT_EQ(rank_1.banks[7].counter.reads, 3); // Check bank WR command count ASSERT_EQ(rank_1.banks[0].counter.writes, 2); ASSERT_EQ(rank_1.banks[1].counter.writes, 1); ASSERT_EQ(rank_1.banks[2].counter.writes, 1); ASSERT_EQ(rank_1.banks[3].counter.writes, 1); ASSERT_EQ(rank_1.banks[4].counter.writes, 1); ASSERT_EQ(rank_1.banks[5].counter.writes, 1); ASSERT_EQ(rank_1.banks[6].counter.writes, 1); ASSERT_EQ(rank_1.banks[7].counter.writes, 1); } TEST_F(DramPowerTest_LPDDR4_12, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 4160); ASSERT_EQ(std::round(energy.bank_energy[0].E_RD*1e12), 1280); ASSERT_EQ(std::round(energy.bank_energy[1].E_RD*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[2].E_RD*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[3].E_RD*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[4].E_RD*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[5].E_RD*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[6].E_RD*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[7].E_RD*1e12), 960); ASSERT_EQ(std::round(total_energy.E_WR*1e12), 4608); ASSERT_EQ(std::round(energy.bank_energy[0].E_WR*1e12), 1024); ASSERT_EQ(std::round(energy.bank_energy[1].E_WR*1e12), 512); ASSERT_EQ(std::round(energy.bank_energy[2].E_WR*1e12), 512); ASSERT_EQ(std::round(energy.bank_energy[3].E_WR*1e12), 512); ASSERT_EQ(std::round(energy.bank_energy[4].E_WR*1e12), 512); ASSERT_EQ(std::round(energy.bank_energy[5].E_WR*1e12), 512); ASSERT_EQ(std::round(energy.bank_energy[6].E_WR*1e12), 512); ASSERT_EQ(std::round(energy.bank_energy[7].E_WR*1e12), 512); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_13.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_13 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { // Timestamp, Cmd, { Bank, BG, Rank} { 0, CmdType::ACT, { 0, 0, 0 } }, { 10, CmdType::RDA, { 0, 0, 0 } }, { 15, CmdType::ACT, { 1, 0, 0 } }, { 15, CmdType::ACT, { 2, 0, 0 } }, { 25, CmdType::WRA, { 1, 0, 0 } }, { 30, CmdType::PRE, { 0, 0, 0 } }, { 35, CmdType::RDA, { 2, 0, 0 } }, { 50, CmdType::ACT, { 0, 0, 0 } }, { 50, CmdType::ACT, { 2, 0, 0 } }, { 60, CmdType::WRA, { 0, 0, 0 } }, { 60, CmdType::RDA, { 2, 0, 0 } }, { 75, CmdType::ACT, { 1, 0, 0 } }, { 80, CmdType::ACT, { 0, 0, 0 } }, { 85, CmdType::WRA, { 0, 0, 0 } }, { 95, CmdType::RDA, { 1, 0, 0 } }, { 125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); auto memSpec = DRAMPower::MemSpecLPDDR4::from_memspec(*data); memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 8; memSpec.banksPerGroup = 8; memSpec.numberOfBankGroups = 1; memSpec.bitWidth = 16; memSpec.numberOfDevices = 1; memSpec.memTimingSpec.tRAS = 20; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tWR = 11; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1e-9; memSpec.memTimingSpec.tRP = 10; memSpec.memPowerSpec[0].vDDX = 1; memSpec.memPowerSpec[0].iDD0X = 64e-3; memSpec.memPowerSpec[0].iDD2NX = 8e-3; memSpec.memPowerSpec[0].iDD3NX = 32e-3; memSpec.memPowerSpec[0].iDD4RX = 72e-3; memSpec.memPowerSpec[0].iDD4WX = 96e-3; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iDD0X; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; memSpec.prechargeOffsetRD = memSpec.memTimingSpec.tRTP; memSpec.prechargeOffsetWR = memSpec.memTimingSpec.tWL + memSpec.burstLength/2 + memSpec.memTimingSpec.tWR + 1; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_13, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; const Rank & rank_1 = internal::LPDDR4TestAccessor.getRanks(ddr->getCore()).at(0); auto stats = ddr->getStats(); // Check global command count ASSERT_EQ(rank_1.commandCounter.get(CmdType::ACT), 7); ASSERT_EQ(rank_1.commandCounter.get(CmdType::PRE), 1); ASSERT_EQ(rank_1.commandCounter.get(CmdType::RDA), 4); ASSERT_EQ(rank_1.commandCounter.get(CmdType::WRA), 3); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 3); ASSERT_EQ(stats.bank[1].counter.act, 2); ASSERT_EQ(stats.bank[2].counter.act, 2); ASSERT_EQ(stats.bank[3].counter.act, 0); ASSERT_EQ(stats.bank[4].counter.act, 0); ASSERT_EQ(stats.bank[5].counter.act, 0); ASSERT_EQ(stats.bank[6].counter.act, 0); ASSERT_EQ(stats.bank[7].counter.act, 0); // Check bank command count: RDA ASSERT_EQ(stats.bank[0].counter.readAuto, 1); ASSERT_EQ(stats.bank[1].counter.readAuto, 1); ASSERT_EQ(stats.bank[2].counter.readAuto, 2); ASSERT_EQ(stats.bank[3].counter.readAuto, 0); ASSERT_EQ(stats.bank[4].counter.readAuto, 0); ASSERT_EQ(stats.bank[5].counter.readAuto, 0); ASSERT_EQ(stats.bank[6].counter.readAuto, 0); ASSERT_EQ(stats.bank[7].counter.readAuto, 0); // Check bank command count: WRA ASSERT_EQ(stats.bank[0].counter.writeAuto, 2); ASSERT_EQ(stats.bank[1].counter.writeAuto, 1); ASSERT_EQ(stats.bank[2].counter.writeAuto, 0); ASSERT_EQ(stats.bank[3].counter.writeAuto, 0); ASSERT_EQ(stats.bank[4].counter.writeAuto, 0); ASSERT_EQ(stats.bank[5].counter.writeAuto, 0); ASSERT_EQ(stats.bank[6].counter.writeAuto, 0); ASSERT_EQ(stats.bank[7].counter.writeAuto, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 100); ASSERT_EQ(stats.rank_total[0].cycles.pre, 25); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 75); ASSERT_EQ(stats.bank[1].cycles.act, 60); ASSERT_EQ(stats.bank[2].cycles.act, 50); ASSERT_EQ(stats.bank[3].cycles.act, 0); ASSERT_EQ(stats.bank[4].cycles.act, 0); ASSERT_EQ(stats.bank[5].cycles.act, 0); ASSERT_EQ(stats.bank[6].cycles.act, 0); ASSERT_EQ(stats.bank[7].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 50); ASSERT_EQ(stats.bank[1].cycles.pre, 65); ASSERT_EQ(stats.bank[2].cycles.pre, 75); ASSERT_EQ(stats.bank[3].cycles.pre, 125); ASSERT_EQ(stats.bank[4].cycles.pre, 125); ASSERT_EQ(stats.bank[5].cycles.pre, 125); ASSERT_EQ(stats.bank[6].cycles.pre, 125); ASSERT_EQ(stats.bank[7].cycles.pre, 125); } TEST_F(DramPowerTest_LPDDR4_13, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 4560); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1600); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 1200); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 960); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 800); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 200); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(total_energy.E_RDA*1e12), 1280); ASSERT_EQ(std::round(energy.bank_energy[0].E_RDA*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[1].E_RDA*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[2].E_RDA*1e12), 640); ASSERT_EQ(std::round(energy.bank_energy[3].E_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[4].E_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_RDA*1e12), 0); ASSERT_EQ(std::round(total_energy.E_WRA*1e12), 1536); ASSERT_EQ(std::round(energy.bank_energy[0].E_WRA*1e12), 1024); ASSERT_EQ(std::round(energy.bank_energy[1].E_WRA*1e12), 512); ASSERT_EQ(std::round(energy.bank_energy[2].E_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[3].E_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[4].E_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_WRA*1e12), 0); ASSERT_EQ(std::round(total_energy.E_pre_RDA*1e12), 2240); ASSERT_EQ(std::round(energy.bank_energy[0].E_pre_RDA*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[1].E_pre_RDA*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[2].E_pre_RDA*1e12), 1120); ASSERT_EQ(std::round(energy.bank_energy[3].E_pre_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[4].E_pre_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_pre_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_pre_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_pre_RDA*1e12), 0); ASSERT_EQ(std::round(total_energy.E_pre_WRA*1e12), 1120+560); ASSERT_EQ(std::round(energy.bank_energy[0].E_pre_WRA*1e12), 1120); ASSERT_EQ(std::round(energy.bank_energy[1].E_pre_WRA*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[2].E_pre_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[3].E_pre_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[4].E_pre_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_pre_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_pre_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_pre_WRA*1e12), 0); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_14.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_14 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 5, CmdType::ACT, { 1, 0, 0 }}, { 10, CmdType::ACT, { 2, 0, 0 }}, { 15, CmdType::PRE, { 0, 0, 0 }}, { 20, CmdType::PREA, { 0, 0, 0 }}, { 25, CmdType::PRE, { 1, 0, 0 }}, { 30, CmdType::PREA, { 0, 0, 0 }}, { 40, CmdType::ACT, { 0, 0, 0 }}, { 40, CmdType::ACT, { 3, 0, 0 }}, { 50, CmdType::PRE, { 3, 0, 0 }}, { 125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); auto memSpec = DRAMPower::MemSpecLPDDR4::from_memspec(*data); memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 8; memSpec.banksPerGroup = 8; memSpec.numberOfBankGroups = 1; memSpec.bitWidth = 16; memSpec.numberOfDevices = 1; memSpec.memTimingSpec.tRAS = 10; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tWR = 20; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tWR = 12; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1e-9; memSpec.memTimingSpec.tRP = 10; memSpec.memPowerSpec[0].vDDX = 1; memSpec.memPowerSpec[0].iDD0X = 64e-3; memSpec.memPowerSpec[0].iDD2NX = 8e-3; memSpec.memPowerSpec[0].iDD3NX = 32e-3; memSpec.memPowerSpec[0].iDD4RX = 72e-3; memSpec.memPowerSpec[0].iDD4WX = 72e-3; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iDD0X; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_14, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 2); ASSERT_EQ(stats.bank[1].counter.act, 1); ASSERT_EQ(stats.bank[2].counter.act, 1); ASSERT_EQ(stats.bank[3].counter.act, 1); ASSERT_EQ(stats.bank[4].counter.act, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 105); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 100); ASSERT_EQ(stats.bank[1].cycles.act, 15); ASSERT_EQ(stats.bank[2].cycles.act, 10); ASSERT_EQ(stats.bank[3].cycles.act, 10); ASSERT_EQ(stats.bank[4].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 25); ASSERT_EQ(stats.bank[1].cycles.pre, 110); ASSERT_EQ(stats.bank[2].cycles.pre, 115); ASSERT_EQ(stats.bank[3].cycles.pre, 115); ASSERT_EQ(stats.bank[4].cycles.pre, 125); } TEST_F(DramPowerTest_LPDDR4_14, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 1600); ASSERT_EQ(std::round(energy.bank_energy[0].E_act*1e12), 640); ASSERT_EQ(std::round(energy.bank_energy[1].E_act*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[2].E_act*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[3].E_act*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[4].E_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 2240); ASSERT_EQ(std::round(energy.bank_energy[0].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[1].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[2].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[3].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[4].E_pre*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_pre*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_pre*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_pre*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 3840); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1680); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 1600); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 240); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 160); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 160); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 160); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 20); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 20); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 20); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 20); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 20); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 20); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 20); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 20); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_15.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_15 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0}}, { 0, CmdType::ACT, { 1, 0, 0}}, { 0, CmdType::ACT, { 2, 0, 0}}, { 15, CmdType::PRE, { 0, 0, 0}}, { 20, CmdType::REFB, { 0, 0, 0}}, { 25, CmdType::PRE, { 1, 0, 0}}, { 30, CmdType::RDA, { 2, 0, 0}}, { 50, CmdType::REFB, { 2, 0, 0}}, { 60, CmdType::ACT, { 1, 0, 0}}, { 80, CmdType::PRE, { 1, 0, 0}}, { 85, CmdType::REFB, { 1, 0, 0}}, { 85, CmdType::REFB, { 0, 0, 0}}, { 125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); auto memSpec = DRAMPower::MemSpecLPDDR4::from_memspec(*data); memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 8; memSpec.banksPerGroup = 8; memSpec.numberOfBankGroups = 1; memSpec.bitWidth = 16; memSpec.numberOfDevices = 1; memSpec.memTimingSpec.tRAS = 10; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tRFCPB = 25; memSpec.memTimingSpec.tWR = 20; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1e-9; memSpec.memTimingSpec.tRP = 10; memSpec.memTimingSpec.tREFI = 1; memSpec.memPowerSpec[0].vDDX = 1; memSpec.memPowerSpec[0].iDD0X = 64e-3; memSpec.memPowerSpec[0].iDD2NX = 8e-3; memSpec.memPowerSpec[0].iDD3NX = 32e-3; memSpec.memPowerSpec[0].iDD4RX = 72e-3; memSpec.memPowerSpec[0].iDD4WX = 72e-3; memSpec.memPowerSpec[0].iDD5PBX = 6008e-3; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iDD0X; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; memSpec.prechargeOffsetRD = memSpec.memTimingSpec.tRTP; memSpec.prechargeOffsetWR = memSpec.memTimingSpec.tWL + memSpec.burstLength/2 + memSpec.memTimingSpec.tWR + 1; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_15, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); ASSERT_EQ(stats.bank[1].counter.act, 2); ASSERT_EQ(stats.bank[2].counter.act, 1); ASSERT_EQ(stats.bank[3].counter.act, 0); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); ASSERT_EQ(stats.bank[1].counter.pre, 2); ASSERT_EQ(stats.bank[2].counter.pre, 1); ASSERT_EQ(stats.bank[3].counter.pre, 0); // Check bank command count: REFPB ASSERT_EQ(stats.bank[0].counter.refPerBank, 2); ASSERT_EQ(stats.bank[1].counter.refPerBank, 1); ASSERT_EQ(stats.bank[2].counter.refPerBank, 1); ASSERT_EQ(stats.bank[3].counter.refPerBank, 0); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 100); ASSERT_EQ(stats.rank_total[0].cycles.pre, 25); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 65); ASSERT_EQ(stats.bank[1].cycles.act, 70); ASSERT_EQ(stats.bank[2].cycles.act, 65); ASSERT_EQ(stats.bank[3].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 60); ASSERT_EQ(stats.bank[1].cycles.pre, 55); ASSERT_EQ(stats.bank[2].cycles.pre, 60); ASSERT_EQ(stats.bank[3].cycles.pre, 125); } TEST_F(DramPowerTest_LPDDR4_15, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 4800); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1600); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 1040); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 1120); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 1040); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 200); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(total_energy.E_ref_PB*1e12), 600); ASSERT_EQ(std::round(energy.bank_energy[0].E_ref_PB*1e12), 300); ASSERT_EQ(std::round(energy.bank_energy[1].E_ref_PB*1e12), 150); ASSERT_EQ(std::round(energy.bank_energy[2].E_ref_PB*1e12), 150); ASSERT_EQ(std::round(energy.bank_energy[3].E_ref_PB*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[4].E_ref_PB*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_ref_PB*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_ref_PB*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_ref_PB*1e12), 0); }; ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_16.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_16 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, {0, 0, 0} }, { 20, CmdType::PDEA }, { 30, CmdType::PDXA }, { 40, CmdType::PRE, {0, 0, 0} }, { 60, CmdType::PDEP }, { 65, CmdType::PDXP }, { 75, CmdType::REFA }, {100, CmdType::PDEP }, {125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); auto memSpec = DRAMPower::MemSpecLPDDR4::from_memspec(*data); memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 8; memSpec.numberOfBankGroups = 2; memSpec.banksPerGroup = 4; memSpec.bitWidth = 16; memSpec.numberOfDevices = 1; memSpec.memTimingSpec.tRAS = 10; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tRCD = 20; memSpec.memTimingSpec.tRFC = 25; //memSpec.memTimingSpec.tRFCPB = 3; memSpec.memTimingSpec.tWR = 20; memSpec.memTimingSpec.tRP = 20; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1e-9; memSpec.memTimingSpec.tREFI = 1; memSpec.memPowerSpec[0].vDDX = 1; memSpec.memPowerSpec[0].iDD0X = 64e-3; memSpec.memPowerSpec[0].iDD2NX = 8e-3; memSpec.memPowerSpec[0].iDD2PX = 6e-3; memSpec.memPowerSpec[0].iDD3NX = 32e-3; memSpec.memPowerSpec[0].iDD3PX = 20e-3; memSpec.memPowerSpec[0].iDD4RX = 72e-3; memSpec.memPowerSpec[0].iDD4WX = 72e-3; //memSpec.memPowerSpec[0].iDD5PBX = 30; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iDD0X; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_16, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.activeTime(), 55); ASSERT_EQ(stats.rank_total[0].cycles.act, 55); ASSERT_EQ(stats.rank_total[0].cycles.pre, 30); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 30); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 55); ASSERT_EQ(stats.bank[0].cycles.activeTime(), 55); ASSERT_EQ(stats.bank[1].cycles.act, 25); ASSERT_EQ(stats.bank[1].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[2].cycles.act, 25); ASSERT_EQ(stats.bank[2].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[3].cycles.act, 25); ASSERT_EQ(stats.bank[3].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[4].cycles.act, 25); ASSERT_EQ(stats.bank[4].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[5].cycles.act, 25); ASSERT_EQ(stats.bank[5].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[6].cycles.act, 25); ASSERT_EQ(stats.bank[6].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[7].cycles.act, 25); ASSERT_EQ(stats.bank[7].cycles.activeTime(), 25); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 30); ASSERT_EQ(stats.bank[1].cycles.pre, 60); ASSERT_EQ(stats.bank[2].cycles.pre, 60); ASSERT_EQ(stats.bank[3].cycles.pre, 60); ASSERT_EQ(stats.bank[4].cycles.pre, 60); ASSERT_EQ(stats.bank[5].cycles.pre, 60); ASSERT_EQ(stats.bank[6].cycles.pre, 60); ASSERT_EQ(stats.bank[7].cycles.pre, 60); } TEST_F(DramPowerTest_LPDDR4_16, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 4560); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 880); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 880); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 400); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 240); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 200); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 180); }; ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_17.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_17 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0,0,0} }, { 5, CmdType::PDEA }, { 30, CmdType::PDXA }, { 40, CmdType::PRE, {0,0,0} }, { 45, CmdType::PDEP }, { 65, CmdType::PDXP }, { 75, CmdType::REFA }, { 85, CmdType::PDEP }, { 90, CmdType::PDXP }, {125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); auto memSpec = DRAMPower::MemSpecLPDDR4::from_memspec(*data); memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 8; memSpec.numberOfBankGroups = 2; memSpec.banksPerGroup = 4; memSpec.bitWidth = 16; memSpec.numberOfDevices = 1; memSpec.memTimingSpec.tRAS = 10; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tRCD = 20; memSpec.memTimingSpec.tRFC = 25; memSpec.memTimingSpec.tRFCPB = 25; memSpec.memTimingSpec.tWR = 20; memSpec.memTimingSpec.tRP = 20; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1e-9; memSpec.memTimingSpec.tREFI = 1; memSpec.memPowerSpec[0].vDDX = 1; memSpec.memPowerSpec[0].iDD0X = 64e-3; memSpec.memPowerSpec[0].iDD2NX = 8e-3; memSpec.memPowerSpec[0].iDD2PX = 6e-3; memSpec.memPowerSpec[0].iDD3NX = 32e-3; memSpec.memPowerSpec[0].iDD3PX = 20e-3; memSpec.memPowerSpec[0].iDD4RX = 72e-3; memSpec.memPowerSpec[0].iDD4WX = 72e-3; memSpec.memPowerSpec[0].iDD5PBX = 30e-3; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iDD0X; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_17, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 55); ASSERT_EQ(stats.rank_total[0].cycles.pre, 55); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 5); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 55); ASSERT_EQ(stats.bank[1].cycles.act, 25); ASSERT_EQ(stats.bank[2].cycles.act, 25); ASSERT_EQ(stats.bank[3].cycles.act, 25); ASSERT_EQ(stats.bank[4].cycles.act, 25); ASSERT_EQ(stats.bank[5].cycles.act, 25); ASSERT_EQ(stats.bank[6].cycles.act, 25); ASSERT_EQ(stats.bank[7].cycles.act, 25); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 55); ASSERT_EQ(stats.bank[1].cycles.pre, 85); ASSERT_EQ(stats.bank[2].cycles.pre, 85); ASSERT_EQ(stats.bank[3].cycles.pre, 85); ASSERT_EQ(stats.bank[4].cycles.pre, 85); ASSERT_EQ(stats.bank[5].cycles.pre, 85); ASSERT_EQ(stats.bank[6].cycles.pre, 85); ASSERT_EQ(stats.bank[7].cycles.pre, 85); } TEST_F(DramPowerTest_LPDDR4_17, CalcWindow) { SimulationStats window; auto iterate_to_timestamp = [this](auto & command, timestamp_t timestamp) { while (command != this->testPattern.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } return this->ddr->getWindowStats(timestamp); }; auto command = testPattern.begin(); // Cycle 5 window = iterate_to_timestamp(command, 5); ASSERT_EQ(window.rank_total[0].cycles.act, 5); ASSERT_EQ(window.bank[0].cycles.act, 5); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 5); // Cycle 10 window = iterate_to_timestamp(command, 10); ASSERT_EQ(window.rank_total[0].cycles.act, 10); ASSERT_EQ(window.bank[0].cycles.act, 10); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 10); // Cycle 15 window = iterate_to_timestamp(command, 15); ASSERT_EQ(window.rank_total[0].cycles.act, 15); ASSERT_EQ(window.bank[0].cycles.act, 15); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 15); // Cycle 20 window = iterate_to_timestamp(command, 20); ASSERT_EQ(window.rank_total[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 20); // Cycle 25 window = iterate_to_timestamp(command, 25); ASSERT_EQ(window.rank_total[0].cycles.act, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 5); ASSERT_EQ(window.bank[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 20); // Cycle 30 window = iterate_to_timestamp(command, 30); ASSERT_EQ(window.rank_total[0].cycles.act, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 20); // Cycle 35 window = iterate_to_timestamp(command, 35); ASSERT_EQ(window.rank_total[0].cycles.act, 25); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 25); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 25); // Cycle 40 window = iterate_to_timestamp(command, 40); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 30); // Cycle 45 window = iterate_to_timestamp(command, 45); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 5); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 35); // Cycle 50 window = iterate_to_timestamp(command, 50); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 10); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 40); // Cycle 55 window = iterate_to_timestamp(command, 55); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 15); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 15); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 45); // Cycle 60 window = iterate_to_timestamp(command, 60); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 20); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 50); // Cycle 65 window = iterate_to_timestamp(command, 65); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 20); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 50); // Cycle 70 window = iterate_to_timestamp(command, 70); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 25); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 25); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 55); // Cycle 75 window = iterate_to_timestamp(command, 75); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 80 window = iterate_to_timestamp(command, 80); ASSERT_EQ(window.rank_total[0].cycles.act, 35); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 35); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 5); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 85 window = iterate_to_timestamp(command, 85); ASSERT_EQ(window.rank_total[0].cycles.act, 40); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 40); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 10); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 90 window = iterate_to_timestamp(command, 90); ASSERT_EQ(window.rank_total[0].cycles.act, 45); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 45); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 15); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 95 window = iterate_to_timestamp(command, 95); ASSERT_EQ(window.rank_total[0].cycles.act, 50); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 50); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 20); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 100 window = iterate_to_timestamp(command, 100); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 105 window = iterate_to_timestamp(command, 105); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 35); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 35); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 65); // Cycle 110 window = iterate_to_timestamp(command, 110); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 40); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 40); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 70); // Cycle 115 window = iterate_to_timestamp(command, 115); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 45); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 45); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 75); // Cycle 120 window = iterate_to_timestamp(command, 120); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 50); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 50); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 80); // Cycle 125 window = iterate_to_timestamp(command, 125); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 55); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 55); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 85); }; TEST_F(DramPowerTest_LPDDR4_17, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 4560); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 880); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 880); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 400); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 440); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 200); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 30); }; ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_18.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_18 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::REFA }, { 5, CmdType::PDEP }, { 15, CmdType::PDXP }, { 30, CmdType::REFB, { 0,0,0} }, { 40, CmdType::ACT, { 1,0,0,}}, { 45, CmdType::PDEA }, { 75, CmdType::PDXA }, {125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); auto memSpec = DRAMPower::MemSpecLPDDR4::from_memspec(*data); memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 8; memSpec.numberOfBankGroups = 2; memSpec.banksPerGroup = 4; memSpec.bitWidth = 16; memSpec.numberOfDevices = 1; memSpec.memTimingSpec.tRAS = 10; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tRCD = 20; memSpec.memTimingSpec.tRFC = 25; memSpec.memTimingSpec.tRFCPB = 25; memSpec.memTimingSpec.tWR = 20; memSpec.memTimingSpec.tRP = 20; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1e-9; memSpec.memTimingSpec.tREFI = 1; memSpec.memPowerSpec[0].vDDX = 1; memSpec.memPowerSpec[0].iDD0X = 64e-3; memSpec.memPowerSpec[0].iDD2NX = 8e-3; memSpec.memPowerSpec[0].iDD2PX = 6e-3; memSpec.memPowerSpec[0].iDD3NX = 32e-3; memSpec.memPowerSpec[0].iDD3PX = 20e-3; memSpec.memPowerSpec[0].iDD4RX = 72e-3; memSpec.memPowerSpec[0].iDD4WX = 72e-3; memSpec.memPowerSpec[0].iDD5PBX = 30e-3; memSpec.memPowerSpec[0].iBeta = memSpec.memPowerSpec[0].iDD0X; memSpec.bwParams.bwPowerFactRho = 0.333333333; memSpec.burstLength = 16; memSpec.dataRate = 2; ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_18, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[1].counter.act, 1); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 105); ASSERT_EQ(stats.rank_total[0].cycles.pre, 5); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 15); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 0); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 50); ASSERT_EQ(stats.bank[1].cycles.act, 95); ASSERT_EQ(stats.bank[2].cycles.act, 25); ASSERT_EQ(stats.bank[3].cycles.act, 25); ASSERT_EQ(stats.bank[4].cycles.act, 25); ASSERT_EQ(stats.bank[5].cycles.act, 25); ASSERT_EQ(stats.bank[6].cycles.act, 25); ASSERT_EQ(stats.bank[7].cycles.act, 25); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 60); ASSERT_EQ(stats.bank[1].cycles.pre, 15); ASSERT_EQ(stats.bank[2].cycles.pre, 85); ASSERT_EQ(stats.bank[3].cycles.pre, 85); ASSERT_EQ(stats.bank[4].cycles.pre, 85); ASSERT_EQ(stats.bank[5].cycles.pre, 85); ASSERT_EQ(stats.bank[6].cycles.pre, 85); ASSERT_EQ(stats.bank[7].cycles.pre, 85); } TEST_F(DramPowerTest_LPDDR4_18, CalcWindow) { SimulationStats window; auto iterate_to_timestamp = [this](auto & command, timestamp_t timestamp) { while (command != this->testPattern.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } return this->ddr->getWindowStats(timestamp); }; auto command = testPattern.begin(); // Cycle 5 window = iterate_to_timestamp(command, 5); ASSERT_EQ(window.rank_total[0].cycles.act, 5); ASSERT_EQ(window.rank_total[0].cycles.pre, 0); ASSERT_EQ(window.bank[0].cycles.act, 5); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 5); ASSERT_EQ(window.bank[1].cycles.pre, 0); ASSERT_EQ(window.bank[2].cycles.act, 5); ASSERT_EQ(window.bank[2].cycles.pre, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 25 window = iterate_to_timestamp(command, 25); ASSERT_EQ(window.rank_total[0].cycles.act, 25); ASSERT_EQ(window.rank_total[0].cycles.pre, 0); ASSERT_EQ(window.bank[0].cycles.act, 25); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 0); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 30 window = iterate_to_timestamp(command, 30); ASSERT_EQ(window.rank_total[0].cycles.act, 25); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 25); ASSERT_EQ(window.bank[0].cycles.pre, 5); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 5); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 5); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 35 window = iterate_to_timestamp(command, 35); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 5); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 10); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 40 window = iterate_to_timestamp(command, 40); ASSERT_EQ(window.rank_total[0].cycles.act, 35); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 35); ASSERT_EQ(window.bank[0].cycles.pre, 5 ); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 15); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 15); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 50 window = iterate_to_timestamp(command, 50); ASSERT_EQ(window.rank_total[0].cycles.act, 45); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 45); ASSERT_EQ(window.bank[0].cycles.pre, 5); ASSERT_EQ(window.bank[1].cycles.act, 35); ASSERT_EQ(window.bank[1].cycles.pre, 15); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 25); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 55 window = iterate_to_timestamp(command, 55); ASSERT_EQ(window.rank_total[0].cycles.act, 50); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 50); ASSERT_EQ(window.bank[0].cycles.pre, 5); ASSERT_EQ(window.bank[1].cycles.act, 40); ASSERT_EQ(window.bank[1].cycles.pre, 15); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 60 window = iterate_to_timestamp(command, 60); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 50); ASSERT_EQ(window.bank[0].cycles.pre, 10); ASSERT_EQ(window.bank[1].cycles.act, 45); ASSERT_EQ(window.bank[1].cycles.pre, 15); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 35); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 75 window = iterate_to_timestamp(command, 75); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 50); ASSERT_EQ(window.bank[0].cycles.pre, 10); ASSERT_EQ(window.bank[1].cycles.act, 45); ASSERT_EQ(window.bank[1].cycles.pre, 15); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 35); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 15); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 95 window = iterate_to_timestamp(command, 95); ASSERT_EQ(window.rank_total[0].cycles.act, 75); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 50); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 65); ASSERT_EQ(window.bank[1].cycles.pre, 15); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 55); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 15); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 125 window = iterate_to_timestamp(command, 125); ASSERT_EQ(window.rank_total[0].cycles.act, 105); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 50); ASSERT_EQ(window.bank[0].cycles.pre, 60); ASSERT_EQ(window.bank[1].cycles.act, 95); ASSERT_EQ(window.bank[1].cycles.pre, 15); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 85); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 15); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); }; TEST_F(DramPowerTest_LPDDR4_18, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 6400); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1680); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 800); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 1520); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 400); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 300); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 0); }; ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_2.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_2 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 35, CmdType::PRE, { 0, 0, 0 }}, { 50, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_2, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check bank command count: RD ASSERT_EQ(stats.bank[0].counter.reads, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.reads, 0); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.pre, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 35); ASSERT_EQ(stats.rank_total[0].cycles.pre, 15); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 35); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 15); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 50); } TEST_F(DramPowerTest_LPDDR4_2, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 196); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 208); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 452); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1131); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1111); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 467); ASSERT_EQ(std::round(total_energy.total()*1e12), 2454); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_3.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_3 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::PRE, { 0, 0, 0 }}, { 45, CmdType::PRE, { 3, 0, 0 }}, { 50, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_3, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); } // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 45); ASSERT_EQ(stats.rank_total[0].cycles.pre, 5); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 35); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 25); else ASSERT_EQ(stats.bank[b].cycles.act, 0); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 15); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 25); else ASSERT_EQ(stats.bank[b].cycles.pre, 50); } } TEST_F(DramPowerTest_LPDDR4_3, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 392); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 452); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1463); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1428); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 156); ASSERT_EQ(std::round(total_energy.total()*1e12), 2878); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_4.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_4 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 40, CmdType::RD, { 0, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 70, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_4, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 2); else if(b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); }; // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 50); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 50); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 30); else ASSERT_EQ(stats.bank[b].cycles.act, 0); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 20); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 40); else ASSERT_EQ(stats.bank[b].cycles.pre, 70); } } TEST_F(DramPowerTest_LPDDR4_4, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 392); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 1357); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1633); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1587); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 623); ASSERT_EQ(std::round(total_energy.total()*1e12), 4420); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_5.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_5 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 40, CmdType::RD, { 0, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 65, CmdType::REFA, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_5, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 2); else if( b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); }; // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check bank command count: REFA for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ ASSERT_EQ(stats.bank[b].counter.refAllBank, 1); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 75); ASSERT_EQ(stats.rank_total[0].cycles.pre, 25); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 75); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 55); else ASSERT_EQ(stats.bank[b].cycles.act, 25); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 25); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 45); else ASSERT_EQ(stats.bank[b].cycles.pre, 75); } } TEST_F(DramPowerTest_LPDDR4_5, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 392); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 1357); ASSERT_EQ(std::round(total_energy.E_ref_AB*1e12), 1699); ASSERT_EQ(std::round(total_energy.E_ref_SB*1e12), 0); ASSERT_EQ(std::round(total_energy.E_ref_PB*1e12), 0); ASSERT_EQ(std::round(total_energy.E_ref_2B*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 2657); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 2380); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 779); ASSERT_EQ(std::round(total_energy.total()*1e12), 7299); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_6.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_6 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RDA, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 65, CmdType::REFA, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_6, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); } for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.readAuto, 1); else ASSERT_EQ(stats.bank[b].counter.readAuto, 0); } // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check bank command count: REFA for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ ASSERT_EQ(stats.bank[b].counter.refAllBank, 1); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 75); ASSERT_EQ(stats.rank_total[0].cycles.pre, 25); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 50); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 55); else ASSERT_EQ(stats.bank[b].cycles.act, 25); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 50); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 45); else ASSERT_EQ(stats.bank[b].cycles.pre, 75); } } TEST_F(DramPowerTest_LPDDR4_6, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 392); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 452); ASSERT_EQ(std::round(total_energy.E_RDA*1e12), 452); ASSERT_EQ(std::round(total_energy.E_ref_AB*1e12), 1699); ASSERT_EQ(std::round(total_energy.E_ref_SB*1e12), 0); ASSERT_EQ(std::round(total_energy.E_ref_2B*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 2642); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 2380); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 779); ASSERT_EQ(std::round(total_energy.total()*1e12), 6833); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_7.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_7 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::SREFEN, { 0, 0, 0 }}, { 40, CmdType::SREFEX, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_7, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check bank command count: REFA for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.refAllBank, 1); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 25); ASSERT_EQ(stats.rank_total[0].cycles.pre, 60); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 15); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 25); // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 60); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 15); } TEST_F(DramPowerTest_LPDDR4_7, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 0); ASSERT_EQ(std::round(total_energy.E_ref_AB*1e12), 1699); ASSERT_EQ(std::round(energy.E_sref*1e12), 280); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1024); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 793); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 1869); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref)*1e12), 4873); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_8.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_8 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::PDEA, { 0, 0, 0 }}, { 30, CmdType::PDXA, { 0, 0, 0 }}, { 45, CmdType::PDEP, { 0, 0, 0 }}, { 70, CmdType::PDXP, { 0, 0, 0 }}, { 85, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_8, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 0); ASSERT_EQ(stats.rank_total[0].cycles.pre, 30); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 30); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 25); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 30); // Check bank specific PDNA cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownAct, 30); // Check bank specific PDNP cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownPre, 25); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 0); } TEST_F(DramPowerTest_LPDDR4_8, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 0); ASSERT_EQ(std::round(energy.E_sref*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 623); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 392); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 935); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref + energy.E_PDNA + energy.E_PDNP)*1e12), 1950); } ================================================ FILE: tests/tests_drampower/core/LPDDR4/lpddr4_test_pattern_9.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR4_9 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 5, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::ACT, { 5, 0, 0 }}, { 20, CmdType::RD, { 0, 0, 0 }}, { 30, CmdType::PDEA, { 0, 0, 0 }}, { 50, CmdType::PDXA, { 0, 0, 0 }}, { 55, CmdType::RD, { 5, 0, 0 }}, { 60, CmdType::RD, { 0, 0, 0 }}, { 70, CmdType::PREA, { 0, 0, 0 }}, { 80, CmdType::PDEP, { 0, 0, 0 }}, { 95, CmdType::PDXP, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR4_9, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 45); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 20); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 15); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats .bank[b].cycles.act, 45); }else if (b == 5){ ASSERT_EQ(stats .bank[b].cycles.act, 35); }else{ ASSERT_EQ(stats .bank[b].cycles.act, 0); } } // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats.bank[b].cycles.pre, 20); }else if (b == 5){ ASSERT_EQ(stats.bank[b].cycles.pre, 30); }else{ ASSERT_EQ(stats.bank[b].cycles.pre, 65); } } // Check bank specific PDNA cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownAct, 20); // Check bank specific PDNP cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownPre, 15); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 0); } TEST_F(DramPowerTest_LPDDR4_9, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 392); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 1357); ASSERT_EQ(std::round(energy.E_sref*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 415); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 235); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1474); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1428); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 623); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref + energy.E_PDNA + energy.E_PDNP)*1e12), 4912); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_multidevice_tests.cpp ================================================ /* Note This test is based on lpddr5_test_pattern_4.cpp and assumes the correctness of the test. Calculation for 5 devices: E_act: 392.30769230769232 E_pre: 415.38461538461542 E_RD: 678.46153846153845 E_bg_act: 1632.6923076923078 E_bg_act_shared: 1586.5384615384617 E_bg_pre: 623.07692307692309 Total: 3741.9230769230776 E_act * 5: 1961.5384615384616 E_pre * 5: 2076.9230769230771 E_RD * 5: 3392.30769230769225 E_bg_act * 5: 8163.461538461539 E_bg_act_shared * 5: 7932.6923076923085 E_bg_pre * 5: 3115.38461538461545 Total * 5: 18709.615384615388 */ #include "DRAMPower/memspec/MemSpecLPDDR5.h" #include #include #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_MultiDevice : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 40, CmdType::RD, { 0, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 70, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; uint64_t numberOfDevices; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); memSpec->numberOfDevices = 5; numberOfDevices = memSpec->numberOfDevices; ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_MultiDevice, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 2); else if(b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); }; // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 50); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 50); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 30); else ASSERT_EQ(stats.bank[b].cycles.act, 0); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 20); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 40); else ASSERT_EQ(stats.bank[b].cycles.pre, 70); } } TEST_F(DramPowerTest_LPDDR5_MultiDevice, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(energy.bank_energy.size(), numberOfDevices * memSpec->numberOfBanks); // Validate every device has the same bank energy for(size_t i = 0; i < numberOfDevices; i++){ for(size_t j = 0; j < memSpec->numberOfBanks; j++){ ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_act, energy.bank_energy[j].E_act); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_pre, energy.bank_energy[j].E_pre); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_RD, energy.bank_energy[j].E_RD); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_bg_act, energy.bank_energy[j].E_bg_act); ASSERT_EQ(energy.bank_energy[i * memSpec->numberOfBanks + j].E_bg_pre, energy.bank_energy[j].E_bg_pre); } } ASSERT_EQ(std::round(total_energy.E_act*1e12), 1962); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 2077); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 3392); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 8163); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 7933); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 3115); ASSERT_EQ(std::round(total_energy.total()*1e12), 18710); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_multirank_tests.cpp ================================================ #include #include #include #include #include #include #include "DRAMPower/command/Command.h" #include "DRAMPower/standards/lpddr5/LPDDR5.h" #include "DRAMPower/memspec/MemSpecLPDDR5.h" using DRAMPower::CmdType; using DRAMPower::Command; using DRAMPower::LPDDR5; using DRAMPower::MemSpecLPDDR5; using DRAMPower::SimulationStats; using DRAMPower::TargetCoordinate; #define SZ_BITS(x) sizeof(x)*8 static constexpr uint8_t wr_data[] = { 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, }; static constexpr uint8_t rd_data[] = { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; class LPDDR5_MultirankTests : public ::testing::Test { public: LPDDR5_MultirankTests() { initSpec(); ddr = std::make_unique(*spec); } void initSpec() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); spec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); spec->numberOfRanks = 2; } void runCommands(const std::vector &commands) { for (const Command &command : commands) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); } } inline size_t bankIndex(int bank, int rank) { return rank * spec->numberOfBanks + bank; } std::unique_ptr spec; std::unique_ptr ddr; }; TEST_F(LPDDR5_MultirankTests, Pattern_1) { runCommands({ {0, CmdType::ACT, {1, 0, 0}}, {2, CmdType::ACT, {1, 0, 1}}, // rank 1 {4, CmdType::WR, {1, 0, 0, 0, 4}, wr_data, SZ_BITS(wr_data)}, {6, CmdType::RD, {1, 0, 1, 0, 4}, rd_data, SZ_BITS(rd_data)}, // rank 1 {10, CmdType::PRE, {1, 0, 1}}, // rank 1 {15, CmdType::PRE, {1, 0, 0}}, {20, CmdType::END_OF_SIMULATION} }); SimulationStats stats = ddr->getStats(); EXPECT_EQ(stats.bank.size(), spec->numberOfBanks * spec->numberOfRanks); EXPECT_EQ(stats.bank[bankIndex(1, 0)].cycles.act, 15); EXPECT_EQ(stats.bank[bankIndex(1, 1)].cycles.act, 8); EXPECT_EQ(stats.bank[bankIndex(1, 0)].cycles.pre, 5); EXPECT_EQ(stats.bank[bankIndex(1, 1)].cycles.pre, 12); EXPECT_EQ(stats.rank_total[0].cycles.act, 15); EXPECT_EQ(stats.rank_total[1].cycles.act, 8); } TEST_F(LPDDR5_MultirankTests, Pattern_2) { runCommands({ {0, CmdType::ACT, TargetCoordinate{0, 0, 0}}, // r0 {5, CmdType::ACT, TargetCoordinate{0, 0, 1}}, // r1 {22, CmdType::ACT, TargetCoordinate{3, 0, 1}}, // r1 {35, CmdType::RD, TargetCoordinate{3, 0, 1, 0, 0}, rd_data, SZ_BITS(rd_data)}, // r1 {55, CmdType::RD, TargetCoordinate{0, 0, 0, 0, 3}, rd_data, SZ_BITS(rd_data)}, // r0 {60, CmdType::PREA, TargetCoordinate{0, 0, 0}}, // r0 {65, CmdType::PREA, TargetCoordinate{0, 0, 1}}, // r1 {75, CmdType::REFA, TargetCoordinate{0, 0, 0}}, // r0 {80, CmdType::REFA, TargetCoordinate{0, 0, 1}}, // r1 {130, CmdType::END_OF_SIMULATION}, }); SimulationStats stats = ddr->getStats(); EXPECT_EQ(stats.bank.size(), spec->numberOfBanks * spec->numberOfRanks); EXPECT_EQ(stats.bank[bankIndex(0, 0)].cycles.act, 85); EXPECT_EQ(stats.bank[bankIndex(0, 1)].cycles.act, 85); EXPECT_EQ(stats.bank[bankIndex(3, 1)].cycles.act, 68); EXPECT_EQ(stats.bank[bankIndex(0, 0)].cycles.pre, 130 - 85); EXPECT_EQ(stats.bank[bankIndex(0, 1)].cycles.pre, 130 - 85); EXPECT_EQ(stats.bank[bankIndex(3, 1)].cycles.pre, 130 - 68); EXPECT_EQ(stats.rank_total[0].cycles.act, 85); EXPECT_EQ(stats.rank_total[1].cycles.act, 85); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_0.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_0 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::PRE, { 0, 0, 0 }}, { 15, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_0, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 15); ASSERT_EQ(stats.rank_total[0].cycles.pre, 0); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 15); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 0); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 15); } TEST_F(DramPowerTest_LPDDR5_0, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 196); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 208); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 476); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 485); ASSERT_EQ(std::round(total_energy.E_bg_pre)*1e12, 0); ASSERT_EQ(std::round(total_energy.total()*1e12), 888); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_1.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_1 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 35, CmdType::PRE, { 0, 0, 0 }}, { 35, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_1, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check bank command count: RD ASSERT_EQ(stats.bank[0].counter.reads, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.reads, 0); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.pre, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 35); ASSERT_EQ(stats.rank_total[0].cycles.pre, 0); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 35); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 0); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 35); } TEST_F(DramPowerTest_LPDDR5_1, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 196); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 208); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 226); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1131); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1111); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 0); ASSERT_EQ(std::round(total_energy.total()*1e12), 1761); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_10.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_10 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 10, CmdType::ACT, { 0, 0, 0 }}, { 30, CmdType::PRE, { 0, 0, 0 }}, { 45, CmdType::SREFEN, { 0, 0, 0 }}, { 70, CmdType::DSMEN, { 0, 0, 0 }}, { 85, CmdType::DSMEX, { 0, 0, 0 }}, { 95, CmdType::SREFEX, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_10, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 45); ASSERT_EQ(stats.rank_total[0].cycles.pre, 30); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 10); ASSERT_EQ(stats.rank_total[0].cycles.deepSleepMode, 15); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 0); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats.bank[b].cycles.act, 45); }else{ ASSERT_EQ(stats.bank[b].cycles.act, 25); } } // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats.bank[b].cycles.pre, 30); }else{ ASSERT_EQ(stats.bank[b].cycles.pre, 50); } } // Check bank specific PDNA cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownAct, 0); // Check bank specific PDNP cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownPre, 0); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 10); // Check bank specific DSM cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.deepSleepMode, 15); } TEST_F(DramPowerTest_LPDDR5_10, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 196); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 208); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 0); ASSERT_EQ(std::round(energy.E_sref*1e12), 187); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 0); ASSERT_EQ(std::round(energy.E_dsm*1e12), 183); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1670); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1428); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 935); ASSERT_EQ(std::round((total_energy.total() + energy.E_dsm +energy.E_sref + energy.E_PDNA + energy.E_PDNP)*1e12), 5078); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_11.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_11 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 5, CmdType::ACT, { 2, 0, 0 }}, { 15, CmdType::ACT, { 10, 0, 0 }}, { 30, CmdType::PREA, { 0, 0, 0 }}, { 40, CmdType::REFP2B, { 9, 0, 0 }}, { 50, CmdType::REFP2B, { 5, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_11, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 60); ASSERT_EQ(stats.rank_total[0].cycles.pre, 40); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 0); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats .bank[b].cycles.act, 30); }else if (b == 2){ ASSERT_EQ(stats .bank[b].cycles.act, 25); }else if(b == 10){ ASSERT_EQ(stats .bank[b].cycles.act, 15); }else if (b == 1 || b == 9 || b == 5 || b == 13){ ASSERT_EQ(stats .bank[b].cycles.act, 20); }else{ ASSERT_EQ(stats .bank[b].cycles.act, 0); } } // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats.bank[b].cycles.pre, 70); }else if (b == 2){ ASSERT_EQ(stats.bank[b].cycles.pre, 75); }else if (b== 10){ ASSERT_EQ(stats.bank[b].cycles.pre, 85); }else if (b == 1 || b == 9 || b == 5 || b == 13){ ASSERT_EQ(stats.bank[b].cycles.pre, 80); }else{ ASSERT_EQ(stats.bank[b].cycles.pre, 100); } } // Check bank specific PDNA cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownAct, 0); // Check bank specific PDNP cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownPre, 0); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 0); } TEST_F(DramPowerTest_LPDDR5_11, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 588); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 623); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 0); ASSERT_EQ(std::round(total_energy.E_ref_2B*1e12), 117); ASSERT_EQ(std::round(total_energy.E_ref_PB*1e12), 0); ASSERT_EQ(std::round(energy.E_sref*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1990); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1904); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 1246); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref + energy.E_PDNA + energy.E_PDNP)*1e12), 4565); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_12.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_12 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { // Timestamp, Cmd, { Bank, BG, Rank} { 0, CmdType::ACT, { 0, 0, 0 } }, { 10, CmdType::ACT, { 1, 0, 0 } }, { 15, CmdType::PRE, { 0, 0, 0 } }, { 25, CmdType::ACT, { 3, 0, 0 } }, { 25, CmdType::PRE, { 1, 0, 0 } }, { 30, CmdType::ACT, { 0, 0, 0 } }, { 35, CmdType::ACT, { 2, 0, 0 } }, { 40, CmdType::PRE, { 1, 0, 0 } }, { 40, CmdType::PRE, { 3, 0, 0 } }, { 45, CmdType::PRE, { 0, 0, 0 } }, { 50, CmdType::PRE, { 2, 0, 0 } }, { 85, CmdType::ACT, { 7, 0, 0 } }, { 100, CmdType::PRE, { 7, 0, 0 } }, { 120, CmdType::ACT, { 6, 0, 0 } }, { 125, CmdType::END_OF_SIMULATION}, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); memSpec->numberOfRanks = 1; memSpec->numberOfBanks = 8; memSpec->banksPerGroup = 8; memSpec->numberOfBankGroups = 1; memSpec->burstLength = 8; memSpec->dataRate = 2; memSpec->bank_arch = MemSpecLPDDR5::M16B; memSpec->numberOfDevices = 1; memSpec->bitWidth = 16; memSpec->memTimingSpec.tRAS = 10; memSpec->memTimingSpec.tRP = 10; memSpec->memTimingSpec.tCK = 1e-9; memSpec->memTimingSpec.tWCK = 1e-9; memSpec->memTimingSpec.WCKtoCK = 2; memSpec->memPowerSpec[0].vDDX = 1; memSpec->memPowerSpec[0].iDD0X = 64e-3; memSpec->memPowerSpec[0].iDD3NX = 32e-3; memSpec->memPowerSpec[0].iDD2NX = 8e-3; memSpec->memPowerSpec[0].iBeta = memSpec->memPowerSpec[0].iDD0X; memSpec->bwParams.bwPowerFactRho = 0.333333333; ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_12, Pattern1) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; // Inspect first rank const auto & rank_1 = internal::LPDDR5TestAccessor.getRanks(ddr->getCore()).at(0); auto stats = ddr->getStats(); // Check global count ASSERT_EQ(stats.rank_total[0].cycles.act, 70); ASSERT_EQ(stats.rank_total[0].cycles.pre, 55); // Check per-bank ACT count ASSERT_EQ(stats.bank[0].cycles.act, 30); ASSERT_EQ(stats.bank[1].cycles.act, 15); ASSERT_EQ(stats.bank[2].cycles.act, 15); ASSERT_EQ(stats.bank[3].cycles.act, 15); ASSERT_EQ(stats.bank[7].cycles.act, 15); ASSERT_EQ(stats.bank[6].cycles.act, 5); ASSERT_EQ(stats.bank[4].cycles.act, 0); ASSERT_EQ(stats.bank[5].cycles.act, 0); // Check per-bank PRE count ASSERT_EQ(stats.bank[0].cycles.pre, 95); ASSERT_EQ(stats.bank[1].cycles.pre, 110); ASSERT_EQ(stats.bank[2].cycles.pre, 110); ASSERT_EQ(stats.bank[3].cycles.pre, 110); ASSERT_EQ(stats.bank[7].cycles.pre, 110); ASSERT_EQ(stats.bank[6].cycles.pre, 120); ASSERT_EQ(stats.bank[4].cycles.pre, 125); ASSERT_EQ(stats.bank[5].cycles.pre, 125); // Check global command count ASSERT_EQ(rank_1.commandCounter.get(CmdType::ACT), 7); ASSERT_EQ(rank_1.commandCounter.get(CmdType::PRE), 7); // per-bank ACT command count ASSERT_EQ(stats.bank[0].counter.act, 2); ASSERT_EQ(stats.bank[1].counter.act, 1); ASSERT_EQ(stats.bank[2].counter.act, 1); ASSERT_EQ(stats.bank[3].counter.act, 1); ASSERT_EQ(stats.bank[6].counter.act, 1); ASSERT_EQ(stats.bank[7].counter.act, 1); ASSERT_EQ(stats.bank[4].counter.act, 0); ASSERT_EQ(stats.bank[5].counter.act, 0); // per-bank PRE command count ASSERT_EQ(stats.bank[0].counter.pre, 2); ASSERT_EQ(stats.bank[1].counter.pre, 1); ASSERT_EQ(stats.bank[2].counter.pre, 1); ASSERT_EQ(stats.bank[3].counter.pre, 1); ASSERT_EQ(stats.bank[7].counter.pre, 1); ASSERT_EQ(stats.bank[6].counter.pre, 0); ASSERT_EQ(stats.bank[4].counter.pre, 0); ASSERT_EQ(stats.bank[5].counter.pre, 0); } TEST_F(DramPowerTest_LPDDR5_12, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 2240); ASSERT_EQ(std::round(energy.bank_energy[0].E_act*1e12), 640); ASSERT_EQ(std::round(energy.bank_energy[1].E_act*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[2].E_act*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[3].E_act*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[4].E_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_act*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[7].E_act*1e12), 320); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 3360); ASSERT_EQ(std::round(energy.bank_energy[0].E_pre*1e12), 1120); ASSERT_EQ(std::round(energy.bank_energy[1].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[2].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[3].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[4].E_pre*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_pre*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_pre*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 480); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 240); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 240); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 240); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 80); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 240); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1120); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 2640); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 440); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 55); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_13.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_13 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0} }, { 5, CmdType::ACT, { 1, 0, 0} }, { 10, CmdType::ACT, { 2, 0, 0} }, { 15, CmdType::ACT, { 3, 0, 0} }, { 20, CmdType::ACT, { 4, 0, 0} }, { 25, CmdType::ACT, { 5, 0, 0} }, { 30, CmdType::ACT, { 6, 0, 0} }, { 35, CmdType::ACT, { 7, 0, 0} }, { 35, CmdType::RD, { 0, 0, 0} }, { 40, CmdType::RD, { 0, 0, 0} }, { 40, CmdType::RD, { 1, 0, 0} }, { 45, CmdType::RD, { 0, 0, 0} }, { 45, CmdType::RD, { 2, 0, 0} }, { 50, CmdType::RD, { 3, 0, 0} }, { 55, CmdType::RD, { 4, 0, 0} }, { 60, CmdType::RD, { 5, 0, 0} }, { 65, CmdType::RD, { 6, 0, 0} }, { 70, CmdType::RD, { 7, 0, 0} }, { 75, CmdType::RD, { 7, 0, 0} }, { 80, CmdType::RD, { 7, 0, 0} }, { 85, CmdType::WR, { 7, 0, 0} }, { 90, CmdType::WR, { 6, 0, 0} }, { 95, CmdType::WR, { 5, 0, 0} }, { 100, CmdType::WR, { 4, 0, 0} }, { 105, CmdType::WR, { 3, 0, 0} }, { 110, CmdType::WR, { 2, 0, 0} }, { 110, CmdType::WR, { 0, 0, 0} }, { 115, CmdType::WR, { 1, 0, 0} }, { 115, CmdType::RD, { 0, 0, 0} }, { 120, CmdType::WR, { 0, 0, 0} }, { 120, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); memSpec->numberOfRanks = 1; memSpec->numberOfBanks = 8; memSpec->banksPerGroup = 8; memSpec->numberOfBankGroups = 1; memSpec->bank_arch = MemSpecLPDDR5::M16B; memSpec->numberOfDevices = 1; memSpec->bitWidth = 16; memSpec->memTimingSpec.tCK = 1e-9; memSpec->memTimingSpec.tWCK = 1e-9; memSpec->memTimingSpec.WCKtoCK = 1; memSpec->memPowerSpec[0].vDDX = 1; memSpec->memPowerSpec[0].iDD0X = 64e-3; memSpec->memPowerSpec[0].iDD3NX = 32e-3; memSpec->memPowerSpec[0].iDD4RX = 72e-3; memSpec->memPowerSpec[0].iDD4WX = 96e-3; memSpec->memPowerSpec[0].iBeta = memSpec->memPowerSpec[0].iDD0X; memSpec->bwParams.bwPowerFactRho = 1; memSpec->burstLength = 16; memSpec->dataRate = 2; memSpec->memTimingSpec.tBurst = memSpec->burstLength/(memSpec->dataRate * memSpec->memTimingSpec.WCKtoCK); memSpec->prechargeOffsetRD = memSpec->memTimingSpec.tBurst + memSpec->memTimingSpec.tRBTP; memSpec->prechargeOffsetWR = memSpec->memTimingSpec.tWL + memSpec->memTimingSpec.tBurst + 1 + memSpec->memTimingSpec.tWR; ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_13, Pattern_2) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; // Inspect first rank const Rank & rank_1 = internal::LPDDR5TestAccessor.getRanks(ddr->getCore()).at(0); // Check global command count ASSERT_EQ(rank_1.commandCounter.get(CmdType::RD), 13); ASSERT_EQ(rank_1.commandCounter.get(CmdType::WR), 9); // Check bank RD command count ASSERT_EQ(rank_1.banks[0].counter.reads, 4); ASSERT_EQ(rank_1.banks[1].counter.reads, 1); ASSERT_EQ(rank_1.banks[2].counter.reads, 1); ASSERT_EQ(rank_1.banks[3].counter.reads, 1); ASSERT_EQ(rank_1.banks[4].counter.reads, 1); ASSERT_EQ(rank_1.banks[5].counter.reads, 1); ASSERT_EQ(rank_1.banks[6].counter.reads, 1); ASSERT_EQ(rank_1.banks[7].counter.reads, 3); // Check bank WR command count ASSERT_EQ(rank_1.banks[0].counter.writes, 2); ASSERT_EQ(rank_1.banks[1].counter.writes, 1); ASSERT_EQ(rank_1.banks[2].counter.writes, 1); ASSERT_EQ(rank_1.banks[3].counter.writes, 1); ASSERT_EQ(rank_1.banks[4].counter.writes, 1); ASSERT_EQ(rank_1.banks[5].counter.writes, 1); ASSERT_EQ(rank_1.banks[6].counter.writes, 1); ASSERT_EQ(rank_1.banks[7].counter.writes, 1); } TEST_F(DramPowerTest_LPDDR5_13, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 4160); ASSERT_EQ(std::round(energy.bank_energy[0].E_RD*1e12), 1280); ASSERT_EQ(std::round(energy.bank_energy[1].E_RD*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[2].E_RD*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[3].E_RD*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[4].E_RD*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[5].E_RD*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[6].E_RD*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[7].E_RD*1e12), 960); ASSERT_EQ(std::round(total_energy.E_WR*1e12), 4608); ASSERT_EQ(std::round(energy.bank_energy[0].E_WR*1e12), 1024); ASSERT_EQ(std::round(energy.bank_energy[1].E_WR*1e12), 512); ASSERT_EQ(std::round(energy.bank_energy[2].E_WR*1e12), 512); ASSERT_EQ(std::round(energy.bank_energy[3].E_WR*1e12), 512); ASSERT_EQ(std::round(energy.bank_energy[4].E_WR*1e12), 512); ASSERT_EQ(std::round(energy.bank_energy[5].E_WR*1e12), 512); ASSERT_EQ(std::round(energy.bank_energy[6].E_WR*1e12), 512); ASSERT_EQ(std::round(energy.bank_energy[7].E_WR*1e12), 512); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_14.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_14 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { // Timestamp, Cmd, { Bank, BG, Rank} { 0, CmdType::ACT, { 0, 0, 0 } }, { 10, CmdType::RDA, { 0, 0, 0 } }, { 15, CmdType::ACT, { 1, 0, 0 } }, { 15, CmdType::ACT, { 2, 0, 0 } }, { 25, CmdType::WRA, { 1, 0, 0 } }, { 30, CmdType::PRE, { 0, 0, 0 } }, { 35, CmdType::RDA, { 2, 0, 0 } }, { 50, CmdType::ACT, { 0, 0, 0 } }, { 50, CmdType::ACT, { 2, 0, 0 } }, { 60, CmdType::WRA, { 0, 0, 0 } }, { 60, CmdType::RDA, { 2, 0, 0 } }, { 75, CmdType::ACT, { 1, 0, 0 } }, { 80, CmdType::ACT, { 0, 0, 0 } }, { 85, CmdType::WRA, { 0, 0, 0 } }, { 95, CmdType::RDA, { 1, 0, 0 } }, { 125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); memSpec->numberOfRanks = 1; memSpec->numberOfBanks = 8; memSpec->banksPerGroup = 8; memSpec->numberOfBankGroups = 1; memSpec->bank_arch = MemSpecLPDDR5::M16B; memSpec->numberOfDevices = 1; memSpec->bitWidth = 16; memSpec->memTimingSpec.tRAS = 20; memSpec->memTimingSpec.tWR = 11; memSpec->memTimingSpec.tWL = 0; memSpec->memTimingSpec.tCK = 1e-9; memSpec->memTimingSpec.tWCK = 1e-9; memSpec->memTimingSpec.WCKtoCK = 1; memSpec->memTimingSpec.tRP = 10; memSpec->memTimingSpec.tRBTP = 2; memSpec->memPowerSpec[0].vDDX = 1; memSpec->memPowerSpec[0].iDD0X = 64e-3; memSpec->memPowerSpec[0].iDD2NX = 8e-3; memSpec->memPowerSpec[0].iDD3NX = 32e-3; memSpec->memPowerSpec[0].iDD4RX = 72e-3; memSpec->memPowerSpec[0].iDD4WX = 96e-3; memSpec->memPowerSpec[0].iBeta = memSpec->memPowerSpec[0].iDD0X; memSpec->bwParams.bwPowerFactRho = 0.333333333; memSpec->burstLength = 16; memSpec->dataRate = 2; memSpec->memTimingSpec.tBurst = memSpec->burstLength/(memSpec->dataRate * memSpec->memTimingSpec.WCKtoCK); memSpec->prechargeOffsetRD = memSpec->memTimingSpec.tBurst + memSpec->memTimingSpec.tRBTP; memSpec->prechargeOffsetWR = memSpec->memTimingSpec.tWL + memSpec->memTimingSpec.tBurst + 1 + memSpec->memTimingSpec.tWR; ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_14, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; const Rank & rank_1 = internal::LPDDR5TestAccessor.getRanks(ddr->getCore()).at(0); auto stats = ddr->getStats(); // Check global command count ASSERT_EQ(rank_1.commandCounter.get(CmdType::ACT), 7); ASSERT_EQ(rank_1.commandCounter.get(CmdType::PRE), 1); ASSERT_EQ(rank_1.commandCounter.get(CmdType::RDA), 4); ASSERT_EQ(rank_1.commandCounter.get(CmdType::WRA), 3); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 3); ASSERT_EQ(stats.bank[1].counter.act, 2); ASSERT_EQ(stats.bank[2].counter.act, 2); ASSERT_EQ(stats.bank[3].counter.act, 0); ASSERT_EQ(stats.bank[4].counter.act, 0); ASSERT_EQ(stats.bank[5].counter.act, 0); ASSERT_EQ(stats.bank[6].counter.act, 0); ASSERT_EQ(stats.bank[7].counter.act, 0); // Check bank command count: RDA ASSERT_EQ(stats.bank[0].counter.readAuto, 1); ASSERT_EQ(stats.bank[1].counter.readAuto, 1); ASSERT_EQ(stats.bank[2].counter.readAuto, 2); ASSERT_EQ(stats.bank[3].counter.readAuto, 0); ASSERT_EQ(stats.bank[4].counter.readAuto, 0); ASSERT_EQ(stats.bank[5].counter.readAuto, 0); ASSERT_EQ(stats.bank[6].counter.readAuto, 0); ASSERT_EQ(stats.bank[7].counter.readAuto, 0); // Check bank command count: WRA ASSERT_EQ(stats.bank[0].counter.writeAuto, 2); ASSERT_EQ(stats.bank[1].counter.writeAuto, 1); ASSERT_EQ(stats.bank[2].counter.writeAuto, 0); ASSERT_EQ(stats.bank[3].counter.writeAuto, 0); ASSERT_EQ(stats.bank[4].counter.writeAuto, 0); ASSERT_EQ(stats.bank[5].counter.writeAuto, 0); ASSERT_EQ(stats.bank[6].counter.writeAuto, 0); ASSERT_EQ(stats.bank[7].counter.writeAuto, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 100); ASSERT_EQ(stats.rank_total[0].cycles.pre, 25); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 75); ASSERT_EQ(stats.bank[1].cycles.act, 60); ASSERT_EQ(stats.bank[2].cycles.act, 50); ASSERT_EQ(stats.bank[3].cycles.act, 0); ASSERT_EQ(stats.bank[4].cycles.act, 0); ASSERT_EQ(stats.bank[5].cycles.act, 0); ASSERT_EQ(stats.bank[6].cycles.act, 0); ASSERT_EQ(stats.bank[7].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 50); ASSERT_EQ(stats.bank[1].cycles.pre, 65); ASSERT_EQ(stats.bank[2].cycles.pre, 75); ASSERT_EQ(stats.bank[3].cycles.pre, 125); ASSERT_EQ(stats.bank[4].cycles.pre, 125); ASSERT_EQ(stats.bank[5].cycles.pre, 125); ASSERT_EQ(stats.bank[6].cycles.pre, 125); ASSERT_EQ(stats.bank[7].cycles.pre, 125); } TEST_F(DramPowerTest_LPDDR5_14, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 4560); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1600); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 1200); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 960); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 800); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 200); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(total_energy.E_RDA*1e12), 1280); ASSERT_EQ(std::round(energy.bank_energy[0].E_RDA*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[1].E_RDA*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[2].E_RDA*1e12), 640); ASSERT_EQ(std::round(energy.bank_energy[3].E_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[4].E_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_RDA*1e12), 0); ASSERT_EQ(std::round(total_energy.E_WRA*1e12), 1536); ASSERT_EQ(std::round(energy.bank_energy[0].E_WRA*1e12), 1024); ASSERT_EQ(std::round(energy.bank_energy[1].E_WRA*1e12), 512); ASSERT_EQ(std::round(energy.bank_energy[2].E_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[3].E_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[4].E_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_WRA*1e12), 0); ASSERT_EQ(std::round(total_energy.E_pre_RDA*1e12), 2240); ASSERT_EQ(std::round(energy.bank_energy[0].E_pre_RDA*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[1].E_pre_RDA*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[2].E_pre_RDA*1e12), 1120); ASSERT_EQ(std::round(energy.bank_energy[3].E_pre_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[4].E_pre_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_pre_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_pre_RDA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_pre_RDA*1e12), 0); ASSERT_EQ(std::round(total_energy.E_pre_WRA*1e12), 1120+560); ASSERT_EQ(std::round(energy.bank_energy[0].E_pre_WRA*1e12), 1120); ASSERT_EQ(std::round(energy.bank_energy[1].E_pre_WRA*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[2].E_pre_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[3].E_pre_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[4].E_pre_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_pre_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_pre_WRA*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_pre_WRA*1e12), 0); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_15.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_15 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 5, CmdType::ACT, { 1, 0, 0 }}, { 10, CmdType::ACT, { 2, 0, 0 }}, { 15, CmdType::PRE, { 0, 0, 0 }}, { 20, CmdType::PREA, { 0, 0, 0 }}, { 25, CmdType::PRE, { 1, 0, 0 }}, { 30, CmdType::PREA, { 0, 0, 0 }}, { 40, CmdType::ACT, { 0, 0, 0 }}, { 40, CmdType::ACT, { 3, 0, 0 }}, { 50, CmdType::PRE, { 3, 0, 0 }}, { 125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); memSpec->numberOfRanks = 1; memSpec->numberOfBanks = 8; memSpec->banksPerGroup = 8; memSpec->numberOfBankGroups = 1; memSpec->bank_arch = MemSpecLPDDR5::M16B; memSpec->numberOfDevices = 1; memSpec->bitWidth = 16; memSpec->memTimingSpec.tRAS = 10; memSpec->memTimingSpec.tWR = 20; memSpec->memTimingSpec.tWR = 12; memSpec->memTimingSpec.tWL = 0; memSpec->memTimingSpec.tCK = 1e-9; memSpec->memTimingSpec.tWCK = 1e-9; memSpec->memTimingSpec.tRP = 10; memSpec->memTimingSpec.WCKtoCK = 2; memSpec->memPowerSpec[0].vDDX = 1; memSpec->memPowerSpec[0].iDD0X = 64e-3; memSpec->memPowerSpec[0].iDD2NX = 8e-3; memSpec->memPowerSpec[0].iDD3NX = 32e-3; memSpec->memPowerSpec[0].iDD4RX = 72e-3; memSpec->memPowerSpec[0].iDD4WX = 72e-3; memSpec->memPowerSpec[0].iBeta = memSpec->memPowerSpec[0].iDD0X; memSpec->bwParams.bwPowerFactRho = 0.333333333; memSpec->burstLength = 16; memSpec->dataRate = 2; ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_15, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 2); ASSERT_EQ(stats.bank[1].counter.act, 1); ASSERT_EQ(stats.bank[2].counter.act, 1); ASSERT_EQ(stats.bank[3].counter.act, 1); ASSERT_EQ(stats.bank[4].counter.act, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 105); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 100); ASSERT_EQ(stats.bank[1].cycles.act, 15); ASSERT_EQ(stats.bank[2].cycles.act, 10); ASSERT_EQ(stats.bank[3].cycles.act, 10); ASSERT_EQ(stats.bank[4].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 25); ASSERT_EQ(stats.bank[1].cycles.pre, 110); ASSERT_EQ(stats.bank[2].cycles.pre, 115); ASSERT_EQ(stats.bank[3].cycles.pre, 115); ASSERT_EQ(stats.bank[4].cycles.pre, 125); } TEST_F(DramPowerTest_LPDDR5_15, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 1600); ASSERT_EQ(std::round(energy.bank_energy[0].E_act*1e12), 640); ASSERT_EQ(std::round(energy.bank_energy[1].E_act*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[2].E_act*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[3].E_act*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[4].E_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 2240); ASSERT_EQ(std::round(energy.bank_energy[0].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[1].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[2].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[3].E_pre*1e12), 560); ASSERT_EQ(std::round(energy.bank_energy[4].E_pre*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_pre*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_pre*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_pre*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 3840); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1680); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 1600); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 240); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 160); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 160); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 160); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 20); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 20); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 20); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 20); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 20); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 20); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 20); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 20); }; ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_16.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_16 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0}}, { 0, CmdType::ACT, { 1, 0, 0}}, { 0, CmdType::ACT, { 2, 0, 0}}, { 15, CmdType::PRE, { 0, 0, 0}}, { 20, CmdType::REFB, { 0, 0, 0}}, { 25, CmdType::PRE, { 1, 0, 0}}, { 30, CmdType::RDA, { 2, 0, 0}}, { 50, CmdType::REFB, { 2, 0, 0}}, { 60, CmdType::ACT, { 1, 0, 0}}, { 80, CmdType::PRE, { 1, 0, 0}}, { 85, CmdType::REFB, { 1, 0, 0}}, { 85, CmdType::REFB, { 0, 0, 0}}, { 125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); memSpec->numberOfRanks = 1; memSpec->numberOfBanks = 8; memSpec->banksPerGroup = 8; memSpec->numberOfBankGroups = 1; memSpec->bank_arch = MemSpecLPDDR5::M16B; memSpec->numberOfDevices = 1; memSpec->bitWidth = 16; memSpec->memTimingSpec.tRAS = 10; memSpec->memTimingSpec.tRFCPB = 25; memSpec->memTimingSpec.tWR = 20; memSpec->memTimingSpec.tWL = 0; memSpec->memTimingSpec.tCK = 1e-9; memSpec->memTimingSpec.tWCK = 1e-9; memSpec->memTimingSpec.tRP = 10; memSpec->memTimingSpec.tREFI = 1; memSpec->memTimingSpec.WCKtoCK = 1; memSpec->memTimingSpec.tRBTP = 2; memSpec->memPowerSpec[0].vDDX = 1; memSpec->memPowerSpec[0].iDD0X = 64e-3; memSpec->memPowerSpec[0].iDD2NX = 8e-3; memSpec->memPowerSpec[0].iDD3NX = 32e-3; memSpec->memPowerSpec[0].iDD4RX = 72e-3; memSpec->memPowerSpec[0].iDD4WX = 72e-3; memSpec->memPowerSpec[0].iDD5PBX = 6008e-3; memSpec->memPowerSpec[0].iBeta = memSpec->memPowerSpec[0].iDD0X; memSpec->bwParams.bwPowerFactRho = 0.333333333; memSpec->burstLength = 16; memSpec->dataRate = 2; memSpec->memTimingSpec.tBurst = memSpec->burstLength/(memSpec->dataRate * memSpec->memTimingSpec.WCKtoCK); memSpec->prechargeOffsetRD = memSpec->memTimingSpec.tBurst + memSpec->memTimingSpec.tRBTP; memSpec->prechargeOffsetWR = memSpec->memTimingSpec.tWL + memSpec->memTimingSpec.tBurst + 1 + memSpec->memTimingSpec.tWR; ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_16, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); ASSERT_EQ(stats.bank[1].counter.act, 2); ASSERT_EQ(stats.bank[2].counter.act, 1); ASSERT_EQ(stats.bank[3].counter.act, 0); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); ASSERT_EQ(stats.bank[1].counter.pre, 2); ASSERT_EQ(stats.bank[2].counter.pre, 1); ASSERT_EQ(stats.bank[3].counter.pre, 0); // Check bank command count: REFPB ASSERT_EQ(stats.bank[0].counter.refPerBank, 2); ASSERT_EQ(stats.bank[1].counter.refPerBank, 1); ASSERT_EQ(stats.bank[2].counter.refPerBank, 1); ASSERT_EQ(stats.bank[3].counter.refPerBank, 0); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 100); ASSERT_EQ(stats.rank_total[0].cycles.pre, 25); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 65); ASSERT_EQ(stats.bank[1].cycles.act, 70); ASSERT_EQ(stats.bank[2].cycles.act, 65); ASSERT_EQ(stats.bank[3].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 60); ASSERT_EQ(stats.bank[1].cycles.pre, 55); ASSERT_EQ(stats.bank[2].cycles.pre, 60); ASSERT_EQ(stats.bank[3].cycles.pre, 125); } TEST_F(DramPowerTest_LPDDR5_16, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 4800); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1600); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 1040); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 1120); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 1040); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 200); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 25); ASSERT_EQ(std::round(total_energy.E_ref_PB*1e12), 600); ASSERT_EQ(std::round(energy.bank_energy[0].E_ref_PB*1e12), 300); ASSERT_EQ(std::round(energy.bank_energy[1].E_ref_PB*1e12), 150); ASSERT_EQ(std::round(energy.bank_energy[2].E_ref_PB*1e12), 150); ASSERT_EQ(std::round(energy.bank_energy[3].E_ref_PB*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[4].E_ref_PB*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[5].E_ref_PB*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_ref_PB*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[7].E_ref_PB*1e12), 0); }; ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_17.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_17 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::REFP2B, { 0, 0, 0 }}, { 5, CmdType::REFP2B, { 1, 0, 0 }}, { 40, CmdType::REFP2B, { 4, 0, 0 }}, { 45, CmdType::ACT, { 0, 0, 0 }}, { 70, CmdType::PRE, { 0, 0, 0 }}, { 80, CmdType::REFP2B, { 0, 0, 0 }}, {125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); memSpec->numberOfRanks = 1; memSpec->numberOfBanks = 8; memSpec->numberOfBankGroups = 2; memSpec->perTwoBankOffset = 2; memSpec->bank_arch = MemSpecLPDDR5::MBG; memSpec->numberOfDevices = 1; memSpec->bitWidth = 16; memSpec->memTimingSpec.tRAS = 10; memSpec->memTimingSpec.tRFCPB = 25; memSpec->memTimingSpec.tWR = 20; memSpec->memTimingSpec.tWL = 0; memSpec->memTimingSpec.tCK = 1e-9; memSpec->memTimingSpec.tWCK = 1e-9; memSpec->memTimingSpec.tRP = 10; memSpec->memTimingSpec.tREFI = 1; memSpec->memTimingSpec.WCKtoCK = 2; memSpec->memPowerSpec[0].vDDX = 1; memSpec->memPowerSpec[0].iDD0X = 64e-3; memSpec->memPowerSpec[0].iDD2NX = 8e-3; memSpec->memPowerSpec[0].iDD3NX = 32e-3; memSpec->memPowerSpec[0].iDD4RX = 72e-3; memSpec->memPowerSpec[0].iDD4WX = 72e-3; memSpec->memPowerSpec[0].iDD5PBX = 10009e-3; memSpec->memPowerSpec[0].iBeta = memSpec->memPowerSpec[0].iDD0X; memSpec->bwParams.bwPowerFactRho = 0.333333333; memSpec->burstLength = 16; memSpec->dataRate = 2; ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_17, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); // Check bank command count: REF2B ASSERT_EQ(stats.bank[0].counter.refPerTwoBanks, 2); ASSERT_EQ(stats.bank[2].counter.refPerTwoBanks, 2); ASSERT_EQ(stats.bank[1].counter.refPerTwoBanks, 1); ASSERT_EQ(stats.bank[3].counter.refPerTwoBanks, 1); ASSERT_EQ(stats.bank[4].counter.refPerTwoBanks, 1); ASSERT_EQ(stats.bank[6].counter.refPerTwoBanks, 1); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 85); ASSERT_EQ(stats.rank_total[0].cycles.pre, 40); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 75); ASSERT_EQ(stats.bank[2].cycles.act, 50); ASSERT_EQ(stats.bank[1].cycles.act, 25); ASSERT_EQ(stats.bank[3].cycles.act, 25); ASSERT_EQ(stats.bank[4].cycles.act, 25); ASSERT_EQ(stats.bank[6].cycles.act, 25); ASSERT_EQ(stats.bank[5].cycles.act, 0); ASSERT_EQ(stats.bank[7].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 50); ASSERT_EQ(stats.bank[2].cycles.pre, 75); ASSERT_EQ(stats.bank[1].cycles.pre, 100); ASSERT_EQ(stats.bank[3].cycles.pre, 100); ASSERT_EQ(stats.bank[4].cycles.pre, 100); ASSERT_EQ(stats.bank[6].cycles.pre, 100); ASSERT_EQ(stats.bank[5].cycles.pre, 125); ASSERT_EQ(stats.bank[7].cycles.pre, 125); } TEST_F(DramPowerTest_LPDDR5_17, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 4960); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1360); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 1200); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 800); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(total_energy.E_ref_2B*1e12), 1000); ASSERT_EQ(std::round(energy.bank_energy[0].E_ref_2B*1e12), 250); ASSERT_EQ(std::round(energy.bank_energy[1].E_ref_2B*1e12), 125); ASSERT_EQ(std::round(energy.bank_energy[2].E_ref_2B*1e12), 250); ASSERT_EQ(std::round(energy.bank_energy[3].E_ref_2B*1e12), 125); ASSERT_EQ(std::round(energy.bank_energy[4].E_ref_2B*1e12), 125); ASSERT_EQ(std::round(energy.bank_energy[5].E_ref_2B*1e12), 0); ASSERT_EQ(std::round(energy.bank_energy[6].E_ref_2B*1e12), 125); ASSERT_EQ(std::round(energy.bank_energy[7].E_ref_2B*1e12), 0); }; ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_18.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_18 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, {0, 0, 0} }, { 20, CmdType::PDEA }, { 30, CmdType::PDXA }, { 40, CmdType::PRE, {0, 0, 0} }, { 60, CmdType::PDEP }, { 65, CmdType::PDXP }, { 75, CmdType::REFA }, {100, CmdType::PDEP }, {125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); memSpec->numberOfRanks = 1; memSpec->numberOfBanks = 8; memSpec->numberOfBankGroups = 2; memSpec->banksPerGroup = 4; memSpec->perTwoBankOffset = 2; memSpec->bank_arch = MemSpecLPDDR5::MBG; memSpec->numberOfDevices = 1; memSpec->bitWidth = 16; memSpec->memTimingSpec.tRAS = 10; memSpec->memTimingSpec.tRCD = 20; memSpec->memTimingSpec.tRFC = 25; memSpec->memTimingSpec.tRFCPB = 25; memSpec->memTimingSpec.tWR = 20; memSpec->memTimingSpec.tRP = 20; memSpec->memTimingSpec.tWL = 0; memSpec->memTimingSpec.tCK = 1e-9; memSpec->memTimingSpec.tWCK = 1e-9; memSpec->memTimingSpec.tREFI = 1; memSpec->memTimingSpec.WCKtoCK = 2; memSpec->memPowerSpec[0].vDDX = 1; memSpec->memPowerSpec[0].iDD0X = 64e-3; memSpec->memPowerSpec[0].iDD2NX = 8e-3; memSpec->memPowerSpec[0].iDD2PX = 6e-3; memSpec->memPowerSpec[0].iDD3NX = 32e-3; memSpec->memPowerSpec[0].iDD3PX = 20e-3; memSpec->memPowerSpec[0].iDD4RX = 72e-3; memSpec->memPowerSpec[0].iDD4WX = 72e-3; memSpec->memPowerSpec[0].iDD5PBX = 30e-3; memSpec->memPowerSpec[0].iBeta = memSpec->memPowerSpec[0].iDD0X; memSpec->bwParams.bwPowerFactRho = 0.333333333; memSpec->burstLength = 16; memSpec->dataRate = 2; ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_18, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.activeTime(), 55); ASSERT_EQ(stats.rank_total[0].cycles.act, 55); ASSERT_EQ(stats.rank_total[0].cycles.pre, 30); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 30); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 55); ASSERT_EQ(stats.bank[0].cycles.activeTime(), 55); ASSERT_EQ(stats.bank[1].cycles.act, 25); ASSERT_EQ(stats.bank[1].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[2].cycles.act, 25); ASSERT_EQ(stats.bank[2].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[3].cycles.act, 25); ASSERT_EQ(stats.bank[3].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[4].cycles.act, 25); ASSERT_EQ(stats.bank[4].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[5].cycles.act, 25); ASSERT_EQ(stats.bank[5].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[6].cycles.act, 25); ASSERT_EQ(stats.bank[6].cycles.activeTime(), 25); ASSERT_EQ(stats.bank[7].cycles.act, 25); ASSERT_EQ(stats.bank[7].cycles.activeTime(), 25); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 30); ASSERT_EQ(stats.bank[1].cycles.pre, 60); ASSERT_EQ(stats.bank[2].cycles.pre, 60); ASSERT_EQ(stats.bank[3].cycles.pre, 60); ASSERT_EQ(stats.bank[4].cycles.pre, 60); ASSERT_EQ(stats.bank[5].cycles.pre, 60); ASSERT_EQ(stats.bank[6].cycles.pre, 60); ASSERT_EQ(stats.bank[7].cycles.pre, 60); } TEST_F(DramPowerTest_LPDDR5_18, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 4560); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 880); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 880); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 400); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 240); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 30); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 200); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 180); }; ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_19.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_19 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0,0,0} }, { 5, CmdType::PDEA }, { 30, CmdType::PDXA }, { 40, CmdType::PRE, {0,0,0} }, { 45, CmdType::PDEP }, { 65, CmdType::PDXP }, { 75, CmdType::REFA }, { 85, CmdType::PDEP }, { 90, CmdType::PDXP }, {125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); memSpec->numberOfRanks = 1; memSpec->numberOfBanks = 8; memSpec->numberOfBankGroups = 2; memSpec->banksPerGroup = 4; memSpec->bank_arch = MemSpecLPDDR5::MBG; memSpec->numberOfDevices = 1; memSpec->bitWidth = 16; memSpec->memTimingSpec.tRAS = 10; memSpec->memTimingSpec.tRCD = 20; memSpec->memTimingSpec.tRFC = 25; memSpec->memTimingSpec.tRFCPB = 25; memSpec->memTimingSpec.tWR = 20; memSpec->memTimingSpec.tRP = 20; memSpec->memTimingSpec.tWL = 0; memSpec->memTimingSpec.tCK = 1e-9; memSpec->memTimingSpec.tWCK = 1e-9; memSpec->memTimingSpec.tREFI = 1; memSpec->memTimingSpec.WCKtoCK = 2; memSpec->memPowerSpec[0].vDDX = 1; memSpec->memPowerSpec[0].iDD0X = 64e-3; memSpec->memPowerSpec[0].iDD2NX = 8e-3; memSpec->memPowerSpec[0].iDD2PX = 6e-3; memSpec->memPowerSpec[0].iDD3NX = 32e-3; memSpec->memPowerSpec[0].iDD3PX = 20e-3; memSpec->memPowerSpec[0].iDD4RX = 72e-3; memSpec->memPowerSpec[0].iDD4WX = 72e-3; memSpec->memPowerSpec[0].iDD5PBX = 30e-3; memSpec->memPowerSpec[0].iBeta = memSpec->memPowerSpec[0].iDD0X; memSpec->bwParams.bwPowerFactRho = 0.333333333; memSpec->burstLength = 16; memSpec->dataRate = 2; ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_19, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 55); ASSERT_EQ(stats.rank_total[0].cycles.pre, 55); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 5); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 55); ASSERT_EQ(stats.bank[1].cycles.act, 25); ASSERT_EQ(stats.bank[2].cycles.act, 25); ASSERT_EQ(stats.bank[3].cycles.act, 25); ASSERT_EQ(stats.bank[4].cycles.act, 25); ASSERT_EQ(stats.bank[5].cycles.act, 25); ASSERT_EQ(stats.bank[6].cycles.act, 25); ASSERT_EQ(stats.bank[7].cycles.act, 25); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 55); ASSERT_EQ(stats.bank[1].cycles.pre, 85); ASSERT_EQ(stats.bank[2].cycles.pre, 85); ASSERT_EQ(stats.bank[3].cycles.pre, 85); ASSERT_EQ(stats.bank[4].cycles.pre, 85); ASSERT_EQ(stats.bank[5].cycles.pre, 85); ASSERT_EQ(stats.bank[6].cycles.pre, 85); ASSERT_EQ(stats.bank[7].cycles.pre, 85); } TEST_F(DramPowerTest_LPDDR5_19, CalcWindow) { SimulationStats window; auto iterate_to_timestamp = [this](auto & command, timestamp_t timestamp) { while (command != this->testPattern.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } return this->ddr->getWindowStats(timestamp); }; auto command = testPattern.begin(); // Cycle 5 window = iterate_to_timestamp(command, 5); ASSERT_EQ(window.rank_total[0].cycles.act, 5); ASSERT_EQ(window.bank[0].cycles.act, 5); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 5); // Cycle 10 window = iterate_to_timestamp(command, 10); ASSERT_EQ(window.rank_total[0].cycles.act, 10); ASSERT_EQ(window.bank[0].cycles.act, 10); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 10); // Cycle 15 window = iterate_to_timestamp(command, 15); ASSERT_EQ(window.rank_total[0].cycles.act, 15); ASSERT_EQ(window.bank[0].cycles.act, 15); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 15); // Cycle 20 window = iterate_to_timestamp(command, 20); ASSERT_EQ(window.rank_total[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 20); // Cycle 25 window = iterate_to_timestamp(command, 25); ASSERT_EQ(window.rank_total[0].cycles.act, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 5); ASSERT_EQ(window.bank[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 20); // Cycle 30 window = iterate_to_timestamp(command, 30); ASSERT_EQ(window.rank_total[0].cycles.act, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 20); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 20); // Cycle 35 window = iterate_to_timestamp(command, 35); ASSERT_EQ(window.rank_total[0].cycles.act, 25); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 25); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 25); // Cycle 40 window = iterate_to_timestamp(command, 40); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 30); // Cycle 45 window = iterate_to_timestamp(command, 45); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 5); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 35); // Cycle 50 window = iterate_to_timestamp(command, 50); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 10); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 40); // Cycle 55 window = iterate_to_timestamp(command, 55); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 15); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 15); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 45); // Cycle 60 window = iterate_to_timestamp(command, 60); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 20); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 50); // Cycle 65 window = iterate_to_timestamp(command, 65); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 20); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 20); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 50); // Cycle 70 window = iterate_to_timestamp(command, 70); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 25); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 25); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 55); // Cycle 75 window = iterate_to_timestamp(command, 75); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 0); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 80 window = iterate_to_timestamp(command, 80); ASSERT_EQ(window.rank_total[0].cycles.act, 35); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 35); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 5); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 85 window = iterate_to_timestamp(command, 85); ASSERT_EQ(window.rank_total[0].cycles.act, 40); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 40); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 10); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 90 window = iterate_to_timestamp(command, 90); ASSERT_EQ(window.rank_total[0].cycles.act, 45); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 45); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 15); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 95 window = iterate_to_timestamp(command, 95); ASSERT_EQ(window.rank_total[0].cycles.act, 50); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 50); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 20); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 100 window = iterate_to_timestamp(command, 100); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 60); // Cycle 105 window = iterate_to_timestamp(command, 105); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 35); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 35); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 65); // Cycle 110 window = iterate_to_timestamp(command, 110); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 40); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 40); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 70); // Cycle 115 window = iterate_to_timestamp(command, 115); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 45); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 45); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 75); // Cycle 120 window = iterate_to_timestamp(command, 120); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 50); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 50); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 80); // Cycle 125 window = iterate_to_timestamp(command, 125); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 55); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 5); ASSERT_EQ(window.bank[0].cycles.act, 55); ASSERT_EQ(window.bank[0].cycles.pre, 55); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 85); }; TEST_F(DramPowerTest_LPDDR5_19, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 4560); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 880); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 880); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 400); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 440); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 55); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 200); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 30); }; ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_2.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_2 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 35, CmdType::PRE, { 0, 0, 0 }}, { 50, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_2, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[0].counter.act, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check bank command count: RD ASSERT_EQ(stats.bank[0].counter.reads, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.reads, 0); // Check bank command count: PRE ASSERT_EQ(stats.bank[0].counter.pre, 1); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.pre, 0); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 35); ASSERT_EQ(stats.rank_total[0].cycles.pre, 15); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 35); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 15); for(uint64_t b = 1; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 50); } TEST_F(DramPowerTest_LPDDR5_2, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 196); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 208); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 226); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1131); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1111); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 467); ASSERT_EQ(std::round(total_energy.total()*1e12), 2228); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_20.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_20 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::REFA }, { 5, CmdType::PDEP }, { 15, CmdType::PDXP }, { 30, CmdType::REFB, { 0,0,0} }, { 40, CmdType::ACT, { 1,0,0,}}, { 45, CmdType::PDEA }, { 75, CmdType::PDXA }, {125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); memSpec->numberOfRanks = 1; memSpec->numberOfBanks = 8; memSpec->numberOfBankGroups = 2; memSpec->banksPerGroup = 4; memSpec->bank_arch = MemSpecLPDDR5::MBG; memSpec->numberOfDevices = 1; memSpec->bitWidth = 16; memSpec->memTimingSpec.tRAS = 10; memSpec->memTimingSpec.tRCD = 20; memSpec->memTimingSpec.tRFC = 25; memSpec->memTimingSpec.tRFCPB = 25; memSpec->memTimingSpec.tWR = 20; memSpec->memTimingSpec.tRP = 20; memSpec->memTimingSpec.tWL = 0; memSpec->memTimingSpec.tCK = 1e-9; memSpec->memTimingSpec.tWCK = 1e-9; memSpec->memTimingSpec.tREFI = 1; memSpec->memTimingSpec.WCKtoCK = 2; memSpec->memPowerSpec[0].vDDX = 1; memSpec->memPowerSpec[0].iDD0X = 64e-3; memSpec->memPowerSpec[0].iDD2NX = 8e-3; memSpec->memPowerSpec[0].iDD2PX = 6e-3; memSpec->memPowerSpec[0].iDD3NX = 32e-3; memSpec->memPowerSpec[0].iDD3PX = 20e-3; memSpec->memPowerSpec[0].iDD4RX = 72e-3; memSpec->memPowerSpec[0].iDD4WX = 72e-3; memSpec->memPowerSpec[0].iDD5PBX = 30e-3; memSpec->memPowerSpec[0].iBeta = memSpec->memPowerSpec[0].iDD0X; memSpec->bwParams.bwPowerFactRho = 0.333333333; memSpec->burstLength = 16; memSpec->dataRate = 2; ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_20, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; auto stats = ddr->getStats(); // Check bank command count: ACT ASSERT_EQ(stats.bank[1].counter.act, 1); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 105); ASSERT_EQ(stats.rank_total[0].cycles.pre, 5); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 15); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 0); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 50); ASSERT_EQ(stats.bank[1].cycles.act, 95); ASSERT_EQ(stats.bank[2].cycles.act, 25); ASSERT_EQ(stats.bank[3].cycles.act, 25); ASSERT_EQ(stats.bank[4].cycles.act, 25); ASSERT_EQ(stats.bank[5].cycles.act, 25); ASSERT_EQ(stats.bank[6].cycles.act, 25); ASSERT_EQ(stats.bank[7].cycles.act, 25); // Check bank specific PRE cycle count ASSERT_EQ(stats.bank[0].cycles.pre, 60); ASSERT_EQ(stats.bank[1].cycles.pre, 15); ASSERT_EQ(stats.bank[2].cycles.pre, 85); ASSERT_EQ(stats.bank[3].cycles.pre, 85); ASSERT_EQ(stats.bank[4].cycles.pre, 85); ASSERT_EQ(stats.bank[5].cycles.pre, 85); ASSERT_EQ(stats.bank[6].cycles.pre, 85); ASSERT_EQ(stats.bank[7].cycles.pre, 85); } TEST_F(DramPowerTest_LPDDR5_20, CalcWindow) { SimulationStats window; auto iterate_to_timestamp = [this](auto & command, timestamp_t timestamp) { while (command != this->testPattern.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } return this->ddr->getWindowStats(timestamp); }; auto command = testPattern.begin(); // Cycle 5 window = iterate_to_timestamp(command, 5); ASSERT_EQ(window.rank_total[0].cycles.act, 5); ASSERT_EQ(window.rank_total[0].cycles.pre, 0); ASSERT_EQ(window.bank[0].cycles.act, 5); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 5); ASSERT_EQ(window.bank[1].cycles.pre, 0); ASSERT_EQ(window.bank[2].cycles.act, 5); ASSERT_EQ(window.bank[2].cycles.pre, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 25 window = iterate_to_timestamp(command, 25); ASSERT_EQ(window.rank_total[0].cycles.act, 25); ASSERT_EQ(window.rank_total[0].cycles.pre, 0); ASSERT_EQ(window.bank[0].cycles.act, 25); ASSERT_EQ(window.bank[0].cycles.pre, 0); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 0); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 30 window = iterate_to_timestamp(command, 30); ASSERT_EQ(window.rank_total[0].cycles.act, 25); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 25); ASSERT_EQ(window.bank[0].cycles.pre, 5); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 5); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 5); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 35 window = iterate_to_timestamp(command, 35); ASSERT_EQ(window.rank_total[0].cycles.act, 30); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 30); ASSERT_EQ(window.bank[0].cycles.pre, 5); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 10); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 10); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 40 window = iterate_to_timestamp(command, 40); ASSERT_EQ(window.rank_total[0].cycles.act, 35); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 35); ASSERT_EQ(window.bank[0].cycles.pre, 5 ); ASSERT_EQ(window.bank[1].cycles.act, 25); ASSERT_EQ(window.bank[1].cycles.pre, 15); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 15); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 50 window = iterate_to_timestamp(command, 50); ASSERT_EQ(window.rank_total[0].cycles.act, 45); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 45); ASSERT_EQ(window.bank[0].cycles.pre, 5); ASSERT_EQ(window.bank[1].cycles.act, 35); ASSERT_EQ(window.bank[1].cycles.pre, 15); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 25); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 55 window = iterate_to_timestamp(command, 55); ASSERT_EQ(window.rank_total[0].cycles.act, 50); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 50); ASSERT_EQ(window.bank[0].cycles.pre, 5); ASSERT_EQ(window.bank[1].cycles.act, 40); ASSERT_EQ(window.bank[1].cycles.pre, 15); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 30); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 60 window = iterate_to_timestamp(command, 60); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 50); ASSERT_EQ(window.bank[0].cycles.pre, 10); ASSERT_EQ(window.bank[1].cycles.act, 45); ASSERT_EQ(window.bank[1].cycles.pre, 15); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 35); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 0); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 75 window = iterate_to_timestamp(command, 75); ASSERT_EQ(window.rank_total[0].cycles.act, 55); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 50); ASSERT_EQ(window.bank[0].cycles.pre, 10); ASSERT_EQ(window.bank[1].cycles.act, 45); ASSERT_EQ(window.bank[1].cycles.pre, 15); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 35); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 15); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 95 window = iterate_to_timestamp(command, 95); ASSERT_EQ(window.rank_total[0].cycles.act, 75); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 50); ASSERT_EQ(window.bank[0].cycles.pre, 30); ASSERT_EQ(window.bank[1].cycles.act, 65); ASSERT_EQ(window.bank[1].cycles.pre, 15); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 55); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 15); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); // Cycle 125 window = iterate_to_timestamp(command, 125); ASSERT_EQ(window.rank_total[0].cycles.act, 105); ASSERT_EQ(window.rank_total[0].cycles.pre, 5); ASSERT_EQ(window.bank[0].cycles.act, 50); ASSERT_EQ(window.bank[0].cycles.pre, 60); ASSERT_EQ(window.bank[1].cycles.act, 95); ASSERT_EQ(window.bank[1].cycles.pre, 15); ASSERT_EQ(window.bank[2].cycles.act, 25); ASSERT_EQ(window.bank[2].cycles.pre, 85); ASSERT_EQ(window.rank_total[0].cycles.powerDownAct, 15); ASSERT_EQ(window.rank_total[0].cycles.powerDownPre, 0); }; TEST_F(DramPowerTest_LPDDR5_20, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 6400); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1680); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 800); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 1520); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 400); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 400); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 5); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 300); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 0); }; ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_21.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_21 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 5, CmdType::SREFEN }, { 15, CmdType::SREFEX }, { 25, CmdType::SREFEN }, { 35, CmdType::DSMEN }, { 45, CmdType::DSMEX }, { 55, CmdType::SREFEX }, { 80, CmdType::SREFEN }, { 100, CmdType::DSMEN }, { 125, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); memSpec->numberOfRanks = 1; memSpec->numberOfBanks = 8; memSpec->numberOfBankGroups = 2; memSpec->banksPerGroup = 4; memSpec->bank_arch = MemSpecLPDDR5::MBG; memSpec->numberOfDevices = 1; memSpec->bitWidth = 16; memSpec->memTimingSpec.tRAS = 10; memSpec->memTimingSpec.tRCD = 20; memSpec->memTimingSpec.tRFC = 10; memSpec->memTimingSpec.tRFCPB = 25; memSpec->memTimingSpec.tWR = 20; memSpec->memTimingSpec.tRP = 20; memSpec->memTimingSpec.tWL = 0; memSpec->memTimingSpec.tCK = 1e-9; memSpec->memTimingSpec.tWCK = 1e-9; memSpec->memTimingSpec.tREFI = 1; memSpec->memTimingSpec.WCKtoCK = 2; memSpec->memPowerSpec[0].vDDX = 1; memSpec->memPowerSpec[0].iDD0X = 64e-3; memSpec->memPowerSpec[0].iDD2NX = 8e-3; memSpec->memPowerSpec[0].iDD2PX = 6e-3; memSpec->memPowerSpec[0].iDD3NX = 32e-3; memSpec->memPowerSpec[0].iDD3PX = 20e-3; memSpec->memPowerSpec[0].iDD4RX = 72e-3; memSpec->memPowerSpec[0].iDD4WX = 72e-3; memSpec->memPowerSpec[0].iDD5PBX = 30e-3; memSpec->memPowerSpec[0].iBeta = memSpec->memPowerSpec[0].iDD0X; memSpec->memPowerSpec[0].iDD6X = 5e-3; memSpec->bwParams.bwPowerFactRho = 0.333333333; memSpec->burstLength = 16; memSpec->dataRate = 2; ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_21, Test) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); }; const Rank & rank_1 = internal::LPDDR5TestAccessor.getRanks(ddr->getCore()).at(0); auto stats = ddr->getStats(); // Check counter ASSERT_EQ(rank_1.counter.selfRefresh, 3); // Check global cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 30); ASSERT_EQ(stats.rank_total[0].cycles.pre, 40); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 20); ASSERT_EQ(stats.rank_total[0].cycles.deepSleepMode, 35); // Check bank specific ACT cycle count ASSERT_EQ(stats.bank[0].cycles.act, 30); ASSERT_EQ(stats.bank[1].cycles.act, 30); ASSERT_EQ(stats.bank[2].cycles.act, 30); ASSERT_EQ(stats.bank[3].cycles.act, 30); ASSERT_EQ(stats.bank[4].cycles.act, 30); ASSERT_EQ(stats.bank[5].cycles.act, 30); ASSERT_EQ(stats.bank[6].cycles.act, 30); ASSERT_EQ(stats.bank[7].cycles.act, 30); } TEST_F(DramPowerTest_LPDDR5_21, CalcEnergy) { auto iterate_to_timestamp = [this](auto & command, const auto & container, timestamp_t timestamp) { while (command != container.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ++command; } }; auto command = testPattern.begin(); iterate_to_timestamp(command, testPattern, 125); auto energy = ddr->calcCoreEnergy(125); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 4320); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 480); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_act*1e12), 480); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_act*1e12), 480); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_act*1e12), 480); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_act*1e12), 480); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_act*1e12), 480); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_act*1e12), 480); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_act*1e12), 480); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_act*1e12), 480); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 320); ASSERT_EQ(std::round(energy.bank_energy[0].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[1].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[2].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[3].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[4].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[5].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[6].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.bank_energy[7].E_bg_pre*1e12), 40); ASSERT_EQ(std::round(energy.E_sref*1e12), 100); }; ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_3.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_3 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::PRE, { 0, 0, 0 }}, { 45, CmdType::PRE, { 3, 0, 0 }}, { 50, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_3, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); } // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 45); ASSERT_EQ(stats.rank_total[0].cycles.pre, 5); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 35); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 25); else ASSERT_EQ(stats.bank[b].cycles.act, 0); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 15); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 25); else ASSERT_EQ(stats.bank[b].cycles.pre, 50); } } TEST_F(DramPowerTest_LPDDR5_3, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 392); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 226); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1463); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1428); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 156); ASSERT_EQ(std::round(total_energy.total()*1e12), 2652); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_4.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_4 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 40, CmdType::RD, { 0, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 70, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_4, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 2); else if(b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); }; // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 50); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 50); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 30); else ASSERT_EQ(stats.bank[b].cycles.act, 0); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 20); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 40); else ASSERT_EQ(stats.bank[b].cycles.pre, 70); } } TEST_F(DramPowerTest_LPDDR5_4, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 392); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 678); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1633); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1587); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 623); ASSERT_EQ(std::round(total_energy.total()*1e12), 3742); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_5.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_5 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RD, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 40, CmdType::RD, { 0, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 65, CmdType::REFA, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_5, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.reads, 2); else if( b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); }; // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check bank command count: REFA for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ ASSERT_EQ(stats.bank[b].counter.refAllBank, 1); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 75); ASSERT_EQ(stats.rank_total[0].cycles.pre, 25); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 75); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 55); else ASSERT_EQ(stats.bank[b].cycles.act, 25); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 25); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 45); else ASSERT_EQ(stats.bank[b].cycles.pre, 75); } } TEST_F(DramPowerTest_LPDDR5_5, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 392); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 678); ASSERT_EQ(std::round(total_energy.E_ref_AB*1e12), 1699); ASSERT_EQ(std::round(total_energy.E_ref_SB*1e12), 0); ASSERT_EQ(std::round(total_energy.E_ref_PB*1e12), 0); ASSERT_EQ(std::round(total_energy.E_ref_2B*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 2657); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 2380); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 779); ASSERT_EQ(std::round(total_energy.total()*1e12), 6621); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_6.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_6 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::RDA, { 0, 0, 0 }}, { 20, CmdType::ACT, { 3, 0, 0 }}, { 35, CmdType::RD, { 3, 0, 0 }}, { 50, CmdType::PREA, { 0, 0, 0 }}, { 65, CmdType::REFA, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_6, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.act, 1); else ASSERT_EQ(stats.bank[b].counter.act, 0); } // Check bank command count: RD for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 3) ASSERT_EQ(stats.bank[b].counter.reads, 1); else ASSERT_EQ(stats.bank[b].counter.reads, 0); } for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].counter.readAuto, 1); else ASSERT_EQ(stats.bank[b].counter.readAuto, 0); } // Check bank command count: PRE for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0 || b == 3) ASSERT_EQ(stats.bank[b].counter.pre, 1); else ASSERT_EQ(stats.bank[b].counter.pre, 0); } // Check bank command count: REFA for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ ASSERT_EQ(stats.bank[b].counter.refAllBank, 1); } // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 75); ASSERT_EQ(stats.rank_total[0].cycles.pre, 25); // Check bank specific ACT cycle count; for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if (b == 0) ASSERT_EQ(stats.bank[b].cycles.act, 45); else if(b == 3) ASSERT_EQ(stats.bank[b].cycles.act, 55); else ASSERT_EQ(stats.bank[b].cycles.act, 25); } // Check bank specific PRE cycle count for(uint64_t b = 1; b < memSpec->numberOfBanks; b++){ if(b == 0) ASSERT_EQ(stats.bank[b].cycles.pre, 55); else if (b == 3) ASSERT_EQ(stats.bank[b].cycles.pre, 45); else ASSERT_EQ(stats.bank[b].cycles.pre, 75); } } TEST_F(DramPowerTest_LPDDR5_6, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 392); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 226); ASSERT_EQ(std::round(total_energy.E_RDA*1e12), 226); ASSERT_EQ(std::round(total_energy.E_ref_AB*1e12), 1699); ASSERT_EQ(std::round(total_energy.E_ref_SB*1e12), 0); ASSERT_EQ(std::round(total_energy.E_ref_2B*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 2639); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 2380); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 779); ASSERT_EQ(std::round(total_energy.total()*1e12), 6377); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_7.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_7 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::SREFEN, { 0, 0, 0 }}, { 40, CmdType::SREFEX, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_7, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check bank command count: ACT for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.act, 0); // Check bank command count: REFA for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].counter.refAllBank, 1); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 25); ASSERT_EQ(stats.rank_total[0].cycles.pre, 60); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 15); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 25); // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 60); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 15); } TEST_F(DramPowerTest_LPDDR5_7, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 0); ASSERT_EQ(std::round(total_energy.E_ref_AB*1e12), 1699); ASSERT_EQ(std::round(energy.E_sref*1e12), 280); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1024); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 793); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 1869); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref)*1e12), 4873); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_8.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_8 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 0, CmdType::PDEA, { 0, 0, 0 }}, { 30, CmdType::PDXA, { 0, 0, 0 }}, { 45, CmdType::PDEP, { 0, 0, 0 }}, { 70, CmdType::PDXP, { 0, 0, 0 }}, { 85, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_8, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 0); ASSERT_EQ(stats.rank_total[0].cycles.pre, 30); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 30); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 25); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.act, 0); // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.pre, 30); // Check bank specific PDNA cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownAct, 30); // Check bank specific PDNP cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownPre, 25); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 0); } TEST_F(DramPowerTest_LPDDR5_8, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 0); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 0); ASSERT_EQ(std::round(energy.E_sref*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 623); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 392); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 0); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 0); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 935); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref + energy.E_PDNA + energy.E_PDNP)*1e12), 1950); } ================================================ FILE: tests/tests_drampower/core/LPDDR5/lpddr5_test_pattern_9.cpp ================================================ #include #include "DRAMPower/command/Command.h" #include #include #include #include #include #include #include using namespace DRAMPower; class DramPowerTest_LPDDR5_9 : public ::testing::Test { protected: // Test pattern std::vector testPattern = { { 5, CmdType::ACT, { 0, 0, 0 }}, { 15, CmdType::ACT, { 5, 0, 0 }}, { 20, CmdType::RD, { 0, 0, 0 }}, { 30, CmdType::PDEA, { 0, 0, 0 }}, { 50, CmdType::PDXA, { 0, 0, 0 }}, { 55, CmdType::RD, { 5, 0, 0 }}, { 60, CmdType::RD, { 0, 0, 0 }}, { 70, CmdType::PREA, { 0, 0, 0 }}, { 80, CmdType::PDEP, { 0, 0, 0 }}, { 95, CmdType::PDXP, { 0, 0, 0 }}, { 100, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr memSpec; std::unique_ptr ddr; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); memSpec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); ddr = std::make_unique(*memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_LPDDR5_9, Counters_and_Cycles){ for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto stats = ddr->getStats(); // Check cycles count ASSERT_EQ(stats.rank_total[0].cycles.act, 45); ASSERT_EQ(stats.rank_total[0].cycles.pre, 20); ASSERT_EQ(stats.rank_total[0].cycles.selfRefresh, 0); ASSERT_EQ(stats.rank_total[0].cycles.powerDownAct, 20); ASSERT_EQ(stats.rank_total[0].cycles.powerDownPre, 15); // Check bank specific ACT cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats .bank[b].cycles.act, 45); }else if (b == 5){ ASSERT_EQ(stats .bank[b].cycles.act, 35); }else{ ASSERT_EQ(stats .bank[b].cycles.act, 0); } } // Check bank specific PRE cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++){ if(b == 0){ ASSERT_EQ(stats.bank[b].cycles.pre, 20); }else if (b == 5){ ASSERT_EQ(stats.bank[b].cycles.pre, 30); }else{ ASSERT_EQ(stats.bank[b].cycles.pre, 65); } } // Check bank specific PDNA cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownAct, 20); // Check bank specific PDNP cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.powerDownPre, 15); // Check bank specific SREF cycle count for(uint64_t b = 0; b < memSpec->numberOfBanks; b++) ASSERT_EQ(stats.bank[b].cycles.selfRefresh, 0); } TEST_F(DramPowerTest_LPDDR5_9, Energy) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); } auto energy = ddr->calcCoreEnergy(testPattern.back().timestamp); auto total_energy = energy.aggregated_bank_energy(); ASSERT_EQ(std::round(total_energy.E_act*1e12), 392); ASSERT_EQ(std::round(total_energy.E_pre*1e12), 415); ASSERT_EQ(std::round(total_energy.E_RD*1e12), 678); ASSERT_EQ(std::round(energy.E_sref*1e12), 0); ASSERT_EQ(std::round(energy.E_PDNA*1e12), 415); ASSERT_EQ(std::round(energy.E_PDNP*1e12), 235); ASSERT_EQ(std::round(total_energy.E_bg_act*1e12), 1474); ASSERT_EQ(std::round(energy.E_bg_act_shared*1e12), 1428); ASSERT_EQ(std::round(total_energy.E_bg_pre*1e12), 623); ASSERT_EQ(std::round((total_energy.total() + energy.E_sref + energy.E_PDNA + energy.E_PDNP)*1e12), 4234); } ================================================ FILE: tests/tests_drampower/interface/test_dbi_ddr4.cpp ================================================ #include #include #include #include #include #include "DRAMPower/data/stats.h" #include "DRAMPower/memspec/MemSpecDDR4.h" #include "DRAMPower/standards/ddr4/DDR4.h" #include "DRAMPower/util/extensions.h" using DRAMPower::interface_energy_info_t; using DRAMPower::InterfaceCalculation_DDR4; using DRAMPower::CmdType; using DRAMPower::Command; using DRAMPower::DDR4; using DRAMPower::MemSpecDDR4; using DRAMPower::SimulationStats; #define SZ_BITS(x) sizeof(x)*8 // burst length = 8 for x8 devices static constexpr uint8_t wr_data[] = { 0xF0, 0x0F, 0xE0, 0xF1, 0x00, 0xFF, 0xFF, 0xFF, // inverted to 0xF0,0x0F,0x1F,0xF1, 0xFF,0xFF,0xFF,0xFF // DBI Line: // H // before burst // H // burst 1 time = 4, virtual_time = 8 ok // H // burst 2 vt = 9 ok // L // burst 3 vt = 10 ok // H // burst 4 vt = 11 ok // L // burst 5 vt = 12 ok // H // burst 6 vt = 13 ok // H // burst 7 vt = 14 ok // H // burst 8 vt = 15 ok // H // after burst ok // 2 inversions // 0xFF to 0xF0; 4 ones to zeroes, 0 zeroes to ones, 8 ones, 0 zeroes // before burst // 0xF0 to 0x0F; 4 ones to zeroes, 4 zeroes to ones, 4 ones, 4 zeroes // 0x0F to 0x1F; 0 ones to zeroes, 1 zeroes to ones, 4 ones, 4 zeroes // 0x1F to 0xF1; 3 ones to zeroes, 3 zeroes to ones, 5 ones, 3 zeroes // 0xF1 to 0xFF; 0 ones to zeroes, 3 zeroes to ones, 5 ones, 3 zeroes // 0xFF to 0xFF; 0 ones to zeroes, 0 zeroes to ones, 8 ones, 0 zeroes // 0xFF to 0xFF; 0 ones to zeroes, 0 zeroes to ones, 8 ones, 0 zeroes // 0xFF to 0xFF; 0 ones to zeroes, 0 zeroes to ones, 8 ones, 0 zeroes // 0xFF to 0xFF; 0 ones to zeroes, 0 zeroes to ones, 8 ones, 0 zeroes // 0xFF to 0xFF; 0 ones to zeroes, 0 zeroes to ones, 8 ones, 0 zeroes // after burst // ones to zeroes: 11, zeroes to ones: 11, zeroes 14 }; // burst length = 8 for x8 devices static constexpr uint8_t rd_data[] = { 0, 0, 0, 0, 0, 0, 255, 1, // inverted to 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE // DBI Line: // H // before burst for test_patterns[0] or test_patterns[1] burst 1 // L // before burst for test_patterns[1] burst 2 // L // burst 1 time = 11, virtual_time = 22 ok // L // burst 2 vt = 23 ok // L // burst 3 vt = 24 ok // L // burst 4 vt = 25 ok // L // burst 5 vt = 26 ok // L // burst 6 vt = 27 ok // H // burst 7 vt = 28 ok // L // burst 8 vt = 29 ok // H // after burst for test_patterns[0] // L // after burst for test_patterns[1] burst 2 // 7 inversions // 0xFF to 0xFF; 0 ones to zeroes, 0 zeroes to ones, 8 ones, 0 zeroes // before burst // 0xFF to 0xFF; 0 ones to zeroes, 0 zeroes to ones, 8 ones, 0 zeroes // 0xFF to 0xFF; 0 ones to zeroes, 0 zeroes to ones, 8 ones, 0 zeroes // 0xFF to 0xFF; 0 ones to zeroes, 0 zeroes to ones, 8 ones, 0 zeroes // 0xFF to 0xFF; 0 ones to zeroes, 0 zeroes to ones, 8 ones, 0 zeroes // 0xFF to 0xFF; 0 ones to zeroes, 0 zeroes to ones, 8 ones, 0 zeroes // 0xFF to 0xFF; 0 ones to zeroes, 0 zeroes to ones, 8 ones, 0 zeroes // 0xFF to 0xFF; 0 ones to zeroes, 0 zeroes to ones, 8 ones, 0 zeroes // 0xFF to 0xFE; 1 ones to zeroes, 0 zeroes to ones, 8 ones, 0 zeroes // 0xFE to 0xFF; 0 ones to zeroes, 1 zeroes to ones, 8 ones, 1 zeroes // after burst // ones to zeroes: 1, zeroes to ones: 1, zeroes 1 }; class DDR4_DBI_Tests : public ::testing::Test { public: DDR4_DBI_Tests() { test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {4, CmdType::WR, {1, 0, 0, 0, 16}, wr_data, SZ_BITS(wr_data)}, {11, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}, {16, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {4, CmdType::WR, {1, 0, 0, 0, 16}, wr_data, SZ_BITS(wr_data)}, {11, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}, {15, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}, // Seamless read {20, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); initSpec(); ddr = std::make_unique(*spec); } void initSpec() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); spec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); } void runCommands(const std::vector &commands) { for (const Command &command : commands) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); } } std::vector> test_patterns; std::unique_ptr spec; std::unique_ptr ddr; }; // Test patterns for stats (counter) TEST_F(DDR4_DBI_Tests, Pattern_0) { ddr->getExtensionManager().withExtension([](DRAMPower::extensions::DBI& dbi) { dbi.enable(0, true); }); runCommands(test_patterns[0]); SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Data bus EXPECT_EQ(stats.writeBus.ones, 370); // 2 (datarate) * 24 (time) * 8 (bus width) - 14 (zeroes) EXPECT_EQ(stats.writeBus.zeroes, 14); EXPECT_EQ(stats.writeBus.ones_to_zeroes, 11); // 0 transitions 1 -> 0 EXPECT_EQ(stats.writeBus.zeroes_to_ones, 11); // back to 1 EXPECT_EQ(stats.readBus.ones, 383); // 2 (datarate) * 24 (time) * 8 (bus width) - 1 (zeroes) EXPECT_EQ(stats.readBus.zeroes, 1); // 1 (zeroes) EXPECT_EQ(stats.readBus.ones_to_zeroes, 1); // 1 transition 1 -> 0 EXPECT_EQ(stats.readBus.zeroes_to_ones, 1); // back to 1 // DBI EXPECT_EQ(stats.readDBI.ones, 48 - 7); EXPECT_EQ(stats.readDBI.zeroes, 7); EXPECT_EQ(stats.readDBI.ones_to_zeroes, 2); EXPECT_EQ(stats.readDBI.zeroes_to_ones, 2); EXPECT_EQ(stats.writeDBI.ones, 48 - 2); EXPECT_EQ(stats.writeDBI.zeroes, 2); EXPECT_EQ(stats.writeDBI.ones_to_zeroes, 2); EXPECT_EQ(stats.writeDBI.zeroes_to_ones, 2); } TEST_F(DDR4_DBI_Tests, Pattern_1) { ddr->getExtensionManager().withExtension([](DRAMPower::extensions::DBI& dbi) { dbi.enable(0, true); }); runCommands(test_patterns[1]); SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Data bus EXPECT_EQ(stats.writeBus.ones, 370); // 2 (datarate) * 24 (time) * 8 (bus width) - 14 (zeroes) EXPECT_EQ(stats.writeBus.zeroes, 14); EXPECT_EQ(stats.writeBus.ones_to_zeroes, 11); // 0 transitions 1 -> 0 EXPECT_EQ(stats.writeBus.zeroes_to_ones, 11); // back to 1 EXPECT_EQ(stats.readBus.ones, 382); // 2 (datarate) * 24 (time) * 8 (bus width) - 2 (zeroes) EXPECT_EQ(stats.readBus.zeroes, 2); // 2 (zeroes) EXPECT_EQ(stats.readBus.ones_to_zeroes, 2); // 2 transitions 1 -> 0 EXPECT_EQ(stats.readBus.zeroes_to_ones, 2); // back to 1 // DBI EXPECT_EQ(stats.readDBI.ones, 48 - 14); EXPECT_EQ(stats.readDBI.zeroes, 14); EXPECT_EQ(stats.readDBI.ones_to_zeroes, 3); EXPECT_EQ(stats.readDBI.zeroes_to_ones, 3); EXPECT_EQ(stats.writeDBI.ones, 48 - 2); EXPECT_EQ(stats.writeDBI.zeroes, 2); EXPECT_EQ(stats.writeDBI.ones_to_zeroes, 2); EXPECT_EQ(stats.writeDBI.zeroes_to_ones, 2); } class DDR4_DBI_Energy_Tests : public ::testing::Test { public: DDR4_DBI_Energy_Tests() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); spec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); t_CK = spec->memTimingSpec.tCK; voltage = spec->vddq; // Change impedances to different values from each other spec->memImpedanceSpec.rdbi_R_eq = 1; spec->memImpedanceSpec.wdbi_R_eq = 2; spec->memImpedanceSpec.rdbi_dyn_E = 3; spec->memImpedanceSpec.wdbi_dyn_E = 4; io_calc = std::make_unique(*spec); } std::unique_ptr spec; double t_CK; double voltage; std::unique_ptr io_calc; }; TEST_F(DDR4_DBI_Energy_Tests, Energy) { SimulationStats stats; stats.readDBI.ones = 1; stats.readDBI.zeroes = 2; stats.readDBI.ones_to_zeroes = 3; stats.readDBI.zeroes_to_ones = 4; // Controller -> write power // Dram -> read power // data rate dbi is 2 -> t_per_bit = 0.5 * t_CK double expected_static_controller = stats.writeDBI.zeroes * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.wdbi_R_eq; double expected_static_dram = stats.readDBI.zeroes * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.rdbi_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = stats.writeDBI.zeroes_to_ones * spec->memImpedanceSpec.wdbi_dyn_E; double expected_dynamic_dram = stats.readDBI.zeroes_to_ones * spec->memImpedanceSpec.rdbi_dyn_E; // DBI interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); } ================================================ FILE: tests/tests_drampower/interface/test_dbi_lpddr4.cpp ================================================ #include #include #include #include #include #include "DRAMPower/data/stats.h" #include "DRAMPower/memspec/MemSpecLPDDR4.h" #include "DRAMPower/standards/lpddr4/LPDDR4.h" #include "DRAMPower/util/extensions.h" using DRAMPower::interface_energy_info_t; using DRAMPower::InterfaceCalculation_LPDDR4; using DRAMPower::CmdType; using DRAMPower::Command; using DRAMPower::LPDDR4; using DRAMPower::MemSpecLPDDR4; using DRAMPower::SimulationStats; #define SZ_BITS(x) sizeof(x)*8 // burst length = 8 for x8 devices static constexpr uint8_t wr_data[] = { 0xF0, 0x0F, 0xE0, 0xF1, 0x00, 0xFF, 0xFF, 0xFF, // inverted to 0xF0,0x0F,0xE0,0x0E, 0x00,0x00,0x00,0x00 // DBI Line: // L // before burst // L // burst 1 time = 4, virtual_time = 8 ok // L // burst 2 vt = 9 ok // L // burst 3 vt = 10 ok // H // burst 4 vt = 11 ok // L // burst 5 vt = 12 ok // H // burst 6 vt = 13 ok // H // burst 7 vt = 14 ok // H // burst 8 vt = 15 ok // L // after burst ok // 4 inversions // 0x00 to 0xF0; 0 ones to zeroes, 4 zeroes to ones, 0 ones, 8 zeroes // before burst // 0xF0 to 0x0F; 4 ones to zeroes, 4 zeroes to ones, 4 ones, 4 zeroes // 0x0F to 0xE0; 4 ones to zeroes, 3 zeroes to ones, 4 ones, 4 zeroes // 0xE0 to 0x0E; 3 ones to zeroes, 3 zeroes to ones, 3 ones, 5 zeroes // 0x0E to 0x00; 3 ones to zeroes, 0 zeroes to ones, 3 ones, 5 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // after burst // ones to zeroes: 14, zeroes to ones: 14, ones: 14 }; // burst length = 8 for x8 devices static constexpr uint8_t rd_data[] = { 0, 0, 0, 0, 0, 0, 255, 1, // inverted to 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01 // DBI Line: // L // before burst for test_patterns[0] or test_patterns[1] burst 1 // L // before burst for test_patterns[1] burst 2 // L // burst 1 time = 11, virtual_time = 22 ok // L // burst 2 vt = 23 ok // L // burst 3 vt = 24 ok // L // burst 4 vt = 25 ok // L // burst 5 vt = 26 ok // L // burst 6 vt = 27 ok // H // burst 7 vt = 28 ok // L // burst 8 vt = 29 ok // L // after burst for test_patterns[0] // L // after burst for test_patterns[1] burst 2 // 1 inversions // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // before burst // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x01; 1 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x01 to 0x00; 0 ones to zeroes, 1 zeroes to ones, 1 ones, 1 zeroes // after burst // ones to zeroes: 1, zeroes to ones: 1, ones 1 }; class LPDDR4_DBI_Tests : public ::testing::Test { public: LPDDR4_DBI_Tests() { test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {4, CmdType::WR, {1, 0, 0, 0, 16}, wr_data, SZ_BITS(wr_data)}, {11, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}, {16, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {4, CmdType::WR, {1, 0, 0, 0, 16}, wr_data, SZ_BITS(wr_data)}, {11, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}, {15, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}, // Seamless read {20, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); initSpec(); ddr = std::make_unique(*spec); } void initSpec() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); spec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); } void runCommands(const std::vector &commands) { for (const Command &command : commands) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); } } std::vector> test_patterns; std::unique_ptr spec; std::unique_ptr ddr; }; // Test patterns for stats (counter) TEST_F(LPDDR4_DBI_Tests, Pattern_0) { ddr->getExtensionManager().withExtension([](DRAMPower::extensions::DBI& dbi) { dbi.enable(0, true); }); runCommands(test_patterns[0]); SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Data bus EXPECT_EQ(stats.writeBus.ones, 14); EXPECT_EQ(stats.writeBus.zeroes, 370); // 2 (datarate) * 24 (time) * 8 (bus width) - 14 (ones) EXPECT_EQ(stats.writeBus.ones_to_zeroes, 14); EXPECT_EQ(stats.writeBus.zeroes_to_ones, 14); EXPECT_EQ(stats.readBus.ones, 1); EXPECT_EQ(stats.readBus.zeroes, 383); // 2 (datarate) * 24 (time) * 8 (bus width) - 1 (ones) EXPECT_EQ(stats.readBus.ones_to_zeroes, 1); EXPECT_EQ(stats.readBus.zeroes_to_ones, 1); // DBI EXPECT_EQ(stats.readDBI.ones, 1); EXPECT_EQ(stats.readDBI.zeroes, 48-1); EXPECT_EQ(stats.readDBI.ones_to_zeroes, 1); EXPECT_EQ(stats.readDBI.zeroes_to_ones, 1); EXPECT_EQ(stats.writeDBI.ones, 4); EXPECT_EQ(stats.writeDBI.zeroes, 48-4); EXPECT_EQ(stats.writeDBI.ones_to_zeroes, 2); EXPECT_EQ(stats.writeDBI.zeroes_to_ones, 2); } TEST_F(LPDDR4_DBI_Tests, Pattern_1) { ddr->getExtensionManager().withExtension([](DRAMPower::extensions::DBI& dbi) { dbi.enable(0, true); }); runCommands(test_patterns[1]); SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Data bus EXPECT_EQ(stats.writeBus.ones, 14); EXPECT_EQ(stats.writeBus.zeroes, 370); // 2 (datarate) * 24 (time) * 8 (bus width) - 14 (ones) EXPECT_EQ(stats.writeBus.ones_to_zeroes, 14); EXPECT_EQ(stats.writeBus.zeroes_to_ones, 14); EXPECT_EQ(stats.readBus.ones, 2); EXPECT_EQ(stats.readBus.zeroes, 382); // 2 (datarate) * 24 (time) * 8 (bus width) - 2 (ones) EXPECT_EQ(stats.readBus.ones_to_zeroes, 2); EXPECT_EQ(stats.readBus.zeroes_to_ones, 2); // DBI EXPECT_EQ(stats.readDBI.ones, 2); EXPECT_EQ(stats.readDBI.zeroes, 48-2); EXPECT_EQ(stats.readDBI.ones_to_zeroes, 2); EXPECT_EQ(stats.readDBI.zeroes_to_ones, 2); EXPECT_EQ(stats.writeDBI.ones, 4); EXPECT_EQ(stats.writeDBI.zeroes, 48 - 4); EXPECT_EQ(stats.writeDBI.ones_to_zeroes, 2); EXPECT_EQ(stats.writeDBI.zeroes_to_ones, 2); } class LPDDR4_DBI_Energy_Tests : public ::testing::Test { public: LPDDR4_DBI_Energy_Tests() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); spec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); t_CK = spec->memTimingSpec.tCK; voltage = spec->vddq; // Change impedances to different values from each other spec->memImpedanceSpec.rdbi_R_eq = 1; spec->memImpedanceSpec.wdbi_R_eq = 2; spec->memImpedanceSpec.rdbi_dyn_E = 3; spec->memImpedanceSpec.wdbi_dyn_E = 4; io_calc = std::make_unique(*spec); } std::unique_ptr spec; double t_CK; double voltage; std::unique_ptr io_calc; }; TEST_F(LPDDR4_DBI_Energy_Tests, Energy) { SimulationStats stats; stats.readDBI.ones = 1; stats.readDBI.zeroes = 2; stats.readDBI.ones_to_zeroes = 3; stats.readDBI.zeroes_to_ones = 4; // Controller -> write power // Dram -> read power // data rate dbi is 2 -> t_per_bit = 0.5 * t_CK double expected_static_controller = stats.writeDBI.ones * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.wdbi_R_eq; double expected_static_dram = stats.readDBI.ones * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.rdbi_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = stats.writeDBI.zeroes_to_ones * spec->memImpedanceSpec.wdbi_dyn_E; double expected_dynamic_dram = stats.readDBI.zeroes_to_ones * spec->memImpedanceSpec.rdbi_dyn_E; // DBI interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); } ================================================ FILE: tests/tests_drampower/interface/test_dbi_lpddr5.cpp ================================================ #include #include #include #include #include #include "DRAMPower/data/stats.h" #include "DRAMPower/memspec/MemSpecLPDDR5.h" #include "DRAMPower/standards/lpddr5/LPDDR5.h" #include "DRAMPower/util/extensions.h" using DRAMPower::interface_energy_info_t; using DRAMPower::InterfaceCalculation_LPDDR5; using DRAMPower::CmdType; using DRAMPower::Command; using DRAMPower::LPDDR5; using DRAMPower::MemSpecLPDDR5; using DRAMPower::SimulationStats; #define SZ_BITS(x) sizeof(x)*8 // burst length = 8 for x8 devices static constexpr uint8_t wr_data[] = { 0xF0, 0x0F, 0xE0, 0xF1, 0x00, 0xFF, 0xFF, 0xFF, // inverted to 0xF0,0x0F,0xE0,0x0E, 0x00,0x00,0x00,0x00 // DBI Line: // L // before burst // L // burst 1 time = 4, virtual_time = 8 ok // L // burst 2 vt = 9 ok // L // burst 3 vt = 10 ok // H // burst 4 vt = 11 ok // L // burst 5 vt = 12 ok // H // burst 6 vt = 13 ok // H // burst 7 vt = 14 ok // H // burst 8 vt = 15 ok // L // after burst ok // 4 inversions // 0x00 to 0xF0; 0 ones to zeroes, 4 zeroes to ones, 0 ones, 8 zeroes // before burst // 0xF0 to 0x0F; 4 ones to zeroes, 4 zeroes to ones, 4 ones, 4 zeroes // 0x0F to 0xE0; 4 ones to zeroes, 3 zeroes to ones, 4 ones, 4 zeroes // 0xE0 to 0x0E; 3 ones to zeroes, 3 zeroes to ones, 3 ones, 5 zeroes // 0x0E to 0x00; 3 ones to zeroes, 0 zeroes to ones, 3 ones, 5 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // after burst // ones to zeroes: 14, zeroes to ones: 14, ones: 14 }; // burst length = 8 for x8 devices static constexpr uint8_t rd_data[] = { 0, 0, 0, 0, 0, 0, 255, 1, // inverted to 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01 // DBI Line: // L // before burst for test_patterns[0] or test_patterns[1] burst 1 // L // before burst for test_patterns[1] burst 2 // L // burst 1 time = 11, virtual_time = 22 ok // L // burst 2 vt = 23 ok // L // burst 3 vt = 24 ok // L // burst 4 vt = 25 ok // L // burst 5 vt = 26 ok // L // burst 6 vt = 27 ok // H // burst 7 vt = 28 ok // L // burst 8 vt = 29 ok // L // after burst for test_patterns[0] // L // after burst for test_patterns[1] burst 2 // 1 inversions // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // before burst // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x00; 0 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x00 to 0x01; 1 ones to zeroes, 0 zeroes to ones, 0 ones, 8 zeroes // 0x01 to 0x00; 0 ones to zeroes, 1 zeroes to ones, 1 ones, 1 zeroes // after burst // ones to zeroes: 1, zeroes to ones: 1, ones 1 }; class LPDDR5_DBI_Tests : public ::testing::Test { public: LPDDR5_DBI_Tests() { test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {4, CmdType::WR, {1, 0, 0, 0, 16}, wr_data, SZ_BITS(wr_data)}, {11, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}, {16, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {4, CmdType::WR, {1, 0, 0, 0, 16}, wr_data, SZ_BITS(wr_data)}, {11, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}, {15, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}, // Seamless read {20, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); initSpec(); ddr = std::make_unique(*spec); } void initSpec() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); spec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); } void runCommands(const std::vector &commands) { for (const Command &command : commands) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); } } std::vector> test_patterns; std::unique_ptr spec; std::unique_ptr ddr; }; // Test patterns for stats (counter) TEST_F(LPDDR5_DBI_Tests, Pattern_0) { ddr->getExtensionManager().withExtension([](DRAMPower::extensions::DBI& dbi) { dbi.enable(0, true); }); runCommands(test_patterns[0]); SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Data bus EXPECT_EQ(stats.writeBus.ones, 14); EXPECT_EQ(stats.writeBus.zeroes, 370); // 2 (datarate) * 24 (time) * 8 (bus width) - 14 (ones) EXPECT_EQ(stats.writeBus.ones_to_zeroes, 14); EXPECT_EQ(stats.writeBus.zeroes_to_ones, 14); EXPECT_EQ(stats.readBus.ones, 1); EXPECT_EQ(stats.readBus.zeroes, 383); // 2 (datarate) * 24 (time) * 8 (bus width) - 1 (ones) EXPECT_EQ(stats.readBus.ones_to_zeroes, 1); EXPECT_EQ(stats.readBus.zeroes_to_ones, 1); // DBI EXPECT_EQ(stats.readDBI.ones, 1); EXPECT_EQ(stats.readDBI.zeroes, 48-1); EXPECT_EQ(stats.readDBI.ones_to_zeroes, 1); EXPECT_EQ(stats.readDBI.zeroes_to_ones, 1); EXPECT_EQ(stats.writeDBI.ones, 4); EXPECT_EQ(stats.writeDBI.zeroes, 48-4); EXPECT_EQ(stats.writeDBI.ones_to_zeroes, 2); EXPECT_EQ(stats.writeDBI.zeroes_to_ones, 2); } TEST_F(LPDDR5_DBI_Tests, Pattern_1) { ddr->getExtensionManager().withExtension([](DRAMPower::extensions::DBI& dbi) { dbi.enable(0, true); }); runCommands(test_patterns[1]); SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Data bus EXPECT_EQ(stats.writeBus.ones, 14); EXPECT_EQ(stats.writeBus.zeroes, 370); // 2 (datarate) * 24 (time) * 8 (bus width) - 14 (ones) EXPECT_EQ(stats.writeBus.ones_to_zeroes, 14); EXPECT_EQ(stats.writeBus.zeroes_to_ones, 14); EXPECT_EQ(stats.readBus.ones, 2); EXPECT_EQ(stats.readBus.zeroes, 382); // 2 (datarate) * 24 (time) * 8 (bus width) - 2 (ones) EXPECT_EQ(stats.readBus.ones_to_zeroes, 2); EXPECT_EQ(stats.readBus.zeroes_to_ones, 2); // DBI EXPECT_EQ(stats.readDBI.ones, 2); EXPECT_EQ(stats.readDBI.zeroes, 48-2); EXPECT_EQ(stats.readDBI.ones_to_zeroes, 2); EXPECT_EQ(stats.readDBI.zeroes_to_ones, 2); EXPECT_EQ(stats.writeDBI.ones, 4); EXPECT_EQ(stats.writeDBI.zeroes, 48 - 4); EXPECT_EQ(stats.writeDBI.ones_to_zeroes, 2); EXPECT_EQ(stats.writeDBI.zeroes_to_ones, 2); } class LPDDR5_DBI_Energy_Tests : public ::testing::Test { public: LPDDR5_DBI_Energy_Tests() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); spec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); t_CK = spec->memTimingSpec.tCK; voltage = spec->vddq; // Change impedances to different values from each other spec->memImpedanceSpec.rdbi_R_eq = 1; spec->memImpedanceSpec.wdbi_R_eq = 2; spec->memImpedanceSpec.rdbi_dyn_E = 3; spec->memImpedanceSpec.wdbi_dyn_E = 4; io_calc = std::make_unique(*spec); } std::unique_ptr spec; double t_CK; double voltage; std::unique_ptr io_calc; }; TEST_F(LPDDR5_DBI_Energy_Tests, Energy) { SimulationStats stats; stats.readDBI.ones = 1; stats.readDBI.zeroes = 2; stats.readDBI.ones_to_zeroes = 3; stats.readDBI.zeroes_to_ones = 4; // Controller -> write power // Dram -> read power // data rate dbi is 2 -> t_per_bit = 0.5 * t_CK double expected_static_controller = stats.writeDBI.ones * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.wdbi_R_eq; double expected_static_dram = stats.readDBI.ones * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.rdbi_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = stats.writeDBI.zeroes_to_ones * spec->memImpedanceSpec.wdbi_dyn_E; double expected_dynamic_dram = stats.readDBI.zeroes_to_ones * spec->memImpedanceSpec.rdbi_dyn_E; // DBI interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); } ================================================ FILE: tests/tests_drampower/interface/test_interface_ddr4.cpp ================================================ #include #include #include #include #include #include #include #include "DRAMPower/data/energy.h" #include "DRAMPower/data/stats.h" #include "DRAMPower/memspec/MemSpecDDR4.h" #include "DRAMPower/standards/ddr4/DDR4.h" #include "DRAMPower/standards/ddr4/interface_calculation_DDR4.h" using DRAMPower::CmdType; using DRAMPower::Command; using DRAMPower::DDR4; using DRAMPower::interface_energy_info_t; using DRAMPower::InterfaceCalculation_DDR4; using DRAMPower::MemSpecDDR4; using DRAMPower::SimulationStats; #define SZ_BITS(x) sizeof(x)*8 // burst length = 8 for x8 devices static constexpr uint8_t wr_data[] = { 0, 0, 0, 0, 0, 0, 0, 255, }; // burst length = 8 for x8 devices static constexpr uint8_t rd_data[] = { 0, 0, 0, 0, 0, 0, 0, 1, }; class DDR4_WindowStats_Tests : public ::testing::Test { public: DDR4_WindowStats_Tests() { test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {4, CmdType::WR, {1, 0, 0, 0, 16}, wr_data, SZ_BITS(wr_data)}, {11, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}, {16, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {4, CmdType::WR, {1, 0, 0, 0, 16}, wr_data, SZ_BITS(wr_data)}, {8, CmdType::WR, {1, 0, 0, 0, 16}, wr_data, SZ_BITS(wr_data)}, {16, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {4, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}, {8, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}, {16, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {4, CmdType::WR, {1, 0, 0, 0, 16}, wr_data, SZ_BITS(wr_data)}, {10, CmdType::WR, {1, 0, 0, 0, 16}, wr_data, SZ_BITS(wr_data)}, {15, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {4, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}, {11, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}, {16, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); initSpec(); ddr = std::make_unique(*spec); } void initSpec() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); spec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); } void runCommands(const std::vector &commands) { for (const Command &command : commands) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); } } std::vector> test_patterns; std::unique_ptr spec; std::unique_ptr ddr; }; // Test patterns for stats (counter) TEST_F(DDR4_WindowStats_Tests, Pattern_0) { runCommands(test_patterns[0]); SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Clock EXPECT_EQ(stats.clockStats.ones, 48); EXPECT_EQ(stats.clockStats.zeroes, 48); EXPECT_EQ(stats.clockStats.ones_to_zeroes, 48); EXPECT_EQ(stats.clockStats.zeroes_to_ones, 48); // Data bus EXPECT_EQ(stats.writeBus.ones, 328); // 2 (datarate) * 24 (time) * 8 (bus width) - 56 (zeroes) EXPECT_EQ(stats.writeBus.zeroes, 56); // 7 (length) * 8 (bus width) EXPECT_EQ(stats.writeBus.ones_to_zeroes, 8); // 8 transitions 1 -> 0 EXPECT_EQ(stats.writeBus.zeroes_to_ones, 8); // back to 1 EXPECT_EQ(stats.readBus.ones, 321); // 2 (datarate) * 24 (time) * 8 (bus width) - 63 (zeroes) EXPECT_EQ(stats.readBus.zeroes, 63); // 7 (length zero bursts) * 8 (bus width) + 7 (zeroes in last burst) EXPECT_EQ(stats.readBus.ones_to_zeroes, 8); EXPECT_EQ(stats.readBus.zeroes_to_ones, 8); EXPECT_EQ(stats.commandBus.ones, 591); EXPECT_EQ(stats.commandBus.zeroes, 57); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 57); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 57); // DQs bus EXPECT_EQ(SZ_BITS(wr_data), SZ_BITS(rd_data)); uint_fast8_t NumDQsPairs = spec->bitWidth == 16 ? 2 : 1; uint64_t number_of_cycles = (SZ_BITS(wr_data) / spec->bitWidth); uint_fast8_t scale = NumDQsPairs * 2; // Differential_Pairs * 2(pairs of 2) // f(t) = t / 2; uint64_t DQS_ones = scale * (number_of_cycles / 2); // scale * (cycles / 2) uint64_t DQS_zeros = DQS_ones; uint64_t DQS_zeros_to_ones = DQS_ones; uint64_t DQS_ones_to_zeros = DQS_zeros; EXPECT_EQ(stats.writeDQSStats.ones, DQS_ones); EXPECT_EQ(stats.writeDQSStats.zeroes, DQS_zeros); EXPECT_EQ(stats.writeDQSStats.ones_to_zeroes, DQS_zeros_to_ones); EXPECT_EQ(stats.writeDQSStats.zeroes_to_ones, DQS_ones_to_zeros); // Read strobe should be the same (only because wr_data is same as rd_data in this test) EXPECT_EQ(stats.readDQSStats.ones, DQS_ones); EXPECT_EQ(stats.readDQSStats.zeroes, DQS_zeros); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, DQS_zeros_to_ones); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, DQS_ones_to_zeros); // PrePostamble // No seamless preambles or postambles auto prepos = stats.rank_total[0].prepos; EXPECT_EQ(prepos.readSeamless, 0); EXPECT_EQ(prepos.writeSeamless, 0); EXPECT_EQ(prepos.readMerged, 0); EXPECT_EQ(prepos.readMergedTime, 0); EXPECT_EQ(prepos.writeMerged, 0); EXPECT_EQ(prepos.writeMergedTime, 0); } TEST_F(DDR4_WindowStats_Tests, Pattern_1) { runCommands(test_patterns[1]); SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Clock EXPECT_EQ(stats.clockStats.ones, 48); EXPECT_EQ(stats.clockStats.zeroes, 48); EXPECT_EQ(stats.clockStats.ones_to_zeroes, 48); EXPECT_EQ(stats.clockStats.zeroes_to_ones, 48); // Data bus EXPECT_EQ(stats.writeBus.ones, 272); // 2 (datarate) * 24 (time) * 8 (bus width) - 112 (zeroes) EXPECT_EQ(stats.writeBus.zeroes, 112); // 7 (zeroe bursts) * 8 (width) * 2 (2 writes) EXPECT_EQ(stats.writeBus.ones_to_zeroes, 16); // 0 -> 255 * 2 = 16 transitions, EXPECT_EQ(stats.writeBus.zeroes_to_ones, 16); // back to 0 EXPECT_EQ(stats.readBus.ones, 384); // 2 (datarate) * 24 (time) * 8 (bus width) EXPECT_EQ(stats.readBus.zeroes, 0); EXPECT_EQ(stats.readBus.ones_to_zeroes, 0); EXPECT_EQ(stats.readBus.zeroes_to_ones, 0); EXPECT_EQ(stats.commandBus.ones, 593); EXPECT_EQ(stats.commandBus.zeroes, 55); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 55); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 55); // DQs bus EXPECT_EQ(SZ_BITS(wr_data), SZ_BITS(rd_data)); uint_fast8_t NumDQsPairs = spec->bitWidth == 16 ? 2 : 1; uint64_t number_of_cycles = (SZ_BITS(wr_data) / spec->bitWidth); uint_fast8_t scale = NumDQsPairs * 2; // Differential_Pairs * 2(pairs of 2) // f(t) = t / 2; uint64_t DQS_ones = scale * (number_of_cycles / 2); // scale * (cycles / 2) uint64_t DQS_zeros = DQS_ones; uint64_t DQS_zeros_to_ones = DQS_ones; uint64_t DQS_ones_to_zeros = DQS_zeros; EXPECT_EQ(stats.writeDQSStats.ones, DQS_ones * 2); // 2 writes EXPECT_EQ(stats.writeDQSStats.zeroes, DQS_zeros* 2); EXPECT_EQ(stats.writeDQSStats.ones_to_zeroes, DQS_zeros_to_ones * 2); EXPECT_EQ(stats.writeDQSStats.zeroes_to_ones, DQS_ones_to_zeros * 2); // Read strobe should be zero (no reads in this test) EXPECT_EQ(stats.readDQSStats.ones, 0); EXPECT_EQ(stats.readDQSStats.zeroes, 0); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, 0); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, 0); // PrePostamble auto prepos = stats.rank_total[0].prepos; EXPECT_EQ(prepos.readSeamless, 0); EXPECT_EQ(prepos.writeSeamless, 1); EXPECT_EQ(prepos.readMerged, 0); EXPECT_EQ(prepos.readMergedTime, 0); EXPECT_EQ(prepos.writeMerged, 0); EXPECT_EQ(prepos.writeMergedTime, 0); } TEST_F(DDR4_WindowStats_Tests, Pattern_2) { runCommands(test_patterns[2]); SimulationStats stats = ddr->getStats(); // Clock EXPECT_EQ(stats.clockStats.ones, 48); EXPECT_EQ(stats.clockStats.zeroes, 48); EXPECT_EQ(stats.clockStats.ones_to_zeroes, 48); EXPECT_EQ(stats.clockStats.zeroes_to_ones, 48); // Data bus EXPECT_EQ(stats.writeBus.ones, 384); // 2 (datarate) * 24 (time) * 8 (bus width) EXPECT_EQ(stats.writeBus.zeroes, 0); EXPECT_EQ(stats.writeBus.ones_to_zeroes, 0); EXPECT_EQ(stats.writeBus.zeroes_to_ones, 0); EXPECT_EQ(stats.readBus.ones, 258); // 2 (datarate) * 24 (time) * 8 (bus width) - 126 (zeroes) EXPECT_EQ(stats.readBus.zeroes, 126); // 2 (reads) * [ 7 (length zero bursts) * 8 (bus width) + 7 (zeroes in last burst)] EXPECT_EQ(stats.readBus.ones_to_zeroes, 9); // 8 (1->0 begin burst) + 1 (1->0 burst to burst data) EXPECT_EQ(stats.readBus.zeroes_to_ones, 9); // 8 (0->1 end burst (last burst data included)) + 1 (0->1 burst to burst data) EXPECT_EQ(stats.commandBus.ones, 589); EXPECT_EQ(stats.commandBus.zeroes, 59); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 59); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 59); // DQs bus EXPECT_EQ(SZ_BITS(wr_data), SZ_BITS(rd_data)); uint_fast8_t NumDQsPairs = spec->bitWidth == 16 ? 2 : 1; uint64_t number_of_cycles = (SZ_BITS(wr_data) / spec->bitWidth); uint_fast8_t scale = NumDQsPairs * 2; // Differential_Pairs * 2(pairs of 2) // f(t) = t / 2; uint64_t DQS_ones = scale * (number_of_cycles / 2); // scale * (cycles / 2) uint64_t DQS_zeros = DQS_ones; uint64_t DQS_zeros_to_ones = DQS_ones; uint64_t DQS_ones_to_zeros = DQS_zeros; EXPECT_EQ(stats.readDQSStats.ones, DQS_ones * 2); // 2 reads EXPECT_EQ(stats.readDQSStats.zeroes, DQS_zeros * 2); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, DQS_zeros_to_ones * 2); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, DQS_ones_to_zeros * 2); // Write strobe should be zero (no writes in this test) EXPECT_EQ(stats.writeDQSStats.ones, 0); EXPECT_EQ(stats.writeDQSStats.zeroes, 0); EXPECT_EQ(stats.writeDQSStats.ones_to_zeroes, 0); EXPECT_EQ(stats.writeDQSStats.zeroes_to_ones, 0); // PrePostamble auto prepos = stats.rank_total[0].prepos; EXPECT_EQ(prepos.readSeamless, 1); EXPECT_EQ(prepos.writeSeamless, 0); EXPECT_EQ(prepos.readMerged, 0); EXPECT_EQ(prepos.readMergedTime, 0); EXPECT_EQ(prepos.writeMerged, 0); EXPECT_EQ(prepos.writeMergedTime, 0); } TEST_F(DDR4_WindowStats_Tests, Pattern_3) { runCommands(test_patterns[3]); SimulationStats stats = ddr->getStats(); // Clock EXPECT_EQ(stats.clockStats.ones, 48); EXPECT_EQ(stats.clockStats.zeroes, 48); EXPECT_EQ(stats.clockStats.ones_to_zeroes, 48); EXPECT_EQ(stats.clockStats.zeroes_to_ones, 48); // Data bus EXPECT_EQ(stats.writeBus.ones, 272); // 2 (datarate) * 24 (time) * 8 (bus width) - 112 (zeroes) EXPECT_EQ(stats.writeBus.zeroes, 112); // 7 (length) * 8 (bus width) EXPECT_EQ(stats.writeBus.ones_to_zeroes, 16); // 8 (begin first burst) + 8 (end first burst, begin second burst) EXPECT_EQ(stats.writeBus.zeroes_to_ones, 16); // 8 (end first burst) + 8 (end second burst) EXPECT_EQ(stats.readBus.ones, 384); // 2 (datarate) * 24 (time) * 8 (bus width) EXPECT_EQ(stats.readBus.zeroes, 0); EXPECT_EQ(stats.readBus.ones_to_zeroes, 0); EXPECT_EQ(stats.readBus.zeroes_to_ones, 0); EXPECT_EQ(stats.commandBus.ones, 593); EXPECT_EQ(stats.commandBus.zeroes, 55); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 55); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 55); // DQs bus EXPECT_EQ(SZ_BITS(wr_data), SZ_BITS(rd_data)); uint_fast8_t NumDQsPairs = spec->bitWidth == 16 ? 2 : 1; uint64_t number_of_cycles = (SZ_BITS(wr_data) / spec->bitWidth); uint_fast8_t scale = NumDQsPairs * 2; // Differential_Pairs * 2(pairs of 2) // f(t) = t / 2; uint64_t DQS_ones = scale * (number_of_cycles / 2); // scale * (cycles / 2) uint64_t DQS_zeros = DQS_ones; uint64_t DQS_zeros_to_ones = DQS_ones; uint64_t DQS_ones_to_zeros = DQS_zeros; EXPECT_EQ(stats.writeDQSStats.ones, DQS_ones * 2); // 2 writes EXPECT_EQ(stats.writeDQSStats.zeroes, DQS_zeros * 2); EXPECT_EQ(stats.writeDQSStats.ones_to_zeroes, DQS_zeros_to_ones * 2); EXPECT_EQ(stats.writeDQSStats.zeroes_to_ones, DQS_ones_to_zeros * 2); // Read strobe should be zero (no reads in this test) EXPECT_EQ(stats.readDQSStats.ones, 0); EXPECT_EQ(stats.readDQSStats.zeroes, 0); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, 0); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, 0); // PrePostamble auto prepos = stats.rank_total[0].prepos; EXPECT_EQ(prepos.readSeamless, 0); EXPECT_EQ(prepos.writeSeamless, 0); EXPECT_EQ(prepos.readMerged, 0); EXPECT_EQ(prepos.readMergedTime, 0); EXPECT_EQ(prepos.writeMerged, 0); EXPECT_EQ(prepos.writeMergedTime, 0); } TEST_F(DDR4_WindowStats_Tests, Pattern_4) { runCommands(test_patterns[4]); SimulationStats stats = ddr->getStats(); // Clock EXPECT_EQ(stats.clockStats.ones, 48); EXPECT_EQ(stats.clockStats.zeroes, 48); EXPECT_EQ(stats.clockStats.ones_to_zeroes, 48); EXPECT_EQ(stats.clockStats.zeroes_to_ones, 48); // Data bus EXPECT_EQ(stats.writeBus.ones, 384); // 2 (datarate) * 24 (time) * 8 (bus width) EXPECT_EQ(stats.writeBus.zeroes, 0); EXPECT_EQ(stats.writeBus.ones_to_zeroes, 0); EXPECT_EQ(stats.writeBus.zeroes_to_ones, 0); EXPECT_EQ(stats.readBus.ones, 258); // 2 (datarate) * 24 (time) * 8 (bus width) - 126 (zeroes) EXPECT_EQ(stats.readBus.zeroes, 126); // 2 (reads) * [ 7 (length zero bursts) * 8 (bus width) + 7 (zeroes in last burst)] EXPECT_EQ(stats.readBus.ones_to_zeroes, 16); // 8 (first burst begin) + 8 (second burst begin) EXPECT_EQ(stats.readBus.zeroes_to_ones, 16); // 8 (end first burst) + 8 (end second burst) EXPECT_EQ(stats.commandBus.ones, 589); EXPECT_EQ(stats.commandBus.zeroes, 59); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 59); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 59); // DQs bus EXPECT_EQ(SZ_BITS(wr_data), SZ_BITS(rd_data)); uint_fast8_t NumDQsPairs = spec->bitWidth == 16 ? 2 : 1; uint64_t number_of_cycles = (SZ_BITS(wr_data) / spec->bitWidth); uint_fast8_t scale = NumDQsPairs * 2; // Differential_Pairs * 2(pairs of 2) // f(t) = t / 2; uint64_t DQS_ones = scale * (number_of_cycles / 2); // scale * (cycles / 2) uint64_t DQS_zeros = DQS_ones; uint64_t DQS_zeros_to_ones = DQS_ones; uint64_t DQS_ones_to_zeros = DQS_zeros; EXPECT_EQ(stats.readDQSStats.ones, DQS_ones * 2); // 2 reads EXPECT_EQ(stats.readDQSStats.zeroes, DQS_zeros * 2); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, DQS_zeros_to_ones * 2); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, DQS_ones_to_zeros * 2); // Write strobe should be zero (no writes in this test) EXPECT_EQ(stats.writeDQSStats.ones, 0); EXPECT_EQ(stats.writeDQSStats.zeroes, 0); EXPECT_EQ(stats.writeDQSStats.ones_to_zeroes, 0); EXPECT_EQ(stats.writeDQSStats.zeroes_to_ones, 0); // PrePostamble auto prepos = stats.rank_total[0].prepos; EXPECT_EQ(prepos.readSeamless, 0); EXPECT_EQ(prepos.writeSeamless, 0); EXPECT_EQ(prepos.readMerged, 0); EXPECT_EQ(prepos.readMergedTime, 0); EXPECT_EQ(prepos.writeMerged, 0); EXPECT_EQ(prepos.writeMergedTime, 0); } // Tests for power consumption (given a known SimulationStats) class DDR4_Energy_Tests : public ::testing::Test { public: DDR4_Energy_Tests() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); spec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); t_CK = spec->memTimingSpec.tCK; voltage = spec->vddq; // Change impedances to different values from each other spec->memImpedanceSpec.ca_R_eq = 2; spec->memImpedanceSpec.ck_R_eq = 3; spec->memImpedanceSpec.rdqs_R_eq = 4; spec->memImpedanceSpec.wdqs_R_eq = 5; spec->memImpedanceSpec.rdq_R_eq = 6; spec->memImpedanceSpec.wdq_R_eq = 7; spec->memImpedanceSpec.ca_dyn_E = 8; spec->memImpedanceSpec.ck_dyn_E = 9; spec->memImpedanceSpec.rdqs_dyn_E = 10; spec->memImpedanceSpec.wdqs_dyn_E = 11; spec->memImpedanceSpec.rdq_dyn_E = 12; spec->memImpedanceSpec.wdq_dyn_E = 13; // PrePostamble is a possible DDR4 pattern // Preamble 2tCK, Postamble 0.5tCK spec->prePostamble.read_ones = 2.5; spec->prePostamble.read_zeroes = 2.5; spec->prePostamble.read_zeroes_to_ones = 2; spec->prePostamble.read_ones_to_zeroes = 2; // Preamble 1tCK, Postamble 0.5tCK spec->prePostamble.write_ones = 1.5; spec->prePostamble.write_zeroes = 1.5; spec->prePostamble.write_zeroes_to_ones = 2; spec->prePostamble.write_ones_to_zeroes = 2; spec->prePostamble.readMinTccd = 3; spec->prePostamble.writeMinTccd = 2; io_calc = std::make_unique(*spec); } std::unique_ptr spec; double t_CK; double voltage; std::unique_ptr io_calc; }; TEST_F(DDR4_Energy_Tests, Parameters) { ASSERT_TRUE(t_CK > 0.0); ASSERT_TRUE(voltage > 0.0); } // Test pattern for energy consumption TEST_F(DDR4_Energy_Tests, Clock_Energy) { SimulationStats stats; stats.clockStats.ones = 200; stats.clockStats.zeroes_to_ones = 200; stats.clockStats.zeroes = 400; // different number to validate termination stats.clockStats.ones_to_zeroes = 400; interface_energy_info_t result = io_calc->calculateEnergy(stats); // Clock is provided by the controller not the device EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, 0.0); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, 0.0); // Note // The clock stats include both lines of the differential pair // DDR4 clock power consumed on 0's double expected_static = stats.clockStats.zeroes * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.ck_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic = stats.clockStats.zeroes_to_ones * spec->memImpedanceSpec.ck_dyn_E; EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static); // value itself doesn't matter, only that it matches the formula EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic); } TEST_F(DDR4_Energy_Tests, DQS_Energy) { SimulationStats stats; stats.readDQSStats.ones = 200; stats.readDQSStats.zeroes = 400; stats.readDQSStats.zeroes_to_ones = 700; stats.readDQSStats.ones_to_zeroes = 1000; stats.writeDQSStats.ones = 300; stats.writeDQSStats.zeroes = 100; stats.writeDQSStats.ones_to_zeroes = 2000; stats.writeDQSStats.zeroes_to_ones = 999; // Note // The DQS stats include both lines of the differential pair // Controller -> write power // Dram -> read power // Note dqs is modeled as clock. The clock class incorporates the data rate double expected_static_controller = stats.writeDQSStats.zeroes * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.wdqs_R_eq; double expected_static_dram = stats.readDQSStats.zeroes * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.rdqs_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = stats.writeDQSStats.zeroes_to_ones * spec->memImpedanceSpec.wdqs_dyn_E; double expected_dynamic_dram = stats.readDQSStats.zeroes_to_ones * spec->memImpedanceSpec.rdqs_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); } TEST_F(DDR4_Energy_Tests, DQ_Energy) { SimulationStats stats; stats.readBus.ones = 7; stats.readBus.zeroes = 11; stats.readBus.zeroes_to_ones = 19; stats.readBus.ones_to_zeroes = 39; stats.writeBus.ones = 43; stats.writeBus.zeroes = 59; stats.writeBus.zeroes_to_ones = 13; stats.writeBus.ones_to_zeroes = 17; // Controller -> write power // Dram -> read power // zeroes and ones of the data bus are the zeroes and ones per pattern (data rate is not modeled in the bus) // data rate data bus is 2 -> t_per_bit = 0.5 * t_CK double expected_static_controller = stats.writeBus.zeroes * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.wdq_R_eq; double expected_static_dram = stats.readBus.zeroes * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.rdq_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = stats.writeBus.zeroes_to_ones * spec->memImpedanceSpec.wdq_dyn_E; double expected_dynamic_dram = stats.readBus.zeroes_to_ones * spec->memImpedanceSpec.rdq_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); } TEST_F(DDR4_Energy_Tests, CA_Energy) { SimulationStats stats; stats.commandBus.ones = 11; stats.commandBus.zeroes = 29; stats.commandBus.zeroes_to_ones = 39; stats.commandBus.ones_to_zeroes = 49; double expected_static_controller = stats.commandBus.zeroes * voltage * voltage * t_CK / spec->memImpedanceSpec.ca_R_eq; double expected_dynamic_controller = stats.commandBus.zeroes_to_ones * spec->memImpedanceSpec.ca_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); // CA bus power is provided by the controller EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, 0.0); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, 0.0); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); } TEST_F(DDR4_Energy_Tests, PrePostamble_Energy) { SimulationStats stats; stats.readDQSStats.ones = 200; stats.readDQSStats.zeroes = 400; stats.readDQSStats.zeroes_to_ones = 700; stats.readDQSStats.ones_to_zeroes = 1000; stats.writeDQSStats.ones = 300; stats.writeDQSStats.zeroes = 100; stats.writeDQSStats.ones_to_zeroes = 2000; stats.writeDQSStats.zeroes_to_ones = 999; // DDR4 doesn't support merged preambles or postambles stats.rank_total.resize(1); stats.rank_total[0].prepos.readMerged = 0; stats.rank_total[0].prepos.readMergedTime = 0; stats.rank_total[0].prepos.writeMerged = 0; stats.rank_total[0].prepos.writeMergedTime = 0; // Required reads + readAuto > readSeamless // Required writes + writeAuto > writeSeamless stats.rank_total[0].prepos.readSeamless = 4; stats.rank_total[0].counter.reads = 4; stats.rank_total[0].counter.readAuto = 10; stats.rank_total[0].prepos.writeSeamless = 5; stats.rank_total[0].counter.writes = 6; stats.rank_total[0].counter.writeAuto = 11; uint64_t writecount = stats.rank_total[0].counter.writes + stats.rank_total[0].counter.writeAuto; uint64_t readcount = stats.rank_total[0].counter.reads + stats.rank_total[0].counter.readAuto; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = stats.writeDQSStats.zeroes_to_ones * spec->memImpedanceSpec.wdqs_dyn_E; double expected_dynamic_dram = stats.readDQSStats.zeroes_to_ones * spec->memImpedanceSpec.rdqs_dyn_E; // Controller -> write power // Dram -> read power // Note dqs is modeled as clock. The clock class incorporates the data rate double expected_static_controller = stats.writeDQSStats.zeroes * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.wdqs_R_eq; double expected_static_dram = stats.readDQSStats.zeroes * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.rdqs_R_eq; // Note DQS already tested in DDR4_Energy_Tests.DQS_Energy // Add seamless preambles and postambles power // Note read_zeroes incorporates the data rate // Note write_zeroes incorporates the data rate expected_static_controller += spec->prePostamble.write_zeroes * (writecount - stats.rank_total[0].prepos.writeSeamless) * voltage * voltage * t_CK / spec->memImpedanceSpec.wdqs_R_eq; expected_static_dram += spec->prePostamble.read_zeroes * (readcount - stats.rank_total[0].prepos.readSeamless) * voltage * voltage * t_CK / spec->memImpedanceSpec.rdqs_R_eq; expected_dynamic_controller += spec->prePostamble.write_zeroes_to_ones * (writecount - stats.rank_total[0].prepos.writeSeamless) * spec->memImpedanceSpec.wdqs_dyn_E; expected_dynamic_dram += spec->prePostamble.read_zeroes_to_ones * (readcount - stats.rank_total[0].prepos.readSeamless) * spec->memImpedanceSpec.rdqs_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); } ================================================ FILE: tests/tests_drampower/interface/test_interface_ddr5.cpp ================================================ #include #include #include #include #include #include #include #include "DRAMPower/data/energy.h" #include "DRAMPower/data/stats.h" #include "DRAMPower/memspec/MemSpecDDR5.h" #include "DRAMPower/standards/ddr5/DDR5.h" #include "DRAMPower/standards/ddr5/interface_calculation_DDR5.h" using DRAMPower::CmdType; using DRAMPower::Command; using DRAMPower::DDR5; using DRAMPower::interface_energy_info_t; using DRAMPower::InterfaceCalculation_DDR5; using DRAMPower::MemSpecDDR5; using DRAMPower::SimulationStats; #define SZ_BITS(x) sizeof(x)*8 static constexpr uint8_t wr_data[] = { 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, }; static constexpr uint8_t rd_data[] = { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; class DDR5_WindowStats_Tests : public ::testing::Test { public: DDR5_WindowStats_Tests() { test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {3, CmdType::WR, {1, 0, 0, 0, 4}, wr_data, SZ_BITS(wr_data)}, {12, CmdType::RD, {1, 0, 0, 0, 4}, rd_data, SZ_BITS(rd_data)}, {21, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); test_patterns.push_back({ {0, CmdType::ACT, {2, 0, 0, 372}}, {3, CmdType::WR, {2, 0, 0, 372, 27}, wr_data, SZ_BITS(wr_data)}, {12, CmdType::PRE, {2, 0, 0, 372}}, {15, CmdType::SREFEN}, {42, CmdType::END_OF_SIMULATION} }); test_patterns.push_back({ {0, CmdType::ACT, {2, 0, 0, 372}}, {3, CmdType::RD, {2, 0, 0, 372, 27}, rd_data, SZ_BITS(rd_data)}, {12, CmdType::WRA, {2, 0, 0, 372, 27}, wr_data, SZ_BITS(wr_data)}, {28, CmdType::SREFEN}, {55, CmdType::END_OF_SIMULATION} }); initSpec(); ddr = std::make_unique(*spec); } void initSpec() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); spec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); spec->bitWidth = 16; } void runCommands(const std::vector &commands) { for (const Command &command : commands) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); } } std::vector> test_patterns; std::unique_ptr spec; std::unique_ptr ddr; }; // Tests for the window stats (ones to zeros, ones etc) // Write and Read bus are trivial // Command bus needs to be calculated from command patterns TEST_F(DDR5_WindowStats_Tests, Pattern_0) { runCommands(test_patterns[0]); SimulationStats stats = ddr->getStats(); EXPECT_EQ(stats.writeBus.ones, 528); // 2 (datarate) * 16 (bus width) * 24 (time) - 240 (zeroes) EXPECT_EQ(stats.writeBus.zeroes, 240); // 14 (bursts) * 16 (bus width) + 2 (bursts) * 8 (zeroes in data burst) EXPECT_EQ(stats.writeBus.ones_to_zeroes, 24); // 16 (first burst) + 8 (data ones to zeroes in bursts) EXPECT_EQ(stats.writeBus.zeroes_to_ones, 24); // 8 (data ones to zeroes in bursts) + 8 (last burst data) + 8 (end last burst) EXPECT_EQ(stats.readBus.ones, 513); // 2 (datarate) * 16 (bus width) * 24 (time) - 255 (zeroes) EXPECT_EQ(stats.readBus.zeroes, 255); // 15 (bursts) * 16 (bus width) + 15 (zeroes in data burst) EXPECT_EQ(stats.readBus.ones_to_zeroes, 17); // 16 (first burst) + 1 (data ones to zeroes in bursts) EXPECT_EQ(stats.readBus.zeroes_to_ones, 17); // 1 (data ones to zeroes in bursts) + 16 (end last burst) // Clock EXPECT_EQ(stats.clockStats.ones, 48); EXPECT_EQ(stats.clockStats.zeroes, 48); EXPECT_EQ(stats.clockStats.ones_to_zeroes, 48); EXPECT_EQ(stats.clockStats.zeroes_to_ones, 48); // Notes // Pattern.h: first 4 bits of column (C0-C3) are set to 0 (for reads and writes) // TODO correct??? EXPECT_EQ(stats.commandBus.ones, 282); EXPECT_EQ(stats.commandBus.zeroes, 54); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 39); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 39); // In this example read data and write data are the same size, so stats should be the same EXPECT_EQ(SZ_BITS(wr_data), SZ_BITS(rd_data)); uint_fast8_t NumDQsPairs = spec->bitWidth == 16 ? 2 : 1; uint64_t number_of_cycles = (SZ_BITS(wr_data) / spec->bitWidth); uint_fast8_t scale = NumDQsPairs * 2; // Differential_Pairs * 2(pairs of 2) // f(t) = t / 2; uint64_t DQS_ones = scale * (number_of_cycles / 2); // scale * (cycles / 2) uint64_t DQS_zeros = DQS_ones; uint64_t DQS_zeros_to_ones = DQS_ones; uint64_t DQS_ones_to_zeros = DQS_zeros; EXPECT_EQ(stats.writeDQSStats.ones, DQS_ones); EXPECT_EQ(stats.writeDQSStats.zeroes, DQS_zeros); EXPECT_EQ(stats.writeDQSStats.ones_to_zeroes, DQS_zeros_to_ones); EXPECT_EQ(stats.writeDQSStats.zeroes_to_ones, DQS_ones_to_zeros); // Read strobe should be the same (only because wr_data is same as rd_data in this test) EXPECT_EQ(stats.readDQSStats.ones, DQS_ones); EXPECT_EQ(stats.readDQSStats.zeroes, DQS_zeros); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, DQS_zeros_to_ones); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, DQS_ones_to_zeros); } TEST_F(DDR5_WindowStats_Tests, Pattern_1) { runCommands(test_patterns[1]); SimulationStats stats = ddr->getStats(); EXPECT_EQ(stats.writeBus.ones, 1104); // 2 (datarate) * 16 (bus width) * 42 (time) - 240 (zeroes) EXPECT_EQ(stats.writeBus.zeroes, 240); // 14 (bursts) * 16 (bus width) + 2 (bursts) * 8 (zeroes in data burst) EXPECT_EQ(stats.writeBus.ones_to_zeroes, 24); // 16 (first burst) + 8 (data ones to zeroes in bursts) EXPECT_EQ(stats.writeBus.zeroes_to_ones, 24); // 8 (data ones to zeroes in bursts) + 8 (last burst data) + 8 (end last burst) EXPECT_EQ(stats.readBus.ones, 1344); // 2 (datarate) * 16 (bus width) * 42 (time) EXPECT_EQ(stats.readBus.zeroes, 0); EXPECT_EQ(stats.readBus.ones_to_zeroes, 0); EXPECT_EQ(stats.readBus.zeroes_to_ones, 0); EXPECT_EQ(stats.commandBus.ones, 551); EXPECT_EQ(stats.commandBus.zeroes, 37); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 28); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 28); } TEST_F(DDR5_WindowStats_Tests, Pattern_2) { runCommands(test_patterns[2]); SimulationStats stats = ddr->getStats(); EXPECT_EQ(stats.writeBus.ones, 1520); // 2 (datarate) * 16 (bus width) * 55 (time) - 240 (zeroes) EXPECT_EQ(stats.writeBus.zeroes, 240); // 14 (bursts) * 16 (bus width) + 2 (bursts) * 8 (zeroes in data burst) EXPECT_EQ(stats.writeBus.ones_to_zeroes, 24); // 16 (first burst) + 8 (data ones to zeroes in bursts) EXPECT_EQ(stats.writeBus.zeroes_to_ones, 24); // 8 (data ones to zeroes in bursts) + 8 (last burst data) + 8 (end last burst) EXPECT_EQ(stats.readBus.ones, 1505); // 2 (datarate) * 16 (bus width) * 55 (time) - 255 (zeroes) EXPECT_EQ(stats.readBus.zeroes, 255); // 15 (bursts) * 16 (bus width) + 15 (zeroes in data burst) EXPECT_EQ(stats.readBus.ones_to_zeroes, 17); // 16 (first burst) + 1 (data ones to zeroes in bursts) EXPECT_EQ(stats.readBus.zeroes_to_ones, 17); // 1 (data ones to zeroes in bursts) + 16 (end last burst) EXPECT_EQ(stats.commandBus.ones, 724); EXPECT_EQ(stats.commandBus.zeroes, 46); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 33); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 33); } // Tests for power consumption (given a known SimulationStats) class DDR5_Energy_Tests : public ::testing::Test { public: DDR5_Energy_Tests() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); spec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); t_CK = spec->memTimingSpec.tCK; voltage = spec->vddq; // Change impedances to different values from each other spec->memImpedanceSpec.ca_R_eq = 2; spec->memImpedanceSpec.ck_R_eq = 3; spec->memImpedanceSpec.rdqs_R_eq = 4; spec->memImpedanceSpec.wdqs_R_eq = 5; spec->memImpedanceSpec.rdq_R_eq = 6; spec->memImpedanceSpec.wdq_R_eq = 7; spec->memImpedanceSpec.ca_dyn_E = 8; spec->memImpedanceSpec.ck_dyn_E = 9; spec->memImpedanceSpec.rdqs_dyn_E = 10; spec->memImpedanceSpec.wdqs_dyn_E = 11; spec->memImpedanceSpec.rdq_dyn_E = 12; spec->memImpedanceSpec.wdq_dyn_E = 13; io_calc = std::make_unique(*spec); } std::unique_ptr spec; double t_CK; double voltage; std::unique_ptr io_calc; }; TEST_F(DDR5_Energy_Tests, Parameters) { ASSERT_TRUE(t_CK > 0.0); ASSERT_TRUE(voltage > 0.0); } TEST_F(DDR5_Energy_Tests, Clock_Energy) { // Note: stats of both clock lines SimulationStats stats; stats.clockStats.ones = 200; stats.clockStats.zeroes_to_ones = 200; stats.clockStats.zeroes = 400; // different number to validate termination stats.clockStats.ones_to_zeroes = 400; interface_energy_info_t result = io_calc->calculateEnergy(stats); // Clock is provided by the controller not the device EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, 0.0); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, 0.0); // DDR5 clock power consumed on 1's double expected_static = stats.clockStats.zeroes * voltage * voltage * 0.5 * t_CK / spec->memImpedanceSpec.ck_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic = stats.clockStats.zeroes_to_ones * spec->memImpedanceSpec.ck_dyn_E; EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static); // value itself doesn't matter, only that it matches the formula EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic); } TEST_F(DDR5_Energy_Tests, DQS_Energy) { SimulationStats stats; stats.readDQSStats.ones = 200; stats.readDQSStats.zeroes = 400; stats.readDQSStats.zeroes_to_ones = 700; stats.readDQSStats.ones_to_zeroes = 1000; stats.writeDQSStats.ones = 300; stats.writeDQSStats.zeroes = 100; stats.writeDQSStats.ones_to_zeroes = 2000; stats.writeDQSStats.zeroes_to_ones = 999; // Term power if consumed by 1's // Controller -> write power // Dram -> read power double expected_static_controller = 0.5 * stats.writeDQSStats.zeroes * voltage * voltage * t_CK / spec->memImpedanceSpec.wdqs_R_eq; double expected_static_dram = 0.5 * stats.readDQSStats.zeroes * voltage * voltage * t_CK / spec->memImpedanceSpec.rdqs_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = stats.writeDQSStats.zeroes_to_ones * spec->memImpedanceSpec.wdqs_dyn_E; double expected_dynamic_dram = stats.readDQSStats.zeroes_to_ones * spec->memImpedanceSpec.rdqs_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); } TEST_F(DDR5_Energy_Tests, DQ_Energy) { SimulationStats stats; stats.readBus.ones = 7; stats.readBus.zeroes = 11; stats.readBus.zeroes_to_ones = 19; stats.readBus.ones_to_zeroes = 39; stats.writeBus.ones = 43; stats.writeBus.zeroes = 59; stats.writeBus.zeroes_to_ones = 13; stats.writeBus.ones_to_zeroes = 17; // Term power if consumed by 0's on DDR 5 (pullup terminated) // Controller -> write power // Dram -> read power double expected_static_controller = stats.writeBus.zeroes * voltage * voltage * 0.5 * t_CK / spec->memImpedanceSpec.wdq_R_eq; double expected_static_dram = stats.readBus.zeroes * voltage * voltage * 0.5 * t_CK / spec->memImpedanceSpec.rdq_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = stats.writeBus.zeroes_to_ones * spec->memImpedanceSpec.wdq_dyn_E; double expected_dynamic_dram = stats.readBus.zeroes_to_ones * spec->memImpedanceSpec.rdq_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); } TEST_F(DDR5_Energy_Tests, CA_Energy) { SimulationStats stats; stats.commandBus.ones = 11; stats.commandBus.zeroes = 29; stats.commandBus.zeroes_to_ones = 39; stats.commandBus.ones_to_zeroes = 49; double expected_static_controller = stats.commandBus.zeroes * voltage * voltage * t_CK / spec->memImpedanceSpec.ca_R_eq; double expected_dynamic_controller = stats.commandBus.zeroes_to_ones * spec->memImpedanceSpec.ca_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); // CA bus power is provided by the controller EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, 0.0); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, 0.0); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); } ================================================ FILE: tests/tests_drampower/interface/test_interface_lpddr4.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include using namespace DRAMPower; #define SZ_BITS(x) sizeof(x)*8 class DramPowerTest_Interface_LPDDR4 : public ::testing::Test { protected: static constexpr uint8_t wr_data[] = { 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, }; // Cycles: 16 // Ones: 16 // Zeroes: 240 // 0 -> 1: 16 // 1 -> 0: 16 // Toggles: 32 static constexpr uint8_t rd_data[] = { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; // Cycles: 16 // Ones: 1 // Zeroes: 255 // 0 -> 1: 1 // 1 -> 0: 1 // Toggles: 2 // Test pattern #1 std::vector testPattern = { // Timestamp, Cmd, { Bank, BG, Rank, Row, Co-lumn} { 0, CmdType::ACT, { 1, 0, 0, 2 } }, { 5, CmdType::WR, { 1, 0, 0, 0, 16 }, wr_data, SZ_BITS(wr_data) }, { 14, CmdType::RD, { 1, 0, 0, 0, 16 }, rd_data, SZ_BITS(rd_data) }, { 23, CmdType::PRE, { 1, 0, 0, 2 } }, { 26, CmdType::END_OF_SIMULATION }, }; // Test pattern #2 std::vector testPattern_2 = { // Timestamp, Cmd, { Bank, BG, Rank, Row, Co-lumn} { 0, CmdType::ACT, { 1, 0, 0, 2 } }, { 5, CmdType::WR, { 1, 0, 0, 0, 16 }, wr_data, SZ_BITS(wr_data) }, { 14, CmdType::RD, { 1, 0, 0, 0, 16 }, rd_data, SZ_BITS(rd_data) }, { 23, CmdType::WR, { 1, 0, 0, 0, 16 }, wr_data, SZ_BITS(wr_data) }, { 31, CmdType::WR, { 1, 0, 0, 0, 16 }, wr_data, SZ_BITS(wr_data) }, { 40, CmdType::RD, { 1, 0, 0, 0, 16 }, rd_data, SZ_BITS(rd_data) }, { 49, CmdType::WR, { 1, 0, 0, 0, 16 }, wr_data, SZ_BITS(wr_data) }, { 57, CmdType::RD, { 1, 0, 0, 0, 16 }, rd_data, SZ_BITS(rd_data) }, { 70, CmdType::END_OF_SIMULATION }, }; // Test variables std::unique_ptr ddr; std::unique_ptr spec; virtual void SetUp() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); auto memSpec = MemSpecLPDDR4::from_memspec(*data); memSpec.numberOfRanks = 1; memSpec.numberOfBanks = 2; memSpec.bitWidth = 16; memSpec.burstLength = 16; memSpec.dataRate = 2; memSpec.numberOfDevices = 1; // mem timings memSpec.memTimingSpec.tRAS = 10; memSpec.memTimingSpec.tRTP = 10; memSpec.memTimingSpec.tRCD = 20; memSpec.memTimingSpec.tRFC = 10; memSpec.memTimingSpec.tRFCPB = 25; memSpec.memTimingSpec.tWR = 20; memSpec.memTimingSpec.tRP = 20; memSpec.memTimingSpec.tWL = 0; memSpec.memTimingSpec.tCK = 1; memSpec.memTimingSpec.tREFI = 1; // Voltage domains memSpec.memPowerSpec[0].vDDX = 1; memSpec.memPowerSpec[0].iDD0X = 64; memSpec.memPowerSpec[0].iDD2NX = 8; memSpec.memPowerSpec[0].iDD2PX = 6; memSpec.memPowerSpec[0].iDD3NX = 32; memSpec.memPowerSpec[0].iDD3PX = 20; memSpec.memPowerSpec[0].iDD4RX = 72; memSpec.memPowerSpec[0].iDD4WX = 72; memSpec.memPowerSpec[1].vDDX = 1; memSpec.memPowerSpec[1].iDD0X = 64; memSpec.memPowerSpec[1].iDD2NX = 8; memSpec.memPowerSpec[1].iDD2PX = 6; memSpec.memPowerSpec[1].iDD3NX = 32; memSpec.memPowerSpec[1].iDD3PX = 20; memSpec.memPowerSpec[1].iDD4RX = 72; memSpec.memPowerSpec[1].iDD4WX = 72; memSpec.bwParams.bwPowerFactRho = 0.333333333; spec = std::make_unique(memSpec); ddr = std::make_unique(memSpec); } virtual void TearDown() { } }; TEST_F(DramPowerTest_Interface_LPDDR4, TestStats) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); }; auto stats = ddr->getStats(); ASSERT_EQ(stats.commandBus.ones, 15); ASSERT_EQ(stats.commandBus.zeroes, 141); ASSERT_EQ(stats.commandBus.ones_to_zeroes, 14); ASSERT_EQ(stats.commandBus.zeroes_to_ones, 14); // Verify read bus stats ASSERT_EQ(stats.readBus.ones, 1); // 1 ASSERT_EQ(stats.readBus.zeroes, 831); // 2 (datarate) * 26 (time) * 16 (bus width) - 1 (ones) ASSERT_EQ(stats.readBus.ones_to_zeroes, 1); ASSERT_EQ(stats.readBus.zeroes_to_ones, 1); ASSERT_EQ(stats.readBus.bit_changes, 2); // Verify write bus stats ASSERT_EQ(stats.writeBus.ones, 16); // 2 (bursts) * 8 (ones per burst) ASSERT_EQ(stats.writeBus.zeroes, 816); // 2 (datarate) * 26 (time) * 16 (bus width) - 16 (ones) ASSERT_EQ(stats.writeBus.ones_to_zeroes, 16); ASSERT_EQ(stats.writeBus.zeroes_to_ones, 16); ASSERT_EQ(stats.writeBus.bit_changes, 32); } TEST_F(DramPowerTest_Interface_LPDDR4, TestPower) { for (const auto& command : testPattern) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); }; auto stats = ddr->getStats(); // TODO add tests InterfaceCalculation_LPDDR4 interface_calc(*spec); // auto interface_stats = interface_calc.calcEnergy(stats); // auto dqs_stats = interface_calc.calcDQSEnergy(stats); } TEST_F(DramPowerTest_Interface_LPDDR4, TestDQS) { for (const auto& command : testPattern_2) { auto stats = ddr->getWindowStats(command.timestamp); ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); }; auto stats = ddr->getStats(); // DQs bus EXPECT_EQ(sizeof(wr_data), sizeof(rd_data)); int number_of_cycles = (SZ_BITS(wr_data) / spec->bitWidth); uint_fast8_t NumDQsPairs = spec->bitWidth == 16 ? 2 : 1; uint_fast8_t scale = NumDQsPairs * 2; // Differential_Pairs * 2(pairs of 2) // f(t) = t / 2; int DQS_ones = scale * (number_of_cycles / 2); // scale * (cycles / 2) int DQS_zeros = DQS_ones; int DQS_zeros_to_ones = DQS_ones; int DQS_ones_to_zeros = DQS_zeros; // 4 writes EXPECT_EQ(stats.writeDQSStats.ones, DQS_ones * 4); EXPECT_EQ(stats.writeDQSStats.zeroes, DQS_zeros * 4); EXPECT_EQ(stats.writeDQSStats.ones_to_zeroes, DQS_zeros_to_ones * 4); EXPECT_EQ(stats.writeDQSStats.zeroes_to_ones, DQS_ones_to_zeros * 4); // 3 reads EXPECT_EQ(stats.readDQSStats.ones, DQS_ones * 3); EXPECT_EQ(stats.readDQSStats.zeroes, DQS_zeros * 3); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, DQS_zeros_to_ones * 3); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, DQS_ones_to_zeros * 3); // ASSERT_EQ(stats.readDQSStats.ones, 16 *3); // 16 (burst length) * 3 (reads) // ASSERT_EQ(stats.readDQSStats.zeroes, 16*3); // ASSERT_EQ(stats.writeDQSStats.ones, 16*4); // 16 (burst length) * 4 (writes) // ASSERT_EQ(stats.writeDQSStats.zeroes, 16*4); } TEST_F(DramPowerTest_Interface_LPDDR4, Test_Detailed) { SimulationStats window; auto iterate_to_timestamp = [this, command = testPattern.begin()](timestamp_t timestamp) mutable { while (command != this->testPattern.end() && command->timestamp <= timestamp) { ddr->doCoreCommand(*command); ddr->doInterfaceCommand(*command); ++command; } return this->ddr->getWindowStats(timestamp); }; // Cycle 0 to 0 (0 delta) window = iterate_to_timestamp(0); ASSERT_EQ(window.commandBus.ones, 0); ASSERT_EQ(window.commandBus.zeroes, 0); // Cycle 0 to 1 (1 delta) window = iterate_to_timestamp(1); ASSERT_EQ(window.commandBus.ones, 1); ASSERT_EQ(window.commandBus.zeroes, 5); // Cycle 0 to 2 (2 delta) window = iterate_to_timestamp(2); ASSERT_EQ(window.commandBus.ones, 2); ASSERT_EQ(window.commandBus.zeroes, 10); // Cycle 0 to 3 (3 delta) window = iterate_to_timestamp(3); ASSERT_EQ(window.commandBus.ones, 2); ASSERT_EQ(window.commandBus.zeroes, 16); // Cycle 0 to 4 (4 delta) window = iterate_to_timestamp(4); ASSERT_EQ(window.commandBus.ones, 3); ASSERT_EQ(window.commandBus.zeroes, 21); // Cycle 0 to 5 (5 delta) window = iterate_to_timestamp(5); ASSERT_EQ(window.commandBus.ones, 3); ASSERT_EQ(window.commandBus.zeroes, 27); // Cycle 0 to 6 (6 delta) window = iterate_to_timestamp(6); ASSERT_EQ(window.commandBus.ones, 4); ASSERT_EQ(window.commandBus.zeroes, 32); // Cycle 0 to 7 (7 delta) window = iterate_to_timestamp(7); ASSERT_EQ(window.commandBus.ones, 5); ASSERT_EQ(window.commandBus.zeroes, 37); // Cycle 0 to 8 (8 delta) window = iterate_to_timestamp(8); ASSERT_EQ(window.commandBus.ones, 7); ASSERT_EQ(window.commandBus.zeroes, 41); // Cycle 0 to 9 (9 delta) window = iterate_to_timestamp(9); ASSERT_EQ(window.commandBus.ones, 8); ASSERT_EQ(window.commandBus.zeroes, 46); // Cycle 0 to 10 (10 delta) window = iterate_to_timestamp(10); ASSERT_EQ(window.commandBus.ones, 8); ASSERT_EQ(window.commandBus.zeroes, 52); // Cycle 0 to 14 (14 delta) window = iterate_to_timestamp(14); ASSERT_EQ(window.commandBus.ones, 8); ASSERT_EQ(window.commandBus.zeroes, 76); // Cycle 0 to 15 (15 delta) window = iterate_to_timestamp(15); ASSERT_EQ(window.commandBus.ones, 9); ASSERT_EQ(window.commandBus.zeroes, 81); // Cycle 0 to 16 (16 delta) window = iterate_to_timestamp(16); ASSERT_EQ(window.commandBus.ones, 10); ASSERT_EQ(window.commandBus.zeroes, 86); // Cycle 0 to 17 (17 delta) window = iterate_to_timestamp(17); ASSERT_EQ(window.commandBus.ones, 12); ASSERT_EQ(window.commandBus.zeroes, 90); // Cycle 0 to 18 (18 delta) window = iterate_to_timestamp(18); ASSERT_EQ(window.commandBus.ones, 13); ASSERT_EQ(window.commandBus.zeroes, 95); // Cycle 0 to 19 (19 delta) window = iterate_to_timestamp(19); ASSERT_EQ(window.commandBus.ones, 13); ASSERT_EQ(window.commandBus.zeroes, 101); // Cycle 0 to 23 (23 delta) window = iterate_to_timestamp(23); ASSERT_EQ(window.commandBus.ones, 13); ASSERT_EQ(window.commandBus.zeroes, 125); // Cycle 0 to 24 (24 delta) window = iterate_to_timestamp(24); ASSERT_EQ(window.commandBus.ones, 14); ASSERT_EQ(window.commandBus.zeroes, 130); window = iterate_to_timestamp(25); ASSERT_EQ(window.commandBus.ones, 15); ASSERT_EQ(window.commandBus.zeroes, 135); window = iterate_to_timestamp(26); ASSERT_EQ(window.commandBus.ones, 15); ASSERT_EQ(window.commandBus.zeroes, 141); window = iterate_to_timestamp(26); ASSERT_EQ(window.commandBus.ones, 15); ASSERT_EQ(window.commandBus.zeroes, 141); window = iterate_to_timestamp(26); ASSERT_EQ(window.commandBus.ones, 15); ASSERT_EQ(window.commandBus.zeroes, 141); // EOS window = iterate_to_timestamp(26); ASSERT_EQ(window.commandBus.ones, 15); ASSERT_EQ(window.commandBus.zeroes, 141); // After EOS for(auto i = 1; i <= 100; i++) { window = iterate_to_timestamp(26+i); ASSERT_EQ(window.commandBus.ones, 15); ASSERT_EQ(window.commandBus.zeroes, 141+i*6); } } // Tests for power consumption (given a known SimulationStats) class LPDDR4_Energy_Tests : public ::testing::Test { public: LPDDR4_Energy_Tests() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); spec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); t_CK = spec->memTimingSpec.tCK; voltage = spec->vddq; // Change impedances to different values from each other spec->memImpedanceSpec.ca_R_eq = 2; spec->memImpedanceSpec.ck_R_eq = 3; spec->memImpedanceSpec.rdqs_R_eq = 4; spec->memImpedanceSpec.wdqs_R_eq = 5; spec->memImpedanceSpec.rdq_R_eq = 6; spec->memImpedanceSpec.wdq_R_eq = 7; spec->memImpedanceSpec.ca_dyn_E = 8; spec->memImpedanceSpec.ck_dyn_E = 9; spec->memImpedanceSpec.rdqs_dyn_E = 10; spec->memImpedanceSpec.wdqs_dyn_E = 11; spec->memImpedanceSpec.rdq_dyn_E = 12; spec->memImpedanceSpec.wdq_dyn_E = 13; io_calc = std::make_unique(*spec); } std::unique_ptr spec; double t_CK; double voltage; std::unique_ptr io_calc; }; TEST_F(LPDDR4_Energy_Tests, Parameters) { ASSERT_TRUE(t_CK > 0.0); ASSERT_TRUE(voltage > 0.0); } TEST_F(LPDDR4_Energy_Tests, Clock_Energy) { SimulationStats stats; stats.clockStats.ones = 43; stats.clockStats.zeroes_to_ones = 47; stats.clockStats.zeroes = 53; stats.clockStats.ones_to_zeroes = 59; interface_energy_info_t result = io_calc->calculateEnergy(stats); // Clock is provided by the controller not the device EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, 0.0); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, 0.0); // Clock is differential so there is always going to be one signal that consumes power // Calculation is done considering number of ones but could also be zeroes, since clock is // a symmetrical signal // f(t) = t / 2 // Differential pair 2 limes in N = 2 * N_single // E = f(N * t_CK) * U^2/R double expected_static = stats.clockStats.ones * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.ck_R_eq; // E = N * 1/2 * C * U^2 double expected_dynamic = stats.clockStats.zeroes_to_ones * spec->memImpedanceSpec.ck_dyn_E; EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static); // value itself doesn't matter, only that it matches the formula EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic); EXPECT_TRUE(result.controller.staticEnergy > 0.0); EXPECT_TRUE(result.controller.dynamicEnergy > 0.0); } TEST_F(LPDDR4_Energy_Tests, DQS_Energy) { // TODO implement test with new interface model SimulationStats stats; stats.readDQSStats.ones = 200; stats.readDQSStats.zeroes = 400; stats.readDQSStats.zeroes_to_ones = 700; stats.readDQSStats.ones_to_zeroes = 1000; stats.writeDQSStats.ones = 300; stats.writeDQSStats.zeroes = 100; stats.writeDQSStats.ones_to_zeroes = 2000; stats.writeDQSStats.zeroes_to_ones = 999; // Term power if consumed by 1's // Controller -> write power // Dram -> read power double expected_static_controller = stats.writeDQSStats.ones * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.wdqs_R_eq; double expected_static_dram = stats.readDQSStats.ones * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.rdqs_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = stats.writeDQSStats.zeroes_to_ones * spec->memImpedanceSpec.wdqs_dyn_E; double expected_dynamic_dram = stats.readDQSStats.zeroes_to_ones * spec->memImpedanceSpec.rdqs_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); EXPECT_TRUE(result.dram.staticEnergy > 0.0); EXPECT_TRUE(result.dram.dynamicEnergy > 0.0); } TEST_F(LPDDR4_Energy_Tests, DQ_Energy) { SimulationStats stats; stats.readBus.ones = 7; stats.readBus.zeroes = 11; stats.readBus.zeroes_to_ones = 19; stats.readBus.ones_to_zeroes = 39; stats.writeBus.ones = 43; stats.writeBus.zeroes = 59; stats.writeBus.zeroes_to_ones = 13; stats.writeBus.ones_to_zeroes = 17; // Term power if consumed by 1's on LPDDR4 (pulldown terminated) // Controller -> write power // Dram -> read power double expected_static_controller = stats.writeBus.ones * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.wdq_R_eq; double expected_static_dram = stats.readBus.ones * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.rdq_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = stats.writeBus.zeroes_to_ones * spec->memImpedanceSpec.wdq_dyn_E; double expected_dynamic_dram = stats.readBus.zeroes_to_ones * spec->memImpedanceSpec.rdq_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); EXPECT_TRUE(result.controller.staticEnergy > 0.0); EXPECT_TRUE(result.controller.dynamicEnergy > 0.0); EXPECT_TRUE(result.dram.staticEnergy > 0.0); EXPECT_TRUE(result.dram.dynamicEnergy > 0.0); } TEST_F(LPDDR4_Energy_Tests, CA_Energy) { SimulationStats stats; stats.commandBus.ones = 11; stats.commandBus.zeroes = 29; stats.commandBus.zeroes_to_ones = 39; stats.commandBus.ones_to_zeroes = 49; double expected_static_controller = stats.commandBus.ones * voltage * voltage * t_CK / spec->memImpedanceSpec.ca_R_eq; double expected_dynamic_controller = stats.commandBus.zeroes_to_ones * spec->memImpedanceSpec.ca_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); // CA bus power is provided by the controller EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, 0.0); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, 0.0); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_TRUE(result.controller.staticEnergy > 0.0); EXPECT_TRUE(result.controller.dynamicEnergy > 0.0); } ================================================ FILE: tests/tests_drampower/interface/test_interface_lpddr5.cpp ================================================ #include #include #include #include #include #include #include #include "DRAMPower/data/energy.h" #include "DRAMPower/data/stats.h" #include "DRAMPower/memspec/MemSpecLPDDR5.h" #include "DRAMPower/standards/lpddr5/LPDDR5.h" #include "DRAMPower/standards/lpddr5/interface_calculation_LPDDR5.h" using DRAMPower::CmdType; using DRAMPower::Command; using DRAMPower::LPDDR5; using DRAMPower::interface_energy_info_t; using DRAMPower::InterfaceCalculation_LPDDR5; using DRAMPower::MemSpecLPDDR5; using DRAMPower::SimulationStats; #define SZ_BITS(x) sizeof(x)*8 using namespace DRAMPower; static constexpr uint8_t wr_data[] = { 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, }; static constexpr uint8_t rd_data[] = { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; class LPDDR5_WindowStats_Tests : public ::testing::Test { public: LPDDR5_WindowStats_Tests() { // Timestamp, Cmd, { Bank, BG, Rank, Row, Co-lumn} test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {3, CmdType::WR, {1, 0, 0, 0, 8}, wr_data, SZ_BITS(wr_data)}, {12, CmdType::RD, {1, 0, 0, 0, 8}, rd_data, SZ_BITS(rd_data)}, {21, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); test_patterns.push_back({ {0, CmdType::ACT, {2, 0, 0, 372}}, {3, CmdType::WRA, {2, 0, 0, 372, 27}, wr_data, SZ_BITS(wr_data)}, {18, CmdType::SREFEN}, {45, CmdType::SREFEX}, {48, CmdType::END_OF_SIMULATION} }); test_patterns.push_back({ {0, CmdType::ACT, {2, 0, 0, 372}}, {3, CmdType::WR, {2, 0, 0, 372, 27}, wr_data, SZ_BITS(wr_data)}, {12, CmdType::RDA, {2, 0, 0, 372, 27}, rd_data, SZ_BITS(rd_data)}, {24, CmdType::END_OF_SIMULATION} // RD needs time to finish fully }); // With BG != 0 for testing BG mode test_patterns.push_back({ {0, CmdType::ACT, {1, 1, 0, 2}}, {3, CmdType::WR, {1, 1, 0, 0, 19}, wr_data, SZ_BITS(wr_data)}, {12, CmdType::RD, {1, 1, 0, 0, 19}, rd_data, SZ_BITS(rd_data)}, {21, CmdType::PRE, {1, 1, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); initSpec(); ddr = std::make_unique(*spec); } void initSpec() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); spec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); spec->numberOfDevices = 1; spec->bitWidth = 16; } void runCommands(const std::vector &commands) { for (const Command &command : commands) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); } } std::vector> test_patterns; std::unique_ptr spec; std::unique_ptr ddr; }; // Tests for the window stats (ones to zeros, ones etc) // Write and Read bus are trivial // Command bus needs to be calculated from command patterns TEST_F(LPDDR5_WindowStats_Tests, Pattern_0) { // auto test_pattern = test_patterns[0]; // auto iterate_to_timestamp = [this, command = test_pattern.begin(), end = test_pattern.end()](timestamp_t timestamp) mutable { // while (command != end && command->timestamp <= timestamp) { // ddr->doCommand(*command); // ddr->handleInterfaceCommand(*command); // ++command; // } // return this->ddr->getWindowStats(timestamp); // }; // for(auto i = 0; i < 26; i++) // { // SimulationStats window = iterate_to_timestamp(i); // std::cout << "Timestamp: " << i << std::endl; // } // iterate_to_timestamp(test_patterns[0].back().timestamp); runCommands(test_patterns[0]); SimulationStats stats = ddr->getStats(); EXPECT_EQ(stats.writeBus.ones, 16); EXPECT_EQ(stats.writeBus.zeroes, 752); // 2 (datarate) * 24 (time) * 16 (bus width) - 16 (ones) EXPECT_EQ(stats.writeBus.ones_to_zeroes, 16); // 0 -> 255 = 8 transitions, *2 = 16 EXPECT_EQ(stats.writeBus.zeroes_to_ones, 16); // back to 0 EXPECT_EQ(stats.readBus.ones, 1); EXPECT_EQ(stats.readBus.zeroes, 767); // 2 (datarate) * 24 (time) * 16 (bus width) - 1 (ones) EXPECT_EQ(stats.readBus.ones_to_zeroes, 1); EXPECT_EQ(stats.readBus.zeroes_to_ones, 1); EXPECT_EQ(stats.commandBus.ones, 19); EXPECT_EQ(stats.commandBus.zeroes, 317); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 15); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 15); // For read the number of clock cycles the strobes stay on is EXPECT_EQ(sizeof(wr_data), sizeof(rd_data)); uint64_t number_of_cycles = (SZ_BITS(wr_data) / spec->bitWidth); uint_fast8_t scale = 1 * 2; // Differential_Pairs * 2(pairs of 2) // f(t) = t / 2; uint64_t DQS_ones = scale * (number_of_cycles / 2); // scale * (cycles / 2) uint64_t DQS_zeros = DQS_ones; uint64_t DQS_zeros_to_ones = DQS_ones; uint64_t DQS_ones_to_zeros = DQS_zeros; EXPECT_EQ(stats.readDQSStats.ones, DQS_ones); EXPECT_EQ(stats.readDQSStats.zeroes, DQS_zeros); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, DQS_zeros_to_ones); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, DQS_ones_to_zeros); // TODO WCK } TEST_F(LPDDR5_WindowStats_Tests, Pattern_1) { // auto test_pattern = test_patterns[1]; // auto iterate_to_timestamp = [this, command = test_pattern.begin(), end = test_pattern.end()](timestamp_t timestamp) mutable { // while (command != end && command->timestamp <= timestamp) { // ddr->doCommand(*command); // ddr->handleInterfaceCommand(*command); // ++command; // } // return this->ddr->getWindowStats(timestamp); // }; // for(auto i = 0; i < 50; i++) // { // SimulationStats window = iterate_to_timestamp(i); // std::cout << "Timestamp: " << i << std::endl; // } // iterate_to_timestamp(test_patterns[1].back().timestamp); runCommands(test_patterns[1]); SimulationStats stats = ddr->getStats(); EXPECT_EQ(stats.writeBus.ones, 16); EXPECT_EQ(stats.writeBus.zeroes, 1520); // 2 (datarate) * 48 (time) * 16 (bus width) - 16 (ones) EXPECT_EQ(stats.writeBus.ones_to_zeroes, 16); EXPECT_EQ(stats.writeBus.zeroes_to_ones, 16); EXPECT_EQ(stats.readBus.ones, 0); EXPECT_EQ(stats.readBus.zeroes, 1536); // 2 (datarate) * 48 (time) * 16 (bus width) EXPECT_EQ(stats.readBus.ones_to_zeroes, 0); EXPECT_EQ(stats.readBus.zeroes_to_ones, 0); EXPECT_EQ(stats.commandBus.ones, 24); EXPECT_EQ(stats.commandBus.zeroes, 648); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 19); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 19); EXPECT_EQ(stats.readDQSStats.ones, 0); EXPECT_EQ(stats.readDQSStats.zeroes, 0); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, 0); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, 0); } TEST_F(LPDDR5_WindowStats_Tests, Pattern_2) { // auto test_pattern = test_patterns[2]; // auto iterate_to_timestamp = [this, command = test_pattern.begin(), end = test_pattern.end()](timestamp_t timestamp) mutable { // while (command != end && command->timestamp <= timestamp) { // ddr->doCommand(*command); // ddr->handleInterfaceCommand(*command); // ++command; // } // return this->ddr->getWindowStats(timestamp); // }; // for(auto i = 0; i < 30; i++) // { // SimulationStats window = iterate_to_timestamp(i); // std::cout << "Timestamp: " << i << std::endl; // } // iterate_to_timestamp(test_patterns[2].back().timestamp); runCommands(test_patterns[2]); SimulationStats stats = ddr->getStats(); EXPECT_EQ(stats.writeBus.ones, 16); EXPECT_EQ(stats.writeBus.zeroes, 752); // 2 (datarate) * 24 (time) * 16 (bus width) - 16 (ones) EXPECT_EQ(stats.writeBus.ones_to_zeroes, 16); EXPECT_EQ(stats.writeBus.zeroes_to_ones, 16); EXPECT_EQ(stats.readBus.ones, 1); EXPECT_EQ(stats.readBus.zeroes, 767); // 2 (datarate) * 24 (time) * 16 (bus width) - 1 (ones) EXPECT_EQ(stats.readBus.ones_to_zeroes, 1); EXPECT_EQ(stats.readBus.zeroes_to_ones, 1); EXPECT_EQ(stats.commandBus.ones, 25); EXPECT_EQ(stats.commandBus.zeroes, 311); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 19); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 19); } // Write clock tests TEST_F(LPDDR5_WindowStats_Tests, WriteClockAlwaysOn) { runCommands(test_patterns[0]); SimulationStats stats = ddr->getStats(); uint64_t wck_rate = spec->dataRate / spec->memTimingSpec.WCKtoCK; // Number of cycles for always on is the simulation time uint64_t wck_ones = 24 * wck_rate; EXPECT_EQ(stats.wClockStats.ones, wck_ones); EXPECT_EQ(stats.wClockStats.zeroes, wck_ones); EXPECT_EQ(stats.wClockStats.ones_to_zeroes, wck_ones); EXPECT_EQ(stats.wClockStats.zeroes_to_ones, wck_ones); } TEST_F(LPDDR5_WindowStats_Tests, WriteClockOnDemand) { spec->wckAlwaysOnMode = false; ddr = std::make_unique(*spec); runCommands(test_patterns[0]); SimulationStats stats = ddr->getStats(); uint64_t wck_rate = spec->dataRate / spec->memTimingSpec.WCKtoCK; // Number of clocks of WCK is the length of the write data uint64_t cycles = SZ_BITS(wr_data) / 16; uint64_t wck_ones = cycles * wck_rate; EXPECT_EQ(stats.wClockStats.ones, wck_ones); EXPECT_EQ(stats.wClockStats.zeroes, wck_ones); EXPECT_EQ(stats.wClockStats.ones_to_zeroes, wck_ones); EXPECT_EQ(stats.wClockStats.zeroes_to_ones, wck_ones); } TEST_F(LPDDR5_WindowStats_Tests, Pattern_3_BG_Mode) { spec->bank_arch = MemSpecLPDDR5::MBG; ddr = std::make_unique(*spec); // auto test_pattern = test_patterns[3]; // auto iterate_to_timestamp = [this, command = test_pattern.begin(), end = test_pattern.end()](timestamp_t timestamp) mutable { // while (command != end && command->timestamp <= timestamp) { // ddr->doCommand(*command); // ddr->handleInterfaceCommand(*command); // ++command; // } // return this->ddr->getWindowStats(timestamp); // }; // for(auto i = 0; i < 30; i++) // { // SimulationStats window = iterate_to_timestamp(i); // std::cout << "Timestamp: " << i << std::endl; // } // iterate_to_timestamp(test_patterns[2].back().timestamp); runCommands(test_patterns[3]); SimulationStats stats = ddr->getStats(); EXPECT_EQ(stats.writeBus.ones, 16); EXPECT_EQ(stats.writeBus.zeroes, 752); // 2 (datarate) * 24 (time) * 16 (bus width) - 16 (ones) EXPECT_EQ(stats.writeBus.ones_to_zeroes, 16); EXPECT_EQ(stats.writeBus.zeroes_to_ones, 16); EXPECT_EQ(stats.readBus.ones, 1); EXPECT_EQ(stats.readBus.zeroes, 767); // 2 (datarate) * 24 (time) * 16 (bus width) - 1 (ones) EXPECT_EQ(stats.readBus.ones_to_zeroes, 1); EXPECT_EQ(stats.readBus.zeroes_to_ones, 1); EXPECT_EQ(stats.commandBus.ones, 27); EXPECT_EQ(stats.commandBus.zeroes, 309); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 21); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 21); EXPECT_EQ(sizeof(wr_data), sizeof(rd_data)); uint64_t number_of_cycles = (SZ_BITS(wr_data) / spec->bitWidth); uint_fast8_t scale = 1 * 2; // Differential_Pairs * 2(pairs of 2) // f(t) = t / 2; uint64_t DQS_ones = scale * (number_of_cycles / 2); // scale * (cycles / 2) uint64_t DQS_zeros = DQS_ones; uint64_t DQS_zeros_to_ones = DQS_ones; uint64_t DQS_ones_to_zeros = DQS_zeros; EXPECT_EQ(stats.readDQSStats.ones, DQS_ones); EXPECT_EQ(stats.readDQSStats.zeroes, DQS_zeros); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, DQS_zeros_to_ones); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, DQS_ones_to_zeros); } // Tests for power consumption (given a known SimulationStats) class LPDDR5_Energy_Tests : public ::testing::Test { public: LPDDR5_Energy_Tests() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); spec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); t_CK = spec->memTimingSpec.tCK; t_WCK = spec->memTimingSpec.tWCK; voltage = spec->vddq; // Change impedances to different values from each other spec->memImpedanceSpec.ca_R_eq = 2; spec->memImpedanceSpec.ck_R_eq = 3; spec->memImpedanceSpec.rdqs_R_eq = 4; spec->memImpedanceSpec.wck_R_eq = 5; spec->memImpedanceSpec.rdq_R_eq = 6; spec->memImpedanceSpec.wdq_R_eq = 7; spec->memImpedanceSpec.ca_dyn_E = 8; spec->memImpedanceSpec.ck_dyn_E = 9; spec->memImpedanceSpec.rdqs_dyn_E = 10; spec->memImpedanceSpec.wck_dyn_E = 11; spec->memImpedanceSpec.rdq_dyn_E = 12; spec->memImpedanceSpec.wdq_dyn_E = 13; io_calc = std::make_unique(*spec); } std::unique_ptr spec; double t_CK; double t_WCK; double voltage; std::unique_ptr io_calc; }; TEST_F(LPDDR5_Energy_Tests, Parameters) { ASSERT_TRUE(t_CK > 0.0); ASSERT_TRUE(voltage > 0.0); ASSERT_TRUE(t_WCK > 0.0); } TEST_F(LPDDR5_Energy_Tests, Clock_Energy) { SimulationStats stats; stats.clockStats.ones = 43; stats.clockStats.zeroes_to_ones = 47; stats.clockStats.zeroes = 53; stats.clockStats.ones_to_zeroes = 59; stats.wClockStats.ones = 61; stats.wClockStats.zeroes = 67; stats.wClockStats.zeroes_to_ones = 71; stats.wClockStats.ones_to_zeroes = 73; interface_energy_info_t result = io_calc->calculateEnergy(stats); // Clock is provided by the controller not the device EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, 0.0); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, 0.0); // Clock is differential so there is always going to be one signal that consumes power // Calculation is done considering number of ones but could also be zeroes, since clock is // a symmetrical signal double expected_static = stats.clockStats.ones * voltage * voltage * 0.5 * t_CK / spec->memImpedanceSpec.ck_R_eq; expected_static += stats.wClockStats.ones * voltage * voltage * 0.5 * t_WCK / spec->memImpedanceSpec.wck_R_eq; double expected_dynamic = stats.clockStats.zeroes_to_ones * spec->memImpedanceSpec.ck_dyn_E; expected_dynamic += stats.wClockStats.zeroes_to_ones * spec->memImpedanceSpec.wck_dyn_E; EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static); // value itself doesn't matter, only that it matches the formula EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic); EXPECT_TRUE(result.controller.staticEnergy > 0.0); EXPECT_TRUE(result.controller.dynamicEnergy > 0.0); } TEST_F(LPDDR5_Energy_Tests, DQS_Energy) { SimulationStats stats; stats.readDQSStats.ones = 200; stats.readDQSStats.zeroes = 400; stats.readDQSStats.zeroes_to_ones = 700; stats.readDQSStats.ones_to_zeroes = 1000; // Write DQS is not used in LPDDR5, instead there is WCK stats.writeDQSStats.ones = 300; stats.writeDQSStats.zeroes = 100; stats.writeDQSStats.ones_to_zeroes = 2000; stats.writeDQSStats.zeroes_to_ones = 999; // Term power if consumed by 1's // Controller -> write power // Dram -> read power double expected_static_controller = 0.0; double expected_static_dram = 0.5 * stats.readDQSStats.ones * voltage * voltage * t_CK / spec->memImpedanceSpec.rdqs_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = 0; double expected_dynamic_dram = stats.readDQSStats.zeroes_to_ones * spec->memImpedanceSpec.rdqs_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); EXPECT_TRUE(result.dram.staticEnergy > 0.0); EXPECT_TRUE(result.dram.dynamicEnergy > 0.0); } TEST_F(LPDDR5_Energy_Tests, DQ_Energy) { SimulationStats stats; stats.readBus.ones = 7; stats.readBus.zeroes = 11; stats.readBus.zeroes_to_ones = 19; stats.readBus.ones_to_zeroes = 39; stats.writeBus.ones = 43; stats.writeBus.zeroes = 59; stats.writeBus.zeroes_to_ones = 13; stats.writeBus.ones_to_zeroes = 17; // Term power if consumed by 1's on LPDDR5 (pulldown terminated) // Controller -> write power // Dram -> read power double expected_static_controller = 0.5 * stats.writeBus.ones * voltage * voltage * t_CK / spec->memImpedanceSpec.wdq_R_eq; double expected_static_dram = 0.5 * stats.readBus.ones * voltage * voltage * t_CK / spec->memImpedanceSpec.rdq_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = stats.writeBus.zeroes_to_ones * spec->memImpedanceSpec.wdq_dyn_E; double expected_dynamic_dram = stats.readBus.zeroes_to_ones * spec->memImpedanceSpec.rdq_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); EXPECT_TRUE(result.controller.staticEnergy > 0.0); EXPECT_TRUE(result.controller.dynamicEnergy > 0.0); EXPECT_TRUE(result.dram.staticEnergy > 0.0); EXPECT_TRUE(result.dram.dynamicEnergy > 0.0); } TEST_F(LPDDR5_Energy_Tests, CA_Energy) { SimulationStats stats; stats.commandBus.ones = 11; stats.commandBus.zeroes = 29; stats.commandBus.zeroes_to_ones = 39; stats.commandBus.ones_to_zeroes = 49; // Data rate 2 double expected_static_controller = stats.commandBus.ones * voltage * voltage * 0.5 * t_CK / spec->memImpedanceSpec.ca_R_eq; double expected_dynamic_controller = stats.commandBus.zeroes_to_ones * spec->memImpedanceSpec.ca_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); // CA bus power is provided by the controller EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, 0.0); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, 0.0); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_TRUE(result.controller.staticEnergy > 0.0); EXPECT_TRUE(result.controller.dynamicEnergy > 0.0); } ================================================ FILE: tests/tests_drampower/interface/test_togglingrate_ddr4.cpp ================================================ #include #include #include #include #include #include "DRAMPower/simconfig/simconfig.h" #include "DRAMPower/standards/ddr4/DDR4.h" #include "DRAMPower/standards/test_accessor.h" #include "DRAMPower/memspec/MemSpecDDR4.h" #include "DRAMPower/standards/ddr4/interface_calculation_DDR4.h" #include "DRAMPower/data/energy.h" #include "DRAMPower/data/stats.h" using namespace DRAMPower; using namespace DRAMUtils::Config; #define SZ_BITS(x) sizeof(x)*8 // burst length = 16 for x8 devices static constexpr uint8_t wr_data[] = { 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, }; // burst length = 16 for x8 devices static constexpr uint8_t rd_data[] = { 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; class DDR4_TogglingRate_Tests : public ::testing::Test { public: void SetUp() override { test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {4, CmdType::WR, {1, 0, 0, 0, 16}, nullptr, datasize_bits}, {11, CmdType::RD, {1, 0, 0, 0, 16}, nullptr, datasize_bits}, {16, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); initSpec(); } void initSpec() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); spec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); } void initDDR(const ToggleRateDefinition& trd) { ddr = std::make_unique(*spec, config::SimConfig{trd}); } void runCommands(const std::vector &commands) { for (const Command &command : commands) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); } } std::vector> test_patterns; std::unique_ptr spec; std::unique_ptr ddr; uint64_t datasize_bits = 8 * 8; // 8 bytes }; TEST_F(DDR4_TogglingRate_Tests, Pattern_0_LH) { // Setup toggling rate double togglingRateRead = 0.7; double togglingRateWrite = 0.3; double dutyCycleRead = 0.6; double dutyCycleWrite = 0.4; TogglingRateIdlePattern idlePatternRead = TogglingRateIdlePattern::L; TogglingRateIdlePattern idlePatternWrite = TogglingRateIdlePattern::H; auto trd = ToggleRateDefinition { togglingRateRead, // togglingRateRead togglingRateWrite, // togglingRateWrite dutyCycleRead, // dutyCycleRead dutyCycleWrite, // dutyCycleWrite idlePatternRead, // idlePatternRead idlePatternWrite // idlePatternWrite }; initDDR(trd); // Run commands runCommands(test_patterns.at(0)); // SZ_BITS: 64, width: 8 -> Burstlength: 8 (datarate bus) // 0: ACT, 4: WR, 11: RD, 16: PRE, 24: EOS // Read bus: idle: L // 0 to 11 idle // 11 to 15 toggle // 15 to 24 idle // idle: 20 zeroes, toggle: 4 (datarate clock) // idle: 40 zeroes, toggle: 8 (datarate bus) // Write bus: idle: H // 0 to 4 idle // 4 to 8 toggle // 8 to 24 idle // idle: 20 ones, toggle: 4 (datarate clock) // idle: 40 ones, toggle: 8 (datarate bus) uint64_t toggles_read = 8; uint64_t toggles_write = 8; uint64_t idleread_ones = 0; uint64_t idleread_zeroes = 40; uint64_t idlewrite_ones = 40; uint64_t idlewrite_zeroes = 0; SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Data bus // Read bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.ones, (idleread_ones + static_cast(std::floor(dutyCycleRead * toggles_read))) * spec->bitWidth); // 32 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.zeroes, (idleread_zeroes + static_cast(std::floor((1 - dutyCycleRead) * toggles_read))) * spec->bitWidth); // 344 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.ones_to_zeroes, std::floor((togglingRateRead / 2) * toggles_read) * spec->bitWidth); // 16 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.zeroes_to_ones, static_cast(std::floor((togglingRateRead / 2) * toggles_read)) * spec->bitWidth); // 16 // Write bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.ones, (idlewrite_ones + static_cast(std::floor(dutyCycleWrite * toggles_write))) * spec->bitWidth); // 344 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.zeroes, (idlewrite_zeroes + static_cast(std::floor((1 - dutyCycleWrite) * toggles_write))) * spec->bitWidth); // 32 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.ones_to_zeroes, std::floor((togglingRateWrite / 2) * toggles_write) * spec->bitWidth); // 8 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.zeroes_to_ones, static_cast(std::floor((togglingRateWrite / 2) * toggles_write)) * spec->bitWidth); // 8 // Clock (see test_interface_ddr4) EXPECT_EQ(stats.clockStats.ones, 48); EXPECT_EQ(stats.clockStats.zeroes, 48); EXPECT_EQ(stats.clockStats.ones_to_zeroes, 48); EXPECT_EQ(stats.clockStats.zeroes_to_ones, 48); // Command bus (see test_interface_ddr4) EXPECT_EQ(stats.commandBus.ones, 591); EXPECT_EQ(stats.commandBus.zeroes, 57); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 57); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 57); // DQs (see test_interface_ddr4) uint_fast8_t NumDQsPairs = spec->bitWidth == 16 ? 2 : 1; int number_of_cycles = (datasize_bits / spec->bitWidth); uint_fast8_t scale = NumDQsPairs * 2; // Differential_Pairs * 2(pairs of 2) // f(t) = t / 2; int DQS_ones = scale * (number_of_cycles / 2); // scale * (cycles / 2) int DQS_zeros = DQS_ones; int DQS_zeros_to_ones = DQS_ones; int DQS_ones_to_zeros = DQS_zeros; EXPECT_EQ(stats.writeDQSStats.ones, DQS_ones); EXPECT_EQ(stats.writeDQSStats.zeroes, DQS_zeros); EXPECT_EQ(stats.writeDQSStats.ones_to_zeroes, DQS_zeros_to_ones); EXPECT_EQ(stats.writeDQSStats.zeroes_to_ones, DQS_ones_to_zeros); EXPECT_EQ(stats.readDQSStats.ones, DQS_ones); EXPECT_EQ(stats.readDQSStats.zeroes, DQS_zeros); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, DQS_zeros_to_ones); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, DQS_ones_to_zeros); // PrePostamble auto prepos = stats.rank_total[0].prepos; EXPECT_EQ(prepos.readSeamless, 0); EXPECT_EQ(prepos.writeSeamless, 0); EXPECT_EQ(prepos.readMerged, 0); EXPECT_EQ(prepos.readMergedTime, 0); EXPECT_EQ(prepos.writeMerged, 0); EXPECT_EQ(prepos.writeMergedTime, 0); } TEST_F(DDR4_TogglingRate_Tests, Pattern_0_HZ) { // Setup toggling rate double togglingRateRead = 0.7; double togglingRateWrite = 0.3; double dutyCycleRead = 0.6; double dutyCycleWrite = 0.4; TogglingRateIdlePattern idlePatternRead = TogglingRateIdlePattern::H; TogglingRateIdlePattern idlePatternWrite = TogglingRateIdlePattern::Z; auto trd = ToggleRateDefinition { togglingRateRead, // togglingRateRead togglingRateWrite, // togglingRateWrite dutyCycleRead, // dutyCycleRead dutyCycleWrite, // dutyCycleWrite idlePatternRead, // idlePatternRead idlePatternWrite // idlePatternWrite }; initDDR(trd); // Run commands runCommands(test_patterns[0]); // SZ_BITS: 64, width: 8 -> Burstlength: 8 (datarate bus) // 0: ACT, 4: WR, 11: RD, 16: PRE, 24: EOS // Read bus: idle: H // 0 to 11 idle // 11 to 15 toggle // 15 to 24 idle // idle: 20 ones, toggle: 4 (datarate clock) // idle: 40 ones, toggle: 8 (datarate bus) // Write bus: idle: Z // 0 to 4 idle // 4 to 8 toggle // 8 to 24 idle // idle: 0 ones/zeroes, toggle: 4 (datarate clock) // idle: 0 ones/zeroes, toggle: 8 (datarate bus) uint64_t toggles_read = 8; uint64_t toggles_write = 8; uint64_t idleread_zeroes = 0; uint64_t idleread_ones = 40; uint64_t idlewrite_ones = 0; uint64_t idlewrite_zeroes = 0; SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Data bus // Read bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.ones, (idleread_ones + static_cast(std::floor(dutyCycleRead * toggles_read))) * spec->bitWidth); // 352 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.zeroes, (idleread_zeroes + static_cast(std::floor((1 - dutyCycleRead) * toggles_read))) * spec->bitWidth); // 24 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.ones_to_zeroes, std::floor((togglingRateRead / 2) * toggles_read) * spec->bitWidth); // 16 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.zeroes_to_ones, static_cast(std::floor((togglingRateRead / 2) * toggles_read)) * spec->bitWidth); // 16 // Write bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.ones, (idlewrite_ones + static_cast(std::floor(dutyCycleWrite * toggles_write))) * spec->bitWidth); // 24 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.zeroes, (idlewrite_zeroes + static_cast(std::floor((1 - dutyCycleWrite) * toggles_write))) * spec->bitWidth); // 32 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.ones_to_zeroes, std::floor((togglingRateWrite / 2) * toggles_write) * spec->bitWidth); // 8 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.zeroes_to_ones, static_cast(std::floor((togglingRateWrite / 2) * toggles_write)) * spec->bitWidth); // 8 } // Tests for power consumption (given a known SimulationStats) class DDR4_TogglingRateEnergy_Tests : public ::testing::Test { public: DDR4_TogglingRateEnergy_Tests() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr4.json"); spec = std::make_unique(DRAMPower::MemSpecDDR4::from_memspec(*data)); t_CK = spec->memTimingSpec.tCK; voltage = spec->memPowerSpec[MemSpecDDR4::VoltageDomain::VDD].vXX; // Change impedances to different values from each other spec->memImpedanceSpec.ca_R_eq = 2; spec->memImpedanceSpec.ck_R_eq = 3; spec->memImpedanceSpec.rdqs_R_eq = 4; spec->memImpedanceSpec.wdqs_R_eq = 5; spec->memImpedanceSpec.rdq_R_eq = 6; spec->memImpedanceSpec.wdq_R_eq = 7; spec->memImpedanceSpec.ca_dyn_E = 8; spec->memImpedanceSpec.ck_dyn_E = 9; spec->memImpedanceSpec.rdqs_dyn_E = 10; spec->memImpedanceSpec.wdqs_dyn_E = 11; spec->memImpedanceSpec.rdq_dyn_E = 12; spec->memImpedanceSpec.wdq_dyn_E = 13; // PrePostamble is a possible DDR4 pattern // Preamble 2tCK, Postamble 0.5tCK spec->prePostamble.read_ones = 2.5; spec->prePostamble.read_zeroes = 2.5; spec->prePostamble.read_zeroes_to_ones = 2; spec->prePostamble.read_ones_to_zeroes = 2; io_calc = std::make_unique(*spec); } std::unique_ptr spec; double t_CK; double voltage; std::unique_ptr io_calc; }; TEST_F(DDR4_TogglingRateEnergy_Tests, DQ_Energy) { SimulationStats stats; stats.togglingStats = TogglingStats(); stats.togglingStats.read.ones = 7; stats.togglingStats.read.zeroes = 11; stats.togglingStats.read.zeroes_to_ones = 19; stats.togglingStats.read.ones_to_zeroes = 39; stats.togglingStats.write.ones = 43; stats.togglingStats.write.zeroes = 59; stats.togglingStats.write.zeroes_to_ones = 13; stats.togglingStats.write.ones_to_zeroes = 17; // Controller -> write power // Dram -> read power double expected_static_controller = stats.togglingStats.write.zeroes * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.wdq_R_eq; double expected_static_dram = stats.togglingStats.read.zeroes * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.rdq_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = stats.togglingStats.write.zeroes_to_ones * spec->memImpedanceSpec.wdq_dyn_E; double expected_dynamic_dram = stats.togglingStats.read.zeroes_to_ones * spec->memImpedanceSpec.rdq_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); } TEST_F(DDR4_TogglingRate_Tests, Pattern_1) { // Setup toggling rate double togglingRateRead = 0.7; double togglingRateWrite = 0.3; double dutyCycleRead = 0.6; double dutyCycleWrite = 0.4; TogglingRateIdlePattern idlePatternRead = TogglingRateIdlePattern::L; TogglingRateIdlePattern idlePatternWrite = TogglingRateIdlePattern::H; auto trd = ToggleRateDefinition { togglingRateRead, // togglingRateRead togglingRateWrite, // togglingRateWrite dutyCycleRead, // dutyCycleRead dutyCycleWrite, // dutyCycleWrite idlePatternRead, // idlePatternRead idlePatternWrite // idlePatternWrite }; initDDR(trd); // Run commands ddr->doCommand({0, CmdType::ACT, {1, 0, 0, 2}}); ddr->doCommand({5, CmdType::WR, {1, 0, 0, 0, 16}, wr_data, SZ_BITS(wr_data)}); ddr->doCommand({14, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}); ddr->doCommand({23, CmdType::PRE, {1, 0, 0, 2}}); ddr->doCommand({30, CmdType::ACT, {1, 0, 0, 2}}); ddr->doCommand({35, CmdType::WR, {1, 0, 0, 0, 16}, nullptr, 16*8}); // burst length = 16 ddr->doCommand({44, CmdType::RD, {1, 0, 0, 0, 16}, nullptr, 16*8}); // burst length = 16 ddr->doCommand({53, CmdType::PRE, {1, 0, 0, 2}}); ddr->doCommand({56, CmdType::END_OF_SIMULATION}); SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Toggling rate uint64_t toggles_read = 2 * 16; uint64_t idleread_ones = 0; uint64_t idleread_zeroes = 2 * 56 - toggles_read; // TogglingRateIdlePattern::L uint64_t toggles_write = 2 * 16; uint64_t idlewrite_ones = 2 * 56 - toggles_write; // TogglingRateIdlePattern::H uint64_t idlewrite_zeroes = 0; // Read bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.ones, (idleread_ones + static_cast(std::floor(dutyCycleRead * toggles_read))) * spec->bitWidth); // 112 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.zeroes, (idleread_zeroes + static_cast(std::floor((1 - dutyCycleRead) * toggles_read))) * spec->bitWidth); // 296 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.ones_to_zeroes, std::floor((togglingRateRead / 2) * toggles_read) * spec->bitWidth); // 64 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.zeroes_to_ones, static_cast(std::floor((togglingRateRead / 2) * toggles_read)) * spec->bitWidth); // 64 // Write bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.ones, (idlewrite_ones + static_cast(std::floor(dutyCycleWrite * toggles_write))) * spec->bitWidth); // 256 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.zeroes, (idlewrite_zeroes + static_cast(std::floor((1 - dutyCycleWrite) * toggles_write))) * spec->bitWidth); // 152 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.ones_to_zeroes, std::floor((togglingRateWrite / 2) * toggles_write) * spec->bitWidth); // 32 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.zeroes_to_ones, static_cast(std::floor((togglingRateWrite / 2) * toggles_write)) * spec->bitWidth); // 32 // Data bus EXPECT_EQ(stats.readBus.zeroes_to_ones, 0); EXPECT_EQ(stats.readBus.ones_to_zeroes, 0); EXPECT_EQ(stats.readBus.zeroes, 0); EXPECT_EQ(stats.readBus.ones, 0); EXPECT_EQ(stats.writeBus.zeroes_to_ones, 0); EXPECT_EQ(stats.writeBus.ones_to_zeroes, 0); EXPECT_EQ(stats.writeBus.zeroes, 0); EXPECT_EQ(stats.writeBus.ones, 0); // Clock (see test_interface_ddr4) EXPECT_EQ(stats.clockStats.zeroes_to_ones, 112); EXPECT_EQ(stats.clockStats.ones_to_zeroes, 112); EXPECT_EQ(stats.clockStats.zeroes, 112); EXPECT_EQ(stats.clockStats.ones, 112); // Command bus (see test_interface_ddr4) EXPECT_EQ(stats.commandBus.zeroes_to_ones, 114); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 114); EXPECT_EQ(stats.commandBus.zeroes, 114); EXPECT_EQ(stats.commandBus.ones, 1398); // DQs (see test_interface_ddr4) EXPECT_EQ(stats.writeDQSStats.zeroes_to_ones, 32); EXPECT_EQ(stats.writeDQSStats.ones_to_zeroes, 32); EXPECT_EQ(stats.writeDQSStats.zeroes, 32); EXPECT_EQ(stats.writeDQSStats.ones, 32); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, 32); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, 32); EXPECT_EQ(stats.readDQSStats.zeroes, 32); EXPECT_EQ(stats.readDQSStats.ones, 32); // PrePostamble auto prepos = stats.rank_total[0].prepos; EXPECT_EQ(prepos.readSeamless, 0); EXPECT_EQ(prepos.writeSeamless, 0); EXPECT_EQ(prepos.readMerged, 0); EXPECT_EQ(prepos.readMergedTime, 0); EXPECT_EQ(prepos.writeMerged, 0); EXPECT_EQ(prepos.writeMergedTime, 0); } ================================================ FILE: tests/tests_drampower/interface/test_togglingrate_ddr5.cpp ================================================ #include #include #include #include #include #include "DRAMPower/simconfig/simconfig.h" #include "DRAMPower/standards/ddr5/DDR5.h" #include "DRAMPower/memspec/MemSpecDDR5.h" #include #include "DRAMPower/standards/ddr5/interface_calculation_DDR5.h" #include "DRAMPower/data/energy.h" #include "DRAMPower/data/stats.h" using namespace DRAMPower; using namespace DRAMUtils::Config; #define SZ_BITS(x) sizeof(x)*8 // burst length = 16 for x8 devices static constexpr uint8_t wr_data[] = { 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, }; // burst length = 16 for x8 devices static constexpr uint8_t rd_data[] = { 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; class DDR5_TogglingRate_Tests : public ::testing::Test { public: void SetUp() override { test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {3, CmdType::WR, {1, 0, 0, 0, 4}, nullptr, datasize_bits}, {12, CmdType::RD, {1, 0, 0, 0, 4}, nullptr, datasize_bits}, {21, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); initSpec(); } void initSpec() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); spec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); } void initDDR(const ToggleRateDefinition& trd) { ddr = std::make_unique(*spec, config::SimConfig{trd}); } void runCommands(const std::vector &commands) { for (const Command &command : commands) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); } } std::vector> test_patterns; std::unique_ptr spec; std::unique_ptr ddr; uint_fast16_t datasize_bits = 32 * 8; // 32 bytes }; TEST_F(DDR5_TogglingRate_Tests, Pattern_0_LH) { // Setup toggling rate double togglingRateRead = 0.7; double togglingRateWrite = 0.3; double dutyCycleRead = 0.6; double dutyCycleWrite = 0.4; TogglingRateIdlePattern idlePatternRead = TogglingRateIdlePattern::L; TogglingRateIdlePattern idlePatternWrite = TogglingRateIdlePattern::H; auto trd = ToggleRateDefinition { togglingRateRead, // togglingRateRead togglingRateWrite, // togglingRateWrite dutyCycleRead, // dutyCycleRead dutyCycleWrite, // dutyCycleWrite idlePatternRead, // idlePatternRead idlePatternWrite // idlePatternWrite }; initDDR(trd); // Run commands runCommands(test_patterns.at(0)); // SZ_BITS: 256, width: 8 -> Burstlength: 32 (datarate bus) // 0: ACT, 3: WR, 12: RD, 21: PRE, 24: EOS // Read bus: idle: L // 0 to 12 idle // 12 to 24 toggle -> toggle cut off by EOS // idle: 12 zeroes, toggle: 12 (datarate clock) // idle: 24 zeroes, toggle: 24 (datarate bus) // Write bus: idle: H // 0 to 3 idle // 3 to 19 toggle // 19 to 24 idle // idle: 8 ones, toggle: 16 (datarate clock) // idle: 16 zeroes, toggle: 32 (datarate bus) uint64_t toggles_read = 24; uint64_t toggles_write = 32; uint64_t idleread_ones = 0; uint64_t idleread_zeroes = 24; uint64_t idlewrite_ones = 16; uint64_t idlewrite_zeroes = 0; SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Data bus // Read bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.ones, (idleread_ones + static_cast(std::floor(dutyCycleRead * toggles_read))) * spec->bitWidth); // 112 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.zeroes, (idleread_zeroes + static_cast(std::floor((1 - dutyCycleRead) * toggles_read))) * spec->bitWidth); // 264 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.ones_to_zeroes, std::floor((togglingRateRead / 2) * toggles_read) * spec->bitWidth); // 64 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.zeroes_to_ones, static_cast(std::floor((togglingRateRead / 2) * toggles_read)) * spec->bitWidth); // 64 // Write bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.ones, (idlewrite_ones + static_cast(std::floor(dutyCycleWrite * toggles_write))) * spec->bitWidth); // 224 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.zeroes, (idlewrite_zeroes + static_cast(std::floor((1 - dutyCycleWrite) * toggles_write))) * spec->bitWidth); // 152 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.ones_to_zeroes, std::floor((togglingRateWrite / 2) * toggles_write) * spec->bitWidth); // 32 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.zeroes_to_ones, static_cast(std::floor((togglingRateWrite / 2) * toggles_write)) * spec->bitWidth); // 32 // Clock (see test_interface_ddr5) EXPECT_EQ(stats.clockStats.ones, 48); EXPECT_EQ(stats.clockStats.zeroes, 48); EXPECT_EQ(stats.clockStats.ones_to_zeroes, 48); EXPECT_EQ(stats.clockStats.zeroes_to_ones, 48); // Command bus (see test_interface_ddr5) EXPECT_EQ(stats.commandBus.ones, 282); EXPECT_EQ(stats.commandBus.zeroes, 54); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 39); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 39); // DQs (see test_interface_ddr5) uint_fast8_t NumDQsPairs = spec->bitWidth == 16 ? 2 : 1; int number_of_cycles = (datasize_bits / spec->bitWidth); uint_fast8_t scale = NumDQsPairs * 2; // Differential_Pairs * 2(pairs of 2) // f(t) = t / 2; int DQS_ones = scale * (number_of_cycles / 2); // scale * (cycles / 2) int DQS_zeros = DQS_ones; int DQS_zeros_to_ones = DQS_ones; int DQS_ones_to_zeros = DQS_zeros; EXPECT_EQ(stats.writeDQSStats.ones, DQS_ones); EXPECT_EQ(stats.writeDQSStats.zeroes, DQS_zeros); EXPECT_EQ(stats.writeDQSStats.ones_to_zeroes, DQS_zeros_to_ones); EXPECT_EQ(stats.writeDQSStats.zeroes_to_ones, DQS_ones_to_zeros); EXPECT_EQ(stats.readDQSStats.ones, DQS_ones); EXPECT_EQ(stats.readDQSStats.zeroes, DQS_zeros); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, DQS_zeros_to_ones); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, DQS_ones_to_zeros); } TEST_F(DDR5_TogglingRate_Tests, Pattern_0_HZ) { // Setup toggling rate double togglingRateRead = 0.7; double togglingRateWrite = 0.3; double dutyCycleRead = 0.6; double dutyCycleWrite = 0.4; TogglingRateIdlePattern idlePatternRead = TogglingRateIdlePattern::H; TogglingRateIdlePattern idlePatternWrite = TogglingRateIdlePattern::Z; auto trd = ToggleRateDefinition { togglingRateRead, // togglingRateRead togglingRateWrite, // togglingRateWrite dutyCycleRead, // dutyCycleRead dutyCycleWrite, // dutyCycleWrite idlePatternRead, // idlePatternRead idlePatternWrite // idlePatternWrite }; initDDR(trd); // Run commands runCommands(test_patterns[0]); // SZ_BITS: 256, width: 8 -> Burstlength: 32 (datarate bus) // 0: ACT, 3: WR, 12: RD, 21: PRE, 24: EOS // Read bus: idle: H // 0 to 12 idle // 12 to 24 toggle -> toggle cut off by EOS // idle: 12 ones, toggle: 12 (datarate clock) // idle: 24 ones, toggle: 24 (datarate bus) // Write bus: idle: Z // 0 to 3 idle // 3 to 19 toggle // 19 to 24 idle // idle: 0 ones/zeroes, toggle: 16 (datarate clock) // idle: 0 ones/zeroes, toggle: 32 (datarate bus) uint64_t toggles_read = 24; uint64_t toggles_write = 32; uint64_t idleread_zeroes = 0; uint64_t idleread_ones = 24; uint64_t idlewrite_ones = 0; uint64_t idlewrite_zeroes = 0; SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Data bus // Read bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.ones, (idleread_ones + static_cast(std::floor(dutyCycleRead * toggles_read))) * spec->bitWidth); // 304 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.zeroes, (idleread_zeroes + static_cast(std::floor((1 - dutyCycleRead) * toggles_read))) * spec->bitWidth); // 72 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.ones_to_zeroes, std::floor((togglingRateRead / 2) * toggles_read) * spec->bitWidth); // 64 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.zeroes_to_ones, static_cast(std::floor((togglingRateRead / 2) * toggles_read)) * spec->bitWidth); // 64 // Write bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.ones, (idlewrite_ones + static_cast(std::floor(dutyCycleWrite * toggles_write))) * spec->bitWidth); // 96 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.zeroes, (idlewrite_zeroes + static_cast(std::floor((1 - dutyCycleWrite) * toggles_write))) * spec->bitWidth); // 152 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.ones_to_zeroes, std::floor((togglingRateWrite / 2) * toggles_write) * spec->bitWidth); // 32 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.zeroes_to_ones, static_cast(std::floor((togglingRateWrite / 2) * toggles_write)) * spec->bitWidth); // 32 } // Tests for power consumption (given a known SimulationStats) class DDR5_TogglingRateEnergy_Tests : public ::testing::Test { public: DDR5_TogglingRateEnergy_Tests() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "ddr5.json"); spec = std::make_unique(DRAMPower::MemSpecDDR5::from_memspec(*data)); t_CK = spec->memTimingSpec.tCK; voltage = spec->vddq; // Change impedances to different values from each other spec->memImpedanceSpec.ca_R_eq = 2; spec->memImpedanceSpec.ck_R_eq = 3; spec->memImpedanceSpec.rdqs_R_eq = 4; spec->memImpedanceSpec.wdqs_R_eq = 5; spec->memImpedanceSpec.rdq_R_eq = 6; spec->memImpedanceSpec.wdq_R_eq = 7; spec->memImpedanceSpec.ca_dyn_E = 8; spec->memImpedanceSpec.ck_dyn_E = 9; spec->memImpedanceSpec.rdqs_dyn_E = 10; spec->memImpedanceSpec.wdqs_dyn_E = 11; spec->memImpedanceSpec.rdq_dyn_E = 12; spec->memImpedanceSpec.wdq_dyn_E = 13; io_calc = std::make_unique(*spec); } std::unique_ptr spec; double t_CK; double voltage; std::unique_ptr io_calc; }; TEST_F(DDR5_TogglingRateEnergy_Tests, DQ_Energy) { SimulationStats stats; stats.togglingStats = TogglingStats(); stats.togglingStats.read.ones = 7; stats.togglingStats.read.zeroes = 11; stats.togglingStats.read.zeroes_to_ones = 19; stats.togglingStats.read.ones_to_zeroes = 39; stats.togglingStats.write.ones = 43; stats.togglingStats.write.zeroes = 59; stats.togglingStats.write.zeroes_to_ones = 13; stats.togglingStats.write.ones_to_zeroes = 17; // Controller -> write power // Dram -> read power double expected_static_controller = stats.togglingStats.write.zeroes * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.wdq_R_eq; double expected_static_dram = stats.togglingStats.read.zeroes * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.rdq_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = stats.togglingStats.write.zeroes_to_ones * spec->memImpedanceSpec.wdq_dyn_E; double expected_dynamic_dram = stats.togglingStats.read.zeroes_to_ones * spec->memImpedanceSpec.rdq_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); } TEST_F(DDR5_TogglingRate_Tests, Pattern_1) { // Setup toggling rate double togglingRateRead = 0.7; double togglingRateWrite = 0.3; double dutyCycleRead = 0.6; double dutyCycleWrite = 0.4; TogglingRateIdlePattern idlePatternRead = TogglingRateIdlePattern::L; TogglingRateIdlePattern idlePatternWrite = TogglingRateIdlePattern::H; auto trd = ToggleRateDefinition { togglingRateRead, // togglingRateRead togglingRateWrite, // togglingRateWrite dutyCycleRead, // dutyCycleRead dutyCycleWrite, // dutyCycleWrite idlePatternRead, // idlePatternRead idlePatternWrite // idlePatternWrite }; initDDR(trd); // Run commands ddr->doCommand({0, CmdType::ACT, {1, 0, 0, 2}}); ddr->doCommand({5, CmdType::WR, {1, 0, 0, 0, 4}, wr_data, SZ_BITS(wr_data)}); ddr->doCommand({14, CmdType::RD, {1, 0, 0, 0, 4}, rd_data, SZ_BITS(rd_data)}); ddr->doCommand({23, CmdType::PRE, {1, 0, 0, 2}}); ddr->doCommand({30, CmdType::ACT, {1, 0, 0, 2}}); ddr->doCommand({35, CmdType::WR, {1, 0, 0, 0, 4}, nullptr, 16*8}); // burst length = 16 ddr->doCommand({44, CmdType::RD, {1, 0, 0, 0, 4}, nullptr, 16*8}); // burst length = 16 ddr->doCommand({53, CmdType::PRE, {1, 0, 0, 2}}); ddr->doCommand({56, CmdType::END_OF_SIMULATION}); SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Toggling rate uint64_t toggles_read = 2 * 16; uint64_t idleread_ones = 0; uint64_t idleread_zeroes = 2 * 56 - toggles_read; // TogglingRateIdlePattern::L uint64_t toggles_write = 2 * 16; uint64_t idlewrite_ones = 2 * 56 - toggles_write; // TogglingRateIdlePattern::H uint64_t idlewrite_zeroes = 0; // Read bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.ones, (idleread_ones + static_cast(std::floor(dutyCycleRead * toggles_read))) * spec->bitWidth); // 112 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.zeroes, (idleread_zeroes + static_cast(std::floor((1 - dutyCycleRead) * toggles_read))) * spec->bitWidth); // 296 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.ones_to_zeroes, std::floor((togglingRateRead / 2) * toggles_read) * spec->bitWidth); // 64 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.zeroes_to_ones, static_cast(std::floor((togglingRateRead / 2) * toggles_read)) * spec->bitWidth); // 64 // Write bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.ones, (idlewrite_ones + static_cast(std::floor(dutyCycleWrite * toggles_write))) * spec->bitWidth); // 256 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.zeroes, (idlewrite_zeroes + static_cast(std::floor((1 - dutyCycleWrite) * toggles_write))) * spec->bitWidth); // 152 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.ones_to_zeroes, std::floor((togglingRateWrite / 2) * toggles_write) * spec->bitWidth); // 32 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.zeroes_to_ones, static_cast(std::floor((togglingRateWrite / 2) * toggles_write)) * spec->bitWidth); // 32 // Data bus EXPECT_EQ(stats.readBus.zeroes_to_ones, 0); EXPECT_EQ(stats.readBus.ones_to_zeroes, 0); EXPECT_EQ(stats.readBus.zeroes, 0); EXPECT_EQ(stats.readBus.ones, 0); EXPECT_EQ(stats.writeBus.zeroes_to_ones, 0); EXPECT_EQ(stats.writeBus.ones_to_zeroes, 0); EXPECT_EQ(stats.writeBus.zeroes, 0); EXPECT_EQ(stats.writeBus.ones, 0); // Clock (see test_interface_ddr5) EXPECT_EQ(stats.clockStats.zeroes_to_ones, 112); EXPECT_EQ(stats.clockStats.ones_to_zeroes, 112); EXPECT_EQ(stats.clockStats.zeroes, 112); EXPECT_EQ(stats.clockStats.ones, 112); // Command bus (see test_interface_ddr5) EXPECT_EQ(stats.commandBus.zeroes_to_ones, 78); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 78); EXPECT_EQ(stats.commandBus.zeroes, 108); EXPECT_EQ(stats.commandBus.ones, 676); // DQs (see test_interface_ddr5) EXPECT_EQ(stats.writeDQSStats.zeroes_to_ones, 32); EXPECT_EQ(stats.writeDQSStats.ones_to_zeroes, 32); EXPECT_EQ(stats.writeDQSStats.zeroes, 32); EXPECT_EQ(stats.writeDQSStats.ones, 32); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, 32); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, 32); EXPECT_EQ(stats.readDQSStats.zeroes, 32); EXPECT_EQ(stats.readDQSStats.ones, 32); // PrePostamble auto prepos = stats.rank_total[0].prepos; EXPECT_EQ(prepos.readSeamless, 0); EXPECT_EQ(prepos.writeSeamless, 0); EXPECT_EQ(prepos.readMerged, 0); EXPECT_EQ(prepos.readMergedTime, 0); EXPECT_EQ(prepos.writeMerged, 0); EXPECT_EQ(prepos.writeMergedTime, 0); } ================================================ FILE: tests/tests_drampower/interface/test_togglingrate_lpddr4.cpp ================================================ #include #include #include #include #include #include "DRAMPower/standards/lpddr4/LPDDR4.h" #include "DRAMPower/memspec/MemSpecLPDDR4.h" #include #include "DRAMPower/standards/lpddr4/interface_calculation_LPDDR4.h" #include "DRAMPower/data/energy.h" #include "DRAMPower/data/stats.h" using namespace DRAMPower; using namespace DRAMUtils::Config; #define SZ_BITS(x) sizeof(x)*8 // burst length = 16 for x8 devices static constexpr uint8_t wr_data[] = { 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, }; // burst length = 16 for x8 devices static constexpr uint8_t rd_data[] = { 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; class LPDDR4_TogglingRate_Tests : public ::testing::Test { public: void SetUp() override { test_patterns.push_back( { // Pattern 1 {0, CmdType::ACT, {1, 0, 0, 2}}, {5, CmdType::WR, {1, 0, 0, 0, 16}, nullptr, datasize_bits}, {14, CmdType::RD, {1, 0, 0, 0, 16}, nullptr, datasize_bits}, {23, CmdType::PRE, {1, 0, 0, 2}}, {26, CmdType::END_OF_SIMULATION}, }); initSpec(); } void initSpec() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); spec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); } void initDDR(const ToggleRateDefinition& trd) { ddr = std::make_unique(*spec, config::SimConfig{trd}); } void runCommands(const std::vector &commands) { for (const Command &command : commands) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); } } std::vector> test_patterns; std::unique_ptr spec; std::unique_ptr ddr; uint_fast16_t datasize_bits = 32 * 8; // 32 bytes }; TEST_F(LPDDR4_TogglingRate_Tests, Pattern_0_LH) { // Setup toggling rate double togglingRateRead = 0.7; double togglingRateWrite = 0.3; double dutyCycleRead = 0.6; double dutyCycleWrite = 0.4; TogglingRateIdlePattern idlePatternRead = TogglingRateIdlePattern::L; TogglingRateIdlePattern idlePatternWrite = TogglingRateIdlePattern::H; auto trd = ToggleRateDefinition { togglingRateRead, // togglingRateRead togglingRateWrite, // togglingRateWrite dutyCycleRead, // dutyCycleRead dutyCycleWrite, // dutyCycleWrite idlePatternRead, // idlePatternRead idlePatternWrite // idlePatternWrite }; initDDR(trd); // Run commands runCommands(test_patterns.at(0)); // SZ_BITS: 256, width: 8 -> Burstlength: 32 (datarate bus) // 0: ACT, 5: WR, 14: RD, 23: PRE, 26: EOS // Read bus: idle: L // 0 to 14 idle // 14 to 26 toggle -> toggle cut off by EOS // idle: 14 zeroes, toggle: 12 (datarate clock) // idle: 28 zeroes, toggle: 24 (datarate bus) // Write bus: idle: H // 0 to 5 idle // 5 to 21 toggle // 21 to 26 idle // idle: 10 ones, toggle: 16 (datarate clock) // idle: 20 ones, toggle: 32 (datarate bus) uint64_t toggles_read = 24; uint64_t toggles_write = 32; uint64_t idleread_ones = 0; uint64_t idleread_zeroes = 28; uint64_t idlewrite_ones = 20; uint64_t idlewrite_zeroes = 0; SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Data bus // Read bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.ones, (idleread_ones + static_cast(std::floor(dutyCycleRead * toggles_read))) * spec->bitWidth); // 112 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.zeroes, (idleread_zeroes + static_cast(std::floor((1 - dutyCycleRead) * toggles_read))) * spec->bitWidth); // 296 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.ones_to_zeroes, std::floor((togglingRateRead / 2) * toggles_read) * spec->bitWidth); // 64 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.zeroes_to_ones, static_cast(std::floor((togglingRateRead / 2) * toggles_read)) * spec->bitWidth); // 64 // Write bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.ones, (idlewrite_ones + static_cast(std::floor(dutyCycleWrite * toggles_write))) * spec->bitWidth); // 256 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.zeroes, (idlewrite_zeroes + static_cast(std::floor((1 - dutyCycleWrite) * toggles_write))) * spec->bitWidth); // 152 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.ones_to_zeroes, std::floor((togglingRateWrite / 2) * toggles_write) * spec->bitWidth); // 32 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.zeroes_to_ones, static_cast(std::floor((togglingRateWrite / 2) * toggles_write)) * spec->bitWidth); // 32 // Clock (see test_interface_lpddr4) EXPECT_EQ(stats.clockStats.ones, 52); EXPECT_EQ(stats.clockStats.zeroes, 52); EXPECT_EQ(stats.clockStats.ones_to_zeroes, 52); EXPECT_EQ(stats.clockStats.zeroes_to_ones, 52); // Command bus (see test_interface_lpddr4) EXPECT_EQ(stats.commandBus.ones, 15); EXPECT_EQ(stats.commandBus.zeroes, 141); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 14); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 14); // DQs (see test_interface_lpddr4) int number_of_cycles = (datasize_bits / spec->bitWidth); uint_fast8_t scale = 1 * 2; // Differential_Pairs * 2(pairs of 2) // f(t) = t / 2; int DQS_ones = scale * (number_of_cycles / 2); // scale * (cycles / 2 int DQS_zeros = DQS_ones; int DQS_zeros_to_ones = DQS_ones; int DQS_ones_to_zeros = DQS_zeros; EXPECT_EQ(stats.writeDQSStats.ones, DQS_ones); EXPECT_EQ(stats.writeDQSStats.zeroes, DQS_zeros); EXPECT_EQ(stats.writeDQSStats.ones_to_zeroes, DQS_zeros_to_ones); EXPECT_EQ(stats.writeDQSStats.zeroes_to_ones, DQS_ones_to_zeros); EXPECT_EQ(stats.readDQSStats.ones, DQS_ones); EXPECT_EQ(stats.readDQSStats.zeroes, DQS_zeros); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, DQS_zeros_to_ones); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, DQS_ones_to_zeros); // PrePostamble auto prepos = stats.rank_total[0].prepos; EXPECT_EQ(prepos.readSeamless, 0); EXPECT_EQ(prepos.writeSeamless, 0); EXPECT_EQ(prepos.readMerged, 0); EXPECT_EQ(prepos.readMergedTime, 0); EXPECT_EQ(prepos.writeMerged, 0); EXPECT_EQ(prepos.writeMergedTime, 0); } TEST_F(LPDDR4_TogglingRate_Tests, Pattern_0_HZ) { // Setup toggling rate double togglingRateRead = 0.7; double togglingRateWrite = 0.3; double dutyCycleRead = 0.6; double dutyCycleWrite = 0.4; TogglingRateIdlePattern idlePatternRead = TogglingRateIdlePattern::H; TogglingRateIdlePattern idlePatternWrite = TogglingRateIdlePattern::Z; auto trd = ToggleRateDefinition { togglingRateRead, // togglingRateRead togglingRateWrite, // togglingRateWrite dutyCycleRead, // dutyCycleRead dutyCycleWrite, // dutyCycleWrite idlePatternRead, // idlePatternRead idlePatternWrite // idlePatternWrite }; initDDR(trd); // Run commands runCommands(test_patterns[0]); // SZ_BITS: 256, width: 8 -> Burstlength: 32 (datarate bus) // 0: ACT, 5: WR, 14: RD, 23: PRE, 26: EOS // Read bus: idle: H // 0 to 14 idle // 14 to 26 toggle -> toggle cut off by EOS // idle: 14 ones, toggle: 12 (datarate clock) // idle: 28 ones, toggle: 24 (datarate bus) // Write bus: idle: H // 0 to 5 idle // 5 to 21 toggle // 21 to 26 idle // idle: 0 ones/zeroes, toggle: 16 (datarate clock) // idle: 0 ones/zeroes, toggle: 32 (datarate bus) uint64_t toggles_read = 24; uint64_t toggles_write = 32; uint64_t idleread_ones = 28; uint64_t idleread_zeroes = 0; uint64_t idlewrite_ones = 0; uint64_t idlewrite_zeroes = 0; SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Data bus // Read bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.ones, (idleread_ones + static_cast(std::floor(dutyCycleRead * toggles_read))) * spec->bitWidth); // 336 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.zeroes, (idleread_zeroes + static_cast(std::floor((1 - dutyCycleRead) * toggles_read))) * spec->bitWidth); // 72 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.ones_to_zeroes, std::floor((togglingRateRead / 2) * toggles_read) * spec->bitWidth); // 64 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.zeroes_to_ones, static_cast(std::floor((togglingRateRead / 2) * toggles_read)) * spec->bitWidth); // 64 // Write bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.ones, (idlewrite_ones + static_cast(std::floor(dutyCycleWrite * toggles_write))) * spec->bitWidth); // 96 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.zeroes, (idlewrite_zeroes + static_cast(std::floor((1 - dutyCycleWrite) * toggles_write))) * spec->bitWidth); // 152 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.ones_to_zeroes, std::floor((togglingRateWrite / 2) * toggles_write) * spec->bitWidth); // 32 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.zeroes_to_ones, static_cast(std::floor((togglingRateWrite / 2) * toggles_write)) * spec->bitWidth); // 32 } // Tests for power consumption (given a known SimulationStats) class LPDDR4_TogglingRateEnergy_Tests : public ::testing::Test { public: LPDDR4_TogglingRateEnergy_Tests() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr4.json"); spec = std::make_unique(DRAMPower::MemSpecLPDDR4::from_memspec(*data)); t_CK = spec->memTimingSpec.tCK; voltage = spec->vddq; // Change impedances to different values from each other spec->memImpedanceSpec.ca_R_eq = 2; spec->memImpedanceSpec.ck_R_eq = 3; spec->memImpedanceSpec.rdqs_R_eq = 4; spec->memImpedanceSpec.wdqs_R_eq = 5; spec->memImpedanceSpec.rdq_R_eq = 6; spec->memImpedanceSpec.wdq_R_eq = 7; spec->memImpedanceSpec.ca_dyn_E = 8; spec->memImpedanceSpec.ck_dyn_E = 9; spec->memImpedanceSpec.rdqs_dyn_E = 10; spec->memImpedanceSpec.wdqs_dyn_E = 11; spec->memImpedanceSpec.rdq_dyn_E = 12; spec->memImpedanceSpec.wdq_dyn_E = 13; io_calc = std::make_unique(*spec); } std::unique_ptr spec; double t_CK; double voltage; std::unique_ptr io_calc; }; TEST_F(LPDDR4_TogglingRateEnergy_Tests, DQ_Energy) { // TODO implement test with new interface model SimulationStats stats; stats.togglingStats = TogglingStats(); stats.togglingStats.read.ones = 7; stats.togglingStats.read.zeroes = 11; stats.togglingStats.read.zeroes_to_ones = 19; stats.togglingStats.read.ones_to_zeroes = 39; stats.togglingStats.write.ones = 43; stats.togglingStats.write.zeroes = 59; stats.togglingStats.write.zeroes_to_ones = 13; stats.togglingStats.write.ones_to_zeroes = 17; // Controller -> write power // Dram -> read power double expected_static_controller = stats.togglingStats.write.ones * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.wdq_R_eq; double expected_static_dram = stats.togglingStats.read.ones * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.rdq_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = stats.togglingStats.write.zeroes_to_ones * spec->memImpedanceSpec.wdq_dyn_E; double expected_dynamic_dram = stats.togglingStats.read.zeroes_to_ones * spec->memImpedanceSpec.rdq_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); } TEST_F(LPDDR4_TogglingRate_Tests, Pattern_1) { // Setup toggling rate double togglingRateRead = 0.7; double togglingRateWrite = 0.3; double dutyCycleRead = 0.6; double dutyCycleWrite = 0.4; TogglingRateIdlePattern idlePatternRead = TogglingRateIdlePattern::L; TogglingRateIdlePattern idlePatternWrite = TogglingRateIdlePattern::H; auto trd = ToggleRateDefinition { togglingRateRead, // togglingRateRead togglingRateWrite, // togglingRateWrite dutyCycleRead, // dutyCycleRead dutyCycleWrite, // dutyCycleWrite idlePatternRead, // idlePatternRead idlePatternWrite // idlePatternWrite }; initDDR(trd); // Run commands ddr->doCommand({0, CmdType::ACT, {1, 0, 0, 2}}); ddr->doCommand({5, CmdType::WR, {1, 0, 0, 0, 16}, wr_data, SZ_BITS(wr_data)}); ddr->doCommand({14, CmdType::RD, {1, 0, 0, 0, 16}, rd_data, SZ_BITS(rd_data)}); ddr->doCommand({23, CmdType::PRE, {1, 0, 0, 2}}); ddr->doCommand({30, CmdType::ACT, {1, 0, 0, 2}}); ddr->doCommand({35, CmdType::WR, {1, 0, 0, 0, 16}, nullptr, 16*8}); // burst length = 16 ddr->doCommand({44, CmdType::RD, {1, 0, 0, 0, 16}, nullptr, 16*8}); // burst length = 16 ddr->doCommand({53, CmdType::PRE, {1, 0, 0, 2}}); ddr->doCommand({56, CmdType::END_OF_SIMULATION}); SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Toggling rate uint64_t toggles_read = 2 * 16; uint64_t idleread_ones = 0; uint64_t idleread_zeroes = 2 * 56 - toggles_read; // TogglingRateIdlePattern::L uint64_t toggles_write = 2 * 16; uint64_t idlewrite_ones = 2 * 56 - toggles_write; // TogglingRateIdlePattern::H uint64_t idlewrite_zeroes = 0; // Read bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.ones, (idleread_ones + static_cast(std::floor(dutyCycleRead * toggles_read))) * spec->bitWidth); // 112 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.zeroes, (idleread_zeroes + static_cast(std::floor((1 - dutyCycleRead) * toggles_read))) * spec->bitWidth); // 296 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.ones_to_zeroes, std::floor((togglingRateRead / 2) * toggles_read) * spec->bitWidth); // 64 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.zeroes_to_ones, static_cast(std::floor((togglingRateRead / 2) * toggles_read)) * spec->bitWidth); // 64 // Write bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.ones, (idlewrite_ones + static_cast(std::floor(dutyCycleWrite * toggles_write))) * spec->bitWidth); // 256 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.zeroes, (idlewrite_zeroes + static_cast(std::floor((1 - dutyCycleWrite) * toggles_write))) * spec->bitWidth); // 152 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.ones_to_zeroes, std::floor((togglingRateWrite / 2) * toggles_write) * spec->bitWidth); // 32 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.zeroes_to_ones, static_cast(std::floor((togglingRateWrite / 2) * toggles_write)) * spec->bitWidth); // 32 // Data bus EXPECT_EQ(stats.readBus.zeroes_to_ones, 0); EXPECT_EQ(stats.readBus.ones_to_zeroes, 0); EXPECT_EQ(stats.readBus.zeroes, 0); EXPECT_EQ(stats.readBus.ones, 0); EXPECT_EQ(stats.writeBus.zeroes_to_ones, 0); EXPECT_EQ(stats.writeBus.ones_to_zeroes, 0); EXPECT_EQ(stats.writeBus.zeroes, 0); EXPECT_EQ(stats.writeBus.ones, 0); // Clock (see test_interface_lpddr4) EXPECT_EQ(stats.clockStats.zeroes_to_ones, 112); EXPECT_EQ(stats.clockStats.ones_to_zeroes, 112); EXPECT_EQ(stats.clockStats.zeroes, 112); EXPECT_EQ(stats.clockStats.ones, 112); // Command bus (see test_interface_lpddr4) EXPECT_EQ(stats.commandBus.zeroes_to_ones, 28); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 28); EXPECT_EQ(stats.commandBus.zeroes, 306); EXPECT_EQ(stats.commandBus.ones, 30); // DQs (see test_interface_lpddr4) EXPECT_EQ(stats.writeDQSStats.zeroes_to_ones, 32); EXPECT_EQ(stats.writeDQSStats.ones_to_zeroes, 32); EXPECT_EQ(stats.writeDQSStats.zeroes, 32); EXPECT_EQ(stats.writeDQSStats.ones, 32); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, 32); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, 32); EXPECT_EQ(stats.readDQSStats.zeroes, 32); EXPECT_EQ(stats.readDQSStats.ones, 32); // PrePostamble auto prepos = stats.rank_total[0].prepos; EXPECT_EQ(prepos.readSeamless, 0); EXPECT_EQ(prepos.writeSeamless, 0); EXPECT_EQ(prepos.readMerged, 0); EXPECT_EQ(prepos.readMergedTime, 0); EXPECT_EQ(prepos.writeMerged, 0); EXPECT_EQ(prepos.writeMergedTime, 0); } ================================================ FILE: tests/tests_drampower/interface/test_togglingrate_lpddr5.cpp ================================================ #include #include #include #include #include #include "DRAMPower/standards/lpddr5/LPDDR5.h" #include "DRAMPower/memspec/MemSpecLPDDR5.h" #include "DRAMPower/standards/lpddr5/interface_calculation_LPDDR5.h" #include "DRAMPower/data/energy.h" #include "DRAMPower/data/stats.h" using namespace DRAMPower; using namespace DRAMUtils::Config; #define SZ_BITS(x) sizeof(x)*8 // burst length = 16 for x8 devices static constexpr uint8_t wr_data[] = { 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, }; // burst length = 16 for x8 devices static constexpr uint8_t rd_data[] = { 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; class LPDDR5_TogglingRate_Tests : public ::testing::Test { public: void SetUp() override { test_patterns.push_back({ {0, CmdType::ACT, {1, 0, 0, 2}}, {3, CmdType::WR, {1, 0, 0, 0, 8}, nullptr, datasize_bits}, {12, CmdType::RD, {1, 0, 0, 0, 8}, nullptr, datasize_bits}, {21, CmdType::PRE, {1, 0, 0, 2}}, {24, CmdType::END_OF_SIMULATION}, }); initSpec(); } void initSpec() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); spec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); } void initDDR(const ToggleRateDefinition& trd) { ddr = std::make_unique(*spec, config::SimConfig{trd}); } void runCommands(const std::vector &commands) { for (const Command &command : commands) { ddr->doCoreCommand(command); ddr->doInterfaceCommand(command); } } std::vector> test_patterns; std::unique_ptr spec; std::unique_ptr ddr; uint_fast16_t datasize_bits = 32 * 8; // 32 bytes }; TEST_F(LPDDR5_TogglingRate_Tests, Pattern_0_LH) { // Setup toggling rate double togglingRateRead = 0.7; double togglingRateWrite = 0.3; double dutyCycleRead = 0.6; double dutyCycleWrite = 0.4; TogglingRateIdlePattern idlePatternRead = TogglingRateIdlePattern::L; TogglingRateIdlePattern idlePatternWrite = TogglingRateIdlePattern::H; auto trd = ToggleRateDefinition { togglingRateRead, // togglingRateRead togglingRateWrite, // togglingRateWrite dutyCycleRead, // dutyCycleRead dutyCycleWrite, // dutyCycleWrite idlePatternRead, // idlePatternRead idlePatternWrite // idlePatternWrite }; initDDR(trd); // Run commands runCommands(test_patterns.at(0)); // SZ_BITS: 256, width: 8 -> Burstlength: 32 (datarate bus) // 0: ACT, 3: WR, 12: RD, 21: PRE, 24: EOS // Read bus: idle: L // 0 to 12 idle // 12 to 24 toggle -> toggle cut off by EOS // idle: 12 zeroes, toggle: 12 (datarate clock) // idle: 24 zeroes, toggle: 24 (datarate bus) // Write bus: idle: H // 0 to 3 idle // 3 to 19 toggle // 19 to 24 idle // idle: 8 ones, toggle: 16 (datarate clock) // idle: 16 ones, toggle: 32 (datarate bus) uint64_t toggles_read = 24; uint64_t toggles_write = 32; uint64_t idleread_ones = 0; uint64_t idleread_zeroes = 24; uint64_t idlewrite_ones = 16; uint64_t idlewrite_zeroes = 0; SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Data bus // Read bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.ones, (idleread_ones + static_cast(std::floor(dutyCycleRead * toggles_read))) * spec->bitWidth); // 112 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.zeroes, (idleread_zeroes + static_cast(std::floor((1 - dutyCycleRead) * toggles_read))) * spec->bitWidth); // 264 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.ones_to_zeroes, std::floor((togglingRateRead / 2) * toggles_read) * spec->bitWidth); // 64 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.zeroes_to_ones, static_cast(std::floor((togglingRateRead / 2) * toggles_read)) * spec->bitWidth); // 64 // Write bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.ones, (idlewrite_ones + static_cast(std::floor(dutyCycleWrite * toggles_write))) * spec->bitWidth); // 224 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.zeroes, (idlewrite_zeroes + static_cast(std::floor((1 - dutyCycleWrite) * toggles_write))) * spec->bitWidth); // 152 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.ones_to_zeroes, std::floor((togglingRateWrite / 2) * toggles_write) * spec->bitWidth); // 32 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.zeroes_to_ones, static_cast(std::floor((togglingRateWrite / 2) * toggles_write)) * spec->bitWidth); // 32 // Clock (see test_interface_lpddr5) EXPECT_EQ(stats.clockStats.ones, 48); EXPECT_EQ(stats.clockStats.zeroes, 48); EXPECT_EQ(stats.clockStats.ones_to_zeroes, 48); EXPECT_EQ(stats.clockStats.zeroes_to_ones, 48); // Command bus (see test_interface_lpddr5) EXPECT_EQ(stats.commandBus.ones, 19); EXPECT_EQ(stats.commandBus.zeroes, 317); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 15); EXPECT_EQ(stats.commandBus.zeroes_to_ones, 15); // DQs (see test_interface_lpddr5) int number_of_cycles = (datasize_bits / spec->bitWidth); uint_fast8_t scale = 1 * 2; // Differential_Pairs * 2(pairs of 2) // f(t) = t / 2; int DQS_ones = scale * (number_of_cycles / 2); // scale * (cycles / 2) int DQS_zeros = DQS_ones; int DQS_zeros_to_ones = DQS_ones; int DQS_ones_to_zeros = DQS_zeros; EXPECT_EQ(stats.readDQSStats.ones, DQS_ones); EXPECT_EQ(stats.readDQSStats.zeroes, DQS_zeros); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, DQS_zeros_to_ones); EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, DQS_ones_to_zeros); // PrePostamble auto prepos = stats.rank_total[0].prepos; EXPECT_EQ(prepos.readSeamless, 0); EXPECT_EQ(prepos.writeSeamless, 0); EXPECT_EQ(prepos.readMerged, 0); EXPECT_EQ(prepos.readMergedTime, 0); EXPECT_EQ(prepos.writeMerged, 0); EXPECT_EQ(prepos.writeMergedTime, 0); } TEST_F(LPDDR5_TogglingRate_Tests, Pattern_0_HZ) { // Setup toggling rate double togglingRateRead = 0.7; double togglingRateWrite = 0.3; double dutyCycleRead = 0.6; double dutyCycleWrite = 0.4; TogglingRateIdlePattern idlePatternRead = TogglingRateIdlePattern::H; TogglingRateIdlePattern idlePatternWrite = TogglingRateIdlePattern::Z; auto trd = ToggleRateDefinition { togglingRateRead, // togglingRateRead togglingRateWrite, // togglingRateWrite dutyCycleRead, // dutyCycleRead dutyCycleWrite, // dutyCycleWrite idlePatternRead, // idlePatternRead idlePatternWrite // idlePatternWrite }; initDDR(trd); // Run commands runCommands(test_patterns[0]); // SZ_BITS: 256, width: 8 -> Burstlength: 32 (datarate bus) // 0: ACT, 3: WR, 12: RD, 21: PRE, 24: EOS // Read bus: idle: H // 0 to 12 idle // 12 to 24 toggle -> toggle cut off by EOS // idle: 12 ones, toggle: 12 (datarate clock) // idle: 24 ones, toggle: 24 (datarate bus) // Write bus: idle: Z // 0 to 3 idle // 3 to 19 toggle // 19 to 24 idle // idle: 0 ones/zeroes, toggle: 16 (datarate clock) // idle: 0 ones/zeroes, toggle: 32 (datarate bus) uint64_t toggles_read = 24; uint64_t toggles_write = 32; uint64_t idleread_ones = 24; uint64_t idleread_zeroes = 0; uint64_t idlewrite_ones = 0; uint64_t idlewrite_zeroes = 0; SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Data bus // Read bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.ones, (idleread_ones + static_cast(std::floor(dutyCycleRead * toggles_read))) * spec->bitWidth); // 304 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.zeroes, (idleread_zeroes + static_cast(std::floor((1 - dutyCycleRead) * toggles_read))) * spec->bitWidth); // 72 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.ones_to_zeroes, std::floor((togglingRateRead / 2) * toggles_read) * spec->bitWidth); // 64 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.zeroes_to_ones, static_cast(std::floor((togglingRateRead / 2) * toggles_read)) * spec->bitWidth); // 64 // Write bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.ones, (idlewrite_ones + static_cast(std::floor(dutyCycleWrite * toggles_write))) * spec->bitWidth); // 96 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.zeroes, (idlewrite_zeroes + static_cast(std::floor((1 - dutyCycleWrite) * toggles_write))) * spec->bitWidth); // 152 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.ones_to_zeroes, std::floor((togglingRateWrite / 2) * toggles_write) * spec->bitWidth); // 32 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.zeroes_to_ones, static_cast(std::floor((togglingRateWrite / 2) * toggles_write)) * spec->bitWidth); // 32 } // Tests for power consumption (given a known SimulationStats) class LPDDR5_TogglingRateEnergy_Tests : public ::testing::Test { public: LPDDR5_TogglingRateEnergy_Tests() { auto data = DRAMUtils::parse_memspec_from_file(std::filesystem::path(TEST_RESOURCE_DIR) / "lpddr5.json"); spec = std::make_unique(DRAMPower::MemSpecLPDDR5::from_memspec(*data)); t_CK = spec->memTimingSpec.tCK; voltage = spec->vddq; // Change impedances to different values from each other spec->memImpedanceSpec.ca_R_eq = 2; spec->memImpedanceSpec.ck_R_eq = 3; spec->memImpedanceSpec.rdqs_R_eq = 4; spec->memImpedanceSpec.wck_R_eq = 5; spec->memImpedanceSpec.rdq_R_eq = 6; spec->memImpedanceSpec.wdq_R_eq = 7; spec->memImpedanceSpec.ca_dyn_E = 8; spec->memImpedanceSpec.ck_dyn_E = 9; spec->memImpedanceSpec.rdqs_dyn_E = 10; spec->memImpedanceSpec.wck_dyn_E = 11; spec->memImpedanceSpec.rdq_dyn_E = 12; spec->memImpedanceSpec.wdq_dyn_E = 13; io_calc = std::make_unique(*spec); } std::unique_ptr spec; double t_CK; double voltage; std::unique_ptr io_calc; }; TEST_F(LPDDR5_TogglingRateEnergy_Tests, DQ_Energy) { SimulationStats stats; stats.togglingStats = TogglingStats(); stats.togglingStats.read.ones = 7; stats.togglingStats.read.zeroes = 11; stats.togglingStats.read.zeroes_to_ones = 19; stats.togglingStats.read.ones_to_zeroes = 39; stats.togglingStats.write.ones = 43; stats.togglingStats.write.zeroes = 59; stats.togglingStats.write.zeroes_to_ones = 13; stats.togglingStats.write.ones_to_zeroes = 17; // Controller -> write power // Dram -> read power double expected_static_controller = stats.togglingStats.write.ones * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.wdq_R_eq; double expected_static_dram = stats.togglingStats.read.ones * voltage * voltage * (0.5 * t_CK) / spec->memImpedanceSpec.rdq_R_eq; // Dynamic power is consumed on 0 -> 1 transition double expected_dynamic_controller = stats.togglingStats.write.zeroes_to_ones * spec->memImpedanceSpec.wdq_dyn_E; double expected_dynamic_dram = stats.togglingStats.read.zeroes_to_ones * spec->memImpedanceSpec.rdq_dyn_E; interface_energy_info_t result = io_calc->calculateEnergy(stats); EXPECT_DOUBLE_EQ(result.controller.staticEnergy, expected_static_controller); EXPECT_DOUBLE_EQ(result.controller.dynamicEnergy, expected_dynamic_controller); EXPECT_DOUBLE_EQ(result.dram.staticEnergy, expected_static_dram); EXPECT_DOUBLE_EQ(result.dram.dynamicEnergy, expected_dynamic_dram); } TEST_F(LPDDR5_TogglingRate_Tests, Pattern_1) { // Setup toggling rate double togglingRateRead = 0.7; double togglingRateWrite = 0.3; double dutyCycleRead = 0.6; double dutyCycleWrite = 0.4; TogglingRateIdlePattern idlePatternRead = TogglingRateIdlePattern::L; TogglingRateIdlePattern idlePatternWrite = TogglingRateIdlePattern::H; auto trd = ToggleRateDefinition { togglingRateRead, // togglingRateRead togglingRateWrite, // togglingRateWrite dutyCycleRead, // dutyCycleRead dutyCycleWrite, // dutyCycleWrite idlePatternRead, // idlePatternRead idlePatternWrite // idlePatternWrite }; initDDR(trd); // Run commands ddr->doCommand({0, CmdType::ACT, {1, 0, 0, 2}}); ddr->doCommand({5, CmdType::WR, {1, 0, 0, 0, 8}, wr_data, SZ_BITS(wr_data)}); ddr->doCommand({14, CmdType::RD, {1, 0, 0, 0, 8}, rd_data, SZ_BITS(rd_data)}); ddr->doCommand({23, CmdType::PRE, {1, 0, 0, 2}}); ddr->doCommand({30, CmdType::ACT, {1, 0, 0, 2}}); ddr->doCommand({35, CmdType::WR, {1, 0, 0, 0, 8}, nullptr, 16*8}); // burst length = 16 ddr->doCommand({44, CmdType::RD, {1, 0, 0, 0, 8}, nullptr, 16*8}); // burst length = 16 ddr->doCommand({53, CmdType::PRE, {1, 0, 0, 2}}); ddr->doCommand({56, CmdType::END_OF_SIMULATION}); SimulationStats stats = ddr->getStats(); EXPECT_EQ(spec->dataRate, 2); // Toggling rate uint64_t toggles_read = 2 * 16; uint64_t idleread_ones = 0; uint64_t idleread_zeroes = 2 * 56 - toggles_read; // TogglingRateIdlePattern::L uint64_t toggles_write = 2 * 16; uint64_t idlewrite_ones = 2 * 56 - toggles_write; // TogglingRateIdlePattern::H uint64_t idlewrite_zeroes = 0; // Read bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.ones, (idleread_ones + static_cast(std::floor(dutyCycleRead * toggles_read))) * spec->bitWidth); // 112 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.read.zeroes, (idleread_zeroes + static_cast(std::floor((1 - dutyCycleRead) * toggles_read))) * spec->bitWidth); // 296 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.ones_to_zeroes, std::floor((togglingRateRead / 2) * toggles_read) * spec->bitWidth); // 64 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.read.zeroes_to_ones, static_cast(std::floor((togglingRateRead / 2) * toggles_read)) * spec->bitWidth); // 64 // Write bus // ones: {idle + floor[duty_cycle * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.ones, (idlewrite_ones + static_cast(std::floor(dutyCycleWrite * toggles_write))) * spec->bitWidth); // 256 // zeroes: {idle + floor[(1 - duty_cycle) * toggling_count]} * width EXPECT_EQ(stats.togglingStats.write.zeroes, (idlewrite_zeroes + static_cast(std::floor((1 - dutyCycleWrite) * toggles_write))) * spec->bitWidth); // 152 // onestozeroes: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.ones_to_zeroes, std::floor((togglingRateWrite / 2) * toggles_write) * spec->bitWidth); // 32 // zeroestoones: floor[(toggle_rate / 2) * toggling_count] * width EXPECT_EQ(stats.togglingStats.write.zeroes_to_ones, static_cast(std::floor((togglingRateWrite / 2) * toggles_write)) * spec->bitWidth); // 32 // Data bus EXPECT_EQ(stats.readBus.zeroes_to_ones, 0); EXPECT_EQ(stats.readBus.ones_to_zeroes, 0); EXPECT_EQ(stats.readBus.zeroes, 0); EXPECT_EQ(stats.readBus.ones, 0); EXPECT_EQ(stats.writeBus.zeroes_to_ones, 0); EXPECT_EQ(stats.writeBus.ones_to_zeroes, 0); EXPECT_EQ(stats.writeBus.zeroes, 0); EXPECT_EQ(stats.writeBus.ones, 0); // Clock (see test_interface_lpddr5) EXPECT_EQ(stats.clockStats.zeroes_to_ones, 112); EXPECT_EQ(stats.clockStats.ones_to_zeroes, 112); EXPECT_EQ(stats.clockStats.zeroes, 112); EXPECT_EQ(stats.clockStats.ones, 112); // Command bus (see test_interface_lpddr5) EXPECT_EQ(stats.commandBus.zeroes_to_ones, 30); EXPECT_EQ(stats.commandBus.ones_to_zeroes, 30); EXPECT_EQ(stats.commandBus.zeroes, 746); EXPECT_EQ(stats.commandBus.ones, 38); // DQs (see test_interface_lpddr5) EXPECT_EQ(stats.readDQSStats.zeroes_to_ones, 32); EXPECT_EQ(stats.readDQSStats.ones_to_zeroes, 32); EXPECT_EQ(stats.readDQSStats.zeroes, 32); EXPECT_EQ(stats.readDQSStats.ones, 32); // PrePostamble auto prepos = stats.rank_total[0].prepos; EXPECT_EQ(prepos.readSeamless, 0); EXPECT_EQ(prepos.writeSeamless, 0); EXPECT_EQ(prepos.readMerged, 0); EXPECT_EQ(prepos.readMergedTime, 0); EXPECT_EQ(prepos.writeMerged, 0); EXPECT_EQ(prepos.writeMergedTime, 0); } ================================================ FILE: tests/tests_drampower/resources/cliconfig.json ================================================ { "useToggleRate": true, "simconfig": { "toggleRateDefinition": { "togglingRateRead": 0.5, "togglingRateWrite": 0.5, "dutyCycleRead": 0.5, "dutyCycleWrite": 0.5, "idlePatternRead": "L", "idlePatternWrite": "L" } } } ================================================ FILE: tests/tests_drampower/resources/ddr4.csv ================================================ 0 ,ACT,0,0,0,0,0 15,WR,0,0,0,0,0,0x0102030405060708 20,RD,0,0,0,0,0,0102030405060708 40,PRE,0,0,0,0,0 45,END,0,0,0,0,0 ================================================ FILE: tests/tests_drampower/resources/ddr4.json ================================================ { "memspec": { "memoryId": "ddr4", "memoryType": "DDR4", "memarchitecturespec": { "nbrOfChannels": 0, "nbrOfColumns": 1024, "nbrOfRows": 65536, "width": 8, "burstLength": 8, "maxBurstLength": 8, "dataRate": 2, "nbrOfBankGroups": 2, "nbrOfBanks": 16, "nbrOfRanks": 1, "nbrOfDevices": 1, "RefMode": 1 }, "mempowerspec": { "vdd": 1.2, "idd0": 56.25e-3, "idd2n": 33.75e-3, "idd3n": 39.5e-3, "idd4r": 157.5e-3, "idd4w": 135.0e-3, "idd6n": 20.20e-3, "idd2p": 17.0e-3, "idd3p": 22.5e-3, "idd5B": 118.0e-3, "idd5F2": 118.0e-3, "idd5F4": 118.0e-3, "vpp": 1.2, "ipp0": 0, "ipp2n": 0, "ipp3n": 0, "ipp4r": 0, "ipp4w": 0, "ipp6n": 0, "ipp2p": 0, "ipp3p": 0, "ipp5B": 0, "ipp5F2": 0, "ipp5F4": 0, "vddq": 1.2, "iBeta_vdd": 56.25e-3, "iBeta_vpp": 0 }, "memtimingspec": { "tCK": 0.76923076923076923076923076923077e-9, "CKE": 0, "CKESR": 0, "RAS": 10, "RC": 0, "RCD": 13, "RL": 0, "RPRE": 0, "RTP": 5, "WL": 5, "WPRE": 0, "WR": 5, "XP": 0, "XS": 0, "REFM": 0, "REFI": 0, "RFC1": 25, "RFC2": 0, "RFC4": 0, "RP": 10, "DQSCK": 0, "CCD_S": 0, "CCD_L": 0, "FAW": 0, "RRD_S": 0, "RRD_L": 0, "WTR_S": 0, "WTR_L": 0, "XPDLL": 0, "XSDLL": 0, "AL": 0, "ACTPDEN": 0, "PRPDEN": 0, "REFPDEN": 0, "RTRS": 0 }, "bankwisespec":{ "factRho":0.5 }, "memimpedancespec": { "ck_termination": true, "ck_R_eq": 1e6, "ck_dyn_E": 1e-12, "ca_termination": true, "ca_R_eq": 1e6, "ca_dyn_E": 1e-12, "rdq_termination": true, "rdq_R_eq": 1e6, "rdq_dyn_E": 1e-12, "wdq_termination": true, "wdq_R_eq": 1e6, "wdq_dyn_E": 1e-12, "wdqs_termination": true, "wdqs_R_eq": 1e6, "wdqs_dyn_E": 1e-12, "rdqs_termination": true, "rdqs_R_eq": 1e6, "rdqs_dyn_E": 1e-12, "wdbi_termination": true, "wdbi_R_eq": 1e6, "wdbi_dyn_E": 1e-12, "rdbi_termination": true, "rdbi_R_eq": 1e6, "rdbi_dyn_E": 1e-12 }, "prepostamble":{ "read_zeroes": 2.5, "write_zeroes": 2.5, "read_ones": 2.5, "write_ones": 2.5, "read_zeroes_to_ones": 2, "write_zeroes_to_ones": 2, "read_ones_to_zeroes": 2, "write_ones_to_zeroes": 2, "readMinTccd": 3, "writeMinTccd": 3 } } } ================================================ FILE: tests/tests_drampower/resources/ddr5.csv ================================================ 0 ,ACT,0,0,0,0,0 15,PRE,0,0,0,0,0 15,END,0,0,0,0,0 ================================================ FILE: tests/tests_drampower/resources/ddr5.json ================================================ { "memspec": { "memoryId": "ddr5", "memoryType": "DDR5", "memarchitecturespec": { "nbrOfChannels": 0, "nbrOfColumns": 1024, "nbrOfRows": 65536, "width": 8, "burstLength": 8, "maxBurstLength": 8, "dataRate": 2, "nbrOfBankGroups": 2, "nbrOfBanks": 16, "nbrOfRanks": 1, "nbrOfDevices": 1, "nbrOfDIMMRanks": 0, "nbrOfPhysicalRanks": 0, "nbrOfLogicalRanks": 0, "cmdMode": 0, "RefMode": 1, "RAAIMT": 0, "RAAMMT": 0, "RAADEC": 0 }, "mempowerspec": { "vdd": 1.2, "idd0": 56.25e-3, "idd2n": 33.75e-3, "idd3n": 39.5e-3, "idd4r": 157.5e-3, "idd4w": 135.0e-3, "idd5b": 118.0e-3, "idd5c": 110.0e-3, "idd5f": 135.0e-3, "idd6n": 20.25e-3, "idd2p": 17.0e-3, "idd3p": 22.5e-3, "vpp": 1.2, "ipp0": 0, "ipp2n": 0, "ipp3n": 0, "ipp4r": 0, "ipp4w": 0, "ipp5b": 0, "ipp5c": 0, "ipp5f": 0, "ipp6n": 0, "ipp2p": 0, "ipp3p": 0, "vddq": 1.1, "iBeta_vdd": 56.25e-3, "iBeta_vpp": 0 }, "memtimingspec": { "tCK": 0.76923076923076923076923076923077e-9, "RAS": 10, "RCD": 13, "RTP": 5, "WL": 5, "WR": 5, "RP": 10, "RFCsb": 20, "RFC1": 25, "RFC2": 30, "PPD": 0, "RL": 0, "RPRE": 0, "RPST": 0, "RDDQS": 0, "WPRE": 0, "WPST": 0, "CCD_L_slr": 0, "CCD_L_WR_slr": 0, "CCD_L_WR2_slr": 0, "CCD_M_slr": 0, "CCD_M_WR_slr": 0, "CCD_S_slr": 0, "CCD_S_WR_slr": 0, "CCD_dlr": 0, "CCD_WR_dlr": 0, "CCD_WR_dpr": 0, "RRD_L_slr": 0, "RRD_S_slr": 0, "RRD_dlr": 0, "FAW_slr": 0, "FAW_dlr": 0, "WTR_L": 0, "WTR_M": 0, "WTR_S": 0, "RFC1_slr": 25, "RFC2_slr": 30, "RFC1_dlr": 0, "RFC2_dlr": 0, "RFC1_dpr": 0, "RFC2_dpr": 0, "RFCsb_slr": 20, "RFCsb_dlr": 0, "REFI1": 0, "REFI2": 0, "REFISB": 0, "REFSBRD_slr": 0, "REFSBRD_dlr": 0, "RTRS": 0, "CPDED": 0, "PD": 0, "XP": 0, "ACTPDEN": 0, "PRPDEN": 0, "REFPDEN": 0 }, "bankwisespec":{ "factRho":0.5 }, "memimpedancespec": { "ck_termination": true, "ck_R_eq": 1e6, "ck_dyn_E": 1e-12, "ca_termination": true, "ca_R_eq": 1e6, "ca_dyn_E": 1e-12, "rdq_termination": true, "rdq_R_eq": 1e6, "rdq_dyn_E": 1e-12, "wdq_termination": true, "wdq_R_eq": 1e6, "wdq_dyn_E": 1e-12, "wdqs_termination": true, "wdqs_R_eq": 1e6, "wdqs_dyn_E": 1e-12, "rdqs_termination": true, "rdqs_R_eq": 1e6, "rdqs_dyn_E": 1e-12 }, "dataratespec": { "ca_bus_rate": 2, "dq_bus_rate": 2, "dqs_bus_rate": 2 } } } ================================================ FILE: tests/tests_drampower/resources/lpddr4.csv ================================================ 0 ,ACT,0,0,0,0,0 15,PRE,0,0,0,0,0 15,END,0,0,0,0,0 ================================================ FILE: tests/tests_drampower/resources/lpddr4.json ================================================ { "memspec": { "memoryId": "lpddr4", "memoryType": "LPDDR4", "memarchitecturespec": { "nbrOfChannels": 0, "nbrOfColumns": 1024, "nbrOfRows": 65536, "width": 8, "burstLength": 8, "maxBurstLength": 8, "dataRate": 2, "nbrOfBankGroups": 1, "nbrOfBanks": 16, "nbrOfRanks": 1, "nbrOfDevices": 1 }, "mempowerspec": { "vdd1": 1.2, "idd01": 56.25e-3, "idd2n1": 33.75e-3, "idd3n1": 35e-3, "idd4r1": 157.5e-3, "idd4w1": 135.0e-3, "idd51": 118.0e-3, "idd5pb1": 74.0e-3, "idd61": 20.25e-3, "idd2p1": 17.0e-3, "idd3p1": 22.5e-3, "vdd2": 1.2, "idd02": 0, "idd2n2": 0, "idd3n2": 0, "idd4r2": 0, "idd4w2": 0, "idd52": 0, "idd5pb2": 0, "idd62": 0, "idd2p2": 0, "idd3p2": 0, "vddq": 1.2, "iBeta_vdd1": 56.25e-3, "iBeta_vdd2": 0 }, "memtimingspec": { "tCK": 0.76923076923076923076923076923077e-9, "CKE": 0, "ESCKE": 0, "CMDCKE": 0, "RAS": 10, "RCD": 13, "RL": 0, "REFM": 0, "REFI": 20, "REFIpb": 0, "RFCpb": 20, "RFCab": 25, "RPpb": 10, "RPab": 0, "RCpb": 0, "RCab": 0, "PPD": 0, "FAW": 0, "RRD": 0, "CCD": 0, "CCDMW": 0, "RPST": 0, "DQSCK": 0, "RTP": 2, "WL": 5, "DQSS": 0, "DQS2DQ": 0, "WR": 5, "WPRE": 0, "WTR": 0, "XP": 0, "SR": 0, "XSR": 0, "RTRS": 0 }, "bankwisespec":{ "factRho":0.5, "factSigma": 1.0, "pasrMode": 0, "hasPASR": false }, "memimpedancespec": { "ck_termination": true, "ck_R_eq": 1e6, "ck_dyn_E": 1e-12, "ca_termination": true, "ca_R_eq": 1e6, "ca_dyn_E": 1e-12, "rdq_termination": true, "rdq_R_eq": 1e6, "rdq_dyn_E": 1e-12, "wdq_termination": true, "wdq_R_eq": 1e6, "wdq_dyn_E": 1e-12, "wdqs_termination": true, "wdqs_R_eq": 1e6, "wdqs_dyn_E": 1e-12, "rdqs_termination": true, "rdqs_R_eq": 1e6, "rdqs_dyn_E": 1e-12, "wdbi_termination": true, "wdbi_R_eq": 1e6, "wdbi_dyn_E": 1e-12, "rdbi_termination": true, "rdbi_R_eq": 1e6, "rdbi_dyn_E": 1e-12 } } } ================================================ FILE: tests/tests_drampower/resources/lpddr5.csv ================================================ 0 ,ACT,0,0,0,0,0 15,PRE,0,0,0,0,0 15,END,0,0,0,0,0 ================================================ FILE: tests/tests_drampower/resources/lpddr5.json ================================================ { "memspec": { "memoryId": "lpddr5", "memoryType": "LPDDR5", "memarchitecturespec": { "nbrOfChannels": 0, "nbrOfColumns": 1024, "nbrOfRows": 65536, "width": 8, "burstLength": 8, "maxBurstLength": 8, "dataRate": 2, "nbrOfBankGroups": 1, "nbrOfBanks": 16, "nbrOfRanks": 1, "nbrOfDevices": 1, "per2BankOffset": 0, "WCKalwaysOn": true }, "mempowerspec": { "vdd1": 1.2, "idd01": 56.25e-3, "idd2n1": 33.75e-3, "idd3n1": 35.0e-3, "idd4r1": 157.5e-3, "idd4w1": 135.0e-3, "idd51": 118.0e-3, "idd5pb1": 74.0e-3, "idd61": 20.25e-3, "idd6ds1": 13.25e-3, "idd2p1": 17.0e-3, "idd3p1": 22.5e-3, "vdd2h": 1.2, "idd02h": 0, "idd2n2h": 0, "idd3n2h": 0, "idd4r2h": 0, "idd4w2h": 0, "idd52h": 0, "idd5pb2h": 0, "idd62h": 0, "idd6ds2h": 0, "idd2p2h": 0, "idd3p2h": 0, "vdd2l": 1.2, "idd02l": 0, "idd2n2l": 0, "idd3n2l": 0, "idd4r2l": 0, "idd4w2l": 0, "idd52l": 0, "idd5pb2l": 0, "idd62l": 0, "idd6ds2l": 0, "idd2p2l": 0, "idd3p2l": 0, "vddq": 0.5, "iBeta_vdd1": 56.25e-3, "iBeta_vdd2h": 0, "iBeta_vdd2l": 0 }, "memtimingspec": { "tCK": 0.76923076923076923076923076923077e-9, "REFI": 20, "REFIpb": 0, "RFCab": 25, "RFCpb": 20, "RAS": 10, "RPab": 0, "RPpb": 10, "RCpb": 0, "RCab": 0, "PPD": 0, "RCD": 13, "RCD_L": 0, "RCD_S": 0, "FAW": 0, "RRD": 0, "RL": 0, "RBTP": 3, "WL": 5, "WR": 5, "RTRS": 0, "BL_n_min_16": 0, "BL_n_max_16": 0, "BL_n_L_16": 0, "BL_n_S_16": 0, "BL_n_min_32": 0, "BL_n_max_32": 0, "BL_n_L_32": 0, "BL_n_S_32": 0, "WTR_L": 0, "WTR_S": 0, "WCK2DQO": 2, "WCK2CK": 2, "pbR2act": 0, "pbR2pbR": 0 }, "bankwisespec":{ "factRho":0.5 }, "memimpedancespec": { "ck_termination": true, "ck_R_eq": 1e6, "ck_dyn_E": 1e-12, "ca_termination": true, "ca_R_eq": 1e6, "ca_dyn_E": 1e-12, "rdq_termination": true, "rdq_R_eq": 1e6, "rdq_dyn_E": 1e-12, "wdq_termination": true, "wdq_R_eq": 1e6, "wdq_dyn_E": 1e-12, "wck_termination": true, "wck_R_eq": 1e6, "wck_dyn_E": 1e-12, "rdqs_termination": true, "rdqs_R_eq": 1e6, "rdqs_dyn_E": 1e-12, "wdbi_termination": true, "wdbi_R_eq": 1e6, "wdbi_dyn_E": 1e-12, "rdbi_termination": true, "rdbi_R_eq": 1e6, "rdbi_dyn_E": 1e-12 } } } ================================================ FILE: tests/tests_misc/CMakeLists.txt ================================================ ############################################### ### tests_misc ### ############################################### add_executable(tests_misc test_bus_extended.cpp test_bus.cpp test_pin.cpp test_clock.cpp test_dynamic_bitset.cpp test_interval.cpp test_misc.cpp test_pattern.cpp test_static_extension_manager.cpp test_dynamic_extension_manager.cpp ) target_link_libraries(tests_misc DRAMPower::DRAMPower DRAMUtils::DRAMUtils gtest gtest_main ) gtest_discover_tests(tests_misc WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) ================================================ FILE: tests/tests_misc/test_bus.cpp ================================================ #include #include #include using namespace DRAMPower; using Bus_512 = util::Bus<512>; using Bus_128 = util::Bus<128>; using Bus_64 = util::Bus<64>; using Bus_16 = util::Bus<16>; using Bus_8 = util::Bus<8>; using Bus_6 = util::Bus<6>; using Bus_4 = util::Bus<4>; class BusTest : public ::testing::Test { protected: virtual void SetUp() { } virtual void TearDown() { } }; #define ASSERT_HAS_DATA(lhs) ASSERT_TRUE(lhs.has_value()) #define ASSERT_NO_DATA(lhs) ASSERT_FALSE(lhs.has_value()) #define ASSERT_EQ_BITSET(N, lhs, rhs) ASSERT_HAS_DATA(lhs); ASSERT_EQ(lhs.value(), std::bitset(rhs)) TEST_F(BusTest, EmptyTest) { Bus_8 bus(8, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); // auto [hasData, data] = bus.at(0); ASSERT_HAS_DATA(bus.at(0)); ASSERT_EQ(bus.at(0).value(), Bus_8::burst_t(0b0000'0000)); ASSERT_EQ_BITSET(8, bus.at(1), 0b0000'0000); }; TEST_F(BusTest, Load_Width_8_Single) { Bus_8 bus(8, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); bus.load(0, 0b1010'1111, 1); ASSERT_EQ_BITSET(8, bus.at(0), 0b1010'1111); ASSERT_EQ_BITSET(8, bus.at(1), 0b0000'0000); }; TEST_F(BusTest, Load_Width_4) { Bus_4 bus(4, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); bus.load(0, 0b1010'1111, 2); ASSERT_EQ_BITSET(4, bus.at(0), 0b1010); ASSERT_EQ_BITSET(4, bus.at(1), 0b1111); }; TEST_F(BusTest, Load_HighImpedance_Width_4_0) { Bus_4 bus(4, 1, util::BusIdlePatternSpec::Z, util::BusInitPatternSpec::L); // Bursts // -1 LLLL // 0 ZZZZ // 1 ZZZZ // 2 ZZZZ // 3 1010 // 4 1111 // 5 ZZZZ ASSERT_NO_DATA(bus.at(0)); ASSERT_NO_DATA(bus.at(1)); ASSERT_NO_DATA(bus.at(2)); auto stats = bus.get_stats(3); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 0); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 0); bus.load(3, 0b1010'1111, 2); ASSERT_EQ_BITSET(4, bus.at(3), 0b1010); ASSERT_EQ_BITSET(4, bus.at(4), 0b1111); ASSERT_NO_DATA(bus.at(5)); stats = bus.get_stats(4); // ZZZZ -> 1010 ASSERT_EQ(stats.ones, 2); ASSERT_EQ(stats.zeroes, 2); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 0); stats = bus.get_stats(5); // 1010 -> 1111 ASSERT_EQ(stats.ones, 2 + 4); ASSERT_EQ(stats.zeroes, 2 + 0); ASSERT_EQ(stats.ones_to_zeroes, 0 + 0); ASSERT_EQ(stats.zeroes_to_ones, 0 + 2); ASSERT_EQ(stats.bit_changes, 0 + 2); }; TEST_F(BusTest, Load_HighImpedance_Width_4_1) { Bus_4 bus(4, 1, util::BusIdlePatternSpec::Z, util::BusInitPatternSpec::Z); // Bursts // -1 ZZZZ // 0 ZZZZ // 1 ZZZZ // 2 ZZZZ // 3 1010 // 4 1111 // 5 ZZZZ ASSERT_NO_DATA(bus.at(0)); ASSERT_NO_DATA(bus.at(1)); ASSERT_NO_DATA(bus.at(2)); bus.load(3, 0b1010'1111, 2); ASSERT_EQ_BITSET(4, bus.at(3), 0b1010); ASSERT_EQ_BITSET(4, bus.at(4), 0b1111); ASSERT_NO_DATA(bus.at(5)); auto stats = bus.get_stats(3); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 0); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 0); stats = bus.get_stats(4); // ZZZZ -> 1010 ASSERT_EQ(stats.ones, 2); ASSERT_EQ(stats.zeroes, 2); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 0); stats = bus.get_stats(5); // 1010 -> 1111 ASSERT_EQ(stats.ones, 2 + 4); ASSERT_EQ(stats.zeroes, 2 + 0); ASSERT_EQ(stats.ones_to_zeroes, 0 + 0); ASSERT_EQ(stats.zeroes_to_ones, 0 + 2); ASSERT_EQ(stats.bit_changes, 0 + 2); }; TEST_F(BusTest, Load_HighImpedance_Width_4_2) { Bus_4 bus(4, 1, util::BusIdlePatternSpec::LAST_PATTERN, util::BusInitPatternSpec::Z); // Bursts // -1 ZZZZ // 0 ZZZZ // 1 ZZZZ // 2 ZZZZ // 3 1010 // 4 0101 // 5 0101 // 6 0101 ASSERT_NO_DATA(bus.at(0)); ASSERT_NO_DATA(bus.at(1)); ASSERT_NO_DATA(bus.at(2)); bus.load(3, 0b1010'0101, 2); ASSERT_EQ_BITSET(4, bus.at(3), 0b1010); ASSERT_EQ_BITSET(4, bus.at(4), 0b0101); ASSERT_HAS_DATA(bus.at(5)); ASSERT_HAS_DATA(bus.at(6)); auto stats = bus.get_stats(3); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 0); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 0); stats = bus.get_stats(4); // ZZZZ -> 1010 ASSERT_EQ(stats.ones, 2); ASSERT_EQ(stats.zeroes, 2); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 0); stats = bus.get_stats(5); // 1010 -> 0101 ASSERT_EQ(stats.ones, 2 + 2); ASSERT_EQ(stats.zeroes, 2 + 2); ASSERT_EQ(stats.ones_to_zeroes, 0 + 2); ASSERT_EQ(stats.zeroes_to_ones, 0 + 2); ASSERT_EQ(stats.bit_changes, 0 + 4); stats = bus.get_stats(7); // 0101 -> 0101 -> 0101 ASSERT_EQ(stats.ones, 2 + 2 + 2 + 2); ASSERT_EQ(stats.zeroes, 2 + 2 + 2 + 2); ASSERT_EQ(stats.ones_to_zeroes, 0 + 2 + 0 + 0); ASSERT_EQ(stats.zeroes_to_ones, 0 + 2 + 0 + 0); ASSERT_EQ(stats.bit_changes, 0 + 4 + 0 + 0); }; TEST_F(BusTest, Load_HighImpedance_Width_4_3) { Bus_4 bus(4, 1, util::BusIdlePatternSpec::Z, util::BusInitPatternSpec::L); // Bursts // -1 0000 // 0 1010 // 1 0101 // 2 ZZZZ // 3 ZZZZ bus.load(0, 0b1010'0101, 2); ASSERT_EQ_BITSET(4, bus.at(0), 0b1010); ASSERT_EQ_BITSET(4, bus.at(1), 0b0101); ASSERT_NO_DATA(bus.at(2)); ASSERT_NO_DATA(bus.at(3)); auto stats = bus.get_stats(0); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 0); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 0); stats = bus.get_stats(1); // 0000 -> 1010 ASSERT_EQ(stats.ones, 2); ASSERT_EQ(stats.zeroes, 2); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 2); ASSERT_EQ(stats.bit_changes, 2); stats = bus.get_stats(2); // 1010 -> 0101 ASSERT_EQ(stats.ones, 2 + 2); ASSERT_EQ(stats.zeroes, 2 + 2); ASSERT_EQ(stats.ones_to_zeroes, 0 + 2); ASSERT_EQ(stats.zeroes_to_ones, 2 + 2); ASSERT_EQ(stats.bit_changes, 2 + 4); stats = bus.get_stats(4); // 0101 -> ZZZZ -> ZZZZ ASSERT_EQ(stats.ones, 2 + 2 + 0 + 0); ASSERT_EQ(stats.zeroes, 2 + 2 + 0 + 0); ASSERT_EQ(stats.ones_to_zeroes, 0 + 2 + 0 + 0); ASSERT_EQ(stats.zeroes_to_ones, 2 + 2 + 0 + 0); ASSERT_EQ(stats.bit_changes, 2 + 4 + 0 + 0); }; TEST_F(BusTest, Load_Width_8) { Bus_8 bus(8, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); bus.load(0, 0b0010'1010'1001'0110, 2); ASSERT_EQ_BITSET(8, bus.at(0), 0b0010'1010); ASSERT_EQ_BITSET(8, bus.at(1), 0b1001'0110); }; TEST_F(BusTest, Load_Width_4_Cont) { Bus_4 bus(4, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); bus.load(0, 0b1010'1111, 2); ASSERT_EQ_BITSET(4, bus.at(0), 0b1010); ASSERT_EQ_BITSET(4, bus.at(1), 0b1111); ASSERT_EQ_BITSET(4, bus.at(2), 0b0000); bus.load(2, 0b0101'0001, 2); ASSERT_EQ_BITSET(4, bus.at(2), 0b0101); ASSERT_EQ_BITSET(4, bus.at(3), 0b0001); ASSERT_EQ_BITSET(4, bus.at(4), 0b0000); bus.load(5, 0b0101'0001'1111, 3); ASSERT_EQ_BITSET(4, bus.at(5), 0b0101); ASSERT_EQ_BITSET(4, bus.at(6), 0b0001); ASSERT_EQ_BITSET(4, bus.at(7), 0b1111); ASSERT_EQ_BITSET(4, bus.at(8), 0b0000); }; TEST_F(BusTest, Stats_Empty_1) { Bus_4 bus(4, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); auto stats = bus.get_stats(0); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 0); ASSERT_EQ(stats.bit_changes, 0); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 0); }; TEST_F(BusTest, Stats_Empty_2) { Bus_4 bus(4, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); bus.load(0, 0b1010'1111, 2); auto stats = bus.get_stats(0); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 0); ASSERT_EQ(stats.bit_changes, 0); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 0); }; TEST_F(BusTest, Stats_Basic_1) { Bus_4 bus(4, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::H); auto stats = bus.get_stats(0); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 0); ASSERT_EQ(stats.bit_changes, 0); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 0); bus.load(1, 0b1111, 1); stats = bus.get_stats(1); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 4); ASSERT_EQ(stats.ones_to_zeroes, 4); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 4); stats = bus.get_stats(2); ASSERT_EQ(stats.ones, 4); ASSERT_EQ(stats.zeroes, 4); ASSERT_EQ(stats.ones_to_zeroes, 4); ASSERT_EQ(stats.zeroes_to_ones, 4); ASSERT_EQ(stats.bit_changes, 8); stats = bus.get_stats(3); ASSERT_EQ(stats.ones, 4); ASSERT_EQ(stats.zeroes, 8); ASSERT_EQ(stats.ones_to_zeroes, 8); ASSERT_EQ(stats.zeroes_to_ones, 4); ASSERT_EQ(stats.bit_changes, 12); stats = bus.get_stats(4); ASSERT_EQ(stats.ones, 4); ASSERT_EQ(stats.zeroes, 12); ASSERT_EQ(stats.ones_to_zeroes, 8); ASSERT_EQ(stats.zeroes_to_ones, 4); ASSERT_EQ(stats.bit_changes, 12); }; TEST_F(BusTest, Stats_Basic_2) { Bus_4 bus(4, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::H); auto stats = bus.get_stats(0); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 0); ASSERT_EQ(stats.bit_changes, 0); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 0); stats = bus.get_stats(1); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 4); ASSERT_EQ(stats.ones_to_zeroes, 4); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 4); bus.load(2, 0b1111, 1); stats = bus.get_stats(2); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 8); ASSERT_EQ(stats.ones_to_zeroes, 4); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 4); stats = bus.get_stats(3); ASSERT_EQ(stats.ones, 4); ASSERT_EQ(stats.zeroes, 8); ASSERT_EQ(stats.ones_to_zeroes, 4); ASSERT_EQ(stats.zeroes_to_ones, 4); ASSERT_EQ(stats.bit_changes, 8); stats = bus.get_stats(4); ASSERT_EQ(stats.ones, 4); ASSERT_EQ(stats.zeroes, 12); ASSERT_EQ(stats.ones_to_zeroes, 8); ASSERT_EQ(stats.zeroes_to_ones, 4); ASSERT_EQ(stats.bit_changes, 12); }; TEST_F(BusTest, Stats_Basic_3) { Bus_4 bus(4, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::H); auto stats = bus.get_stats(0); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 0); ASSERT_EQ(stats.bit_changes, 0); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 0); stats = bus.get_stats(1); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 4); ASSERT_EQ(stats.ones_to_zeroes, 4); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 4); stats = bus.get_stats(2); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 8); ASSERT_EQ(stats.ones_to_zeroes, 4); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 4); stats = bus.get_stats(3); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 12); ASSERT_EQ(stats.ones_to_zeroes, 4); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 4); bus.load(4, 0b1111, 1); stats = bus.get_stats(4); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 16); ASSERT_EQ(stats.ones_to_zeroes, 4); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 4); }; TEST_F(BusTest, Stats_4) { Bus_4 bus(4, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); bus.load(0, 0b1010'1111, 2); auto stats = bus.get_stats(0); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 0); ASSERT_EQ(stats.bit_changes, 0); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 0); stats = bus.get_stats(1); ASSERT_EQ(stats.ones, 2); ASSERT_EQ(stats.zeroes, 2); ASSERT_EQ(stats.bit_changes, 2); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 2); stats = bus.get_stats(2); ASSERT_EQ(stats.ones, 2 + 4); ASSERT_EQ(stats.zeroes, 2 + 0); ASSERT_EQ(stats.bit_changes, 2 + 2); ASSERT_EQ(stats.ones_to_zeroes, 0 + 0); ASSERT_EQ(stats.zeroes_to_ones, 2 + 2); stats = bus.get_stats(3); ASSERT_EQ(stats.ones, 2 + 4 + 0); ASSERT_EQ(stats.zeroes, 2 + 0 + 4); ASSERT_EQ(stats.bit_changes, 2 + 2 + 4); ASSERT_EQ(stats.ones_to_zeroes, 0 + 0 + 4); ASSERT_EQ(stats.zeroes_to_ones, 2 + 2 + 0); }; TEST_F(BusTest, Stats_4_Idle) { Bus_4 bus(4, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); bus.load(0, 0b1010'1111, 2); auto stats = bus.get_stats(3); ASSERT_EQ(stats.ones, 6); ASSERT_EQ(stats.zeroes, 6); ASSERT_EQ(stats.bit_changes, 8); ASSERT_EQ(stats.ones_to_zeroes, 4); ASSERT_EQ(stats.zeroes_to_ones, 4); stats = bus.get_stats(4); ASSERT_EQ(stats.ones, 6); ASSERT_EQ(stats.zeroes, 6 + 4); ASSERT_EQ(stats.bit_changes, 8); ASSERT_EQ(stats.ones_to_zeroes, 4); ASSERT_EQ(stats.zeroes_to_ones, 4); stats = bus.get_stats(5); ASSERT_EQ(stats.ones, 6); ASSERT_EQ(stats.zeroes, 6 + 4 + 4); ASSERT_EQ(stats.bit_changes, 8); ASSERT_EQ(stats.ones_to_zeroes, 4); ASSERT_EQ(stats.zeroes_to_ones, 4); }; TEST_F(BusTest, Stats_8) { Bus_8 bus(8, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); bus.load(0, 0b1010'1111'0110'1001, 2); // 1010'1111 // 0110'1001 auto stats = bus.get_stats(1); ASSERT_EQ(stats.ones, 6); ASSERT_EQ(stats.zeroes, 2); ASSERT_EQ(stats.bit_changes, 6); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 6); stats = bus.get_stats(2); ASSERT_EQ(stats.ones, 6 + 4); ASSERT_EQ(stats.zeroes, 2 + 4); ASSERT_EQ(stats.bit_changes, 6 + 4); ASSERT_EQ(stats.ones_to_zeroes, 0 + 3); ASSERT_EQ(stats.zeroes_to_ones, 6 + 1); }; TEST_F(BusTest, Stats_Second_Load_4) { Bus_4 bus(4, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); bus.load(0, 0b1010'1111, 2); auto stats = bus.get_stats(2); ASSERT_EQ(stats.ones, 6); ASSERT_EQ(stats.zeroes, 2); ASSERT_EQ(stats.bit_changes, 4); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 4); bus.load(2, 0b0110'1001, 2); stats = bus.get_stats(3); // 1111 -> 0110 ASSERT_EQ(stats.ones, 6 + 2); ASSERT_EQ(stats.zeroes, 2 + 2); ASSERT_EQ(stats.bit_changes, 4 + 2); ASSERT_EQ(stats.ones_to_zeroes, 0 + 2); ASSERT_EQ(stats.zeroes_to_ones, 4 + 0); stats = bus.get_stats(4); // 0110 -> 1001 ASSERT_EQ(stats.ones, 6 + 2 + 2); ASSERT_EQ(stats.zeroes, 2 + 2 + 2); ASSERT_EQ(stats.bit_changes, 4 + 2 + 4); ASSERT_EQ(stats.ones_to_zeroes, 0 + 2 + 2); ASSERT_EQ(stats.zeroes_to_ones, 4 + 0 + 2); }; TEST_F(BusTest, Load_4_cycles) { Bus_4 bus(4, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); bus.load(0, 0b1010'1111'1001'0011, 4); /* 1010' 1111' 1001' 0011 0000 */ auto stats = bus.get_stats(1); ASSERT_EQ(stats.ones, 2); ASSERT_EQ(stats.zeroes, 2); ASSERT_EQ(stats.bit_changes, 2); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 2); stats = bus.get_stats(2); ASSERT_EQ(stats.ones, 2 + 4); ASSERT_EQ(stats.zeroes, 2 + 0); ASSERT_EQ(stats.bit_changes, 2 + 2); ASSERT_EQ(stats.ones_to_zeroes, 0 + 0); ASSERT_EQ(stats.zeroes_to_ones, 2 + 2); stats = bus.get_stats(3); ASSERT_EQ(stats.ones, 2 + 4 + 2); ASSERT_EQ(stats.zeroes, 2 + 0 + 2); ASSERT_EQ(stats.bit_changes, 2 + 2 + 2); ASSERT_EQ(stats.ones_to_zeroes, 0 + 0 + 2); ASSERT_EQ(stats.zeroes_to_ones, 2 + 2 + 0); stats = bus.get_stats(4); ASSERT_EQ(stats.ones, 2 + 4 + 2 + 2); ASSERT_EQ(stats.zeroes, 2 + 0 + 2 + 2); ASSERT_EQ(stats.bit_changes, 2 + 2 + 2 + 2); ASSERT_EQ(stats.ones_to_zeroes, 0 + 0 + 2 + 1); ASSERT_EQ(stats.zeroes_to_ones, 2 + 2 + 0 + 1); stats = bus.get_stats(5); ASSERT_EQ(stats.ones, 2 + 4 + 2 + 2 + 0); ASSERT_EQ(stats.zeroes, 2 + 0 + 2 + 2 + 4); ASSERT_EQ(stats.bit_changes, 2 + 2 + 2 + 2 + 2); ASSERT_EQ(stats.ones_to_zeroes, 0 + 0 + 2 + 1 + 2); ASSERT_EQ(stats.zeroes_to_ones, 2 + 2 + 0 + 1 + 0); }; TEST_F(BusTest, Load_Data) { constexpr uint8_t data[] = { 0, 0, 0, 0b0000'0011, 0, 0, 0, 0b0000'0010, 0, 0, 0, 0b0000'0001, 0, 0, 0, 0b0000'0001, }; Bus_16 bus{16, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L}; bus.load(0, data, sizeof(data) * 8); auto stats = bus.get_stats(1); // 0x0000 -> 0...0b0000'0001 ASSERT_EQ(stats.ones, 1); ASSERT_EQ(stats.zeroes, 15); ASSERT_EQ(stats.zeroes_to_ones, 1); ASSERT_EQ(stats.ones_to_zeroes, 0); stats = bus.get_stats(2); // 0...0b0000'0001 -> 0x0000 ASSERT_EQ(stats.ones, 1 + 0); ASSERT_EQ(stats.zeroes, 15 + 16); ASSERT_EQ(stats.zeroes_to_ones, 1); ASSERT_EQ(stats.ones_to_zeroes, 1); stats = bus.get_stats(3); // 0x0000 -> 0...0b0000'0001 ASSERT_EQ(stats.ones, 1 + 0 + 1); ASSERT_EQ(stats.zeroes, 15 + 16 + 15); ASSERT_EQ(stats.zeroes_to_ones, 1 + 1); ASSERT_EQ(stats.ones_to_zeroes, 1); stats = bus.get_stats(4); // 0...0b0000'0001 -> 0x0000 ASSERT_EQ(stats.ones, 1 + 0 + 1 + 0); ASSERT_EQ(stats.zeroes, 15 + 16 + 15 + 16); ASSERT_EQ(stats.zeroes_to_ones, 1 + 1); ASSERT_EQ(stats.ones_to_zeroes, 1 + 1); stats = bus.get_stats(5); // 0x0000 -> 0...0b0000'0010 ASSERT_EQ(stats.ones, 1 + 0 + 1 + 0 + 1); ASSERT_EQ(stats.zeroes, 15 + 16 + 15 + 16 + 15); ASSERT_EQ(stats.zeroes_to_ones, 1 + 1 + 1); ASSERT_EQ(stats.ones_to_zeroes, 1 + 1); stats = bus.get_stats(6); // 0...0b0000'0010 -> 0x0000 ASSERT_EQ(stats.ones, 1 + 0 + 1 + 0 + 1 + 0); ASSERT_EQ(stats.zeroes, 15 + 16 + 15 + 16 + 15 + 16); ASSERT_EQ(stats.zeroes_to_ones, 1 + 1 + 1); ASSERT_EQ(stats.ones_to_zeroes, 1 + 1 + 1); stats = bus.get_stats(7); // 0x0000 -> 0...0b0000'0011 ASSERT_EQ(stats.ones, 1 + 0 + 1 + 0 + 1 + 0 + 2); ASSERT_EQ(stats.zeroes, 15 + 16 + 15 + 16 + 15 + 16 + 14); ASSERT_EQ(stats.zeroes_to_ones, 1 + 1 + 1 + 2); ASSERT_EQ(stats.ones_to_zeroes, 1 + 1 + 1); stats = bus.get_stats(8); // 0...0b0000'0011 -> 0x0000 ASSERT_EQ(stats.ones, 1 + 0 + 1 + 0 + 1 + 0 + 2); ASSERT_EQ(stats.zeroes, 15 + 16 + 15 + 16 + 15 + 16 + 14 + 16); ASSERT_EQ(stats.zeroes_to_ones, 1 + 1 + 1 + 2 + 0); ASSERT_EQ(stats.ones_to_zeroes, 1 + 1 + 1 + 2); stats = bus.get_stats(9); // 0x0000 -> 0x0000 ASSERT_EQ(stats.ones, 1 + 0 + 1 + 0 + 1 + 0 + 2); ASSERT_EQ(stats.zeroes, 15 + 16 + 15 + 16 + 15 + 16 + 14 + 16 + 16); ASSERT_EQ(stats.zeroes_to_ones, 1 + 1 + 1 + 2 + 0); ASSERT_EQ(stats.ones_to_zeroes, 1 + 1 + 1 + 2); }; TEST_F(BusTest, Test_001) { // constexpr uint8_t data[] = { // 0b1010'0000, // 0b0000'0100, // 0b0010'0110, // 0b0001'0000, // 0b0000'0000, // 0b1000'0010, // }; std::bitset<6 * 4> cmd_1("100000100000000000010000"); std::bitset<6 * 4> cmd_2("001001100000010010100000"); std::bitset<6 * 4> cmd_3("010001100000010010100000"); /* 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 */ Bus_6 bus{6, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L}; bus.load(0, cmd_1.to_ulong(), 4); auto stats = bus.get_stats(1); ASSERT_EQ(stats.ones, 1); ASSERT_EQ(stats.zeroes, 5); ASSERT_EQ(stats.zeroes_to_ones, 1); ASSERT_EQ(stats.ones_to_zeroes, 0); stats = bus.get_stats(2); ASSERT_EQ(stats.ones, 2); ASSERT_EQ(stats.zeroes, 10); ASSERT_EQ(stats.zeroes_to_ones, 1); ASSERT_EQ(stats.ones_to_zeroes, 0); stats = bus.get_stats(3); ASSERT_EQ(stats.ones, 2); ASSERT_EQ(stats.zeroes, 16); ASSERT_EQ(stats.zeroes_to_ones, 1); ASSERT_EQ(stats.ones_to_zeroes, 1); stats = bus.get_stats(4); ASSERT_EQ(stats.ones, 3); ASSERT_EQ(stats.zeroes, 21); ASSERT_EQ(stats.zeroes_to_ones, 2); ASSERT_EQ(stats.ones_to_zeroes, 1); bus.load(4, cmd_2.to_ulong(), 4); stats = bus.get_stats(5); ASSERT_EQ(stats.ones, 5); ASSERT_EQ(stats.zeroes, 25); ASSERT_EQ(stats.zeroes_to_ones, 4); ASSERT_EQ(stats.ones_to_zeroes, 2); stats = bus.get_stats(6); ASSERT_EQ(stats.ones, 6); ASSERT_EQ(stats.zeroes, 30); ASSERT_EQ(stats.zeroes_to_ones, 5); ASSERT_EQ(stats.ones_to_zeroes, 4); stats = bus.get_stats(7); ASSERT_EQ(stats.ones, 8); ASSERT_EQ(stats.zeroes, 34); ASSERT_EQ(stats.zeroes_to_ones, 7); ASSERT_EQ(stats.ones_to_zeroes, 5); stats = bus.get_stats(8); ASSERT_EQ(stats.ones, 9); ASSERT_EQ(stats.zeroes, 39); ASSERT_EQ(stats.zeroes_to_ones, 8); ASSERT_EQ(stats.ones_to_zeroes, 7); stats = bus.get_stats(9); ASSERT_EQ(stats.ones, 9); ASSERT_EQ(stats.zeroes, 45); ASSERT_EQ(stats.zeroes_to_ones, 8); ASSERT_EQ(stats.ones_to_zeroes, 8); stats = bus.get_stats(10); bus.load(10, cmd_3.to_ulong(), 4); stats = bus.get_stats(11); ASSERT_EQ(stats.ones, 11); ASSERT_EQ(stats.zeroes, 55); ASSERT_EQ(stats.zeroes_to_ones, 10); ASSERT_EQ(stats.ones_to_zeroes, 8); }; ================================================ FILE: tests/tests_misc/test_bus_extended.cpp ================================================ #include #include #include #include #include #include using namespace DRAMPower; using Bus_512 = util::Bus<512>; using Bus_128 = util::Bus<128>; using Bus_64 = util::Bus<64>; using Bus_8 = util::Bus<8>; class ExtendedBusIdlePatternTest : public ::testing::Test { protected: template void Init( typename util::Bus::burst_t& burst_ones, typename util::Bus::burst_t& burst_zeroes, typename util::Bus::burst_t& burst_custom ) { burst_ones.set(); burst_zeroes.reset(); for(size_t i = 0; i < N; i++) { burst_custom.set(i, (i % 3) ? true : false); } } virtual void SetUp() { } virtual void TearDown() { } }; #define ASSERT_HAS_DATA(lhs) ASSERT_TRUE(lhs.has_value()) #define ASSERT_NO_DATA(lhs) ASSERT_FALSE(!lhs.has_value()) #define ASSERT_EQ_BITSET(N, lhs, rhs) ASSERT_HAS_DATA(lhs); ASSERT_EQ(lhs.value(), std::bitset(rhs)) #define ASSERT_EQ_BURST(lhs, rhs) ASSERT_HAS_DATA(lhs); ASSERT_EQ(lhs.value(), rhs) TEST_F(ExtendedBusIdlePatternTest, EmptyIdleLow_1) { Bus_8::burst_t burst_ones; Bus_8::burst_t burst_zeroes; Bus_8::burst_t burst_custom; Init<8>(burst_ones, burst_zeroes, burst_custom); Bus_8 bus(8, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); ASSERT_EQ_BURST(bus.at(0), burst_zeroes); ASSERT_EQ_BURST(bus.at(1), burst_zeroes); }; TEST_F(ExtendedBusIdlePatternTest, EmptyIdleLow_2) { Bus_8::burst_t burst_ones; Bus_8::burst_t burst_zeroes; Bus_8::burst_t burst_custom; Init<8>(burst_ones, burst_zeroes, burst_custom); Bus_8 bus(8, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::H); ASSERT_EQ_BURST(bus.at(0), burst_zeroes); ASSERT_EQ_BURST(bus.at(1), burst_zeroes); }; TEST_F(ExtendedBusIdlePatternTest, EmptyIdleLow_3) { Bus_8::burst_t burst_ones; Bus_8::burst_t burst_zeroes; Bus_8::burst_t burst_custom; Init<8>(burst_ones, burst_zeroes, burst_custom); Bus_8 bus(8, 1, util::BusIdlePatternSpec::L, burst_custom, true); ASSERT_EQ_BURST(bus.at(0), burst_zeroes); ASSERT_EQ_BURST(bus.at(1), burst_zeroes); }; TEST_F(ExtendedBusIdlePatternTest, EmptyIdleHigh_1) { Bus_8::burst_t burst_ones; Bus_8::burst_t burst_zeroes; Bus_8::burst_t burst_custom; Init<8>(burst_ones, burst_zeroes, burst_custom); Bus_8 bus(8, 1, util::BusIdlePatternSpec::H, util::BusInitPatternSpec::L); ASSERT_EQ_BURST(bus.at(0), burst_ones); ASSERT_EQ_BURST(bus.at(1), burst_ones); }; TEST_F(ExtendedBusIdlePatternTest, EmptyIdleHigh_2) { Bus_8::burst_t burst_ones; Bus_8::burst_t burst_zeroes; Bus_8::burst_t burst_custom; Init<8>(burst_ones, burst_zeroes, burst_custom); Bus_8 bus(8, 1, util::BusIdlePatternSpec::H, util::BusInitPatternSpec::H); ASSERT_EQ_BURST(bus.at(0), burst_ones); ASSERT_EQ_BURST(bus.at(1), burst_ones); }; TEST_F(ExtendedBusIdlePatternTest, EmptyIdleHigh_3) { Bus_8::burst_t burst_ones; Bus_8::burst_t burst_zeroes; Bus_8::burst_t burst_custom; Init<8>(burst_ones, burst_zeroes, burst_custom); Bus_8 bus(8, 1, util::BusIdlePatternSpec::H, burst_custom, true); ASSERT_EQ_BURST(bus.at(0), burst_ones); ASSERT_EQ_BURST(bus.at(1), burst_ones); }; TEST_F(ExtendedBusIdlePatternTest, EmptyIdleLastPattern_1) { Bus_8::burst_t burst_ones; Bus_8::burst_t burst_zeroes; Bus_8::burst_t burst_custom; Init<8>(burst_ones, burst_zeroes, burst_custom); Bus_8 bus(8, 1, util::BusIdlePatternSpec::LAST_PATTERN, util::BusInitPatternSpec::L); ASSERT_EQ_BURST(bus.at(0), burst_zeroes); ASSERT_EQ_BURST(bus.at(1), burst_zeroes); }; TEST_F(ExtendedBusIdlePatternTest, EmptyIdleLastPattern_2) { Bus_8::burst_t burst_ones; Bus_8::burst_t burst_zeroes; Bus_8::burst_t burst_custom; Init<8>(burst_ones, burst_zeroes, burst_custom); Bus_8 bus(8, 1, util::BusIdlePatternSpec::LAST_PATTERN, util::BusInitPatternSpec::H); ASSERT_EQ_BURST(bus.at(0), burst_ones); ASSERT_EQ_BURST(bus.at(1), burst_ones); }; TEST_F(ExtendedBusIdlePatternTest, EmptyIdleLastPattern_3) { Bus_8::burst_t burst_ones; Bus_8::burst_t burst_zeroes; Bus_8::burst_t burst_custom; Init<8>(burst_ones, burst_zeroes, burst_custom); Bus_8 bus(8, 1, util::BusIdlePatternSpec::LAST_PATTERN, burst_custom, true); ASSERT_EQ_BURST(bus.at(0), burst_custom); ASSERT_EQ_BURST(bus.at(1), burst_custom); }; TEST_F(ExtendedBusIdlePatternTest, Load_Width_8) { Bus_8::burst_t burst_ones; Bus_8::burst_t burst_zeroes; Bus_8::burst_t burst_custom; Init<8>(burst_ones, burst_zeroes, burst_custom); Bus_8 bus(8, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); // Init load overrides init pattern bus.load(0, 0b0010'1010'1001'0110, 2); ASSERT_EQ_BITSET(8, bus.at(0), 0b0010'1010); ASSERT_EQ_BITSET(8, bus.at(1), 0b1001'0110); }; TEST_F(ExtendedBusIdlePatternTest, Load_Width_64) { Bus_64::burst_t burst_ones; Bus_64::burst_t burst_zeroes; Bus_64::burst_t burst_custom; Init<64>(burst_ones, burst_zeroes, burst_custom); const uint32_t buswidth = 8 * 8; const uint32_t number_bytes = (buswidth + 7) / 8; Bus_64 bus(64, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); std::array data = { 0 }; auto expected = Bus_64::burst_t(64); auto pattern_gen = [] (size_t i) -> uint8_t { return static_cast(i); }; // Load bus for (size_t i = 0; i < number_bytes; i++) { data[i] = pattern_gen(i); } bus.load(0, data.data(), buswidth); // Create expected burst uint8_t byte = 0; size_t byteidx = 0; for (size_t i = 0; i < buswidth; i++) { if (i % 8 == 0) { byte = pattern_gen(byteidx++); } expected.set(i, ((byte >> (i % 8)) & 1)); } ASSERT_EQ_BURST(bus.at(0), expected); }; TEST_F(ExtendedBusIdlePatternTest, Load_Width_512) { const uint32_t buswidth = 64 * 8; const uint32_t number_bytes = (buswidth + 7) / 8; Bus_512 bus(512, 1, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); std::array data = { 0 }; auto expected = Bus_512::burst_t(512); auto pattern_gen = [] (size_t i) -> uint8_t { return static_cast(i); }; // Load bus for (size_t i = 0; i < number_bytes; i++) { data[i] = pattern_gen(i); } bus.load(0, data.data(), buswidth); // Create expected burst uint8_t byte = 0; size_t byteidx = 0; for (size_t i = 0; i < buswidth; i++) { if (i % 8 == 0) { byte = pattern_gen(byteidx++); } expected.set(i, ((byte >> (i % 8)) & 1)); } ASSERT_EQ_BURST(bus.at(0), expected); }; class ExtendedBusStatsTest : public ::testing::Test { protected: std::bitset<128> burst_ones{128}; std::bitset<128> burst_zeroes{128}; std::bitset<128> burst_custom{128}; const static constexpr size_t buswidth = 128; // test bus width greater than 64 const static constexpr size_t bus_array_size = (buswidth + 7) / 8; template void Init( typename util::Bus::burst_t& burst_ones, typename util::Bus::burst_t& burst_zeroes, typename util::Bus::burst_t& burst_custom ) { burst_ones.set(); burst_zeroes.reset(); for(size_t i = 0; i < N; i++) { burst_custom.set(i, (i % 3 ? true : false)); } } virtual void SetUp() { } virtual void TearDown() { } }; TEST_F(ExtendedBusStatsTest, Stats_Pattern_Datarate_1) { uint_fast8_t datarate = 2; timestamp_t timestamp = 3; Bus_128 bus(128, datarate, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); auto stats = bus.get_stats(timestamp); // 3 cycles with double data rate ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, buswidth * timestamp * datarate); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 0); } TEST_F(ExtendedBusStatsTest, Stats_Pattern_Datarate_2) { uint_fast8_t datarate = 13; timestamp_t timestamp = 47; Bus_128 bus(128, datarate, util::BusIdlePatternSpec::L, util::BusInitPatternSpec::L); auto stats = bus.get_stats(timestamp); // 3 cycles with double data rate ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, buswidth * timestamp * datarate); ASSERT_EQ(stats.ones_to_zeroes, 0); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 0); } TEST_F(ExtendedBusStatsTest, Stats_Pattern_1) { Bus_128::burst_t burst_ones{128}; Bus_128::burst_t burst_zeroes{128}; Bus_128::burst_t burst_custom{128}; Init<128>(burst_ones, burst_zeroes, burst_custom); Bus_128 bus(128, 1, util::BusIdlePatternSpec::L, burst_custom, true); std::size_t custom_ones = burst_custom.count(); std::size_t custom_zeroes = buswidth - custom_ones; uint8_t burst_ones_data[bus_array_size]; std::fill_n(burst_ones_data, bus_array_size, 0xFF); ASSERT_EQ(buswidth, 128); ASSERT_EQ(custom_ones, 85); ASSERT_EQ(custom_zeroes, 43); auto stats = bus.get_stats(1); ASSERT_EQ(stats.ones, 0); ASSERT_EQ(stats.zeroes, 128); ASSERT_EQ(stats.ones_to_zeroes, 85); ASSERT_EQ(stats.zeroes_to_ones, 0); ASSERT_EQ(stats.bit_changes, 85); stats = bus.get_stats(2); ASSERT_EQ(stats.ones, 0 + 0); ASSERT_EQ(stats.zeroes, 128 + 128); ASSERT_EQ(stats.ones_to_zeroes, 85 + 0); ASSERT_EQ(stats.zeroes_to_ones, 0 + 0); ASSERT_EQ(stats.bit_changes, 85 + 0); bus.load(2, burst_ones_data, buswidth); stats = bus.get_stats(3); ASSERT_EQ(stats.ones, 0 + 0 + 128); ASSERT_EQ(stats.zeroes, 128 + 128 + 0); ASSERT_EQ(stats.ones_to_zeroes, 85 + 0 + 0); ASSERT_EQ(stats.zeroes_to_ones, 0 + 0 + 128); ASSERT_EQ(stats.bit_changes, 85 + 0 + 128); stats = bus.get_stats(4); ASSERT_EQ(stats.ones, 0 + 0 + 128 + 0); ASSERT_EQ(stats.zeroes, 128 + 128 + 0 + 128); ASSERT_EQ(stats.ones_to_zeroes, 85 + 0 + 0 + 128); ASSERT_EQ(stats.zeroes_to_ones, 0 + 0 + 128 + 0); ASSERT_EQ(stats.bit_changes, 85 + 0 + 128 + 128); bus.load(4, burst_ones_data, buswidth); stats = bus.get_stats(5); ASSERT_EQ(stats.ones, 0 + 0 + 128 + 0 + 128); ASSERT_EQ(stats.zeroes, 128 + 128 + 0 + 128 + 0); ASSERT_EQ(stats.ones_to_zeroes, 85 + 0 + 0 + 128 + 0); ASSERT_EQ(stats.zeroes_to_ones, 0 + 0 + 128 + 0 + 128); ASSERT_EQ(stats.bit_changes, 85 + 0 + 128 + 128 + 128); stats = bus.get_stats(6); ASSERT_EQ(stats.ones, 0 + 0 + 128 + 0 + 128 + 0); ASSERT_EQ(stats.zeroes, 128 + 128 + 0 + 128 + 0 + 128); ASSERT_EQ(stats.ones_to_zeroes, 85 + 0 + 0 + 128 + 0 + 128); ASSERT_EQ(stats.zeroes_to_ones, 0 + 0 + 128 + 0 + 128 + 0); ASSERT_EQ(stats.bit_changes, 85 + 0 + 128 + 128 + 128 + 128); }; TEST_F(ExtendedBusStatsTest, Stats_Pattern_2) { Bus_128::burst_t burst_ones{128}; Bus_128::burst_t burst_zeroes{128}; Bus_128::burst_t burst_custom{128}; Init<128>(burst_ones, burst_zeroes, burst_custom); Bus_128 bus(128, 1, util::BusIdlePatternSpec::LAST_PATTERN, burst_custom, true); std::size_t custom_ones = burst_custom.count(); std::size_t custom_zeroes = buswidth - custom_ones; uint8_t burst_ones_data[bus_array_size]; std::fill_n(burst_ones_data, bus_array_size, 0xFF); ASSERT_EQ(buswidth, 128); ASSERT_EQ(custom_ones, 85); ASSERT_EQ(custom_zeroes, 43); auto stats = bus.get_stats(1); ASSERT_EQ(stats.ones, 85); // 85 ASSERT_EQ(stats.zeroes, 43); // 43 ASSERT_EQ(stats.ones_to_zeroes, 0); // 0 ASSERT_EQ(stats.zeroes_to_ones, 0); // 0 ASSERT_EQ(stats.bit_changes, 0); // 0 stats = bus.get_stats(2); ASSERT_EQ(stats.ones, 85 + 85); // 85 + 0 ASSERT_EQ(stats.zeroes, 43 + 43); // 43 + 128 ASSERT_EQ(stats.ones_to_zeroes, 0 + 0); // 0 + 85 ASSERT_EQ(stats.zeroes_to_ones, 0 + 0); // 0 + 0 ASSERT_EQ(stats.bit_changes, 0 + 0); // 0 + 85 bus.load(2, burst_ones_data, buswidth); stats = bus.get_stats(3); ASSERT_EQ(stats.ones, 85 + 85 + 128); // 85 + 0 + 128 ASSERT_EQ(stats.zeroes, 43 + 43 + 0); ASSERT_EQ(stats.ones_to_zeroes, 0 + 0 + 0); ASSERT_EQ(stats.zeroes_to_ones, 0 + 0 + 43); ASSERT_EQ(stats.bit_changes, 0 + 0 + 43); stats = bus.get_stats(4); ASSERT_EQ(stats.ones, 85 + 85 + 128 + 128); ASSERT_EQ(stats.zeroes, 43 + 43 + 0 + 0); ASSERT_EQ(stats.ones_to_zeroes, 0 + 0 + 0 + 0); ASSERT_EQ(stats.zeroes_to_ones, 0 + 0 + 43 + 0); ASSERT_EQ(stats.bit_changes, 0 + 0 + 43 + 0); bus.load(4, burst_ones_data, buswidth); stats = bus.get_stats(5); ASSERT_EQ(stats.ones, 85 + 85 + 128 + 128 + 128); ASSERT_EQ(stats.zeroes, 43 + 43 + 0 + 0 + 0); ASSERT_EQ(stats.ones_to_zeroes, 0 + 0 + 0 + 0 + 0); ASSERT_EQ(stats.zeroes_to_ones, 0 + 0 + 43 + 0 + 0); ASSERT_EQ(stats.bit_changes, 0 + 0 + 43 + 0 + 0); stats = bus.get_stats(6); ASSERT_EQ(stats.ones, 85 + 85 + 128 + 128 + 128 + 128); ASSERT_EQ(stats.zeroes, 43 + 43 + 0 + 0 + 0 + 0); ASSERT_EQ(stats.ones_to_zeroes, 0 + 0 + 0 + 0 + 0 + 0); ASSERT_EQ(stats.zeroes_to_ones, 0 + 0 + 43 + 0 + 0 + 0); ASSERT_EQ(stats.bit_changes, 0 + 0 + 43 + 0 + 0 + 0); }; ================================================ FILE: tests/tests_misc/test_clock.cpp ================================================ #include #include using namespace DRAMPower; class ClockTest : public ::testing::Test { protected: virtual void SetUp() { } virtual void TearDown() { } }; TEST_F(ClockTest, Test_1) { util::Clock clock; util::Clock::clock_stats_t stats; stats = clock.get_stats_at(0); ASSERT_EQ(stats.zeroes, 0); ASSERT_EQ(stats.ones, 0); stats = clock.get_stats_at(1); ASSERT_EQ(stats.zeroes, 1); ASSERT_EQ(stats.ones, 1); stats = clock.get_stats_at(2); ASSERT_EQ(stats.zeroes, 2); ASSERT_EQ(stats.ones, 2); stats = clock.get_stats_at(10); ASSERT_EQ(stats.zeroes, 10); ASSERT_EQ(stats.ones, 10); }; TEST_F(ClockTest, Test_Period) { util::Clock clock; util::Clock::clock_stats_t stats; // _ _ - - _ _ - - stats = clock.get_stats_at(0); ASSERT_EQ(stats.zeroes, 0); ASSERT_EQ(stats.ones, 0); stats = clock.get_stats_at(1); ASSERT_EQ(stats.zeroes, 1); ASSERT_EQ(stats.ones, 1); stats = clock.get_stats_at(2); ASSERT_EQ(stats.zeroes, 2); ASSERT_EQ(stats.ones, 2); stats = clock.get_stats_at(3); ASSERT_EQ(stats.zeroes, 3); ASSERT_EQ(stats.ones, 3); stats = clock.get_stats_at(4); ASSERT_EQ(stats.zeroes, 4); ASSERT_EQ(stats.ones, 4); stats = clock.get_stats_at(5); ASSERT_EQ(stats.zeroes, 5); ASSERT_EQ(stats.ones, 5); stats = clock.get_stats_at(6); ASSERT_EQ(stats.zeroes, 6); ASSERT_EQ(stats.ones, 6); stats = clock.get_stats_at(7); ASSERT_EQ(stats.zeroes, 7); ASSERT_EQ(stats.ones, 7); }; TEST_F(ClockTest, Test_Stop_Start) { util::Clock clock; util::Clock::clock_stats_t stats; clock.stop(5); stats = clock.get_stats_at(5); ASSERT_EQ(stats.zeroes, 5); ASSERT_EQ(stats.ones, 5); stats = clock.get_stats_at(6); ASSERT_EQ(stats.zeroes, 5); ASSERT_EQ(stats.ones, 5); clock.start(7); stats = clock.get_stats_at(7); ASSERT_EQ(stats.zeroes, 5); ASSERT_EQ(stats.ones, 5); stats = clock.get_stats_at(8); ASSERT_EQ(stats.zeroes, 6); ASSERT_EQ(stats.ones, 6); }; ================================================ FILE: tests/tests_misc/test_dynamic_bitset.cpp ================================================ #include #include using namespace DRAMPower; class DynamicBitsetTest : public ::testing::Test { protected: virtual void SetUp() { } virtual void TearDown() { } }; TEST_F(DynamicBitsetTest, Constructor_1) { util::dynamic_bitset<4> bitset{}; ASSERT_EQ(bitset.size(), 0); }; TEST_F(DynamicBitsetTest, Constructor_2) { util::dynamic_bitset<8> bitset{8, 0b11110011}; ASSERT_EQ(bitset.size(), 8); }; TEST_F(DynamicBitsetTest, Constructor_3) { util::dynamic_bitset<4> bitset{4, 0b101010}; ASSERT_EQ(bitset.size(), 4); }; TEST_F(DynamicBitsetTest, Clear_1) { util::dynamic_bitset<8> bitset{ 8, 0b11110011 }; ASSERT_EQ(bitset.size(), 8); bitset.clear(); ASSERT_EQ(bitset.size(), 0); }; TEST_F(DynamicBitsetTest, PushBack_1) { util::dynamic_bitset<4> bitset; ASSERT_EQ(bitset.size(), 0); bitset.push_back(false); ASSERT_EQ(bitset.size(), 1); bitset.push_back(true); ASSERT_EQ(bitset.size(), 2); }; TEST_F(DynamicBitsetTest, ElementAccess_1) { util::dynamic_bitset<4> bitset{ 4, 0b10101010 }; ASSERT_EQ(bitset[0], false); ASSERT_EQ(bitset[1], true); ASSERT_EQ(bitset[2], false); ASSERT_EQ(bitset[3], true); }; TEST_F(DynamicBitsetTest, ElementAccess_2) { util::dynamic_bitset<4> bitset; bitset.push_back(false); ASSERT_EQ(bitset[0], false); bitset.push_back(true); ASSERT_EQ(bitset[1], true); }; TEST_F(DynamicBitsetTest, ElementAccess_3) { util::dynamic_bitset<4> bitset; bitset.push_back(false); ASSERT_EQ(bitset[0], false); bitset.set(0, true); ASSERT_EQ(bitset[0], true); }; TEST_F(DynamicBitsetTest, Count_1) { util::dynamic_bitset<4> bitset_1{ 4, 0b00001111 }; util::dynamic_bitset<4> bitset_2{ 4, 0b11110000 }; util::dynamic_bitset<6> bitset_3{ 6, 0b11111111 }; util::dynamic_bitset<8> bitset_4{ 8, 0b11110000 }; util::dynamic_bitset<4> bitset_5{ 8, 0b00001111 }; util::dynamic_bitset<5> bitset_6{ 8, 0b10010110 }; ASSERT_EQ(bitset_1.count(), 4); ASSERT_EQ(bitset_2.count(), 0); ASSERT_EQ(bitset_3.count(), 6); ASSERT_EQ(bitset_4.count(), 4); ASSERT_EQ(bitset_5.count(), 4); ASSERT_EQ(bitset_6.count(), 4); }; TEST_F(DynamicBitsetTest, Compare_1) { util::dynamic_bitset<4> bitset_1; util::dynamic_bitset<4> bitset_2; ASSERT_EQ(bitset_1, bitset_2); }; TEST_F(DynamicBitsetTest, Compare_2) { util::dynamic_bitset<4> bitset_1{ 4, 0b00001111 }; util::dynamic_bitset<8> bitset_2{ 4, 0b11111111 }; util::dynamic_bitset<2> bitset_3{ 4, 0b11110000 }; util::dynamic_bitset<1> bitset_4{ 8, 0b00001111 }; ASSERT_EQ(bitset_1, bitset_1); ASSERT_EQ(bitset_1, bitset_2); ASSERT_NE(bitset_1, bitset_3); ASSERT_NE(bitset_2, bitset_3); ASSERT_NE(bitset_2, bitset_4); }; TEST_F(DynamicBitsetTest, Compare_3) { util::dynamic_bitset<4> bitset_1{ 4, 0b00001111 }; ASSERT_EQ(bitset_1, 0b1111); ASSERT_EQ(bitset_1, 0b00001111); ASSERT_EQ(bitset_1, 0b11111111); ASSERT_NE(bitset_1, 0b11); ASSERT_NE(bitset_1, 0b11110000); }; TEST_F(DynamicBitsetTest, Flip_1) { util::dynamic_bitset<4> bitset; bitset.push_back(true); ASSERT_EQ(bitset[0], true); bitset.flip(0); ASSERT_EQ(bitset[0], false); }; TEST_F(DynamicBitsetTest, Negate_1) { util::dynamic_bitset<8> bitset{8, 0b0000'1111}; ASSERT_NE(bitset, ~bitset); ASSERT_EQ(~bitset, 0b1111'0000); ASSERT_NE(~bitset, 0b1111'1111); ASSERT_NE(~bitset, 0b0000'0000); }; TEST_F(DynamicBitsetTest, BitwiseOp_1) { util::dynamic_bitset<8> bitset_1{ 8, 0b0000'0000 }; util::dynamic_bitset<8> bitset_2{ 8, 0b1111'1111 }; util::dynamic_bitset<8> bitset_3{ 8, 0b1000'0001 }; util::dynamic_bitset<8> bitset_4{ 8, 0b1001'1001 }; ASSERT_EQ(bitset_1 & bitset_1, 0); ASSERT_EQ(bitset_1 & bitset_2, 0); ASSERT_EQ(bitset_2 & bitset_3, 0b1000'0001); ASSERT_EQ(bitset_3 & bitset_4, 0b1000'0001); ASSERT_EQ(bitset_4 & bitset_4, bitset_4); }; TEST_F(DynamicBitsetTest, BitwiseOp_2) { util::dynamic_bitset<8> bitset_1{ 8, 0b0000'0000 }; util::dynamic_bitset<8> bitset_2{ 8, 0b1111'1111 }; util::dynamic_bitset<8> bitset_3{ 8, 0b1000'0000 }; util::dynamic_bitset<8> bitset_4{ 8, 0b0001'1001 }; ASSERT_EQ(bitset_1 | bitset_1, 0); ASSERT_EQ(bitset_1 | bitset_2, 0b1111'1111); ASSERT_EQ(bitset_1 | bitset_3, bitset_3); ASSERT_EQ(bitset_3 | bitset_4, 0b1001'1001); }; TEST_F(DynamicBitsetTest, BitwiseOp_3) { util::dynamic_bitset<8> bitset_1{ 8, 0b0000'0000 }; util::dynamic_bitset<8> bitset_2{ 8, 0b1110'1111 }; util::dynamic_bitset<8> bitset_3{ 8, 0b1000'0000 }; util::dynamic_bitset<8> bitset_4{ 8, 0b0001'1001 }; ASSERT_EQ(bitset_1 ^ bitset_1, 0); ASSERT_EQ(bitset_1 ^ bitset_2, 0b1110'1111); ASSERT_EQ(bitset_2 ^ bitset_3, 0b0110'1111); ASSERT_EQ(bitset_1 ^ bitset_3, bitset_3); ASSERT_EQ(bitset_3 ^ bitset_4, 0b1001'1001); }; ================================================ FILE: tests/tests_misc/test_dynamic_extension_manager.cpp ================================================ #include "DRAMPower/util/Serialize.h" #include #include #include #include #include #include #include using namespace DRAMPower; using namespace DRAMPower::util; // dynamic extension example without hooks class DynamicExtensionBase : public util::Serialize, public util::Deserialize { protected: int m_base_variable = 47; virtual void serialize_impl(std::ostream& stream) const = 0; virtual void deserialize_impl(std::istream& stream) = 0; public: void serialize(std::ostream& stream) const override { stream.write(reinterpret_cast(&m_base_variable), sizeof(m_base_variable)); serialize_impl(stream); } void deserialize(std::istream& stream) override { stream.read(reinterpret_cast(&m_base_variable), sizeof(m_base_variable)); deserialize_impl(stream); } }; // dynamic extension example without hooks // This class can also inherit from extension_manager::Extension class DynamicExtensionExample : public DynamicExtensionBase { public: explicit DynamicExtensionExample(bool initstate, int& captured_int) : m_state(initstate) , m_captured_int(captured_int) { captured_int = m_state ? 1 : 0; } bool getState() const { return m_state; } void setState(bool state) { m_state = state; // Also update a captured variable m_captured_int = state ? 1 : 0; } int getBaseVariable() const { return m_base_variable; } void serialize_impl(std::ostream& stream) const override { // Serialize additional data if needed stream.write(reinterpret_cast(&m_state), sizeof(m_state)); stream.write(reinterpret_cast(&m_captured_int), sizeof(m_captured_int)); } void deserialize_impl(std::istream& stream) override { // Deserialize additional data if needed stream.read(reinterpret_cast(&m_state), sizeof(m_state)); stream.read(reinterpret_cast(&m_captured_int), sizeof(m_captured_int)); } private: bool m_state = false; int& m_captured_int; }; // Hook example enum class DynamicExtensionHookExample : uint64_t { Hook_0 = 1 << 0, Hook_1 = 1 << 1, Hook_2 = 1 << 2, Hook_3 = 1 << 3, Hook_4 = 1 << 4, // ... ALL = Hook_0 | Hook_1 | Hook_2 | Hook_3 | Hook_4 }; constexpr DynamicExtensionHookExample operator|(DynamicExtensionHookExample lhs, DynamicExtensionHookExample rhs) { return static_cast(static_cast::type>(lhs) | static_cast::type>(rhs)); } constexpr DynamicExtensionHookExample operator&(DynamicExtensionHookExample lhs, DynamicExtensionHookExample rhs) { return static_cast(static_cast::type>(lhs) & static_cast::type>(rhs)); } constexpr bool operator!=(DynamicExtensionHookExample lhs, size_t rhs) { return static_cast::type>(lhs) != rhs; } // dynamic extension base with hooks class DynamicExtensionWithHooksBase : public extension_manager::ExtensionWithHooks , public util::Serialize, public util::Deserialize { public: using extension_manager::ExtensionWithHooks::ExtensionWithHooks; int getBaseVariable() const { return m_base_variable; } // Hook functions with default implementation virtual void Hook_0(int&) {} virtual void Hook_1(int&) const {} virtual void Hook_2(int&) {} virtual void Hook_3(int&) const {} virtual void Hook_4(int&) {} virtual void serialize_impl(std::ostream& stream) const = 0; virtual void deserialize_impl(std::istream& stream) = 0; void serialize(std::ostream& stream) const override { stream.write(reinterpret_cast(&m_base_variable), sizeof(m_base_variable)); serialize_impl(stream); } void deserialize(std::istream& stream) override { stream.read(reinterpret_cast(&m_base_variable), sizeof(m_base_variable)); deserialize_impl(stream); } private: int m_base_variable = 47; }; // dynamic extension example with hooks class DynamicExtensionWithHooksExample : public DynamicExtensionWithHooksBase { public: explicit DynamicExtensionWithHooksExample(bool initstate, int& captured_int) : m_state(initstate) , m_captured_int(captured_int) { captured_int = m_state ? 1 : 0; } bool getState() const { return m_state; } void setState(bool state) { m_state = state; m_captured_int = state ? 1 : 0; } DynamicExtensionHookExample getSupportedHooks() const override { return DynamicExtensionHookExample::Hook_0 | DynamicExtensionHookExample::Hook_3 | DynamicExtensionHookExample::Hook_4; } void Hook_0(int& i) override { i = 0; m_captured_int = 10; } void Hook_3(int& i) const override{ i = 3; m_captured_int = 30; } void Hook_4(int& i) override { i = 4; m_captured_int = 40; } void serialize_impl(std::ostream& stream) const override { // Serialize additional data if needed stream.write(reinterpret_cast(&m_state), sizeof(m_state)); stream.write(reinterpret_cast(&m_captured_int), sizeof(m_captured_int)); } void deserialize_impl(std::istream& stream) override { // Deserialize additional data if needed stream.read(reinterpret_cast(&m_state), sizeof(m_state)); stream.read(reinterpret_cast(&m_captured_int), sizeof(m_captured_int)); } private: bool m_state = false; int& m_captured_int; }; class DynamicExtensionExampleType { public: // Type definitions using Extension_manager_t = util::extension_manager::ExtensionManager< DynamicExtensionBase >; // Member variables Extension_manager_t m_extensionManager; // Retrieve extension manager Extension_manager_t& getExtensionManager() { return m_extensionManager; } const Extension_manager_t& getExtensionManager() const { return m_extensionManager; } }; class DynamicExtensionHookExampleType { public: // Type definitions using Extension_manager_Hooks_t = util::extension_manager::ExtensionManagerWithHooks< DynamicExtensionWithHooksBase, DynamicExtensionHookExample >; // Member variables Extension_manager_Hooks_t m_extensionManagerHooks; // Retrieve extension manager Extension_manager_Hooks_t& getExtensionManager() { return m_extensionManagerHooks; } const Extension_manager_Hooks_t& getExtensionManager() const { return m_extensionManagerHooks; } // Member functions void testhook0(int &i) { m_extensionManagerHooks.callHook(DynamicExtensionHookExample::Hook_0, [&i](auto& ext) { ext.Hook_0(i); }); } void testhook1(int &i) { m_extensionManagerHooks.callHook(DynamicExtensionHookExample::Hook_1, [&i](const auto& ext) { ext.Hook_1(i); }); } void testhook2(int &i) { m_extensionManagerHooks.callHook(DynamicExtensionHookExample::Hook_2, [&i](auto& ext) { ext.Hook_2(i); }); } void testhook3(int &i) { m_extensionManagerHooks.callHook(DynamicExtensionHookExample::Hook_3, [&i](const auto& ext) { ext.Hook_3(i); }); } void testhook4(int &i) { m_extensionManagerHooks.callHook(DynamicExtensionHookExample::Hook_4, [&i](auto& ext) { ext.Hook_4(i); }); } }; class MiscTestExtension : public ::testing::Test { protected: // Test variables using testclass_t = DynamicExtensionExampleType; using testclass_hook_t = DynamicExtensionHookExampleType; std::unique_ptr dut; std::unique_ptr dut_hook; virtual void SetUp() { dut = std::make_unique(); dut_hook = std::make_unique(); } virtual void TearDown() { } }; #define ASSERT_EQ_BITSET(N, lhs, rhs) ASSERT_EQ(lhs, util::dynamic_bitset(N, rhs)) TEST_F(MiscTestExtension, DynamicExtension0) { // Get reference to extension manager auto& ext_manager = dut->getExtensionManager(); // no extension registered ASSERT_FALSE(ext_manager.hasExtension()); auto ext = ext_manager.getExtension(); ASSERT_TRUE(ext.expired()); // Register extension with captured reference int captured_int = 0; ext_manager.registerExtension(true, captured_int); // Test registration ASSERT_EQ(ext_manager.hasExtension(), true); ext = ext_manager.getExtension(); ASSERT_FALSE(ext.expired()); ASSERT_EQ(ext.lock()->getState(), true); ASSERT_EQ(ext.lock()->getBaseVariable(), 47); ASSERT_EQ(captured_int, 1); ext.lock()->setState(false); ASSERT_EQ(ext.lock()->getState(), false); ASSERT_EQ(captured_int, 0); ext.lock()->setState(true); ASSERT_EQ(ext.lock()->getState(), true); ASSERT_EQ(captured_int, 1); ASSERT_EQ(dut->getExtensionManager().getExtension().lock()->getState(), true); } TEST_F(MiscTestExtension, DynamicExtensionHooks0) { // Get reference to extension manager auto& ext_manager = dut_hook->getExtensionManager(); // no extension registered ASSERT_FALSE(ext_manager.hasExtension()); auto ext = ext_manager.getExtension(); ASSERT_TRUE(ext.expired()); // Register extension with captured reference int captured_int = 0; ext_manager.registerExtension(true, captured_int); // Test registration ASSERT_EQ(ext_manager.hasExtension(), true); ext = ext_manager.getExtension(); ASSERT_FALSE(ext.expired()); ASSERT_EQ(ext.lock()->getState(), true); ASSERT_EQ(ext.lock()->getBaseVariable(), 47); ASSERT_EQ(captured_int, 1); ext.lock()->setState(false); ASSERT_EQ(ext.lock()->getState(), false); ASSERT_EQ(captured_int, 0); ext.lock()->setState(true); ASSERT_EQ(ext.lock()->getState(), true); ASSERT_EQ(captured_int, 1); ASSERT_EQ(dut_hook->getExtensionManager().getExtension().lock()->getState(), true); // Test visitor dut_hook->getExtensionManager().withExtension([](auto& ext) { ext.setState(false); }); ASSERT_EQ(dut_hook->getExtensionManager().getExtension().lock()->getState(), false); dut_hook->getExtensionManager().withExtension([](auto& ext) { ext.setState(true); }); // Test hooks ASSERT_EQ(captured_int, 1); int i = -1; dut_hook->testhook0(i); ASSERT_EQ(i, 0); ASSERT_EQ(captured_int, 10); i = -1; dut_hook->testhook1(i); ASSERT_EQ(i, -1); ASSERT_EQ(captured_int, 10); i = -1; dut_hook->testhook2(i); ASSERT_EQ(i, -1); ASSERT_EQ(captured_int, 10); i = -1; dut_hook->testhook3(i); ASSERT_EQ(i, 3); ASSERT_EQ(captured_int, 30); i = -1; dut_hook->testhook4(i); ASSERT_EQ(i, 4); ASSERT_EQ(captured_int, 40); } ================================================ FILE: tests/tests_misc/test_interval.cpp ================================================ #include #include using namespace DRAMPower; class IntervalTest : public ::testing::Test { protected: virtual void SetUp() { } virtual void TearDown() { } }; TEST_F(IntervalTest, Constructor) { util::interval_counter i; ASSERT_FALSE(i.is_open()); ASSERT_FALSE(i.is_closed()); ASSERT_EQ(i.get_count(), 0); ASSERT_EQ(i.get_count_at(50), 0); }; TEST_F(IntervalTest, Open) { util::interval_counter i(20); ASSERT_TRUE(i.is_open()); ASSERT_FALSE(i.is_closed()); ASSERT_EQ(i.get_start(), 20); ASSERT_EQ(i.get_count(), 0); ASSERT_EQ(i.get_count_at(15), 0); ASSERT_EQ(i.get_count_at(20), 0); ASSERT_EQ(i.get_count_at(25), 5); }; TEST_F(IntervalTest, Close) { util::interval_counter i(20); ASSERT_TRUE(i.is_open()); ASSERT_FALSE(i.is_closed()); ASSERT_EQ(i.close_interval(30), 10); ASSERT_FALSE(i.is_open()); ASSERT_TRUE(i.is_closed()); ASSERT_EQ(i.get_count(), 10); ASSERT_EQ(i.get_count_at(30), 10); ASSERT_EQ(i.get_count_at(35), 10); }; TEST_F(IntervalTest, Reset) { util::interval_counter i; i.start_interval(20); ASSERT_EQ(i.close_interval(30), 10); i.reset_interval(); ASSERT_FALSE(i.is_open()); ASSERT_FALSE(i.is_closed()); ASSERT_EQ(i.get_count(), 10); ASSERT_EQ(i.get_count_at(30), 10); ASSERT_EQ(i.get_count_at(35), 10); }; TEST_F(IntervalTest, Restart) { util::interval_counter i; i.start_interval(20); ASSERT_EQ(i.close_interval(30), 10); i.start_interval(50); ASSERT_TRUE(i.is_open()); ASSERT_FALSE(i.is_closed()); ASSERT_EQ(i.get_count(), 10); ASSERT_EQ(i.get_count_at(55), 15); ASSERT_EQ(i.close_interval(60), 10); ASSERT_EQ(i.get_count(), 20); ASSERT_EQ(i.get_count_at(65), 20); }; ================================================ FILE: tests/tests_misc/test_misc.cpp ================================================ #include #include #include #include #include #include using namespace DRAMPower; template using burst_storage_t = util::burst_storage>; class MiscTest : public ::testing::Test { protected: // Test variables using input_t = std::tuple; using test_list_t = std::vector; using data_t = std::array; test_list_t pattern = { { 0b0000000000000000000000000000000000000000000000000000000000000000, 0b1111111111111111111111111111111111111111111111111111111111111111 }, { 0b1111111111111111111111111111111111111111111111111111111111111111, 0b0000000000000000000000000000000000000000000000000000000000000000 }, { 0b1111111111111111111111111111111111111111111111111111111111111111, 0b1111111111111111111111111111111111111111111111111111111111111111 }, { 0b0000000000000000000000000000000000000000000000000000000000000000, 0b0000000000000000000000000000000000000000000000000000000000000001 }, { 0b1000000000000000000000000000000000000000000000000000000000000000, 0b0000000000000000000000000000000000000000000000000000000000000001 }, { 0b0000000000000000000000000000000000000000000000000000000010101010, 0b0000000000000000000000000000000000000000000000000000000001010101 }, { 0b0000000000000000000000000000000000000000000000000000000000000000, 0b0000000000000000000000000000000000000000000000000000000000001010 } }; virtual void SetUp() { } virtual void TearDown() { } }; #define ASSERT_EQ_BITSET(N, lhs, rhs) ASSERT_EQ(lhs, std::bitset(rhs)) TEST_F(MiscTest, TestChunking) { // Test data data_t data = { 0b1100'0000, 0b1010'1111, 0b0000'0010, 0b0011'1111, 0b1111'0000, 0b1010'1011, 0b0001'0101, 0b1111'0000, 0b1010'1011, 0b1100'0000, 0b0000'1111, 0b1111'1100, }; // Test setup constexpr std::size_t width = 6; burst_storage_t burst_storage{width}; util::BurstStorageInsertHelper::insert_data(burst_storage, 0, width, data.data(), data.size() * 8); // Test assertions ASSERT_EQ(burst_storage.size(), 16); ASSERT_EQ_BITSET(width, burst_storage.get_burst( 0), 0b111111); ASSERT_EQ_BITSET(width, burst_storage.get_burst( 1), 0b000000); ASSERT_EQ_BITSET(width, burst_storage.get_burst( 2), 0b111111); ASSERT_EQ_BITSET(width, burst_storage.get_burst( 3), 0b000000); ASSERT_EQ_BITSET(width, burst_storage.get_burst( 4), 0b101010); ASSERT_EQ_BITSET(width, burst_storage.get_burst( 5), 0b111111); ASSERT_EQ_BITSET(width, burst_storage.get_burst( 6), 0b000000); ASSERT_EQ_BITSET(width, burst_storage.get_burst( 7), 0b010101); ASSERT_EQ_BITSET(width, burst_storage.get_burst( 8), 0b101010); ASSERT_EQ_BITSET(width, burst_storage.get_burst( 9), 0b111111); ASSERT_EQ_BITSET(width, burst_storage.get_burst(10), 0b000000); ASSERT_EQ_BITSET(width, burst_storage.get_burst(11), 0b111111); ASSERT_EQ_BITSET(width, burst_storage.get_burst(12), 0b000000); ASSERT_EQ_BITSET(width, burst_storage.get_burst(13), 0b101010); ASSERT_EQ_BITSET(width, burst_storage.get_burst(14), 0b111111); ASSERT_EQ_BITSET(width, burst_storage.get_burst(15), 0b000000); }; TEST_F(MiscTest, TestBurstLoad_Uneven) { // Test data data_t data = { 0b0001'0101, 0b1010'1111, }; // Test setup constexpr std::size_t width = 4; burst_storage_t burst_storage{width}; util::BurstStorageInsertHelper::insert_data(burst_storage, 0, width, data.data(), 12); // Test assertions ASSERT_EQ(burst_storage.size(), 3); ASSERT_EQ_BITSET(width, burst_storage.get_burst(0), 0b1111); ASSERT_EQ_BITSET(width, burst_storage.get_burst(1), 0b0001); ASSERT_EQ_BITSET(width, burst_storage.get_burst(2), 0b0101); }; TEST_F(MiscTest, BitChanges) { auto apply = [](const input_t & input) { return util::BinaryOps::bit_changes(std::get<0>(input), std::get<1>(input)); }; ASSERT_EQ(apply(pattern[0]), 64); ASSERT_EQ(apply(pattern[1]), 64); ASSERT_EQ(apply(pattern[2]), 0); ASSERT_EQ(apply(pattern[3]), 1); ASSERT_EQ(apply(pattern[4]), 2); ASSERT_EQ(apply(pattern[5]), 8); ASSERT_EQ(apply(pattern[6]), 2); } TEST_F(MiscTest, ZeroToOne) { auto apply = [](const input_t & input) { return util::BinaryOps::zero_to_ones(std::get<0>(input), std::get<1>(input)); }; ASSERT_EQ(apply(pattern[0]), 64); ASSERT_EQ(apply(pattern[1]), 0); ASSERT_EQ(apply(pattern[2]), 0); ASSERT_EQ(apply(pattern[3]), 1); ASSERT_EQ(apply(pattern[4]), 1); ASSERT_EQ(apply(pattern[5]), 4); ASSERT_EQ(apply(pattern[6]), 2); } TEST_F(MiscTest, OneToZero) { auto apply = [](const input_t & input) { return util::BinaryOps::one_to_zeroes(std::get<0>(input), std::get<1>(input)); }; ASSERT_EQ(apply(pattern[0]), 0); ASSERT_EQ(apply(pattern[1]), 64); ASSERT_EQ(apply(pattern[2]), 0); ASSERT_EQ(apply(pattern[3]), 0); ASSERT_EQ(apply(pattern[4]), 1); ASSERT_EQ(apply(pattern[5]), 4); ASSERT_EQ(apply(pattern[6]), 0); } ================================================ FILE: tests/tests_misc/test_pattern.cpp ================================================ #include #include #include #include #include #include using namespace DRAMPower; class PatternTest : public ::testing::Test { protected: std::vector pattern; std::vector pattern2; std::vector pattern3; virtual void SetUp() { using namespace pattern_descriptor; pattern = { H, L, X, V, AP, BL, H, L, // 8 BA0, BA1, BA2, BA3, BA4, BA5, BA6, BA7, // 8 BG0, BG1, BG2, X, X, X, X, X, // 8 C0, C1, C2, C3, C4, // 5 R0, R1, R2, // 3 }; pattern2 = { H, L, H, OPCODE, OPCODE, OPCODE, OPCODE, OPCODE, OPCODE, OPCODE, OPCODE, L, H, L }; pattern3 = { BA0, BA1, BA2, BA3, X, X, X, X, }; } virtual void TearDown() { } }; TEST_F(PatternTest, Test_Override_Low) { using namespace pattern_descriptor; auto encoder = PatternEncoder(PatternEncoderOverrides{ {X, PatternEncoderBitSpec::L}, {V, PatternEncoderBitSpec::L}, {AP, PatternEncoderBitSpec::L}, {BL, PatternEncoderBitSpec::L}, }); // Bank, Bank Group, Rank, Row, Column auto result = encoder.encode(Command{0, CmdType::ACT, { 1,2,3,4,17} }, pattern, 0); ASSERT_EQ(result, 2189443209); }; TEST_F(PatternTest, Test_Pattern_LastBit) { using namespace pattern_descriptor; auto encoder = PatternEncoder(PatternEncoderOverrides{ {X, PatternEncoderBitSpec::LAST_BIT}, }); const uint64_t init_pattern = 0b0101; // Bank, Bank Group, Rank, Row, Column auto result = encoder.encode({ 0xB, 2, 3, 4,}, pattern3, init_pattern); ASSERT_EQ(result, 0b1101'0101); }; TEST_F(PatternTest, Test_Override_High) { using namespace pattern_descriptor; auto encoder = PatternEncoder(PatternEncoderOverrides{ {X, PatternEncoderBitSpec::H}, {V, PatternEncoderBitSpec::H}, {AP, PatternEncoderBitSpec::H}, {BL, PatternEncoderBitSpec::H}, }); // Bank, Bank Group, Rank, Row, Column auto result = encoder.encode(Command{0, CmdType::ACT, { 1,2,3,4,17} }, pattern, 0); ASSERT_EQ(result, 3196084105); }; TEST_F(PatternTest, Test_Override_Last) { using namespace pattern_descriptor; auto encoder = PatternEncoder(PatternEncoderOverrides{ {X, PatternEncoderBitSpec::LAST_BIT}, {V, PatternEncoderBitSpec::LAST_BIT}, {AP, PatternEncoderBitSpec::LAST_BIT}, {BL, PatternEncoderBitSpec::LAST_BIT}, }); // last_pattern uint64_t init_pattern = 0xAA'AA'AA'AA'AA'AA'AA'AA; // 0b10101010... // Bank, Bank Group, Rank, Row, Column auto result = encoder.encode(Command{0, CmdType::ACT, { 1,2,3,4,17} }, pattern, init_pattern); ASSERT_EQ(result, 2860534409); }; TEST_F(PatternTest, Test_Override_2_Patterns) { using namespace pattern_descriptor; auto encoder = PatternEncoder(PatternEncoderOverrides{ {X, PatternEncoderBitSpec::LAST_BIT}, {V, PatternEncoderBitSpec::LAST_BIT}, {AP, PatternEncoderBitSpec::LAST_BIT}, {BL, PatternEncoderBitSpec::LAST_BIT}, }); // last_pattern uint64_t init_pattern = 0xAA'AA'AA'AA'AA'AA'AA'AA; // 0b10101010... // Set X after BG2 to one init_pattern |= 0b1111100000000; // Bank, Bank Group, Rank, Row, Column auto result = encoder.encode(Command{0, CmdType::ACT, { 1,2,3,4,17} }, pattern, init_pattern); ASSERT_EQ(result, 2860539785); result = encoder.encode(Command{0, CmdType::ACT, { 7,3,3,7,17} }, pattern, result); ASSERT_EQ(result, 2866864015); }; TEST_F(PatternTest, Test_Opcode_Patterns) { using namespace pattern_descriptor; auto encoder = PatternEncoder(PatternEncoderOverrides{ {X, PatternEncoderBitSpec::LAST_BIT}, {V, PatternEncoderBitSpec::LAST_BIT}, {AP, PatternEncoderBitSpec::LAST_BIT}, {BL, PatternEncoderBitSpec::LAST_BIT}, }); // last_pattern uint64_t init_pattern = 0x0; const uint64_t opcode = 0x1234'56A5; // 0b...10100101 const uint16_t opcodeLength = 32; // 32 bits encoder.setOpcode(opcode, opcodeLength); // Bank, Bank Group, Rank, Row, Column auto result = encoder.encode(TargetCoordinate{ 1,2,3,4,17}, pattern2, init_pattern); // HLH uint64_t result_expected = 0b101 << 11; // OPCODE result_expected |= (opcode & 0xFF) << 3; // LHL result_expected |= 0b010; ASSERT_EQ(result, result_expected); result = encoder.encode(TargetCoordinate{ 7,6,3,7,21}, pattern2, result); ASSERT_EQ(result, result_expected); }; ================================================ FILE: tests/tests_misc/test_pin.cpp ================================================ #include "DRAMPower/util/bus_types.h" #include "DRAMPower/util/pin_types.h" #include #include #include #include using namespace DRAMPower; // Use builder to make sure all fields are checked in the tests struct StatsBuilder { using stats_t = util::bus_stats_t; StatsBuilder& z(uint64_t z) { m_mask |= 0b00001; m_target.zeroes = z; return *this; } StatsBuilder& o(uint64_t o) { m_mask |= 0b00010; m_target.ones = o; return *this; } StatsBuilder& zto(uint64_t z2o) { m_mask |= 0b00100; m_target.zeroes_to_ones = z2o; return *this; } StatsBuilder& otz(uint64_t o2z) { m_mask |= 0b01000; m_target.ones_to_zeroes = o2z; return *this; } StatsBuilder& bc(uint64_t bc) { m_mask |= 0b10000; m_target.bit_changes = bc; return *this; } StatsBuilder& infer_changes() { m_mask |= 0b10000; m_target.bit_changes = m_target.ones_to_zeroes + m_target.zeroes_to_ones; return *this; } stats_t build() { // All fields of stats must be set EXPECT_EQ(m_mask, 0b11111); return m_target; } private: stats_t m_target{}; // Default constructor uint8_t m_mask = 0; }; class PinTest : public ::testing::Test { protected: virtual void SetUp() { } virtual void TearDown() { } }; TEST_F(PinTest, Idle_L_0) { util::Pin<8> pin_8{util::PinState::H, util::PinState::L}; auto builder = StatsBuilder{} .o(0) .zto(0) .otz(0) .infer_changes(); // First cycle util::bus_stats_t stats_0_1 = pin_8.get_stats_at(0, 1); util::bus_stats_t stats_0_2 = pin_8.get_stats_at(0, 2); EXPECT_EQ(stats_0_1, stats_0_2); EXPECT_EQ(stats_0_1, builder.z(0).build()); // Second cycle util::bus_stats_t stats_1_1 = pin_8.get_stats_at(1, 1); EXPECT_EQ(stats_1_1, builder.z(1).otz(1).infer_changes().build()); util::bus_stats_t stats_48_1 = pin_8.get_stats_at(48, 1); util::bus_stats_t stats_24_2 = pin_8.get_stats_at(24, 2); EXPECT_EQ(stats_48_1, stats_24_2); EXPECT_EQ(stats_48_1, builder.z(48).build()); util::bus_stats_t stats_96_1 = pin_8.get_stats_at(96, 1); util::bus_stats_t stats_48_2 = pin_8.get_stats_at(48, 2); EXPECT_EQ(stats_96_1, stats_48_2); EXPECT_EQ(stats_96_1, builder.z(96).build()); // Cycles for (std::size_t i = 97; i < 1024; i ++) { EXPECT_EQ(pin_8.get_stats_at(i, 1), builder.z(i).build()); } }; TEST_F(PinTest, Idle_H_0) { util::Pin<8> pin_8{util::PinState::L, util::PinState::H}; auto builder = StatsBuilder{} .z(0) .zto(0) .otz(0) .infer_changes(); // First cycle util::bus_stats_t stats_0_1 = pin_8.get_stats_at(0, 1); util::bus_stats_t stats_0_2 = pin_8.get_stats_at(0, 2); EXPECT_EQ(stats_0_1, stats_0_2); EXPECT_EQ(stats_0_1, builder.o(0).build()); // Second cycle util::bus_stats_t stats_1_1 = pin_8.get_stats_at(1, 1); EXPECT_EQ(stats_1_1, builder.o(1).zto(1).infer_changes().build()); util::bus_stats_t stats_48_1 = pin_8.get_stats_at(48, 1); util::bus_stats_t stats_24_2 = pin_8.get_stats_at(24, 2); EXPECT_EQ(stats_48_1, stats_24_2); EXPECT_EQ(stats_48_1, builder.o(48).infer_changes().build()); util::bus_stats_t stats_96_1 = pin_8.get_stats_at(96, 1); util::bus_stats_t stats_48_2 = pin_8.get_stats_at(48, 2); EXPECT_EQ(stats_96_1, stats_48_2); EXPECT_EQ(stats_96_1, builder.o(96).build()); // Cycles for (std::size_t i = 97; i < 1024; i ++) { EXPECT_EQ(pin_8.get_stats_at(i, 1), builder.o(i).build()); } }; TEST_F(PinTest, Load_Init_L_Idle_L_0) { util::Pin<8> pin_8{util::PinState::L, util::PinState::L}; pin_8.set(0, util::PinState::H); EXPECT_EQ( // t = 0 pin_8.get_stats_at(0, 1), StatsBuilder{}.z(0).o(0).zto(0).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 1 pin_8.get_stats_at(1, 1), StatsBuilder{}.z(0).o(1).zto(1).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 2 pin_8.get_stats_at(2, 1), StatsBuilder{}.z(1).o(1).zto(1).otz(1).infer_changes().build() ); EXPECT_EQ( // t = 23 pin_8.get_stats_at(23, 1), StatsBuilder{}.z(22).o(1).zto(1).otz(1).infer_changes().build() ); pin_8.set(24, util::PinState::L); EXPECT_EQ( // t = 45 pin_8.get_stats_at(45, 1), StatsBuilder{}.z(44).o(1).zto(1).otz(1).infer_changes().build() ); }; TEST_F(PinTest, Load_Init_H_Idle_L_0) { util::Pin<8> pin_8{util::PinState::H, util::PinState::L}; pin_8.set(0, util::PinState::H); EXPECT_EQ( // t = 0 pin_8.get_stats_at(0, 1), StatsBuilder{}.z(0).o(0).zto(0).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 1 pin_8.get_stats_at(1, 1), StatsBuilder{}.z(0).o(1).zto(0).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 2 pin_8.get_stats_at(2, 1), StatsBuilder{}.z(1).o(1).zto(0).otz(1).infer_changes().build() ); EXPECT_EQ( // t = 23 pin_8.get_stats_at(23, 1), StatsBuilder{}.z(22).o(1).zto(0).otz(1).infer_changes().build() ); pin_8.set(24, util::PinState::L); EXPECT_EQ( // t = 45 pin_8.get_stats_at(45, 1), StatsBuilder{}.z(44).o(1).zto(0).otz(1).infer_changes().build() ); }; TEST_F(PinTest, Load_Init_H_Idle_H_1) { util::Pin<8> pin_8{util::PinState::H, util::PinState::H}; EXPECT_EQ( // t = 0 pin_8.get_stats_at(0, 1), StatsBuilder{}.z(0).o(0).zto(0).otz(0).infer_changes().build() ); pin_8.set(1, util::PinState::L); EXPECT_EQ( // t = 1 pin_8.get_stats_at(1, 1), StatsBuilder{}.z(0).o(1).zto(0).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 2 pin_8.get_stats_at(2, 1), StatsBuilder{}.z(1).o(1).zto(0).otz(1).infer_changes().build() ); EXPECT_EQ( // t = 3 pin_8.get_stats_at(3, 1), StatsBuilder{}.z(1).o(2).zto(1).otz(1).infer_changes().build() ); EXPECT_EQ( // t = 23 pin_8.get_stats_at(23, 1), StatsBuilder{}.z(1).o(22).zto(1).otz(1).infer_changes().build() ); pin_8.set(24, util::PinState::L); EXPECT_EQ( // t = 46 pin_8.get_stats_at(46, 1), StatsBuilder{}.z(2).o(44).zto(2).otz(2).infer_changes().build() ); }; TEST_F(PinTest, Load_Init_L_Idle_H_1) { util::Pin<8> pin_8{util::PinState::L, util::PinState::H}; EXPECT_EQ( // t = 0 pin_8.get_stats_at(0, 1), StatsBuilder{}.z(0).o(0).zto(0).otz(0).infer_changes().build() ); pin_8.set(1, util::PinState::L); EXPECT_EQ( // t = 1 pin_8.get_stats_at(1, 1), StatsBuilder{}.z(0).o(1).zto(1).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 2 pin_8.get_stats_at(2, 1), StatsBuilder{}.z(1).o(1).zto(1).otz(1).infer_changes().build() ); EXPECT_EQ( // t = 2 pin_8.get_stats_at(3, 1), StatsBuilder{}.z(1).o(2).zto(2).otz(1).infer_changes().build() ); EXPECT_EQ( // t = 23 pin_8.get_stats_at(23, 1), StatsBuilder{}.z(1).o(22).zto(2).otz(1).infer_changes().build() ); pin_8.set(24, util::PinState::L); EXPECT_EQ( // t = 45 pin_8.get_stats_at(46, 1), StatsBuilder{}.z(2).o(44).zto(3).otz(2).infer_changes().build() ); }; TEST_F(PinTest, Idle_to_Burst_to_Idle) { util::Pin<8> pin_8{util::PinState::L, util::PinState::L}; EXPECT_EQ( // t = 9 pin_8.get_stats_at(9, 1), StatsBuilder{}.z(9).o(0).zto(0).otz(0).infer_changes().build() ); // Burst length 5 pin_8.set(10, util::PinState::H); pin_8.set(10, util::PinState::H); pin_8.set(10, util::PinState::H); pin_8.set(10, util::PinState::H); pin_8.set(10, util::PinState::H); EXPECT_EQ( // t = 10 pin_8.get_stats_at(10, 1), StatsBuilder{}.z(10).o(0).zto(0).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 11 // H pin_8.get_stats_at(11, 1), StatsBuilder{}.z(10).o(1).zto(1).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 12 // H pin_8.get_stats_at(12, 1), StatsBuilder{}.z(10).o(2).zto(1).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 13 // H pin_8.get_stats_at(13, 1), StatsBuilder{}.z(10).o(3).zto(1).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 14 // H pin_8.get_stats_at(14, 1), StatsBuilder{}.z(10).o(4).zto(1).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 15 // H pin_8.get_stats_at(15, 1), StatsBuilder{}.z(10).o(5).zto(1).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 16 pin_8.get_stats_at(16, 1), StatsBuilder{}.z(11).o(5).zto(1).otz(1).infer_changes().build() ); EXPECT_EQ( // t = 24 pin_8.get_stats_at(24, 1), StatsBuilder{}.z(19).o(5).zto(1).otz(1).infer_changes().build() ); // Burst length 3 pin_8.set(25, util::PinState::H); pin_8.set(25, util::PinState::L); pin_8.set(25, util::PinState::H); EXPECT_EQ( // t = 25 pin_8.get_stats_at(25, 1), StatsBuilder{}.z(20).o(5).zto(1).otz(1).infer_changes().build() ); EXPECT_EQ( // t = 26 // H pin_8.get_stats_at(26, 1), StatsBuilder{}.z(20).o(6).zto(2).otz(1).infer_changes().build() ); EXPECT_EQ( // t = 27 // L pin_8.get_stats_at(27, 1), StatsBuilder{}.z(21).o(6).zto(2).otz(2).infer_changes().build() ); EXPECT_EQ( // t = 28 // H pin_8.get_stats_at(28, 1), StatsBuilder{}.z(21).o(7).zto(3).otz(2).infer_changes().build() ); EXPECT_EQ( // t = 29 pin_8.get_stats_at(29, 1), StatsBuilder{}.z(22).o(7).zto(3).otz(3).infer_changes().build() ); EXPECT_EQ( // t = 48 pin_8.get_stats_at(48, 1), StatsBuilder{}.z(41).o(7).zto(3).otz(3).infer_changes().build() ); }; TEST_F(PinTest, SeamlessBursts) { util::Pin<8> pin_8{util::PinState::L, util::PinState::L}; EXPECT_EQ( // t = 9 pin_8.get_stats_at(9, 1), StatsBuilder{}.z(9).o(0).zto(0).otz(0).infer_changes().build() ); // Burst length 5 pin_8.set(10, util::PinState::H); pin_8.set(10, util::PinState::H); pin_8.set(10, util::PinState::H); pin_8.set(10, util::PinState::H); pin_8.set(10, util::PinState::H); EXPECT_EQ( // t = 10 pin_8.get_stats_at(10, 1), StatsBuilder{}.z(10).o(0).zto(0).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 11 // B1: H pin_8.get_stats_at(11, 1), StatsBuilder{}.z(10).o(1).zto(1).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 12 // B1: H pin_8.get_stats_at(12, 1), StatsBuilder{}.z(10).o(2).zto(1).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 13 // B1: H pin_8.get_stats_at(13, 1), StatsBuilder{}.z(10).o(3).zto(1).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 14 // B1: H pin_8.get_stats_at(14, 1), StatsBuilder{}.z(10).o(4).zto(1).otz(0).infer_changes().build() ); // Burst length 3 pin_8.set(15, util::PinState::H); pin_8.set(15, util::PinState::L); pin_8.set(15, util::PinState::H); EXPECT_EQ( // t = 15 // B1: H pin_8.get_stats_at(15, 1), StatsBuilder{}.z(10).o(5).zto(1).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 16 // B2: H pin_8.get_stats_at(16, 1), StatsBuilder{}.z(10).o(6).zto(1).otz(0).infer_changes().build() ); EXPECT_EQ( // t = 17 // B2: L pin_8.get_stats_at(17, 1), StatsBuilder{}.z(11).o(6).zto(1).otz(1).infer_changes().build() ); EXPECT_EQ( // t = 18 // B2: H pin_8.get_stats_at(18, 1), StatsBuilder{}.z(11).o(7).zto(2).otz(1).infer_changes().build() ); EXPECT_EQ( // t = 19 pin_8.get_stats_at(19, 1), StatsBuilder{}.z(12).o(7).zto(2).otz(2).infer_changes().build() ); EXPECT_EQ( // t = 48 pin_8.get_stats_at(48, 1), StatsBuilder{}.z(41).o(7).zto(2).otz(2).infer_changes().build() ); }; ================================================ FILE: tests/tests_misc/test_static_extension_manager.cpp ================================================ #include #include #include #include #include #include using namespace DRAMPower; // Hook example enum class StaticExtensionHookExample : uint64_t { Hook_0 = 1 << 0, Hook_1 = 1 << 1, Hook_2 = 1 << 2, Hook_3 = 1 << 3, Hook_4 = 1 << 4, // ... ALL = Hook_0 | Hook_1 | Hook_2 | Hook_3 | Hook_4 }; constexpr StaticExtensionHookExample operator|(StaticExtensionHookExample lhs, StaticExtensionHookExample rhs) { return static_cast(static_cast::type>(lhs) | static_cast::type>(rhs)); } constexpr StaticExtensionHookExample operator&(StaticExtensionHookExample lhs, StaticExtensionHookExample rhs) { return static_cast(static_cast::type>(lhs) & static_cast::type>(rhs)); } constexpr bool operator!=(StaticExtensionHookExample lhs, size_t rhs) { return static_cast::type>(lhs) != rhs; } // static extension with hook example class StaticExtensionWithHookExample { public: static constexpr StaticExtensionHookExample getSupportedHooks() { return StaticExtensionHookExample::Hook_0 | StaticExtensionHookExample::Hook_3 | StaticExtensionHookExample::Hook_4; } // Hooks void Hook_0(int& i) {i = 0;} void Hook_3(int& i) {i = 3;} void Hook_4(int& i) {i = 4;} // Member functions void setState(int state) { m_state = state; } int getState() const { return m_state; } private: int m_state = -1; }; template class StaticExtensionHookExampleType { public: // Type definitions using Extension_manager_t = util::extension_manager_static::StaticExtensionManager< DRAMUtils::util::type_sequence, StaticExtensionHookExample >; // Member variables Extension_manager_t extensionManager; // Hook helper template void invokeHook(Args&&... args) { extensionManager.template callHook(std::forward(args)...); } // Member functions void testhook0(int &i) { invokeHook([&i](auto& ext) { ext.Hook_0(i); }); } void testhook1(int &i) { invokeHook([&i](auto& ext) { ext.Hook_1(i); }); } void testhook2(int &i) { invokeHook([&i](auto& ext) { ext.Hook_2(i); }); } void testhook3(int &i) { invokeHook([&i](auto& ext) { ext.Hook_3(i); }); } void testhook4(int &i) { invokeHook([&i](auto& ext) { ext.Hook_4(i); }); } }; class MiscTestStaticExtensionHook : public ::testing::Test { protected: // Test variables using testclass_t = StaticExtensionHookExampleType; std::unique_ptr dut; virtual void SetUp() { dut = std::make_unique(); } virtual void TearDown() { } }; TEST_F(MiscTestStaticExtensionHook, StaticExtensionManager0) { // Extension registered ASSERT_TRUE(dut->extensionManager.hasExtension()); // Set Extension directly ASSERT_EQ(dut->extensionManager.getExtension().getState(), -1); dut->extensionManager.getExtension().setState(1); ASSERT_EQ(dut->extensionManager.getExtension().getState(), 1); // Set Extension with a visitor lambda dut->extensionManager.withExtension([](auto& ext) { ext.setState(2); }); ASSERT_EQ(dut->extensionManager.getExtension().getState(), 2); } TEST_F(MiscTestStaticExtensionHook, StaticExtensionManager1) { // Test assertions int i = -1; dut->testhook0(i); ASSERT_EQ(i, 0); i = -1; dut->testhook1(i); ASSERT_EQ(i, -1); i = -1; dut->testhook2(i); ASSERT_EQ(i, -1); i = -1; dut->testhook3(i); ASSERT_EQ(i, 3); i = -1; dut->testhook4(i); ASSERT_EQ(i, 4); i = -1; }