Simulation.py revision 9606:0a4b702628bd
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 CpuConfig
47
48import m5
49from m5.defines import buildEnv
50from m5.objects import *
51from m5.util import *
52
53addToPath('../common')
54
55def getCPUClass(cpu_type):
56    """Returns the required cpu class and the mode of operation."""
57    cls = CpuConfig.get(cpu_type)
58    return cls, cls.memory_mode()
59
60def setCPUClass(options):
61    """Returns two cpu classes and the initial mode of operation.
62
63       Restoring from a checkpoint or fast forwarding through a benchmark
64       can be done using one type of cpu, and then the actual
65       simulation can be carried out using another type. This function
66       returns these two types of cpus and the initial mode of operation
67       depending on the options provided.
68    """
69
70    TmpClass, test_mem_mode = getCPUClass(options.cpu_type)
71    CPUClass = None
72    if TmpClass.require_caches() and \
73            not options.caches and not options.ruby:
74        fatal("%s must be used with caches" % options.cpu_type)
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_event
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        m5.switchCpus(testsys, repeat_switch_cpu_list)
246
247        tmp_cpu_list = []
248        for old_cpu, new_cpu in repeat_switch_cpu_list:
249            tmp_cpu_list.append((new_cpu, old_cpu))
250        repeat_switch_cpu_list = tmp_cpu_list
251
252        if (maxtick - m5.curTick()) <= switch_freq:
253            exit_event = m5.simulate(maxtick - m5.curTick())
254            return exit_event
255
256def run(options, root, testsys, cpu_class):
257    if options.maxtick:
258        maxtick = options.maxtick
259    elif options.maxtime:
260        simtime = m5.ticks.seconds(simtime)
261        print "simulating for: ", simtime
262        maxtick = simtime
263    else:
264        maxtick = m5.MaxTick
265
266    if options.checkpoint_dir:
267        cptdir = options.checkpoint_dir
268    elif m5.options.outdir:
269        cptdir = m5.options.outdir
270    else:
271        cptdir = getcwd()
272
273    if options.fast_forward and options.checkpoint_restore != None:
274        fatal("Can't specify both --fast-forward and --checkpoint-restore")
275
276    if options.standard_switch and not options.caches:
277        fatal("Must specify --caches when using --standard-switch")
278
279    if options.standard_switch and options.repeat_switch:
280        fatal("Can't specify both --standard-switch and --repeat-switch")
281
282    if options.repeat_switch and options.take_checkpoints:
283        fatal("Can't specify both --repeat-switch and --take-checkpoints")
284
285    np = options.num_cpus
286    switch_cpus = None
287
288    if options.prog_interval:
289        for i in xrange(np):
290            testsys.cpu[i].progress_interval = options.prog_interval
291
292    if options.maxinsts:
293        for i in xrange(np):
294            testsys.cpu[i].max_insts_any_thread = options.maxinsts
295
296    if cpu_class:
297        switch_cpus = [cpu_class(switched_out=True, cpu_id=(i))
298                       for i in xrange(np)]
299
300        for i in xrange(np):
301            if options.fast_forward:
302                testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
303            switch_cpus[i].system =  testsys
304            switch_cpus[i].workload = testsys.cpu[i].workload
305            switch_cpus[i].clock = testsys.cpu[i].clock
306            # simulation period
307            if options.maxinsts:
308                switch_cpus[i].max_insts_any_thread = options.maxinsts
309            # Add checker cpu if selected
310            if options.checker:
311                switch_cpus[i].addCheckerCpu()
312
313        testsys.switch_cpus = switch_cpus
314        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
315
316    if options.repeat_switch:
317        switch_class = getCPUClass(options.cpu_type)[0]
318        if switch_class.require_caches() and \
319                not options.caches:
320            print "%s: Must be used with caches" % str(switch_class)
321            sys.exit(1)
322        if not switch_class.support_take_over():
323            print "%s: CPU switching not supported" % str(switch_class)
324            sys.exit(1)
325
326        repeat_switch_cpus = [switch_class(switched_out=True, \
327                                               cpu_id=(i)) for i in xrange(np)]
328
329        for i in xrange(np):
330            repeat_switch_cpus[i].system = testsys
331            repeat_switch_cpus[i].workload = testsys.cpu[i].workload
332            repeat_switch_cpus[i].clock = testsys.cpu[i].clock
333
334            if options.maxinsts:
335                repeat_switch_cpus[i].max_insts_any_thread = options.maxinsts
336
337            if options.checker:
338                repeat_switch_cpus[i].addCheckerCpu()
339
340        testsys.repeat_switch_cpus = repeat_switch_cpus
341
342        if cpu_class:
343            repeat_switch_cpu_list = [(switch_cpus[i], repeat_switch_cpus[i])
344                                      for i in xrange(np)]
345        else:
346            repeat_switch_cpu_list = [(testsys.cpu[i], repeat_switch_cpus[i])
347                                      for i in xrange(np)]
348
349    if options.standard_switch:
350        switch_cpus = [TimingSimpleCPU(switched_out=True, cpu_id=(i))
351                       for i in xrange(np)]
352        switch_cpus_1 = [DerivO3CPU(switched_out=True, cpu_id=(i))
353                        for i in xrange(np)]
354
355        for i in xrange(np):
356            switch_cpus[i].system =  testsys
357            switch_cpus_1[i].system =  testsys
358            switch_cpus[i].workload = testsys.cpu[i].workload
359            switch_cpus_1[i].workload = testsys.cpu[i].workload
360            switch_cpus[i].clock = testsys.cpu[i].clock
361            switch_cpus_1[i].clock = testsys.cpu[i].clock
362
363            # if restoring, make atomic cpu simulate only a few instructions
364            if options.checkpoint_restore != None:
365                testsys.cpu[i].max_insts_any_thread = 1
366            # Fast forward to specified location if we are not restoring
367            elif options.fast_forward:
368                testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
369            # Fast forward to a simpoint (warning: time consuming)
370            elif options.simpoint:
371                if testsys.cpu[i].workload[0].simpoint == 0:
372                    fatal('simpoint not found')
373                testsys.cpu[i].max_insts_any_thread = \
374                    testsys.cpu[i].workload[0].simpoint
375            # No distance specified, just switch
376            else:
377                testsys.cpu[i].max_insts_any_thread = 1
378
379            # warmup period
380            if options.warmup_insts:
381                switch_cpus[i].max_insts_any_thread =  options.warmup_insts
382
383            # simulation period
384            if options.maxinsts:
385                switch_cpus_1[i].max_insts_any_thread = options.maxinsts
386
387            # attach the checker cpu if selected
388            if options.checker:
389                switch_cpus[i].addCheckerCpu()
390                switch_cpus_1[i].addCheckerCpu()
391
392        testsys.switch_cpus = switch_cpus
393        testsys.switch_cpus_1 = switch_cpus_1
394        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
395        switch_cpu_list1 = [(switch_cpus[i], switch_cpus_1[i]) for i in xrange(np)]
396
397    # set the checkpoint in the cpu before m5.instantiate is called
398    if options.take_checkpoints != None and \
399           (options.simpoint or options.at_instruction):
400        offset = int(options.take_checkpoints)
401        # Set an instruction break point
402        if options.simpoint:
403            for i in xrange(np):
404                if testsys.cpu[i].workload[0].simpoint == 0:
405                    fatal('no simpoint for testsys.cpu[%d].workload[0]', i)
406                checkpoint_inst = int(testsys.cpu[i].workload[0].simpoint) + offset
407                testsys.cpu[i].max_insts_any_thread = checkpoint_inst
408                # used for output below
409                options.take_checkpoints = checkpoint_inst
410        else:
411            options.take_checkpoints = offset
412            # Set all test cpus with the right number of instructions
413            # for the upcoming simulation
414            for i in xrange(np):
415                testsys.cpu[i].max_insts_any_thread = offset
416
417    checkpoint_dir = None
418    if options.checkpoint_restore != None:
419        maxtick, checkpoint_dir = findCptDir(options, maxtick, cptdir, testsys)
420    m5.instantiate(checkpoint_dir)
421
422    if options.standard_switch or cpu_class:
423        if options.standard_switch:
424            print "Switch at instruction count:%s" % \
425                    str(testsys.cpu[0].max_insts_any_thread)
426            exit_event = m5.simulate()
427        elif cpu_class and options.fast_forward:
428            print "Switch at instruction count:%s" % \
429                    str(testsys.cpu[0].max_insts_any_thread)
430            exit_event = m5.simulate()
431        else:
432            print "Switch at curTick count:%s" % str(10000)
433            exit_event = m5.simulate(10000)
434        print "Switched CPUS @ tick %s" % (m5.curTick())
435
436        m5.switchCpus(testsys, switch_cpu_list)
437
438        if options.standard_switch:
439            print "Switch at instruction count:%d" % \
440                    (testsys.switch_cpus[0].max_insts_any_thread)
441
442            #warmup instruction count may have already been set
443            if options.warmup_insts:
444                exit_event = m5.simulate()
445            else:
446                exit_event = m5.simulate(options.standard_switch)
447            print "Switching CPUS @ tick %s" % (m5.curTick())
448            print "Simulation ends instruction count:%d" % \
449                    (testsys.switch_cpus_1[0].max_insts_any_thread)
450            m5.switchCpus(testsys, switch_cpu_list1)
451
452    # If we're taking and restoring checkpoints, use checkpoint_dir
453    # option only for finding the checkpoints to restore from.  This
454    # lets us test checkpointing by restoring from one set of
455    # checkpoints, generating a second set, and then comparing them.
456    if options.take_checkpoints and options.checkpoint_restore:
457        if m5.options.outdir:
458            cptdir = m5.options.outdir
459        else:
460            cptdir = getcwd()
461
462    if options.take_checkpoints != None :
463        # Checkpoints being taken via the command line at <when> and at
464        # subsequent periods of <period>.  Checkpoint instructions
465        # received from the benchmark running are ignored and skipped in
466        # favor of command line checkpoint instructions.
467        exit_event = scriptCheckpoints(options, maxtick, cptdir)
468    else:
469        if options.fast_forward:
470            m5.stats.reset()
471        print "**** REAL SIMULATION ****"
472
473        # If checkpoints are being taken, then the checkpoint instruction
474        # will occur in the benchmark code it self.
475        if options.repeat_switch and maxtick > options.repeat_switch:
476            exit_event = repeatSwitch(testsys, repeat_switch_cpu_list,
477                                      maxtick, options.repeat_switch)
478        else:
479            exit_event = benchCheckpoints(options, maxtick, cptdir)
480
481    print 'Exiting @ tick %i because %s' % (m5.curTick(), exit_event.getCause())
482    if options.checkpoint_at_end:
483        m5.checkpoint(joinpath(cptdir, "cpt.%d"))
484
485    if not m5.options.interactive:
486        sys.exit(exit_event.getCode())
487