stdlib,configs: Add DRAMSys to the gem5 standard library

Add DRAMSys as a new AbstractMemorySystem to the gem5 stdlib.
Also, provide convenient subclasses with predefined DRAMSys
configurations.

Add two new stdlib examples:
    - dramsys-traffic.py: Demonstrates the usage of DRAMSys
      using the stdlib TrafficGenerators
    - arm-hello-dramsys.py: A variant of the arm-hello.py
      script that uses DRAMSys as it's memory.

These DRAMSys memory components are only compiled into the standard
library if DRAMSys is not compiled into gem5.

Change-Id: I9db87c41fbd9c28bc44e9d6bde13fc225dc16be9
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/62914
Tested-by: kokoro <noreply+kokoro@google.com>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
diff --git a/configs/example/gem5_library/dramsys/arm-hello-dramsys.py b/configs/example/gem5_library/dramsys/arm-hello-dramsys.py
new file mode 100644
index 0000000..8b25a36
--- /dev/null
+++ b/configs/example/gem5_library/dramsys/arm-hello-dramsys.py
@@ -0,0 +1,92 @@
+# 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 gem5 configuation script creates a simple board to run an ARM
+"hello world" binary using the DRAMSys simulator.
+
+**Important Note**: DRAMSys must be compiled into the gem5 binary to use the
+DRRAMSys simulator. Please consult 'ext/dramsys/README' on how to compile
+correctly. If this is not done correctly this script will run with error.
+"""
+
+from gem5.isas import ISA
+from gem5.utils.requires import requires
+from gem5.resources.resource import Resource
+from gem5.components.memory import DRAMSysDDR3_1600
+from gem5.components.processors.cpu_types import CPUTypes
+from gem5.components.boards.simple_board import SimpleBoard
+from gem5.components.cachehierarchies.classic.private_l1_cache_hierarchy import (
+    PrivateL1CacheHierarchy,
+)
+from gem5.components.processors.simple_processor import SimpleProcessor
+from gem5.simulate.simulator import Simulator
+
+# This check ensures the gem5 binary is compiled to the ARM ISA target. If not,
+# an exception will be thrown.
+requires(isa_required=ISA.ARM)
+
+# We need a cache as DRAMSys only accepts requests with the size of a cache line
+cache_hierarchy = PrivateL1CacheHierarchy(l1d_size="32kB", l1i_size="32kB")
+
+# We use a single channel DDR3_1600 memory system
+memory = DRAMSysDDR3_1600(recordable=True)
+
+# We use a simple Timing processor with one core.
+processor = SimpleProcessor(cpu_type=CPUTypes.TIMING, isa=ISA.ARM, num_cores=1)
+
+# The gem5 library simble board which can be used to run simple SE-mode
+# simulations.
+board = SimpleBoard(
+    clk_freq="3GHz",
+    processor=processor,
+    memory=memory,
+    cache_hierarchy=cache_hierarchy,
+)
+
+# Here we set the workload. In this case we want to run a simple "Hello World!"
+# program compiled to the ARM ISA. The `Resource` class will automatically
+# download the binary from the gem5 Resources cloud bucket if it's not already
+# present.
+board.set_se_binary_workload(
+    # The `Resource` class reads the `resources.json` file from the gem5
+    # resources repository:
+    # https://gem5.googlesource.com/public/gem5-resource.
+    # Any resource specified in this file will be automatically retrieved.
+    # At the time of writing, this file is a WIP and does not contain all
+    # resources. Jira ticket: https://gem5.atlassian.net/browse/GEM5-1096
+    Resource("arm-hello64-static")
+)
+
+# Lastly we run the simulation.
+simulator = Simulator(board=board)
+simulator.run()
+
+print(
+    "Exiting @ tick {} because {}.".format(
+        simulator.get_current_tick(), simulator.get_last_exit_event_cause()
+    )
+)
diff --git a/configs/example/gem5_library/dramsys/dramsys-traffic.py b/configs/example/gem5_library/dramsys/dramsys-traffic.py
new file mode 100644
index 0000000..ee9ad72
--- /dev/null
+++ b/configs/example/gem5_library/dramsys/dramsys-traffic.py
@@ -0,0 +1,62 @@
+# Copyright (c) 2023 The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+This script is used for running a traffic generator connected to the
+DRAMSys simulator.
+
+**Important Note**: DRAMSys must be compiled into the gem5 binary to use the
+DRRAMSys simulator. Please consult 'ext/dramsys/README' on how to compile
+correctly. If this is not done correctly this script will run with error.
+"""
+import m5
+from gem5.components.memory import DRAMSysMem
+from gem5.components.boards.test_board import TestBoard
+from gem5.components.processors.linear_generator import LinearGenerator
+from m5.objects import Root
+
+memory = DRAMSysMem(
+    configuration="ext/dramsys/DRAMSys/DRAMSys/"
+    "library/resources/simulations/ddr4-example.json",
+    resource_directory="ext/dramsys/DRAMSys/DRAMSys/library/resources",
+    recordable=True,
+    size="4GB",
+)
+
+generator = LinearGenerator(
+    duration="250us",
+    rate="40GB/s",
+    num_cores=1,
+    max_addr=memory.get_size(),
+)
+board = TestBoard(
+    clk_freq="3GHz", generator=generator, memory=memory, cache_hierarchy=None
+)
+
+root = Root(full_system=False, system=board)
+board._pre_instantiate()
+m5.instantiate()
+generator.start_traffic()
+exit_event = m5.simulate()
diff --git a/src/mem/dramsys_wrapper.hh b/src/mem/dramsys_wrapper.hh
index 702bf61..f1437cb 100644
--- a/src/mem/dramsys_wrapper.hh
+++ b/src/mem/dramsys_wrapper.hh
@@ -33,6 +33,7 @@
 #include <memory>
 
 #include "DRAMSysConfiguration.h"
