Simulation.py revision 9494:50da272a1300
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
30import sys
31from os import getcwd
32from os.path import join as joinpath
33
34import m5
35from m5.defines import buildEnv
36from m5.objects import *
37from m5.util import *
38from O3_ARM_v7a import *
39
40addToPath('../common')
41
42def getCPUClass(cpu_type):
43    """Returns the required cpu class and the mode of operation.
44    """
45
46    if cpu_type == "timing":
47        return TimingSimpleCPU, 'timing'
48    elif cpu_type == "detailed":
49        return DerivO3CPU, 'timing'
50    elif cpu_type == "arm_detailed":
51        return O3_ARM_v7a_3, 'timing'
52    elif cpu_type == "inorder":
53        return InOrderCPU, 'timing'
54    else:
55        return AtomicSimpleCPU, 'atomic'
56
57def setCPUClass(options):
58    """Returns two cpu classes and the initial mode of operation.
59
60       Restoring from a checkpoint or fast forwarding through a benchmark
61       can be done using one type of cpu, and then the actual
62       simulation can be carried out using another type. This function
63       returns these two types of cpus and the initial mode of operation
64       depending on the options provided.
65    """
66
67    if options.cpu_type == "detailed" or \
68       options.cpu_type == "arm_detailed" or \
69       options.cpu_type == "inorder" :
70        if not options.caches and not options.ruby:
71            fatal("O3/Inorder CPU must be used with caches")
72
73    TmpClass, test_mem_mode = getCPUClass(options.cpu_type)
74    CPUClass = None
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        if options.cpu_type == "arm_detailed":
321            if not options.caches:
322                print "O3 CPU must be used with caches"
323                sys.exit(1)
324
325            repeat_switch_cpus = [O3_ARM_v7a_3(switched_out=True, \
326                                  cpu_id=(i)) for i in xrange(np)]
327        elif options.cpu_type == "detailed":
328            if not options.caches:
329                print "O3 CPU must be used with caches"
330                sys.exit(1)
331
332            repeat_switch_cpus = [DerivO3CPU(switched_out=True, \
333                                  cpu_id=(i)) for i in xrange(np)]
334        elif options.cpu_type == "inorder":
335            print "inorder CPU switching not supported"
336            sys.exit(1)
337        elif options.cpu_type == "timing":
338            repeat_switch_cpus = [TimingSimpleCPU(switched_out=True, \
339                                  cpu_id=(i)) for i in xrange(np)]
340        else:
341            repeat_switch_cpus = [AtomicSimpleCPU(switched_out=True, \
342                                  cpu_id=(i)) for i in xrange(np)]
343
344        for i in xrange(np):
345            repeat_switch_cpus[i].system = testsys
346            repeat_switch_cpus[i].workload = testsys.cpu[i].workload
347            repeat_switch_cpus[i].clock = testsys.cpu[i].clock
348
349            if options.maxinsts:
350                repeat_switch_cpus[i].max_insts_any_thread = options.maxinsts
351
352            if options.checker:
353                repeat_switch_cpus[i].addCheckerCpu()
354
355        testsys.repeat_switch_cpus = repeat_switch_cpus
356
357        if cpu_class:
358            repeat_switch_cpu_list = [(switch_cpus[i], repeat_switch_cpus[i])
359                                      for i in xrange(np)]
360        else:
361            repeat_switch_cpu_list = [(testsys.cpu[i], repeat_switch_cpus[i])
362                                      for i in xrange(np)]
363
364    if options.standard_switch:
365        switch_cpus = [TimingSimpleCPU(switched_out=True, cpu_id=(i))
366                       for i in xrange(np)]
367        switch_cpus_1 = [DerivO3CPU(switched_out=True, cpu_id=(i))
368                        for i in xrange(np)]
369
370        for i in xrange(np):
371            switch_cpus[i].system =  testsys
372            switch_cpus_1[i].system =  testsys
373            switch_cpus[i].workload = testsys.cpu[i].workload
374            switch_cpus_1[i].workload = testsys.cpu[i].workload
375            switch_cpus[i].clock = testsys.cpu[i].clock
376            switch_cpus_1[i].clock = testsys.cpu[i].clock
377
378            # if restoring, make atomic cpu simulate only a few instructions
379            if options.checkpoint_restore != None:
380                testsys.cpu[i].max_insts_any_thread = 1
381            # Fast forward to specified location if we are not restoring
382            elif options.fast_forward:
383                testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
384            # Fast forward to a simpoint (warning: time consuming)
385            elif options.simpoint:
386                if testsys.cpu[i].workload[0].simpoint == 0:
387                    fatal('simpoint not found')
388                testsys.cpu[i].max_insts_any_thread = \
389                    testsys.cpu[i].workload[0].simpoint
390            # No distance specified, just switch
391            else:
392                testsys.cpu[i].max_insts_any_thread = 1
393
394            # warmup period
395            if options.warmup_insts:
396                switch_cpus[i].max_insts_any_thread =  options.warmup_insts
397
398            # simulation period
399            if options.maxinsts:
400                switch_cpus_1[i].max_insts_any_thread = options.maxinsts
401
402            # attach the checker cpu if selected
403            if options.checker:
404                switch_cpus[i].addCheckerCpu()
405                switch_cpus_1[i].addCheckerCpu()
406
407        testsys.switch_cpus = switch_cpus
408        testsys.switch_cpus_1 = switch_cpus_1
409        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
410        switch_cpu_list1 = [(switch_cpus[i], switch_cpus_1[i]) for i in xrange(np)]
411
412    # set the checkpoint in the cpu before m5.instantiate is called
413    if options.take_checkpoints != None and \
414           (options.simpoint or options.at_instruction):
415        offset = int(options.take_checkpoints)
416        # Set an instruction break point
417        if options.simpoint:
418            for i in xrange(np):
419                if testsys.cpu[i].workload[0].simpoint == 0:
420                    fatal('no simpoint for testsys.cpu[%d].workload[0]', i)
421                checkpoint_inst = int(testsys.cpu[i].workload[0].simpoint) + offset
422                testsys.cpu[i].max_insts_any_thread = checkpoint_inst
423                # used for output below
424                options.take_checkpoints = checkpoint_inst
425        else:
426            options.take_checkpoints = offset
427            # Set all test cpus with the right number of instructions
428            # for the upcoming simulation
429            for i in xrange(np):
430                testsys.cpu[i].max_insts_any_thread = offset
431
432    checkpoint_dir = None
433    if options.checkpoint_restore != None:
434        maxtick, checkpoint_dir = findCptDir(options, maxtick, cptdir, testsys)
435    m5.instantiate(checkpoint_dir)
436
437    if options.standard_switch or cpu_class:
438        if options.standard_switch:
439            print "Switch at instruction count:%s" % \
440                    str(testsys.cpu[0].max_insts_any_thread)
441            exit_event = m5.simulate()
442        elif cpu_class and options.fast_forward:
443            print "Switch at instruction count:%s" % \
444                    str(testsys.cpu[0].max_insts_any_thread)
445            exit_event = m5.simulate()
446        else:
447            print "Switch at curTick count:%s" % str(10000)
448            exit_event = m5.simulate(10000)
449        print "Switched CPUS @ tick %s" % (m5.curTick())
450
451        # when you change to Timing (or Atomic), you halt the system
452        # given as argument.  When you are finished with the system
453        # changes (including switchCpus), you must resume the system
454        # manually.  You DON'T need to resume after just switching
455        # CPUs if you haven't changed anything on the system level.
456
457        m5.changeToTiming(testsys)
458        m5.switchCpus(switch_cpu_list)
459        m5.resume(testsys)
460
461        if options.standard_switch:
462            print "Switch at instruction count:%d" % \
463                    (testsys.switch_cpus[0].max_insts_any_thread)
464
465            #warmup instruction count may have already been set
466            if options.warmup_insts:
467                exit_event = m5.simulate()
468            else:
469                exit_event = m5.simulate(options.standard_switch)
470            print "Switching CPUS @ tick %s" % (m5.curTick())
471            print "Simulation ends instruction count:%d" % \
472                    (testsys.switch_cpus_1[0].max_insts_any_thread)
473            m5.drain(testsys)
474            m5.switchCpus(switch_cpu_list1)
475            m5.resume(testsys)
476
477    # If we're taking and restoring checkpoints, use checkpoint_dir
478    # option only for finding the checkpoints to restore from.  This
479    # lets us test checkpointing by restoring from one set of
480    # checkpoints, generating a second set, and then comparing them.
481    if options.take_checkpoints and options.checkpoint_restore:
482        if m5.options.outdir:
483            cptdir = m5.options.outdir
484        else:
485            cptdir = getcwd()
486
487    if options.take_checkpoints != None :
488        # Checkpoints being taken via the command line at <when> and at
489        # subsequent periods of <period>.  Checkpoint instructions
490        # received from the benchmark running are ignored and skipped in
491        # favor of command line checkpoint instructions.
492        exit_cause = scriptCheckpoints(options, maxtick, cptdir)
493    else:
494        if options.fast_forward:
495            m5.stats.reset()
496        print "**** REAL SIMULATION ****"
497
498        # If checkpoints are being taken, then the checkpoint instruction
499        # will occur in the benchmark code it self.
500        if options.repeat_switch and maxtick > options.repeat_switch:
501            exit_event = repeatSwitch(testsys, repeat_switch_cpu_list,
502                                      maxtick, options.repeat_switch)
503        else:
504            exit_event = benchCheckpoints(options, maxtick, cptdir)
505
506    print 'Exiting @ tick %i because %s' % (m5.curTick(), exit_event.getCause())
507    if options.checkpoint_at_end:
508        m5.checkpoint(joinpath(cptdir, "cpt.%d"))
509
510    if not m5.options.interactive:
511        sys.exit(exit_event.getCode())
512