blob: 4e381550707676e4cef3118e546de9cd9d88d1e0 [file] [log] [blame]
# Copyright (c) 2021 The Regents of the University of California
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met: redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer;
# redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution;
# neither the name of the copyright holders nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
This scripts is used for checking the correctness of statistics reported
by the gem5 simulator. It can excercise certain components in the memory
subsystem. The reported values could be used to compare against a validated
set of statistics.
"""
import m5
import argparse
import importlib
from pathlib import Path
from m5.objects import Root, MemorySize
from m5.stats.gem5stats import get_simstat
from gem5.components.boards.test_board import TestBoard
def generator_factory(
generator_class: str, generator_cores: int, mem_size: MemorySize
):
if generator_class == "LinearGenerator":
from gem5.components.processors.linear_generator import LinearGenerator
return LinearGenerator(
duration="250us",
rate="40GB/s",
num_cores=generator_cores,
max_addr=mem_size,
)
elif generator_class == "RandomGenerator":
from gem5.components.processors.random_generator import RandomGenerator
return RandomGenerator(
duration="250us",
rate="40GB/s",
num_cores=generator_cores,
max_addr=mem_size,
)
elif generator_class == "GUPSGenerator":
if generator_cores != 1:
raise ValueError(
"Only one core should be used with GUPSGenerator. "
"In order to use multiple cores of GUPS generator, use either "
"GUPSGeneratorEP or GUPSGeneratorPAR."
)
from gem5.components.processors.gups_generator import GUPSGenerator
table_size = f"{int(mem_size / 2)}B"
return GUPSGenerator(0, table_size, update_limit=1000, clk_freq="2GHz")
elif generator_class == "GUPSGeneratorEP":
from gem5.components.processors.gups_generator_ep import (
GUPSGeneratorEP,
)
table_size = f"{int(mem_size / 2)}B"
return GUPSGeneratorEP(
generator_cores, 0, table_size, update_limit=1000, clk_freq="2GHz"
)
elif generator_class == "GUPSGeneratorPAR":
from gem5.components.processors.gups_generator_par import (
GUPSGeneratorPAR,
)
table_size = f"{int(mem_size / 2)}B"
return GUPSGeneratorPAR(
generator_cores, 0, table_size, update_limit=1000, clk_freq="2GHz"
)
else:
raise ValueError(f"Unknown generator class {generator_class}")
def cache_factory(cache_class: str):
if cache_class == "NoCache":
from gem5.components.cachehierarchies.classic.no_cache import NoCache
return NoCache()
elif cache_class == "PrivateL1":
from gem5.components.cachehierarchies.classic.private_l1_cache_hierarchy import (
PrivateL1CacheHierarchy,
)
return PrivateL1CacheHierarchy(l1d_size="32KiB", l1i_size="32KiB")
elif cache_class == "PrivateL1PrivateL2":
from gem5.components.cachehierarchies.classic.private_l1_private_l2_cache_hierarchy import (
PrivateL1PrivateL2CacheHierarchy,
)
return PrivateL1PrivateL2CacheHierarchy(
l1d_size="32KiB", l1i_size="32KiB", l2_size="256KiB"
)
elif cache_class == "MESITwoLevel":
from gem5.components.cachehierarchies.ruby.mesi_two_level_cache_hierarchy import (
MESITwoLevelCacheHierarchy,
)
return MESITwoLevelCacheHierarchy(
l1i_size="32KiB",
l1i_assoc="8",
l1d_size="32KiB",
l1d_assoc="8",
l2_size="256KiB",
l2_assoc="4",
num_l2_banks=1,
)
else:
raise ValueError(f"The cache class {cache_class} is not supported.")
parser = argparse.ArgumentParser(
description="A traffic generator that can be used to test a gem5 "
"memory component."
)
parser.add_argument(
"generator_class",
type=str,
help="The class of generator to use.",
choices=[
"LinearGenerator",
"RandomGenerator",
"GUPSGenerator",
"GUPSGeneratorEP",
"GUPSGeneratorPAR",
],
)
parser.add_argument(
"generator_cores", type=int, help="The number of generator cores to use."
)
parser.add_argument(
"cache_class",
type=str,
help="The cache class to import and instantiate.",
choices=["NoCache", "PrivateL1", "PrivateL1PrivateL2", "MESITwoLevel"],
)
parser.add_argument(
"mem_module", type=str, help="The python module to import for memory."
)
parser.add_argument(
"mem_class", type=str, help="The memory class to import and instantiate."
)
parser.add_argument(
"mem_args",
nargs="*",
help="The arguments needed to instantiate the memory class.",
)
args = parser.parse_args()
cache_hierarchy = cache_factory(args.cache_class)
memory_class = getattr(
importlib.import_module(args.mem_module), args.mem_class
)
memory = memory_class(*args.mem_args)
generator = generator_factory(
args.generator_class, args.generator_cores, memory.get_size()
)
# We use the Test Board. This is a special board to run traffic generation
# tasks
motherboard = TestBoard(
clk_freq="3GHz",
generator=generator,
memory=memory,
cache_hierarchy=cache_hierarchy,
)
root = Root(full_system=False, system=motherboard)
motherboard._pre_instantiate()
m5.instantiate()
generator.start_traffic()
print("Beginning simulation!")
exit_event = m5.simulate()
print(
"Exiting @ tick {} because {}.".format(m5.curTick(), exit_event.getCause())
)
simstats = get_simstat(root, prepare_stats=True)
json_output = Path(m5.options.outdir) / "output.json"
with open(json_output, "w") as stats_file:
simstats.dump(stats_file, indent=2)