Simulation.py revision 9520:ea7c03ae2d5e
1# Copyright (c) 2012 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# Copyright (c) 2006-2008 The Regents of The University of Michigan
14# Copyright (c) 2010 Advanced Micro Devices, Inc.
15# All rights reserved.
16#
17# Redistribution and use in source and binary forms, with or without
18# modification, are permitted provided that the following conditions are
19# met: redistributions of source code must retain the above copyright
20# notice, this list of conditions and the following disclaimer;
21# redistributions in binary form must reproduce the above copyright
22# notice, this list of conditions and the following disclaimer in the
23# documentation and/or other materials provided with the distribution;
24# neither the name of the copyright holders nor the names of its
25# contributors may be used to endorse or promote products derived from
26# this software without specific prior written permission.
27#
28# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39#
40# Authors: Lisa Hsu
41
42import sys
43from os import getcwd
44from os.path import join as joinpath
45
46import CpuConfig
47
48import m5
49from m5.defines import buildEnv
50from m5.objects import *
51from m5.util import *
52
53addToPath('../common')
54
55def getCPUClass(cpu_type):
56    """Returns the required cpu class and the mode of operation."""
57    cls = CpuConfig.get(cpu_type)
58    return cls, cls.memory_mode()
59
60def setCPUClass(options):
61    """Returns two cpu classes and the initial mode of operation.
62
63       Restoring from a checkpoint or fast forwarding through a benchmark
64       can be done using one type of cpu, and then the actual
65       simulation can be carried out using another type. This function
66       returns these two types of cpus and the initial mode of operation
67       depending on the options provided.
68    """
69
70    TmpClass, test_mem_mode = getCPUClass(options.cpu_type)
71    CPUClass = None
72    if TmpClass.require_caches() and \
73            not options.caches and not options.ruby:
74        fatal("%s must be used with caches" % options.cpu_type)
75
76    if options.checkpoint_restore != None:
77        if options.restore_with_cpu != options.cpu_type:
78            CPUClass = TmpClass
79            TmpClass, test_mem_mode = getCPUClass(options.restore_with_cpu)
80    elif options.fast_forward:
81        CPUClass = TmpClass
82        TmpClass = AtomicSimpleCPU
83        test_mem_mode = 'atomic'
84
85    return (TmpClass, test_mem_mode, CPUClass)
86
87def setWorkCountOptions(system, options):
88    if options.work_item_id != None:
89        system.work_item_id = options.work_item_id
90    if options.work_begin_cpu_id_exit != None:
91        system.work_begin_cpu_id_exit = options.work_begin_cpu_id_exit
92    if options.work_end_exit_count != None:
93        system.work_end_exit_count = options.work_end_exit_count
94    if options.work_end_checkpoint_count != None:
95        system.work_end_ckpt_count = options.work_end_checkpoint_count
96    if options.work_begin_exit_count != None:
97        system.work_begin_exit_count = options.work_begin_exit_count
98    if options.work_begin_checkpoint_count != None:
99        system.work_begin_ckpt_count = options.work_begin_checkpoint_count
100    if options.work_cpus_checkpoint_count != None:
101        system.work_cpus_ckpt_count = options.work_cpus_checkpoint_count
102
103def findCptDir(options, maxtick, cptdir, testsys):
104    """Figures out the directory from which the checkpointed state is read.
105
106    There are two different ways in which the directories holding checkpoints
107    can be named --
108    1. cpt.<benchmark name>.<instruction count when the checkpoint was taken>
109    2. cpt.<some number, usually the tick value when the checkpoint was taken>
110
111    This function parses through the options to figure out which one of the
112    above should be used for selecting the checkpoint, and then figures out
113    the appropriate directory.
114
115    It also sets the value of the maximum tick value till which the simulation
116    will run.
117    """
118
119    from os.path import isdir, exists
120    from os import listdir
121    import re
122
123    if not isdir(cptdir):
124        fatal("checkpoint dir %s does not exist!", cptdir)
125
126    if options.at_instruction or options.simpoint:
127        inst = options.checkpoint_restore
128        if options.simpoint:
129            # assume workload 0 has the simpoint
130            if testsys.cpu[0].workload[0].simpoint == 0:
131                fatal('Unable to find simpoint')
132            inst += int(testsys.cpu[0].workload[0].simpoint)
133
134        checkpoint_dir = joinpath(cptdir, "cpt.%s.%s" % (options.bench, inst))
135        if not exists(checkpoint_dir):
136            fatal("Unable to find checkpoint directory %s", checkpoint_dir)
137    else:
138        dirs = listdir(cptdir)
139        expr = re.compile('cpt\.([0-9]*)')
140        cpts = []
141        for dir in dirs:
142            match = expr.match(dir)
143            if match:
144                cpts.append(match.group(1))
145
146        cpts.sort(lambda a,b: cmp(long(a), long(b)))
147
148        cpt_num = options.checkpoint_restore
149        if cpt_num > len(cpts):
150            fatal('Checkpoint %d not found', cpt_num)
151
152        maxtick = maxtick - int(cpts[cpt_num - 1])
153        checkpoint_dir = joinpath(cptdir, "cpt.%s" % cpts[cpt_num - 1])
154
155    return maxtick, checkpoint_dir
156
157def scriptCheckpoints(options, maxtick, cptdir):
158    if options.at_instruction or options.simpoint:
159        checkpoint_inst = int(options.take_checkpoints)
160
161        # maintain correct offset if we restored from some instruction
162        if options.checkpoint_restore != None:
163            checkpoint_inst += options.checkpoint_restore
164
165        print "Creating checkpoint at inst:%d" % (checkpoint_inst)
166        exit_event = m5.simulate()
167        exit_cause = exit_event.getCause()
168        print "exit cause = %s" % exit_cause
169
170        # skip checkpoint instructions should they exist
171        while exit_cause == "checkpoint":
172            exit_event = m5.simulate()
173            exit_cause = exit_event.getCause()
174
175        if exit_cause == "a thread reached the max instruction count":
176            m5.checkpoint(joinpath(cptdir, "cpt.%s.%d" % \
177                    (options.bench, checkpoint_inst)))
178            print "Checkpoint written."
179
180    else:
181        when, period = options.take_checkpoints.split(",", 1)
182        when = int(when)
183        period = int(period)
184        num_checkpoints = 0
185
186        exit_event = m5.simulate(when)
187        exit_cause = exit_event.getCause()
188        while exit_cause == "checkpoint":
189            exit_event = m5.simulate(when - m5.curTick())
190            exit_cause = exit_event.getCause()
191
192        if exit_cause == "simulate() limit reached":
193            m5.checkpoint(joinpath(cptdir, "cpt.%d"))
194            num_checkpoints += 1
195
196        sim_ticks = when
197        max_checkpoints = options.max_checkpoints
198
199        while num_checkpoints < max_checkpoints and \
200                exit_cause == "simulate() limit reached":
201            if (sim_ticks + period) > maxtick:
202                exit_event = m5.simulate(maxtick - sim_ticks)
203                exit_cause = exit_event.getCause()
204                break
205            else:
206                exit_event = m5.simulate(period)
207                exit_cause = exit_event.getCause()
208                sim_ticks += period
209                while exit_event.getCause() == "checkpoint":
210                    exit_event = m5.simulate(sim_ticks - m5.curTick())
211                if exit_event.getCause() == "simulate() limit reached":
212                    m5.checkpoint(joinpath(cptdir, "cpt.%d"))
213                    num_checkpoints += 1
214
215    return exit_cause
216
217def benchCheckpoints(options, maxtick, cptdir):
218    exit_event = m5.simulate(maxtick)
219    exit_cause = exit_event.getCause()
220
221    num_checkpoints = 0
222    max_checkpoints = options.max_checkpoints
223
224    while exit_cause == "checkpoint":
225        m5.checkpoint(joinpath(cptdir, "cpt.%d"))
226        num_checkpoints += 1
227        if num_checkpoints == max_checkpoints:
228            exit_cause = "maximum %d checkpoints dropped" % max_checkpoints
229            break
230
231        exit_event = m5.simulate(maxtick - m5.curTick())
232        exit_cause = exit_event.getCause()
233
234    return exit_event
235
236def repeatSwitch(testsys, repeat_switch_cpu_list, maxtick, switch_freq):
237    print "starting switch loop"
238    while True:
239        exit_event = m5.simulate(switch_freq)
240        exit_cause = exit_event.getCause()
241
242        if exit_cause != "simulate() limit reached":
243            return exit_event
244
245        print "draining the system"
246        m5.drain(testsys)
247        m5.switchCpus(repeat_switch_cpu_list)
248        m5.resume(testsys)
249
250        tmp_cpu_list = []
251        for old_cpu, new_cpu in repeat_switch_cpu_list:
252            tmp_cpu_list.append((new_cpu, old_cpu))
253        repeat_switch_cpu_list = tmp_cpu_list
254
255        if (maxtick - m5.curTick()) <= switch_freq:
256            exit_event = m5.simulate(maxtick - m5.curTick())
257            return exit_event
258
259def run(options, root, testsys, cpu_class):
260    if options.maxtick:
261        maxtick = options.maxtick
262    elif options.maxtime:
263        simtime = m5.ticks.seconds(simtime)
264        print "simulating for: ", simtime
265        maxtick = simtime
266    else:
267        maxtick = m5.MaxTick
268
269    if options.checkpoint_dir:
270        cptdir = options.checkpoint_dir
271    elif m5.options.outdir:
272        cptdir = m5.options.outdir
273    else:
274        cptdir = getcwd()
275
276    if options.fast_forward and options.checkpoint_restore != None:
277        fatal("Can't specify both --fast-forward and --checkpoint-restore")
278
279    if options.standard_switch and not options.caches:
280        fatal("Must specify --caches when using --standard-switch")
281
282    if options.standard_switch and options.repeat_switch:
283        fatal("Can't specify both --standard-switch and --repeat-switch")
284
285    if options.repeat_switch and options.take_checkpoints:
286        fatal("Can't specify both --repeat-switch and --take-checkpoints")
287
288    np = options.num_cpus
289    switch_cpus = None
290
291    if options.prog_interval:
292        for i in xrange(np):
293            testsys.cpu[i].progress_interval = options.prog_interval
294
295    if options.maxinsts:
296        for i in xrange(np):
297            testsys.cpu[i].max_insts_any_thread = options.maxinsts
298
299    if cpu_class:
300        switch_cpus = [cpu_class(switched_out=True, cpu_id=(i))
301                       for i in xrange(np)]
302
303        for i in xrange(np):
304            if options.fast_forward:
305                testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
306            switch_cpus[i].system =  testsys
307            switch_cpus[i].workload = testsys.cpu[i].workload
308            switch_cpus[i].clock = testsys.cpu[i].clock
309            # simulation period
310            if options.maxinsts:
311                switch_cpus[i].max_insts_any_thread = options.maxinsts
312            # Add checker cpu if selected
313            if options.checker:
314                switch_cpus[i].addCheckerCpu()
315
316        testsys.switch_cpus = switch_cpus
317        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
318
319    if options.repeat_switch:
320        switch_class = getCPUClass(options.cpu_type)[0]
321        if switch_class.require_caches() and \
322                not options.caches:
323            print "%s: Must be used with caches" % str(switch_class)
324            sys.exit(1)
325        if not switch_class.support_take_over():
326            print "%s: CPU switching not supported" % str(switch_class)
327            sys.exit(1)
328
329        repeat_switch_cpus = [switch_class(switched_out=True, \
330                                               cpu_id=(i)) for i in xrange(np)]
331
332        for i in xrange(np):
333            repeat_switch_cpus[i].system = testsys
334            repeat_switch_cpus[i].workload = testsys.cpu[i].workload
335            repeat_switch_cpus[i].clock = testsys.cpu[i].clock
336
337            if options.maxinsts:
338                repeat_switch_cpus[i].max_insts_any_thread = options.maxinsts
339
340            if options.checker:
341                repeat_switch_cpus[i].addCheckerCpu()
342
343        testsys.repeat_switch_cpus = repeat_switch_cpus
344
345        if cpu_class:
346            repeat_switch_cpu_list = [(switch_cpus[i], repeat_switch_cpus[i])
347                                      for i in xrange(np)]
348        else:
349            repeat_switch_cpu_list = [(testsys.cpu[i], repeat_switch_cpus[i])
350                                      for i in xrange(np)]
351
352    if options.standard_switch:
353        switch_cpus = [TimingSimpleCPU(switched_out=True, cpu_id=(i))
354                       for i in xrange(np)]
355        switch_cpus_1 = [DerivO3CPU(switched_out=True, cpu_id=(i))
356                        for i in xrange(np)]
357
358        for i in xrange(np):
359            switch_cpus[i].system =  testsys
360            switch_cpus_1[i].system =  testsys
361            switch_cpus[i].workload = testsys.cpu[i].workload
362            switch_cpus_1[i].workload = testsys.cpu[i].workload
363            switch_cpus[i].clock = testsys.cpu[i].clock
364            switch_cpus_1[i].clock = testsys.cpu[i].clock
365
366            # if restoring, make atomic cpu simulate only a few instructions
367            if options.checkpoint_restore != None:
368                testsys.cpu[i].max_insts_any_thread = 1
369            # Fast forward to specified location if we are not restoring
370            elif options.fast_forward:
371                testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
372            # Fast forward to a simpoint (warning: time consuming)
373            elif options.simpoint:
374                if testsys.cpu[i].workload[0].simpoint == 0:
375                    fatal('simpoint not found')
376                testsys.cpu[i].max_insts_any_thread = \
377                    testsys.cpu[i].workload[0].simpoint
378            # No distance specified, just switch
379            else:
380                testsys.cpu[i].max_insts_any_thread = 1
381
382            # warmup period
383            if options.warmup_insts:
384                switch_cpus[i].max_insts_any_thread =  options.warmup_insts
385
386            # simulation period
387            if options.maxinsts:
388                switch_cpus_1[i].max_insts_any_thread = options.maxinsts
389
390            # attach the checker cpu if selected
391            if options.checker:
392                switch_cpus[i].addCheckerCpu()
393                switch_cpus_1[i].addCheckerCpu()
394
395        testsys.switch_cpus = switch_cpus
396        testsys.switch_cpus_1 = switch_cpus_1
397        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
398        switch_cpu_list1 = [(switch_cpus[i], switch_cpus_1[i]) for i in xrange(np)]
399
400    # set the checkpoint in the cpu before m5.instantiate is called
401    if options.take_checkpoints != None and \
402           (options.simpoint or options.at_instruction):
403        offset = int(options.take_checkpoints)
404        # Set an instruction break point
405        if options.simpoint:
406            for i in xrange(np):
407                if testsys.cpu[i].workload[0].simpoint == 0:
408                    fatal('no simpoint for testsys.cpu[%d].workload[0]', i)
409                checkpoint_inst = int(testsys.cpu[i].workload[0].simpoint) + offset
410                testsys.cpu[i].max_insts_any_thread = checkpoint_inst
411                # used for output below
412                options.take_checkpoints = checkpoint_inst
413        else:
414            options.take_checkpoints = offset
415            # Set all test cpus with the right number of instructions
416            # for the upcoming simulation
417            for i in xrange(np):
418                testsys.cpu[i].max_insts_any_thread = offset
419
420    checkpoint_dir = None
421    if options.checkpoint_restore != None:
422        maxtick, checkpoint_dir = findCptDir(options, maxtick, cptdir, testsys)
423    m5.instantiate(checkpoint_dir)
424
425    if options.standard_switch or cpu_class:
426        if options.standard_switch:
427            print "Switch at instruction count:%s" % \
428                    str(testsys.cpu[0].max_insts_any_thread)
429            exit_event = m5.simulate()
430        elif cpu_class and options.fast_forward:
431            print "Switch at instruction count:%s" % \
432                    str(testsys.cpu[0].max_insts_any_thread)
433            exit_event = m5.simulate()
434        else:
435            print "Switch at curTick count:%s" % str(10000)
436            exit_event = m5.simulate(10000)
437        print "Switched CPUS @ tick %s" % (m5.curTick())
438
439        # when you change to Timing (or Atomic), you halt the system
440        # given as argument.  When you are finished with the system
441        # changes (including switchCpus), you must resume the system
442        # manually.  You DON'T need to resume after just switching
443        # CPUs if you haven't changed anything on the system level.
444
445        m5.changeToTiming(testsys)
446        m5.switchCpus(switch_cpu_list)
447        m5.resume(testsys)
448
449        if options.standard_switch:
450            print "Switch at instruction count:%d" % \
451                    (testsys.switch_cpus[0].max_insts_any_thread)
452
453            #warmup instruction count may have already been set
454            if options.warmup_insts:
455                exit_event = m5.simulate()
456            else:
457                exit_event = m5.simulate(options.standard_switch)
458            print "Switching CPUS @ tick %s" % (m5.curTick())
459            print "Simulation ends instruction count:%d" % \
460                    (testsys.switch_cpus_1[0].max_insts_any_thread)
461            m5.drain(testsys)
462            m5.switchCpus(switch_cpu_list1)
463            m5.resume(testsys)
464
465    # If we're taking and restoring checkpoints, use checkpoint_dir
466    # option only for finding the checkpoints to restore from.  This
467    # lets us test checkpointing by restoring from one set of
468    # checkpoints, generating a second set, and then comparing them.
469    if options.take_checkpoints and options.checkpoint_restore:
470        if m5.options.outdir:
471            cptdir = m5.options.outdir
472        else:
473            cptdir = getcwd()
474
475    if options.take_checkpoints != None :
476        # Checkpoints being taken via the command line at <when> and at
477        # subsequent periods of <period>.  Checkpoint instructions
478        # received from the benchmark running are ignored and skipped in
479        # favor of command line checkpoint instructions.
480        exit_cause = scriptCheckpoints(options, maxtick, cptdir)
481    else:
482        if options.fast_forward:
483            m5.stats.reset()
484        print "**** REAL SIMULATION ****"
485
486        # If checkpoints are being taken, then the checkpoint instruction
487        # will occur in the benchmark code it self.
488        if options.repeat_switch and maxtick > options.repeat_switch:
489            exit_event = repeatSwitch(testsys, repeat_switch_cpu_list,
490                                      maxtick, options.repeat_switch)
491        else:
492            exit_event = benchCheckpoints(options, maxtick, cptdir)
493
494    print 'Exiting @ tick %i because %s' % (m5.curTick(), exit_event.getCause())
495    if options.checkpoint_at_end:
496        m5.checkpoint(joinpath(cptdir, "cpt.%d"))
497
498    if not m5.options.interactive:
499        sys.exit(exit_event.getCode())
500