ext, configs: Add example Arm gem5 SST platform

JIRA: https://gem5.atlassian.net/browse/GEM5-1121

Change-Id: I7544823710d9a63146d18d4fe9b7c969312ad4d7
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/53627
Reviewed-by: Hoa Nguyen <hoanguyen@ucdavis.edu>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
Tested-by: kokoro <noreply+kokoro@google.com>
diff --git a/configs/example/sst/arm_fs.py b/configs/example/sst/arm_fs.py
new file mode 100644
index 0000000..d6c1635
--- /dev/null
+++ b/configs/example/sst/arm_fs.py
@@ -0,0 +1,176 @@
+# Copyright (c) 2021 Arm Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Copyright (c) 2021 The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import argparse
+from os import path
+
+import m5
+from m5.objects import *
+
+m5.util.addToPath('../..')
+from common import SysPaths
+
+class ArmSstSystem(ArmSystem):
+    def __init__(self, cpu_clock_rate, **kwargs):
+        super(ArmSstSystem, self).__init__(**kwargs)
+
+        self.voltage_domain=VoltageDomain(voltage="1.0V")
+        self.clk_domain = SrcClockDomain(
+            clock=cpu_clock_rate, voltage_domain=Parent.voltage_domain
+        )
+
+        self.terminal = Terminal()
+        self.vncserver = VncServer()
+
+        self.iobus = IOXBar()
+
+        # Since the latency from CPU to the bus was set in SST,
+        # additional latency is undesirable.
+        self.membus = NoncoherentXBar(
+            frontend_latency=0, forward_latency=0,
+            response_latency=0, header_latency=0, width=64)
+
+        self.membus.badaddr_responder = BadAddr()
+        self.membus.default = \
+            self.membus.badaddr_responder.pio
+
+        _my_ranges = [
+            AddrRange(0, size='64MiB'),
+            AddrRange(0x80000000, size='16GiB')
+        ]
+        self.memory_outgoing_bridge = OutgoingRequestBridge(
+            physical_address_ranges=_my_ranges)
+
+        self.memory_outgoing_bridge.port = self.membus.mem_side_ports
+
+        self.cpu = [TimingSimpleCPU(cpu_id=0)]
+        self.mem_mode = 'timing'
+
+        for cpu in self.cpu:
+            cpu.createThreads()
+            cpu.icache_port = self.membus.cpu_side_ports
+            cpu.dcache_port = self.membus.cpu_side_ports
+
+            cpu.mmu.connectWalkerPorts(
+                self.membus.cpu_side_ports, self.membus.cpu_side_ports)
+
+        self.bridge = Bridge(delay='50ns')
+        self.bridge.mem_side_port = self.iobus.cpu_side_ports
+        self.bridge.cpu_side_port = self.membus.mem_side_ports
+
+    def getMemRanges(self, mem_size):
+        """
+        Define system memory ranges. This depends on the physical
+        memory map provided by the realview platform and by the memory
+        size provided by the user (mem_size argument).
+        The method is iterating over all platform ranges until they cover
+        the entire user's memory requirements.
+        """
+        mem_ranges = []
+        for mem_range in self.platform._mem_regions:
+            size_in_range = min(mem_size, mem_range.size())
+
+            mem_ranges.append(
+                AddrRange(start=mem_range.start, size=size_in_range))
+
+            mem_size -= size_in_range
+            if mem_size == 0:
+                return mem_ranges
+
+        raise ValueError("memory size too big for platform capabilities")
+
+def createArmPlatform(system):
+    class VExpress_GEM5_V1_SST(VExpress_GEM5_V1):
+        bootmem = SubSystem()
+
+    system.platform = VExpress_GEM5_V1_SST()
+
+    if hasattr(system.platform.gic, 'cpu_addr'):
+        system.gic_cpu_addr = system.platform.gic.cpu_addr
+
+    system.platform.attachOnChipIO(system.membus, system.bridge)
+    system.platform.attachIO(system.iobus)
+
+    system.platform.setupBootLoader(system, SysPaths.binary)
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--kernel', help='Path to the Kernel')
+parser.add_argument('--cpu-clock-rate', type=str, help='CPU clock rate, e.g. 3GHz')
+parser.add_argument('--memory-size', type=str, help='Memory size, e.g. 4GiB')
+parser.add_argument('--root-device', type=str, default='/dev/vda')
+args = parser.parse_args()
+
+system = ArmSstSystem(args.cpu_clock_rate)
+
+# Setup Linux workload
+system.workload = ArmFsLinux()
+system.workload.object_file = args.kernel
+system.workload.dtb_filename = path.join(m5.options.outdir, 'system.dtb')
+system.workload.addr_check = False
+
+# Create RealView platform
+createArmPlatform(system)
+
+system.mem_ranges = system.getMemRanges(int(Addr(args.memory_size)))
+
+system.system_outgoing_bridge = OutgoingRequestBridge()
+system.system_port = system.system_outgoing_bridge.port
+system.generateDtb(system.workload.dtb_filename)
+
+# Linux boot command flags
+kernel_cmd = [
+    # Tell Linux to use the simulated serial port as a console
+    "console=ttyAMA0",
+    # Hard-code timi
+    "lpj=19988480",
+    # Disable address space randomisation to get a consistent
+    # memory layout.
+    "norandmaps",
+    # Tell Linux where to find the root disk image.
+    f"root={args.root_device}",
+    # Mount the root disk read-write by default.
+    "rw",
+    # Tell Linux about the amount of physical memory present.
+    f"mem={args.memory_size}",
+]
+system.workload.command_line = " ".join(kernel_cmd)
+
+for cpu in system.cpu:
+    cpu.createInterruptController()
+
+root = Root(full_system=True, system=system)
diff --git a/ext/sst/sst/arm_example.py b/ext/sst/sst/arm_example.py
new file mode 100644
index 0000000..cdee3ca
--- /dev/null
+++ b/ext/sst/sst/arm_example.py
@@ -0,0 +1,142 @@
+# Copyright (c) 2021 Arm Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Copyright (c) 2021 The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import sst
+import sys
+import os
+
+from sst import UnitAlgebra
+
+cache_link_latency = "1ps"
+
+kernel = "vmlinux_exit.arm64"
+cpu_clock_rate = "3GHz"
+# gem5 will send requests to physical addresses of range [0x80000000, inf) to memory
+# currently, we do not subtract 0x80000000 from the request's address to get the "real" address
+# so, the mem_size would always be 2GiB larger than the desired memory size
+memory_size_gem5 = "4GiB"
+memory_size_sst = "16GiB"
+addr_range_end = UnitAlgebra(memory_size_sst).getRoundedValue()
+
+l1_params = {
+    "access_latency_cycles" : "1",
+    "cache_frequency" : cpu_clock_rate,
+    "replacement_policy" : "lru",
+    "coherence_protocol" : "MESI",
+    "associativity" : "4",
+    "cache_line_size" : "64",
+    "cache_size" : "4 KiB",
+    "L1" : "1",
+}
+
+gem5_command = f" ../../configs/example/sst/arm_fs.py \
+    --kernel {kernel} \
+    --cpu-clock-rate {cpu_clock_rate} \
+    --memory-size {memory_size_gem5}"
+
+cpu_params = {
+    "frequency": cpu_clock_rate,
+    "cmd": gem5_command,
+}
+
+gem5_node = sst.Component("gem5_node", "gem5.gem5Component")
+gem5_node.addParams(cpu_params)
+
+cache_bus = sst.Component("cache_bus", "memHierarchy.Bus")
+cache_bus.addParams( { "bus_frequency" : cpu_clock_rate } )
+
+system_port = gem5_node.setSubComponent("system_port", "gem5.gem5Bridge", 0) # for initialization
+system_port.addParams({
+    "response_receiver_name": "system.system_outgoing_bridge",
+    "mem_size": memory_size_sst
+})
+
+cache_port = gem5_node.setSubComponent("cache_port", "gem5.gem5Bridge", 0) # SST -> gem5
+cache_port.addParams({
+    "response_receiver_name": "system.memory_outgoing_bridge",
+    "mem_size": memory_size_sst
+})
+
+# L1 cache
+l1_cache = sst.Component("l1_cache", "memHierarchy.Cache")
+l1_cache.addParams(l1_params)
+
+# Memory
+memctrl = sst.Component("memory", "memHierarchy.MemController")
+memctrl.addParams({
+    "debug" : "0",
+    "clock" : "1GHz",
+    "request_width" : "64",
+    "addr_range_end" : addr_range_end, # should be changed accordingly to memory_size_sst
+})
+memory = memctrl.setSubComponent("backend", "memHierarchy.simpleMem")
+memory.addParams({
+    "access_time" : "30ns",
+    "mem_size" : memory_size_sst
+})
+
+# Connections
+# cpu <-> L1
+cpu_cache_link = sst.Link("cpu_l1_cache_link")
+cpu_cache_link.connect(
+    (cache_port, "port", cache_link_latency),
+    (cache_bus, "high_network_0", cache_link_latency)
+)
+system_cache_link = sst.Link("system_cache_link")
+system_cache_link.connect(
+    (system_port, "port", cache_link_latency),
+    (cache_bus, "high_network_1", cache_link_latency)
+)
+cache_bus_cache_link = sst.Link("cache_bus_cache_link")
+cache_bus_cache_link.connect(
+    (cache_bus, "low_network_0", cache_link_latency),
+    (l1_cache, "high_network_0", cache_link_latency)
+)
+# L1 <-> mem
+cache_mem_link = sst.Link("l1_cache_mem_link")
+cache_mem_link.connect(
+    (l1_cache, "low_network_0", cache_link_latency),
+    (memctrl, "direct_link", cache_link_latency)
+)
+
+# enable Statistics
+stat_params = { "rate" : "0ns" }
+sst.setStatisticLoadLevel(5)
+sst.setStatisticOutput("sst.statOutputTXT", {"filepath" : "./sst-stats.txt"})
+sst.enableAllStatisticsForComponentName("l1_cache", stat_params)
+sst.enableAllStatisticsForComponentName("memory", stat_params)