Simulation.py revision 9140:cfd2a8364ea1
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):
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
184        exit_event = m5.simulate(when)
185        exit_cause = exit_event.getCause()
186        while exit_cause == "checkpoint":
187            exit_event = m5.simulate(when - m5.curTick())
188            exit_cause = exit_event.getCause()
189
190        if exit_cause == "simulate() limit reached":
191            m5.checkpoint(joinpath(cptdir, "cpt.%d"))
192            num_checkpoints += 1
193
194        sim_ticks = when
195        num_checkpoints = 0
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    if options.fast_forward:
218        m5.stats.reset()
219
220    print "**** REAL SIMULATION ****"
221    exit_event = m5.simulate(maxtick)
222    exit_cause = exit_event.getCause()
223
224    num_checkpoints = 0
225    max_checkpoints = options.max_checkpoints
226
227    while exit_cause == "checkpoint":
228        m5.checkpoint(joinpath(cptdir, "cpt.%d"))
229        num_checkpoints += 1
230        if num_checkpoints == max_checkpoints:
231            exit_cause = "maximum %d checkpoints dropped" % max_checkpoints
232            break
233
234        exit_event = m5.simulate(maxtick - m5.curTick())
235        exit_cause = exit_event.getCause()
236
237    return exit_cause
238
239def run(options, root, testsys, cpu_class):
240    if options.maxtick:
241        maxtick = options.maxtick
242    elif options.maxtime:
243        simtime = m5.ticks.seconds(simtime)
244        print "simulating for: ", simtime
245        maxtick = simtime
246    else:
247        maxtick = m5.MaxTick
248
249    if options.checkpoint_dir:
250        cptdir = options.checkpoint_dir
251    elif m5.options.outdir:
252        cptdir = m5.options.outdir
253    else:
254        cptdir = getcwd()
255
256    if options.fast_forward and options.checkpoint_restore != None:
257        fatal("Can't specify both --fast-forward and --checkpoint-restore")
258
259    if options.standard_switch and not options.caches:
260        fatal("Must specify --caches when using --standard-switch")
261
262    np = options.num_cpus
263    switch_cpus = None
264
265    if options.prog_interval:
266        for i in xrange(np):
267            testsys.cpu[i].progress_interval = options.prog_interval
268
269    if options.maxinsts:
270        for i in xrange(np):
271            testsys.cpu[i].max_insts_any_thread = options.maxinsts
272
273    if cpu_class:
274        switch_cpus = [cpu_class(defer_registration=True, cpu_id=(np+i))
275                       for i in xrange(np)]
276
277        for i in xrange(np):
278            if options.fast_forward:
279                testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
280            switch_cpus[i].system =  testsys
281            switch_cpus[i].workload = testsys.cpu[i].workload
282            switch_cpus[i].clock = testsys.cpu[i].clock
283            # simulation period
284            if options.maxinsts:
285                switch_cpus[i].max_insts_any_thread = options.maxinsts
286            # Add checker cpu if selected
287            if options.checker:
288                switch_cpus[i].addCheckerCpu()
289
290        testsys.switch_cpus = switch_cpus
291        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
292
293    if options.standard_switch:
294        if not options.caches:
295            # O3 CPU must have a cache to work.
296            print "O3 CPU must be used with caches"
297            sys.exit(1)
298
299        switch_cpus = [TimingSimpleCPU(defer_registration=True, cpu_id=(np+i))
300                       for i in xrange(np)]
301        switch_cpus_1 = [DerivO3CPU(defer_registration=True, cpu_id=(2*np+i))
302                        for i in xrange(np)]
303
304        for i in xrange(np):
305            switch_cpus[i].system =  testsys
306            switch_cpus_1[i].system =  testsys
307            switch_cpus[i].workload = testsys.cpu[i].workload
308            switch_cpus_1[i].workload = testsys.cpu[i].workload
309            switch_cpus[i].clock = testsys.cpu[i].clock
310            switch_cpus_1[i].clock = testsys.cpu[i].clock
311
312            # if restoring, make atomic cpu simulate only a few instructions
313            if options.checkpoint_restore != None:
314                testsys.cpu[i].max_insts_any_thread = 1
315            # Fast forward to specified location if we are not restoring
316            elif options.fast_forward:
317                testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
318            # Fast forward to a simpoint (warning: time consuming)
319            elif options.simpoint:
320                if testsys.cpu[i].workload[0].simpoint == 0:
321                    fatal('simpoint not found')
322                testsys.cpu[i].max_insts_any_thread = \
323                    testsys.cpu[i].workload[0].simpoint
324            # No distance specified, just switch
325            else:
326                testsys.cpu[i].max_insts_any_thread = 1
327
328            # warmup period
329            if options.warmup_insts:
330                switch_cpus[i].max_insts_any_thread =  options.warmup_insts
331
332            # simulation period
333            if options.maxinsts:
334                switch_cpus_1[i].max_insts_any_thread = options.maxinsts
335
336            # attach the checker cpu if selected
337            if options.checker:
338                switch_cpus[i].addCheckerCpu()
339                switch_cpus_1[i].addCheckerCpu()
340
341        testsys.switch_cpus = switch_cpus
342        testsys.switch_cpus_1 = switch_cpus_1
343        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
344        switch_cpu_list1 = [(switch_cpus[i], switch_cpus_1[i]) for i in xrange(np)]
345
346    # set the checkpoint in the cpu before m5.instantiate is called
347    if options.take_checkpoints != None and \
348           (options.simpoint or options.at_instruction):
349        offset = int(options.take_checkpoints)
350        # Set an instruction break point
351        if options.simpoint:
352            for i in xrange(np):
353                if testsys.cpu[i].workload[0].simpoint == 0:
354                    fatal('no simpoint for testsys.cpu[%d].workload[0]', i)
355                checkpoint_inst = int(testsys.cpu[i].workload[0].simpoint) + offset
356                testsys.cpu[i].max_insts_any_thread = checkpoint_inst
357                # used for output below
358                options.take_checkpoints = checkpoint_inst
359        else:
360            options.take_checkpoints = offset
361            # Set all test cpus with the right number of instructions
362            # for the upcoming simulation
363            for i in xrange(np):
364                testsys.cpu[i].max_insts_any_thread = offset
365
366    checkpoint_dir = None
367    if options.checkpoint_restore != None:
368        maxtick, checkpoint_dir = findCptDir(options, maxtick, cptdir, testsys)
369    m5.instantiate(checkpoint_dir)
370
371    if options.standard_switch or cpu_class:
372        if options.standard_switch:
373            print "Switch at instruction count:%s" % \
374                    str(testsys.cpu[0].max_insts_any_thread)
375            exit_event = m5.simulate()
376        elif cpu_class and options.fast_forward:
377            print "Switch at instruction count:%s" % \
378                    str(testsys.cpu[0].max_insts_any_thread)
379            exit_event = m5.simulate()
380        else:
381            print "Switch at curTick count:%s" % str(10000)
382            exit_event = m5.simulate(10000)
383        print "Switched CPUS @ tick %s" % (m5.curTick())
384
385        # when you change to Timing (or Atomic), you halt the system
386        # given as argument.  When you are finished with the system
387        # changes (including switchCpus), you must resume the system
388        # manually.  You DON'T need to resume after just switching
389        # CPUs if you haven't changed anything on the system level.
390
391        m5.changeToTiming(testsys)
392        m5.switchCpus(switch_cpu_list)
393        m5.resume(testsys)
394
395        if options.standard_switch:
396            print "Switch at instruction count:%d" % \
397                    (testsys.switch_cpus[0].max_insts_any_thread)
398
399            #warmup instruction count may have already been set
400            if options.warmup_insts:
401                exit_event = m5.simulate()
402            else:
403                exit_event = m5.simulate(options.warmup)
404            print "Switching CPUS @ tick %s" % (m5.curTick())
405            print "Simulation ends instruction count:%d" % \
406                    (testsys.switch_cpus_1[0].max_insts_any_thread)
407            m5.drain(testsys)
408            m5.switchCpus(switch_cpu_list1)
409            m5.resume(testsys)
410
411    # If we're taking and restoring checkpoints, use checkpoint_dir
412    # option only for finding the checkpoints to restore from.  This
413    # lets us test checkpointing by restoring from one set of
414    # checkpoints, generating a second set, and then comparing them.
415    if options.take_checkpoints and options.checkpoint_restore:
416        if m5.options.outdir:
417            cptdir = m5.options.outdir
418        else:
419            cptdir = getcwd()
420
421    if options.take_checkpoints != None :
422        # Checkpoints being taken via the command line at <when> and at
423        # subsequent periods of <period>.  Checkpoint instructions
424        # received from the benchmark running are ignored and skipped in
425        # favor of command line checkpoint instructions.
426        exit_cause = scriptCheckpoints(options)
427    else:
428        # If checkpoints are being taken, then the checkpoint instruction
429        # will occur in the benchmark code it self.
430        exit_cause = benchCheckpoints(options, maxtick, cptdir)
431
432    print 'Exiting @ tick %i because %s' % (m5.curTick(), exit_cause)
433    if options.checkpoint_at_end:
434        m5.checkpoint(joinpath(cptdir, "cpt.%d"))
435