1# Copyright (c) 2013, 2017 ARM Limited
2# All rights reserved.
3#
4# The license below extends only to copyright in the software and shall
5# not be construed as granting a license to any other intellectual
6# property including but not limited to intellectual property relating
7# to a hardware implementation of the functionality of the software
8# licensed hereunder.  You may use the software subject to the license
9# terms below provided that you ensure that this notice is replicated
10# unmodified and in its entirety in all distributions of the software,
11# modified or unmodified, in source code or in binary form.
12#
13# Redistribution and use in source and binary forms, with or without
14# modification, are permitted provided that the following conditions are
15# met: redistributions of source code must retain the above copyright
16# notice, this list of conditions and the following disclaimer;
17# redistributions in binary form must reproduce the above copyright
18# notice, this list of conditions and the following disclaimer in the
19# documentation and/or other materials provided with the distribution;
20# neither the name of the copyright holders nor the names of its
21# contributors may be used to endorse or promote products derived from
22# this software without specific prior written permission.
23#
24# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35#
36# Authors: Andreas Sandberg
37#          Andreas Hansson
38
39from __future__ import print_function
40from __future__ import absolute_import
41
42import m5.objects
43import inspect
44import sys
45from textwrap import  TextWrapper
46from . import HMC
47
48# Dictionary of mapping names of real memory controller models to
49# classes.
50_mem_classes = {}
51
52def is_mem_class(cls):
53    """Determine if a class is a memory controller that can be instantiated"""
54
55    # We can't use the normal inspect.isclass because the ParamFactory
56    # and ProxyFactory classes have a tendency to confuse it.
57    try:
58        return issubclass(cls, m5.objects.AbstractMemory) and \
59            not cls.abstract
60    except TypeError:
61        return False
62
63def get(name):
64    """Get a memory class from a user provided class name."""
65
66    try:
67        mem_class = _mem_classes[name]
68        return mem_class
69    except KeyError:
70        print("%s is not a valid memory controller." % (name,))
71        sys.exit(1)
72
73def print_mem_list():
74    """Print a list of available memory classes."""
75
76    print("Available memory classes:")
77    doc_wrapper = TextWrapper(initial_indent="\t\t", subsequent_indent="\t\t")
78    for name, cls in _mem_classes.items():
79        print("\t%s" % name)
80
81        # Try to extract the class documentation from the class help
82        # string.
83        doc = inspect.getdoc(cls)
84        if doc:
85            for line in doc_wrapper.wrap(doc):
86                print(line)
87
88def mem_names():
89    """Return a list of valid memory names."""
90    return list(_mem_classes.keys())
91
92# Add all memory controllers in the object hierarchy.
93for name, cls in inspect.getmembers(m5.objects, is_mem_class):
94    _mem_classes[name] = cls
95
96def create_mem_ctrl(cls, r, i, nbr_mem_ctrls, intlv_bits, intlv_size):
97    """
98    Helper function for creating a single memoy controller from the given
99    options.  This function is invoked multiple times in config_mem function
100    to create an array of controllers.
101    """
102
103    import math
104    intlv_low_bit = int(math.log(intlv_size, 2))
105
106    # Use basic hashing for the channel selection, and preferably use
107    # the lower tag bits from the last level cache. As we do not know
108    # the details of the caches here, make an educated guess. 4 MByte
109    # 4-way associative with 64 byte cache lines is 6 offset bits and
110    # 14 index bits.
111    xor_low_bit = 20
112
113    # Create an instance so we can figure out the address
114    # mapping and row-buffer size
115    ctrl = cls()
116
117    # Only do this for DRAMs
118    if issubclass(cls, m5.objects.DRAMCtrl):
119        # Inform each controller how many channels to account
120        # for
121        ctrl.channels = nbr_mem_ctrls
122
123        # If the channel bits are appearing after the column
124        # bits, we need to add the appropriate number of bits
125        # for the row buffer size
126        if ctrl.addr_mapping.value == 'RoRaBaChCo':
127            # This computation only really needs to happen
128            # once, but as we rely on having an instance we
129            # end up having to repeat it for each and every
130            # one
131            rowbuffer_size = ctrl.device_rowbuffer_size.value * \
132                ctrl.devices_per_rank.value
133
134            intlv_low_bit = int(math.log(rowbuffer_size, 2))
135
136    # We got all we need to configure the appropriate address
137    # range
138    ctrl.range = m5.objects.AddrRange(r.start, size = r.size(),
139                                      intlvHighBit = \
140                                          intlv_low_bit + intlv_bits - 1,
141                                      xorHighBit = \
142                                          xor_low_bit + intlv_bits - 1,
143                                      intlvBits = intlv_bits,
144                                      intlvMatch = i)
145    return ctrl
146
147def config_mem(options, system):
148    """
149    Create the memory controllers based on the options and attach them.
150
151    If requested, we make a multi-channel configuration of the
152    selected memory controller class by creating multiple instances of
153    the specific class. The individual controllers have their
154    parameters set such that the address range is interleaved between
155    them.
156    """
157
158    # Mandatory options
159    opt_mem_type = options.mem_type
160    opt_mem_channels = options.mem_channels
161
162    # Optional options
163    opt_tlm_memory = getattr(options, "tlm_memory", None)
164    opt_external_memory_system = getattr(options, "external_memory_system",
165                                         None)
166    opt_elastic_trace_en = getattr(options, "elastic_trace_en", False)
167    opt_mem_ranks = getattr(options, "mem_ranks", None)
168    opt_dram_powerdown = getattr(options, "enable_dram_powerdown", None)
169
170    if opt_mem_type == "HMC_2500_1x32":
171        HMChost = HMC.config_hmc_host_ctrl(options, system)
172        HMC.config_hmc_dev(options, system, HMChost.hmc_host)
173        subsystem = system.hmc_dev
174        xbar = system.hmc_dev.xbar
175    else:
176        subsystem = system
177        xbar = system.membus
178
179    if opt_tlm_memory:
180        system.external_memory = m5.objects.ExternalSlave(
181            port_type="tlm_slave",
182            port_data=opt_tlm_memory,
183            port=system.membus.master,
184            addr_ranges=system.mem_ranges)
185        system.kernel_addr_check = False
186        return
187
188    if opt_external_memory_system:
189        subsystem.external_memory = m5.objects.ExternalSlave(
190            port_type=opt_external_memory_system,
191            port_data="init_mem0", port=xbar.master,
192            addr_ranges=system.mem_ranges)
193        subsystem.kernel_addr_check = False
194        return
195
196    nbr_mem_ctrls = opt_mem_channels
197    import math
198    from m5.util import fatal
199    intlv_bits = int(math.log(nbr_mem_ctrls, 2))
200    if 2 ** intlv_bits != nbr_mem_ctrls:
201        fatal("Number of memory channels must be a power of 2")
202
203    cls = get(opt_mem_type)
204    mem_ctrls = []
205
206    if opt_elastic_trace_en and not issubclass(cls, m5.objects.SimpleMemory):
207        fatal("When elastic trace is enabled, configure mem-type as "
208                "simple-mem.")
209
210    # The default behaviour is to interleave memory channels on 128
211    # byte granularity, or cache line granularity if larger than 128
212    # byte. This value is based on the locality seen across a large
213    # range of workloads.
214    intlv_size = max(128, system.cache_line_size.value)
215
216    # For every range (most systems will only have one), create an
217    # array of controllers and set their parameters to match their
218    # address mapping in the case of a DRAM
219    for r in system.mem_ranges:
220        for i in range(nbr_mem_ctrls):
221            mem_ctrl = create_mem_ctrl(cls, r, i, nbr_mem_ctrls, intlv_bits,
222                                       intlv_size)
223            # Set the number of ranks based on the command-line
224            # options if it was explicitly set
225            if issubclass(cls, m5.objects.DRAMCtrl) and opt_mem_ranks:
226                mem_ctrl.ranks_per_channel = opt_mem_ranks
227
228            # Enable low-power DRAM states if option is set
229            if issubclass(cls, m5.objects.DRAMCtrl):
230                mem_ctrl.enable_dram_powerdown = opt_dram_powerdown
231
232            if opt_elastic_trace_en:
233                mem_ctrl.latency = '1ns'
234                print("For elastic trace, over-riding Simple Memory "
235                    "latency to 1ns.")
236
237            mem_ctrls.append(mem_ctrl)
238
239    subsystem.mem_ctrls = mem_ctrls
240
241    # Connect the controllers to the membus
242    for i in range(len(subsystem.mem_ctrls)):
243        if opt_mem_type == "HMC_2500_1x32":
244            subsystem.mem_ctrls[i].port = xbar[i/4].master
245            # Set memory device size. There is an independent controller for
246            # each vault. All vaults are same size.
247            subsystem.mem_ctrls[i].device_size = options.hmc_dev_vault_size
248        else:
249            subsystem.mem_ctrls[i].port = xbar.master
250