Simulation.py revision 10159:ca6f1407f8f8
1# Copyright (c) 2012-2013 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
47import MemConfig
48
49import m5
50from m5.defines import buildEnv
51from m5.objects import *
52from m5.util import *
53
54addToPath('../common')
55
56def getCPUClass(cpu_type):
57    """Returns the required cpu class and the mode of operation."""
58    cls = CpuConfig.get(cpu_type)
59    return cls, cls.memory_mode()
60
61def setCPUClass(options):
62    """Returns two cpu classes and the initial mode of operation.
63
64       Restoring from a checkpoint or fast forwarding through a benchmark
65       can be done using one type of cpu, and then the actual
66       simulation can be carried out using another type. This function
67       returns these two types of cpus and the initial mode of operation
68       depending on the options provided.
69    """
70
71    TmpClass, test_mem_mode = getCPUClass(options.cpu_type)
72    CPUClass = None
73    if TmpClass.require_caches() and \
74            not options.caches and not options.ruby:
75        fatal("%s must be used with caches" % options.cpu_type)
76
77    if options.checkpoint_restore != None:
78        if options.restore_with_cpu != options.cpu_type:
79            CPUClass = TmpClass
80            TmpClass, test_mem_mode = getCPUClass(options.restore_with_cpu)
81    elif options.fast_forward:
82        CPUClass = TmpClass
83        TmpClass = AtomicSimpleCPU
84        test_mem_mode = 'atomic'
85
86    return (TmpClass, test_mem_mode, CPUClass)
87
88def setMemClass(options):
89    """Returns a memory controller class."""
90
91    return MemConfig.get(options.mem_type)
92
93def setWorkCountOptions(system, options):
94    if options.work_item_id != None:
95        system.work_item_id = options.work_item_id
96    if options.num_work_ids != None:
97        system.num_work_ids = options.num_work_ids
98    if options.work_begin_cpu_id_exit != None:
99        system.work_begin_cpu_id_exit = options.work_begin_cpu_id_exit
100    if options.work_end_exit_count != None:
101        system.work_end_exit_count = options.work_end_exit_count
102    if options.work_end_checkpoint_count != None:
103        system.work_end_ckpt_count = options.work_end_checkpoint_count
104    if options.work_begin_exit_count != None:
105        system.work_begin_exit_count = options.work_begin_exit_count
106    if options.work_begin_checkpoint_count != None:
107        system.work_begin_ckpt_count = options.work_begin_checkpoint_count
108    if options.work_cpus_checkpoint_count != None:
109        system.work_cpus_ckpt_count = options.work_cpus_checkpoint_count
110
111def findCptDir(options, cptdir, testsys):
112    """Figures out the directory from which the checkpointed state is read.
113
114    There are two different ways in which the directories holding checkpoints
115    can be named --
116    1. cpt.<benchmark name>.<instruction count when the checkpoint was taken>
117    2. cpt.<some number, usually the tick value when the checkpoint was taken>
118
119    This function parses through the options to figure out which one of the
120    above should be used for selecting the checkpoint, and then figures out
121    the appropriate directory.
122    """
123
124    from os.path import isdir, exists
125    from os import listdir
126    import re
127
128    if not isdir(cptdir):
129        fatal("checkpoint dir %s does not exist!", cptdir)
130
131    cpt_starttick = 0
132    if options.at_instruction or options.simpoint:
133        inst = options.checkpoint_restore
134        if options.simpoint:
135            # assume workload 0 has the simpoint
136            if testsys.cpu[0].workload[0].simpoint == 0:
137                fatal('Unable to find simpoint')
138            inst += int(testsys.cpu[0].workload[0].simpoint)
139
140        checkpoint_dir = joinpath(cptdir, "cpt.%s.%s" % (options.bench, inst))
141        if not exists(checkpoint_dir):
142            fatal("Unable to find checkpoint directory %s", checkpoint_dir)
143    else:
144        dirs = listdir(cptdir)
145        expr = re.compile('cpt\.([0-9]*)')
146        cpts = []
147        for dir in dirs:
148            match = expr.match(dir)
149            if match:
150                cpts.append(match.group(1))
151
152        cpts.sort(lambda a,b: cmp(long(a), long(b)))
153
154        cpt_num = options.checkpoint_restore
155        if cpt_num > len(cpts):
156            fatal('Checkpoint %d not found', cpt_num)
157
158        cpt_starttick = int(cpts[cpt_num - 1])
159        checkpoint_dir = joinpath(cptdir, "cpt.%s" % cpts[cpt_num - 1])
160
161    return cpt_starttick, checkpoint_dir
162
163def scriptCheckpoints(options, maxtick, cptdir):
164    if options.at_instruction or options.simpoint:
165        checkpoint_inst = int(options.take_checkpoints)
166
167        # maintain correct offset if we restored from some instruction
168        if options.checkpoint_restore != None:
169            checkpoint_inst += options.checkpoint_restore
170
171        print "Creating checkpoint at inst:%d" % (checkpoint_inst)
172        exit_event = m5.simulate()
173        exit_cause = exit_event.getCause()
174        print "exit cause = %s" % exit_cause
175
176        # skip checkpoint instructions should they exist
177        while exit_cause == "checkpoint":
178            exit_event = m5.simulate()
179            exit_cause = exit_event.getCause()
180
181        if exit_cause == "a thread reached the max instruction count":
182            m5.checkpoint(joinpath(cptdir, "cpt.%s.%d" % \
183                    (options.bench, checkpoint_inst)))
184            print "Checkpoint written."
185
186    else:
187        when, period = options.take_checkpoints.split(",", 1)
188        when = int(when)
189        period = int(period)
190        num_checkpoints = 0
191
192        exit_event = m5.simulate(when - m5.curTick())
193        exit_cause = exit_event.getCause()
194        while exit_cause == "checkpoint":
195            exit_event = m5.simulate(when - m5.curTick())
196            exit_cause = exit_event.getCause()
197
198        if exit_cause == "simulate() limit reached":
199            m5.checkpoint(joinpath(cptdir, "cpt.%d"))
200            num_checkpoints += 1
201
202        sim_ticks = when
203        max_checkpoints = options.max_checkpoints
204
205        while num_checkpoints < max_checkpoints and \
206                exit_cause == "simulate() limit reached":
207            if (sim_ticks + period) > maxtick:
208                exit_event = m5.simulate(maxtick - sim_ticks)
209                exit_cause = exit_event.getCause()
210                break
211            else:
212                exit_event = m5.simulate(period)
213                exit_cause = exit_event.getCause()
214                sim_ticks += period
215                while exit_event.getCause() == "checkpoint":
216                    exit_event = m5.simulate(sim_ticks - m5.curTick())
217                if exit_event.getCause() == "simulate() limit reached":
218                    m5.checkpoint(joinpath(cptdir, "cpt.%d"))
219                    num_checkpoints += 1
220
221    return exit_event
222
223def benchCheckpoints(options, maxtick, cptdir):
224    exit_event = m5.simulate(maxtick - m5.curTick())
225    exit_cause = exit_event.getCause()
226
227    num_checkpoints = 0
228    max_checkpoints = options.max_checkpoints
229
230    while exit_cause == "checkpoint":
231        m5.checkpoint(joinpath(cptdir, "cpt.%d"))
232        num_checkpoints += 1
233        if num_checkpoints == max_checkpoints:
234            exit_cause = "maximum %d checkpoints dropped" % max_checkpoints
235            break
236
237        exit_event = m5.simulate(maxtick - m5.curTick())
238        exit_cause = exit_event.getCause()
239
240    return exit_event
241
242def repeatSwitch(testsys, repeat_switch_cpu_list, maxtick, switch_freq):
243    print "starting switch loop"
244    while True:
245        exit_event = m5.simulate(switch_freq)
246        exit_cause = exit_event.getCause()
247
248        if exit_cause != "simulate() limit reached":
249            return exit_event
250
251        m5.switchCpus(testsys, repeat_switch_cpu_list)
252
253        tmp_cpu_list = []
254        for old_cpu, new_cpu in repeat_switch_cpu_list:
255            tmp_cpu_list.append((new_cpu, old_cpu))
256        repeat_switch_cpu_list = tmp_cpu_list
257
258        if (maxtick - m5.curTick()) <= switch_freq:
259            exit_event = m5.simulate(maxtick - m5.curTick())
260            return exit_event
261
262def run(options, root, testsys, cpu_class):
263    if options.checkpoint_dir:
264        cptdir = options.checkpoint_dir
265    elif m5.options.outdir:
266        cptdir = m5.options.outdir
267    else:
268        cptdir = getcwd()
269
270    if options.fast_forward and options.checkpoint_restore != None:
271        fatal("Can't specify both --fast-forward and --checkpoint-restore")
272
273    if options.standard_switch and not options.caches:
274        fatal("Must specify --caches when using --standard-switch")
275
276    if options.standard_switch and options.repeat_switch:
277        fatal("Can't specify both --standard-switch and --repeat-switch")
278
279    if options.repeat_switch and options.take_checkpoints:
280        fatal("Can't specify both --repeat-switch and --take-checkpoints")
281
282    np = options.num_cpus
283    switch_cpus = None
284
285    if options.prog_interval:
286        for i in xrange(np):
287            testsys.cpu[i].progress_interval = options.prog_interval
288
289    if options.maxinsts:
290        for i in xrange(np):
291            testsys.cpu[i].max_insts_any_thread = options.maxinsts
292
293    if cpu_class:
294        switch_cpus = [cpu_class(switched_out=True, cpu_id=(i))
295                       for i in xrange(np)]
296
297        for i in xrange(np):
298            if options.fast_forward:
299                testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
300            switch_cpus[i].system =  testsys
301            switch_cpus[i].workload = testsys.cpu[i].workload
302            switch_cpus[i].clk_domain = testsys.cpu[i].clk_domain
303            # simulation period
304            if options.maxinsts:
305                switch_cpus[i].max_insts_any_thread = options.maxinsts
306            # Add checker cpu if selected
307            if options.checker:
308                switch_cpus[i].addCheckerCpu()
309
310        testsys.switch_cpus = switch_cpus
311        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
312
313    if options.repeat_switch:
314        switch_class = getCPUClass(options.cpu_type)[0]
315        if switch_class.require_caches() and \
316                not options.caches:
317            print "%s: Must be used with caches" % str(switch_class)
318            sys.exit(1)
319        if not switch_class.support_take_over():
320            print "%s: CPU switching not supported" % str(switch_class)
321            sys.exit(1)
322
323        repeat_switch_cpus = [switch_class(switched_out=True, \
324                                               cpu_id=(i)) for i in xrange(np)]
325
326        for i in xrange(np):
327            repeat_switch_cpus[i].system = testsys
328            repeat_switch_cpus[i].workload = testsys.cpu[i].workload
329            repeat_switch_cpus[i].clk_domain = testsys.cpu[i].clk_domain
330
331            if options.maxinsts:
332                repeat_switch_cpus[i].max_insts_any_thread = options.maxinsts
333
334            if options.checker:
335                repeat_switch_cpus[i].addCheckerCpu()
336
337        testsys.repeat_switch_cpus = repeat_switch_cpus
338
339        if cpu_class:
340            repeat_switch_cpu_list = [(switch_cpus[i], repeat_switch_cpus[i])
341                                      for i in xrange(np)]
342        else:
343            repeat_switch_cpu_list = [(testsys.cpu[i], repeat_switch_cpus[i])
344                                      for i in xrange(np)]
345
346    if options.standard_switch:
347        switch_cpus = [TimingSimpleCPU(switched_out=True, cpu_id=(i))
348                       for i in xrange(np)]
349        switch_cpus_1 = [DerivO3CPU(switched_out=True, cpu_id=(i))
350                        for i in xrange(np)]
351
352        for i in xrange(np):
353            switch_cpus[i].system =  testsys
354            switch_cpus_1[i].system =  testsys
355            switch_cpus[i].workload = testsys.cpu[i].workload
356            switch_cpus_1[i].workload = testsys.cpu[i].workload
357            switch_cpus[i].clk_domain = testsys.cpu[i].clk_domain
358            switch_cpus_1[i].clk_domain = testsys.cpu[i].clk_domain
359
360            # if restoring, make atomic cpu simulate only a few instructions
361            if options.checkpoint_restore != None:
362                testsys.cpu[i].max_insts_any_thread = 1
363            # Fast forward to specified location if we are not restoring
364            elif options.fast_forward:
365                testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
366            # Fast forward to a simpoint (warning: time consuming)
367            elif options.simpoint:
368                if testsys.cpu[i].workload[0].simpoint == 0:
369                    fatal('simpoint not found')
370                testsys.cpu[i].max_insts_any_thread = \
371                    testsys.cpu[i].workload[0].simpoint
372            # No distance specified, just switch
373            else:
374                testsys.cpu[i].max_insts_any_thread = 1
375
376            # warmup period
377            if options.warmup_insts:
378                switch_cpus[i].max_insts_any_thread =  options.warmup_insts
379
380            # simulation period
381            if options.maxinsts:
382                switch_cpus_1[i].max_insts_any_thread = options.maxinsts
383
384            # attach the checker cpu if selected
385            if options.checker:
386                switch_cpus[i].addCheckerCpu()
387                switch_cpus_1[i].addCheckerCpu()
388
389        testsys.switch_cpus = switch_cpus
390        testsys.switch_cpus_1 = switch_cpus_1
391        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
392        switch_cpu_list1 = [(switch_cpus[i], switch_cpus_1[i]) for i in xrange(np)]
393
394    # set the checkpoint in the cpu before m5.instantiate is called
395    if options.take_checkpoints != None and \
396           (options.simpoint or options.at_instruction):
397        offset = int(options.take_checkpoints)
398        # Set an instruction break point
399        if options.simpoint:
400            for i in xrange(np):
401                if testsys.cpu[i].workload[0].simpoint == 0:
402                    fatal('no simpoint for testsys.cpu[%d].workload[0]', i)
403                checkpoint_inst = int(testsys.cpu[i].workload[0].simpoint) + offset
404                testsys.cpu[i].max_insts_any_thread = checkpoint_inst
405                # used for output below
406                options.take_checkpoints = checkpoint_inst
407        else:
408            options.take_checkpoints = offset
409            # Set all test cpus with the right number of instructions
410            # for the upcoming simulation
411            for i in xrange(np):
412                testsys.cpu[i].max_insts_any_thread = offset
413
414    checkpoint_dir = None
415    if options.checkpoint_restore:
416        cpt_starttick, checkpoint_dir = findCptDir(options, cptdir, testsys)
417    m5.instantiate(checkpoint_dir)
418
419    # Handle the max tick settings now that tick frequency was resolved
420    # during system instantiation
421    # NOTE: the maxtick variable here is in absolute ticks, so it must
422    # include any simulated ticks before a checkpoint
423    explicit_maxticks = 0
424    maxtick_from_abs = m5.MaxTick
425    maxtick_from_rel = m5.MaxTick
426    maxtick_from_maxtime = m5.MaxTick
427    if options.abs_max_tick:
428        maxtick_from_abs = options.abs_max_tick
429        explicit_maxticks += 1
430    if options.rel_max_tick:
431        maxtick_from_rel = options.rel_max_tick
432        if options.checkpoint_restore:
433            # NOTE: this may need to be updated if checkpoints ever store
434            # the ticks per simulated second
435            maxtick_from_rel += cpt_starttick
436            if options.at_instruction or options.simpoint:
437                warn("Relative max tick specified with --at-instruction or" \
438                     " --simpoint\n      These options don't specify the " \
439                     "checkpoint start tick, so assuming\n      you mean " \
440                     "absolute max tick")
441        explicit_maxticks += 1
442    if options.maxtime:
443        maxtick_from_maxtime = m5.ticks.fromSeconds(options.maxtime)
444        explicit_maxticks += 1
445    if explicit_maxticks > 1:
446        warn("Specified multiple of --abs-max-tick, --rel-max-tick, --maxtime."\
447             " Using least")
448    maxtick = min([maxtick_from_abs, maxtick_from_rel, maxtick_from_maxtime])
449
450    if options.checkpoint_restore != None and maxtick < cpt_starttick:
451        fatal("Bad maxtick (%d) specified: " \
452              "Checkpoint starts starts from tick: %d", maxtick, cpt_starttick)
453
454    if options.standard_switch or cpu_class:
455        if options.standard_switch:
456            print "Switch at instruction count:%s" % \
457                    str(testsys.cpu[0].max_insts_any_thread)
458            exit_event = m5.simulate()
459        elif cpu_class and options.fast_forward:
460            print "Switch at instruction count:%s" % \
461                    str(testsys.cpu[0].max_insts_any_thread)
462            exit_event = m5.simulate()
463        else:
464            print "Switch at curTick count:%s" % str(10000)
465            exit_event = m5.simulate(10000)
466        print "Switched CPUS @ tick %s" % (m5.curTick())
467
468        m5.switchCpus(testsys, switch_cpu_list)
469
470        if options.standard_switch:
471            print "Switch at instruction count:%d" % \
472                    (testsys.switch_cpus[0].max_insts_any_thread)
473
474            #warmup instruction count may have already been set
475            if options.warmup_insts:
476                exit_event = m5.simulate()
477            else:
478                exit_event = m5.simulate(options.standard_switch)
479            print "Switching CPUS @ tick %s" % (m5.curTick())
480            print "Simulation ends instruction count:%d" % \
481                    (testsys.switch_cpus_1[0].max_insts_any_thread)
482            m5.switchCpus(testsys, switch_cpu_list1)
483
484    # If we're taking and restoring checkpoints, use checkpoint_dir
485    # option only for finding the checkpoints to restore from.  This
486    # lets us test checkpointing by restoring from one set of
487    # checkpoints, generating a second set, and then comparing them.
488    if options.take_checkpoints and options.checkpoint_restore:
489        if m5.options.outdir:
490            cptdir = m5.options.outdir
491        else:
492            cptdir = getcwd()
493
494    if options.take_checkpoints != None :
495        # Checkpoints being taken via the command line at <when> and at
496        # subsequent periods of <period>.  Checkpoint instructions
497        # received from the benchmark running are ignored and skipped in
498        # favor of command line checkpoint instructions.
499        exit_event = scriptCheckpoints(options, maxtick, cptdir)
500    else:
501        if options.fast_forward:
502            m5.stats.reset()
503        print "**** REAL SIMULATION ****"
504
505        # If checkpoints are being taken, then the checkpoint instruction
506        # will occur in the benchmark code it self.
507        if options.repeat_switch and maxtick > options.repeat_switch:
508            exit_event = repeatSwitch(testsys, repeat_switch_cpu_list,
509                                      maxtick, options.repeat_switch)
510        else:
511            exit_event = benchCheckpoints(options, maxtick, cptdir)
512
513    print 'Exiting @ tick %i because %s' % (m5.curTick(), exit_event.getCause())
514    if options.checkpoint_at_end:
515        m5.checkpoint(joinpath(cptdir, "cpt.%d"))
516
517    if not m5.options.interactive:
518        sys.exit(exit_event.getCode())
519