2014-03-23 16:12:00 +01:00
|
|
|
# Copyright (c) 2014 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.
|
|
|
|
#
|
|
|
|
# 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.
|
|
|
|
#
|
|
|
|
# Authors: Andreas Hansson
|
|
|
|
|
|
|
|
import optparse
|
|
|
|
|
|
|
|
import m5
|
|
|
|
from m5.objects import *
|
|
|
|
from m5.util import addToPath
|
|
|
|
from m5.internal.stats import periodicStatDump
|
|
|
|
|
|
|
|
addToPath('../common')
|
|
|
|
|
|
|
|
import MemConfig
|
|
|
|
|
|
|
|
# this script is helpful to sweep the efficiency of a specific memory
|
|
|
|
# controller configuration, by varying the number of banks accessed,
|
|
|
|
# and the sequential stride size (how many bytes per activate), and
|
|
|
|
# observe what bus utilisation (bandwidth) is achieved
|
|
|
|
|
|
|
|
parser = optparse.OptionParser()
|
|
|
|
|
|
|
|
# Use a single-channel DDR3-1600 x64 by default
|
|
|
|
parser.add_option("--mem-type", type="choice", default="ddr3_1600_x64",
|
|
|
|
choices=MemConfig.mem_names(),
|
|
|
|
help = "type of memory to use")
|
|
|
|
|
2014-09-20 23:17:55 +02:00
|
|
|
parser.add_option("--ranks", "-r", type="int", default=1,
|
|
|
|
help = "Number of ranks to iterate across")
|
|
|
|
|
|
|
|
parser.add_option("--rd_perc", type="int", default=100,
|
|
|
|
help = "Percentage of read commands")
|
|
|
|
|
|
|
|
parser.add_option("--mode", type="choice", default="DRAM",
|
|
|
|
choices=["DRAM", "DRAM_ROTATE"],
|
|
|
|
help = "DRAM: Random traffic; \
|
|
|
|
DRAM_ROTATE: Traffic rotating across banks and ranks")
|
|
|
|
|
|
|
|
parser.add_option("--addr_map", type="int", default=1,
|
|
|
|
help = "0: RoCoRaBaCh; 1: RoRaBaCoCh/RoRaBaChCo")
|
|
|
|
|
2014-03-23 16:12:00 +01:00
|
|
|
(options, args) = parser.parse_args()
|
|
|
|
|
|
|
|
if args:
|
|
|
|
print "Error: script doesn't take any positional arguments"
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
# at the moment we stay with the default open-adaptive page policy,
|
|
|
|
# and address mapping
|
|
|
|
|
2014-05-10 00:58:49 +02:00
|
|
|
# start with the system itself, using a multi-layer 1.5 GHz
|
2014-09-20 23:18:32 +02:00
|
|
|
# crossbar, delivering 64 bytes / 5 cycles (one header cycle)
|
2014-05-10 00:58:49 +02:00
|
|
|
# which amounts to 19.2 GByte/s per layer and thus per port
|
2015-03-02 10:00:47 +01:00
|
|
|
system = System(membus = IOXBar(width = 16))
|
2014-05-10 00:58:49 +02:00
|
|
|
system.clk_domain = SrcClockDomain(clock = '1.5GHz',
|
2014-03-23 16:12:00 +01:00
|
|
|
voltage_domain =
|
|
|
|
VoltageDomain(voltage = '1V'))
|
|
|
|
|
|
|
|
# we are fine with 256 MB memory for now
|
|
|
|
mem_range = AddrRange('256MB')
|
|
|
|
system.mem_ranges = [mem_range]
|
|
|
|
|
|
|
|
# force a single channel to match the assumptions in the DRAM traffic
|
|
|
|
# generator
|
|
|
|
options.mem_channels = 1
|
|
|
|
MemConfig.config_mem(options, system)
|
|
|
|
|
|
|
|
# the following assumes that we are using the native DRAM
|
|
|
|
# controller, check to be sure
|
2014-03-23 16:12:12 +01:00
|
|
|
if not isinstance(system.mem_ctrls[0], m5.objects.DRAMCtrl):
|
|
|
|
fatal("This script assumes the memory is a DRAMCtrl subclass")
|
2014-03-23 16:12:00 +01:00
|
|
|
|
2014-09-20 23:17:55 +02:00
|
|
|
# Set number of ranks based on input argument; default is 1 rank
|
|
|
|
system.mem_ctrls[0].ranks_per_channel = options.ranks
|
|
|
|
|
|
|
|
# Set the address mapping based on input argument
|
|
|
|
# Default to RoRaBaCoCh
|
|
|
|
if options.addr_map == 0:
|
|
|
|
system.mem_ctrls[0].addr_mapping = "RoCoRaBaCh"
|
|
|
|
elif options.addr_map == 1:
|
|
|
|
system.mem_ctrls[0].addr_mapping = "RoRaBaCoCh"
|
|
|
|
else:
|
|
|
|
fatal("Did not specify a valid address map argument")
|
2014-03-23 16:12:00 +01:00
|
|
|
|
|
|
|
# stay in each state for 0.25 ms, long enough to warm things up, and
|
|
|
|
# short enough to avoid hitting a refresh
|
|
|
|
period = 250000000
|
|
|
|
|
|
|
|
# this is where we go off piste, and print the traffic generator
|
|
|
|
# configuration that we will later use, crazy but it works
|
|
|
|
cfg_file_name = "configs/dram/sweep.cfg"
|
|
|
|
cfg_file = open(cfg_file_name, 'w')
|
|
|
|
|
|
|
|
# stay in each state as long as the dump/reset period, use the entire
|
|
|
|
# range, issue transactions of the right DRAM burst size, and match
|
|
|
|
# the DRAM maximum bandwidth to ensure that it is saturated
|
|
|
|
|
|
|
|
# get the number of banks
|
|
|
|
nbr_banks = system.mem_ctrls[0].banks_per_rank.value
|
|
|
|
|
|
|
|
# determine the burst length in bytes
|
|
|
|
burst_size = int((system.mem_ctrls[0].devices_per_rank.value *
|
|
|
|
system.mem_ctrls[0].device_bus_width.value *
|
|
|
|
system.mem_ctrls[0].burst_length.value) / 8)
|
|
|
|
|
|
|
|
# next, get the page size in bytes
|
|
|
|
page_size = system.mem_ctrls[0].devices_per_rank.value * \
|
|
|
|
system.mem_ctrls[0].device_rowbuffer_size.value
|
|
|
|
|
|
|
|
# match the maximum bandwidth of the memory, the parameter is in ns
|
|
|
|
# and we need it in ticks
|
|
|
|
itt = system.mem_ctrls[0].tBURST.value * 1000000000000
|
|
|
|
|
|
|
|
# assume we start at 0
|
|
|
|
max_addr = mem_range.end
|
|
|
|
|
2014-09-03 13:42:29 +02:00
|
|
|
# use min of the page size and 512 bytes as that should be more than
|
|
|
|
# enough
|
|
|
|
max_stride = min(512, page_size)
|
|
|
|
|
2014-03-23 16:12:00 +01:00
|
|
|
# now we create the state by iterating over the stride size from burst
|
2014-09-03 13:42:29 +02:00
|
|
|
# size to the max stride, and from using only a single bank up to the
|
|
|
|
# number of banks available
|
2014-03-23 16:12:00 +01:00
|
|
|
nxt_state = 0
|
|
|
|
for bank in range(1, nbr_banks + 1):
|
2014-09-03 13:42:29 +02:00
|
|
|
for stride_size in range(burst_size, max_stride + 1, burst_size):
|
2014-09-20 23:17:55 +02:00
|
|
|
cfg_file.write("STATE %d %d %s %d 0 %d %d "
|
|
|
|
"%d %d %d %d %d %d %d %d %d\n" %
|
|
|
|
(nxt_state, period, options.mode, options.rd_perc,
|
|
|
|
max_addr, burst_size, itt, itt, 0, stride_size,
|
|
|
|
page_size, nbr_banks, bank, options.addr_map,
|
|
|
|
options.ranks))
|
2014-03-23 16:12:00 +01:00
|
|
|
nxt_state = nxt_state + 1
|
|
|
|
|
|
|
|
cfg_file.write("INIT 0\n")
|
|
|
|
|
|
|
|
# go through the states one by one
|
|
|
|
for state in range(1, nxt_state):
|
|
|
|
cfg_file.write("TRANSITION %d %d 1\n" % (state - 1, state))
|
|
|
|
|
|
|
|
cfg_file.write("TRANSITION %d %d 1\n" % (nxt_state - 1, nxt_state - 1))
|
|
|
|
|
|
|
|
cfg_file.close()
|
|
|
|
|
|
|
|
# create a traffic generator, and point it to the file we just created
|
|
|
|
system.tgen = TrafficGen(config_file = cfg_file_name)
|
|
|
|
|
|
|
|
# add a communication monitor
|
|
|
|
system.monitor = CommMonitor()
|
|
|
|
|
|
|
|
# connect the traffic generator to the bus via a communication monitor
|
|
|
|
system.tgen.port = system.monitor.slave
|
|
|
|
system.monitor.master = system.membus.slave
|
|
|
|
|
|
|
|
# connect the system port even if it is not used in this example
|
|
|
|
system.system_port = system.membus.slave
|
|
|
|
|
|
|
|
# every period, dump and reset all stats
|
|
|
|
periodicStatDump(period)
|
|
|
|
|
|
|
|
# run Forrest, run!
|
|
|
|
root = Root(full_system = False, system = system)
|
|
|
|
root.system.mem_mode = 'timing'
|
|
|
|
|
|
|
|
m5.instantiate()
|
|
|
|
m5.simulate(nxt_state * period)
|
2014-09-03 13:42:29 +02:00
|
|
|
|
|
|
|
print "DRAM sweep with burst: %d, banks: %d, max stride: %d" % \
|
|
|
|
(burst_size, nbr_banks, max_stride)
|