+#include "mem/abstract_mem.hh"
 #include "params/DRAMSys.hh"
 #include "sim/core.hh"
 #include "simulation/DRAMSysRecordable.h"
diff --git a/src/python/SConscript b/src/python/SConscript
index b0f11dd..3b00b34 100644
--- a/src/python/SConscript
+++ b/src/python/SConscript
@@ -170,6 +170,10 @@
 PySource('gem5.components.memory', 'gem5/components/memory/__init__.py')
 PySource('gem5.components.memory', 'gem5/components/memory/abstract_memory_system.py')
 PySource('gem5.components.memory', 'gem5/components/memory/dramsim_3.py')
+
+if env['HAVE_DRAMSYS']:
+    PySource('gem5.components.memory', 'gem5/components/memory/dramsys.py')
+
 PySource('gem5.components.memory', 'gem5/components/memory/simple.py')
 PySource('gem5.components.memory', 'gem5/components/memory/memory.py')
 PySource('gem5.components.memory', 'gem5/components/memory/single_channel.py')
diff --git a/src/python/gem5/components/memory/__init__.py b/src/python/gem5/components/memory/__init__.py
index 78aa4b8..8a7b5ef 100644
--- a/src/python/gem5/components/memory/__init__.py
+++ b/src/python/gem5/components/memory/__init__.py
@@ -34,3 +34,15 @@
 from .multi_channel import DualChannelDDR4_2400
 from .multi_channel import DualChannelLPDDR3_1600
 from .hbm import HBM2Stack
+
+try:
+    from .dramsys import DRAMSysMem
+    from .dramsys import DRAMSysDDR4_1866
+    from .dramsys import DRAMSysDDR3_1600
+    from .dramsys import DRAMSysLPDDR4_3200
+    from .dramsys import DRAMSysHBM2
+except:
+    # In the case that DRAMSys is not compiled into the gem5 binary, importing
+    # DRAMSys components will fail. This try-exception statement is needed to
+    # ignore these imports in this case.
+    pass
diff --git a/src/python/gem5/components/memory/dramsys.py b/src/python/gem5/components/memory/dramsys.py
index ab95548..28f3bd3 100644
--- a/src/python/gem5/components/memory/dramsys.py
+++ b/src/python/gem5/components/memory/dramsys.py
@@ -25,17 +25,22 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 import m5
-import os
-import configparser
 
