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