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