-from m5.objects import DRAMSys, AddrRange, Port, MemCtrl, Gem5ToTlmBridge32
+from m5.objects import (
+    DRAMSys,
+    AddrRange,
+    Port,
+    MemCtrl,
+    Gem5ToTlmBridge32,
+    SystemC_Kernel,
+)
 from m5.util.convert import toMemorySize
 
 from ...utils.override import overrides
 from ..boards.abstract_board import AbstractBoard
 from .abstract_memory_system import AbstractMemorySystem
 
-from typing import Optional, Tuple, Sequence, List
+from typing import Tuple, Sequence, List
 
 
 class DRAMSysMem(AbstractMemorySystem):
@@ -60,8 +65,9 @@
         )
 
         self._size = toMemorySize(size)
-        self._bridge = Gem5ToTlmBridge32()
-        self.dramsys.port = self._bridge.tlm
+        self.bridge = Gem5ToTlmBridge32()
+        self.dramsys.tlm = self.bridge.tlm
+        self.kernel = SystemC_Kernel()
 
     @overrides(AbstractMemorySystem)
     def incorporate_memory(self, board: AbstractBoard) -> None:
@@ -69,7 +75,7 @@
 
     @overrides(AbstractMemorySystem)
     def get_mem_ports(self) -> Sequence[Tuple[AddrRange, Port]]:
-        return [(self.dramsys.range, self._bridge.gem5)]
+        return [(self.dramsys.range, self.bridge.gem5)]
 
     @overrides(AbstractMemorySystem)
     def get_memory_controllers(self) -> List[MemCtrl]:
@@ -87,3 +93,60 @@
                 "range which matches the memory's size."
             )
         self.dramsys.range = ranges[0]
+        self.bridge.addr_ranges = ranges[0]
+
+
+class DRAMSysDDR4_1866(DRAMSysMem):
+    def __init__(self, recordable: bool):
+        """
+        :param recordable: Whether the database recording feature of DRAMSys is enabled.
+        """
+        super().__init__(
+            configuration="ext/dramsys/DRAMSys/DRAMSys/"
+            "library/resources/simulations/ddr4-example.json",
+            size="4GB",
+            resource_directory="ext/dramsys/DRAMSys/DRAMSys/library/resources",
+            recordable=recordable,
+        )
+
+
+class DRAMSysDDR3_1600(DRAMSysMem):
+    def __init__(self, recordable: bool):
+        """
+        :param recordable: Whether the database recording feature of DRAMSys is enabled.
+        """
+        super().__init__(
+            configuration="ext/dramsys/DRAMSys/DRAMSys/"
+            "library/resources/simulations/ddr3-gem5-se.json",
+            size="4GB",
+            resource_directory="ext/dramsys/DRAMSys/DRAMSys/library/resources",
+            recordable=recordable,
+        )
+
+
+class DRAMSysLPDDR4_3200(DRAMSysMem):
+    def __init__(self, recordable: bool):
+        """
+        :param recordable: Whether the database recording feature of DRAMSys is enabled.
+        """
+        super().__init__(
+            configuration="ext/dramsys/DRAMSys/DRAMSys/"
+            "library/resources/simulations/lpddr4-example.json",
+            size="4GB",
+            resource_directory="ext/dramsys/DRAMSys/DRAMSys/library/resources",
+            recordable=recordable,
+        )
+
+
+class DRAMSysHBM2(DRAMSysMem):
+    def __init__(self, recordable: bool):
+        """
+        :param recordable: Whether the database recording feature of DRAMSys is enabled.
+        """
+        super().__init__(
+            configuration="ext/dramsys/DRAMSys/DRAMSys/"
+            "library/resources/simulations/hbm2-example.json",
+            size="4GB",
+            resource_directory="ext/dramsys/DRAMSys/DRAMSys/library/resources",
+            recordable=recordable,
+        )