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