Simulation.py revision 8803:f6c5785bc8fd
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 setCPUClass(options):
42
43    atomic = False
44    if options.cpu_type == "timing":
45        class TmpClass(TimingSimpleCPU): pass
46    elif options.cpu_type == "detailed" or options.cpu_type == "arm_detailed":
47        if not options.caches and not options.ruby:
48            print "O3 CPU must be used with caches"
49            sys.exit(1)
50        if options.cpu_type == "arm_detailed":
51            class TmpClass(O3_ARM_v7a_3): pass
52        else:
53            class TmpClass(DerivO3CPU): pass
54    elif options.cpu_type == "inorder":
55        if not options.caches:
56            print "InOrder CPU must be used with caches"
57            sys.exit(1)
58        class TmpClass(InOrderCPU): pass
59    else:
60        class TmpClass(AtomicSimpleCPU): pass
61        atomic = True
62
63    CPUClass = None
64    test_mem_mode = 'atomic'
65
66    if not atomic:
67        if options.checkpoint_restore != None:
68            if options.restore_with_cpu != options.cpu_type:
69                CPUClass = TmpClass
70                class TmpClass(AtomicSimpleCPU): pass
71            else:
72                if options.restore_with_cpu != "atomic":
73                    test_mem_mode = 'timing'
74
75        elif options.fast_forward:
76            CPUClass = TmpClass
77            class TmpClass(AtomicSimpleCPU): pass
78        else:
79            test_mem_mode = 'timing'
80
81    return (TmpClass, test_mem_mode, CPUClass)
82
83
84def run(options, root, testsys, cpu_class):
85    if options.maxtick:
86        maxtick = options.maxtick
87    elif options.maxtime:
88        simtime = m5.ticks.seconds(simtime)
89        print "simulating for: ", simtime
90        maxtick = simtime
91    else:
92        maxtick = m5.MaxTick
93
94    if options.checkpoint_dir:
95        cptdir = options.checkpoint_dir
96    elif m5.options.outdir:
97        cptdir = m5.options.outdir
98    else:
99        cptdir = getcwd()
100
101    if options.fast_forward and options.checkpoint_restore != None:
102        fatal("Can't specify both --fast-forward and --checkpoint-restore")
103
104    if options.standard_switch and not options.caches:
105        fatal("Must specify --caches when using --standard-switch")
106
107    np = options.num_cpus
108    max_checkpoints = options.max_checkpoints
109    switch_cpus = None
110
111    if options.prog_interval:
112        for i in xrange(np):
113            testsys.cpu[i].progress_interval = options.prog_interval
114
115    if options.maxinsts:
116        for i in xrange(np):
117            testsys.cpu[i].max_insts_any_thread = options.maxinsts
118
119    if cpu_class:
120        switch_cpus = [cpu_class(defer_registration=True, cpu_id=(np+i))
121                       for i in xrange(np)]
122
123        for i in xrange(np):
124            if options.fast_forward:
125                testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
126            switch_cpus[i].system =  testsys
127            switch_cpus[i].workload = testsys.cpu[i].workload
128            switch_cpus[i].clock = testsys.cpu[0].clock
129            # simulation period
130            if options.maxinsts:
131                switch_cpus[i].max_insts_any_thread = options.maxinsts
132
133        testsys.switch_cpus = switch_cpus
134        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
135
136    if options.standard_switch:
137        if not options.caches:
138            # O3 CPU must have a cache to work.
139            print "O3 CPU must be used with caches"
140            sys.exit(1)
141
142        switch_cpus = [TimingSimpleCPU(defer_registration=True, cpu_id=(np+i))
143                       for i in xrange(np)]
144        switch_cpus_1 = [DerivO3CPU(defer_registration=True, cpu_id=(2*np+i))
145                        for i in xrange(np)]
146
147        for i in xrange(np):
148            switch_cpus[i].system =  testsys
149            switch_cpus_1[i].system =  testsys
150            switch_cpus[i].workload = testsys.cpu[i].workload
151            switch_cpus_1[i].workload = testsys.cpu[i].workload
152            switch_cpus[i].clock = testsys.cpu[0].clock
153            switch_cpus_1[i].clock = testsys.cpu[0].clock
154
155            # if restoring, make atomic cpu simulate only a few instructions
156            if options.checkpoint_restore != None:
157                testsys.cpu[i].max_insts_any_thread = 1
158            # Fast forward to specified location if we are not restoring
159            elif options.fast_forward:
160                testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
161            # Fast forward to a simpoint (warning: time consuming)
162            elif options.simpoint:
163                if testsys.cpu[i].workload[0].simpoint == 0:
164                    fatal('simpoint not found')
165                testsys.cpu[i].max_insts_any_thread = \
166                    testsys.cpu[i].workload[0].simpoint
167            # No distance specified, just switch
168            else:
169                testsys.cpu[i].max_insts_any_thread = 1
170
171            # warmup period
172            if options.warmup_insts:
173                switch_cpus[i].max_insts_any_thread =  options.warmup_insts
174
175            # simulation period
176            if options.maxinsts:
177                switch_cpus_1[i].max_insts_any_thread = options.maxinsts
178
179        testsys.switch_cpus = switch_cpus
180        testsys.switch_cpus_1 = switch_cpus_1
181        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
182        switch_cpu_list1 = [(switch_cpus[i], switch_cpus_1[i]) for i in xrange(np)]
183
184    # set the checkpoint in the cpu before m5.instantiate is called
185    if options.take_checkpoints != None and \
186           (options.simpoint or options.at_instruction):
187        offset = int(options.take_checkpoints)
188        # Set an instruction break point
189        if options.simpoint:
190            for i in xrange(np):
191                if testsys.cpu[i].workload[0].simpoint == 0:
192                    fatal('no simpoint for testsys.cpu[%d].workload[0]', i)
193                checkpoint_inst = int(testsys.cpu[i].workload[0].simpoint) + offset
194                testsys.cpu[i].max_insts_any_thread = checkpoint_inst
195                # used for output below
196                options.take_checkpoints = checkpoint_inst
197        else:
198            options.take_checkpoints = offset
199            # Set all test cpus with the right number of instructions
200            # for the upcoming simulation
201            for i in xrange(np):
202                testsys.cpu[i].max_insts_any_thread = offset
203
204    checkpoint_dir = None
205    if options.checkpoint_restore != None:
206        from os.path import isdir, exists
207        from os import listdir
208        import re
209
210        if not isdir(cptdir):
211            fatal("checkpoint dir %s does not exist!", cptdir)
212
213        if options.at_instruction or options.simpoint:
214            inst = options.checkpoint_restore
215            if options.simpoint:
216                # assume workload 0 has the simpoint
217                if testsys.cpu[0].workload[0].simpoint == 0:
218                    fatal('Unable to find simpoint')
219                inst += int(testsys.cpu[0].workload[0].simpoint)
220
221            checkpoint_dir = joinpath(cptdir,
222                                      "cpt.%s.%s" % (options.bench, inst))
223            if not exists(checkpoint_dir):
224                fatal("Unable to find checkpoint directory %s", checkpoint_dir)
225        else:
226            dirs = listdir(cptdir)
227            expr = re.compile('cpt\.([0-9]*)')
228            cpts = []
229            for dir in dirs:
230                match = expr.match(dir)
231                if match:
232                    cpts.append(match.group(1))
233
234            cpts.sort(lambda a,b: cmp(long(a), long(b)))
235
236            cpt_num = options.checkpoint_restore
237
238            if cpt_num > len(cpts):
239                fatal('Checkpoint %d not found', cpt_num)
240
241            ## Adjust max tick based on our starting tick
242            maxtick = maxtick - int(cpts[cpt_num - 1])
243            checkpoint_dir = joinpath(cptdir, "cpt.%s" % cpts[cpt_num - 1])
244
245    m5.instantiate(checkpoint_dir)
246
247    if options.standard_switch or cpu_class:
248        if options.standard_switch:
249            print "Switch at instruction count:%s" % \
250                    str(testsys.cpu[0].max_insts_any_thread)
251            exit_event = m5.simulate()
252        elif cpu_class and options.fast_forward:
253            print "Switch at instruction count:%s" % \
254                    str(testsys.cpu[0].max_insts_any_thread)
255            exit_event = m5.simulate()
256        else:
257            print "Switch at curTick count:%s" % str(10000)
258            exit_event = m5.simulate(10000)
259        print "Switched CPUS @ tick %s" % (m5.curTick())
260
261        # when you change to Timing (or Atomic), you halt the system
262        # given as argument.  When you are finished with the system
263        # changes (including switchCpus), you must resume the system
264        # manually.  You DON'T need to resume after just switching
265        # CPUs if you haven't changed anything on the system level.
266
267        m5.changeToTiming(testsys)
268        m5.switchCpus(switch_cpu_list)
269        m5.resume(testsys)
270
271        if options.standard_switch:
272            print "Switch at instruction count:%d" % \
273                    (testsys.switch_cpus[0].max_insts_any_thread)
274
275            #warmup instruction count may have already been set
276            if options.warmup_insts:
277                exit_event = m5.simulate()
278            else:
279                exit_event = m5.simulate(options.warmup)
280            print "Switching CPUS @ tick %s" % (m5.curTick())
281            print "Simulation ends instruction count:%d" % \
282                    (testsys.switch_cpus_1[0].max_insts_any_thread)
283            m5.drain(testsys)
284            m5.switchCpus(switch_cpu_list1)
285            m5.resume(testsys)
286
287    num_checkpoints = 0
288    exit_cause = ''
289
290    # If we're taking and restoring checkpoints, use checkpoint_dir
291    # option only for finding the checkpoints to restore from.  This
292    # lets us test checkpointing by restoring from one set of
293    # checkpoints, generating a second set, and then comparing them.
294    if options.take_checkpoints and options.checkpoint_restore:
295        if m5.options.outdir:
296            cptdir = m5.options.outdir
297        else:
298            cptdir = getcwd()
299
300    # Checkpoints being taken via the command line at <when> and at
301    # subsequent periods of <period>.  Checkpoint instructions
302    # received from the benchmark running are ignored and skipped in
303    # favor of command line checkpoint instructions.
304    if options.take_checkpoints != None :
305        if options.at_instruction or options.simpoint:
306            checkpoint_inst = int(options.take_checkpoints)
307
308            # maintain correct offset if we restored from some instruction
309            if options.checkpoint_restore != None:
310                checkpoint_inst += options.checkpoint_restore
311
312            print "Creating checkpoint at inst:%d" % (checkpoint_inst)
313            exit_event = m5.simulate()
314            print "exit cause = %s" % (exit_event.getCause())
315
316            # skip checkpoint instructions should they exist
317            while exit_event.getCause() == "checkpoint":
318                exit_event = m5.simulate()
319
320            if exit_event.getCause() == \
321                   "a thread reached the max instruction count":
322                m5.checkpoint(joinpath(cptdir, "cpt.%s.%d" % \
323                        (options.bench, checkpoint_inst)))
324                print "Checkpoint written."
325                num_checkpoints += 1
326
327            if exit_event.getCause() == "user interrupt received":
328                exit_cause = exit_event.getCause();
329        else:
330            when, period = options.take_checkpoints.split(",", 1)
331            when = int(when)
332            period = int(period)
333
334            exit_event = m5.simulate(when)
335            while exit_event.getCause() == "checkpoint":
336                exit_event = m5.simulate(when - m5.curTick())
337
338            if exit_event.getCause() == "simulate() limit reached":
339                m5.checkpoint(joinpath(cptdir, "cpt.%d"))
340                num_checkpoints += 1
341
342            sim_ticks = when
343            exit_cause = "maximum %d checkpoints dropped" % max_checkpoints
344            while num_checkpoints < max_checkpoints and \
345                    exit_event.getCause() == "simulate() limit reached":
346                if (sim_ticks + period) > maxtick:
347                    exit_event = m5.simulate(maxtick - sim_ticks)
348                    exit_cause = exit_event.getCause()
349                    break
350                else:
351                    exit_event = m5.simulate(period)
352                    sim_ticks += period
353                    while exit_event.getCause() == "checkpoint":
354                        exit_event = m5.simulate(sim_ticks - m5.curTick())
355                    if exit_event.getCause() == "simulate() limit reached":
356                        m5.checkpoint(joinpath(cptdir, "cpt.%d"))
357                        num_checkpoints += 1
358
359            if exit_event.getCause() != "simulate() limit reached":
360                exit_cause = exit_event.getCause();
361
362    else: # no checkpoints being taken via this script
363        if options.fast_forward:
364            m5.stats.reset()
365        print "**** REAL SIMULATION ****"
366        exit_event = m5.simulate(maxtick)
367
368        while exit_event.getCause() == "checkpoint":
369            m5.checkpoint(joinpath(cptdir, "cpt.%d"))
370            num_checkpoints += 1
371            if num_checkpoints == max_checkpoints:
372                exit_cause = "maximum %d checkpoints dropped" % max_checkpoints
373                break
374
375            exit_event = m5.simulate(maxtick - m5.curTick())
376            exit_cause = exit_event.getCause()
377
378    if exit_cause == '':
379        exit_cause = exit_event.getCause()
380    print 'Exiting @ tick %i because %s' % (m5.curTick(), exit_cause)
381
382    if options.checkpoint_at_end:
383        m5.checkpoint(joinpath(cptdir, "cpt.%d"))
384
385