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