Simulation.py revision 9816:971507cbbe65
1# Copyright (c) 2012-2013 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
47import MemConfig
48
49import m5
50from m5.defines import buildEnv
51from m5.objects import *
52from m5.util import *
53
54addToPath('../common')
55
56def getCPUClass(cpu_type):
57    """Returns the required cpu class and the mode of operation."""
58    cls = CpuConfig.get(cpu_type)
59    return cls, cls.memory_mode()
60
61def setCPUClass(options):
62    """Returns two cpu classes and the initial mode of operation.
63
64       Restoring from a checkpoint or fast forwarding through a benchmark
65       can be done using one type of cpu, and then the actual
66       simulation can be carried out using another type. This function
67       returns these two types of cpus and the initial mode of operation
68       depending on the options provided.
69    """
70
71    TmpClass, test_mem_mode = getCPUClass(options.cpu_type)
72    CPUClass = None
73    if TmpClass.require_caches() and \
74            not options.caches and not options.ruby:
75        fatal("%s must be used with caches" % options.cpu_type)
76
77    if options.checkpoint_restore != None:
78        if options.restore_with_cpu != options.cpu_type:
79            CPUClass = TmpClass
80            TmpClass, test_mem_mode = getCPUClass(options.restore_with_cpu)
81    elif options.fast_forward:
82        CPUClass = TmpClass
83        TmpClass = AtomicSimpleCPU
84        test_mem_mode = 'atomic'
85
86    return (TmpClass, test_mem_mode, CPUClass)
87
88def setMemClass(options):
89    """Returns a memory controller class."""
90
91    return MemConfig.get(options.mem_type)
92
93def setWorkCountOptions(system, options):
94    if options.work_item_id != None:
95        system.work_item_id = options.work_item_id
96    if options.work_begin_cpu_id_exit != None:
97        system.work_begin_cpu_id_exit = options.work_begin_cpu_id_exit
98    if options.work_end_exit_count != None:
99        system.work_end_exit_count = options.work_end_exit_count
100    if options.work_end_checkpoint_count != None:
101        system.work_end_ckpt_count = options.work_end_checkpoint_count
102    if options.work_begin_exit_count != None:
103        system.work_begin_exit_count = options.work_begin_exit_count
104    if options.work_begin_checkpoint_count != None:
105        system.work_begin_ckpt_count = options.work_begin_checkpoint_count
106    if options.work_cpus_checkpoint_count != None:
107        system.work_cpus_ckpt_count = options.work_cpus_checkpoint_count
108
109def findCptDir(options, cptdir, testsys):
110    """Figures out the directory from which the checkpointed state is read.
111
112    There are two different ways in which the directories holding checkpoints
113    can be named --
114    1. cpt.<benchmark name>.<instruction count when the checkpoint was taken>
115    2. cpt.<some number, usually the tick value when the checkpoint was taken>
116
117    This function parses through the options to figure out which one of the
118    above should be used for selecting the checkpoint, and then figures out
119    the appropriate directory.
120    """
121
122    from os.path import isdir, exists
123    from os import listdir
124    import re
125
126    if not isdir(cptdir):
127        fatal("checkpoint dir %s does not exist!", cptdir)
128
129    if options.at_instruction or options.simpoint:
130        inst = options.checkpoint_restore
131        if options.simpoint:
132            # assume workload 0 has the simpoint
133            if testsys.cpu[0].workload[0].simpoint == 0:
134                fatal('Unable to find simpoint')
135            inst += int(testsys.cpu[0].workload[0].simpoint)
136
137        checkpoint_dir = joinpath(cptdir, "cpt.%s.%s" % (options.bench, inst))
138        if not exists(checkpoint_dir):
139            fatal("Unable to find checkpoint directory %s", checkpoint_dir)
140    else:
141        dirs = listdir(cptdir)
142        expr = re.compile('cpt\.([0-9]*)')
143        cpts = []
144        for dir in dirs:
145            match = expr.match(dir)
146            if match:
147                cpts.append(match.group(1))
148
149        cpts.sort(lambda a,b: cmp(long(a), long(b)))
150
151        cpt_num = options.checkpoint_restore
152        if cpt_num > len(cpts):
153            fatal('Checkpoint %d not found', cpt_num)
154
155        cpt_starttick = int(cpts[cpt_num - 1])
156        checkpoint_dir = joinpath(cptdir, "cpt.%s" % cpts[cpt_num - 1])
157
158    return cpt_starttick, checkpoint_dir
159
160def scriptCheckpoints(options, maxtick, cptdir):
161    if options.at_instruction or options.simpoint:
162        checkpoint_inst = int(options.take_checkpoints)
163
164        # maintain correct offset if we restored from some instruction
165        if options.checkpoint_restore != None:
166            checkpoint_inst += options.checkpoint_restore
167
168        print "Creating checkpoint at inst:%d" % (checkpoint_inst)
169        exit_event = m5.simulate()
170        exit_cause = exit_event.getCause()
171        print "exit cause = %s" % exit_cause
172
173        # skip checkpoint instructions should they exist
174        while exit_cause == "checkpoint":
175            exit_event = m5.simulate()
176            exit_cause = exit_event.getCause()
177
178        if exit_cause == "a thread reached the max instruction count":
179            m5.checkpoint(joinpath(cptdir, "cpt.%s.%d" % \
180                    (options.bench, checkpoint_inst)))
181            print "Checkpoint written."
182
183    else:
184        when, period = options.take_checkpoints.split(",", 1)
185        when = int(when)
186        period = int(period)
187        num_checkpoints = 0
188
189        exit_event = m5.simulate(when - m5.curTick())
190        exit_cause = exit_event.getCause()
191        while exit_cause == "checkpoint":
192            exit_event = m5.simulate(when - m5.curTick())
193            exit_cause = exit_event.getCause()
194
195        if exit_cause == "simulate() limit reached":
196            m5.checkpoint(joinpath(cptdir, "cpt.%d"))
197            num_checkpoints += 1
198
199        sim_ticks = when
200        max_checkpoints = options.max_checkpoints
201
202        while num_checkpoints < max_checkpoints and \
203                exit_cause == "simulate() limit reached":
204            if (sim_ticks + period) > maxtick:
205                exit_event = m5.simulate(maxtick - sim_ticks)
206                exit_cause = exit_event.getCause()
207                break
208            else:
209                exit_event = m5.simulate(period)
210                exit_cause = exit_event.getCause()
211                sim_ticks += period
212                while exit_event.getCause() == "checkpoint":
213                    exit_event = m5.simulate(sim_ticks - m5.curTick())
214                if exit_event.getCause() == "simulate() limit reached":
215                    m5.checkpoint(joinpath(cptdir, "cpt.%d"))
216                    num_checkpoints += 1
217
218    return exit_event
219
220def benchCheckpoints(options, maxtick, cptdir):
221    exit_event = m5.simulate(maxtick - m5.curTick())
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_event
238
239def repeatSwitch(testsys, repeat_switch_cpu_list, maxtick, switch_freq):
240    print "starting switch loop"
241    while True:
242        exit_event = m5.simulate(switch_freq)
243        exit_cause = exit_event.getCause()
244
245        if exit_cause != "simulate() limit reached":
246            return exit_event
247
248        m5.switchCpus(testsys, repeat_switch_cpu_list)
249
250        tmp_cpu_list = []
251        for old_cpu, new_cpu in repeat_switch_cpu_list:
252            tmp_cpu_list.append((new_cpu, old_cpu))
253        repeat_switch_cpu_list = tmp_cpu_list
254
255        if (maxtick - m5.curTick()) <= switch_freq:
256            exit_event = m5.simulate(maxtick - m5.curTick())
257            return exit_event
258
259def run(options, root, testsys, cpu_class):
260    if options.checkpoint_dir:
261        cptdir = options.checkpoint_dir
262    elif m5.options.outdir:
263        cptdir = m5.options.outdir
264    else:
265        cptdir = getcwd()
266
267    if options.fast_forward and options.checkpoint_restore != None:
268        fatal("Can't specify both --fast-forward and --checkpoint-restore")
269
270    if options.standard_switch and not options.caches:
271        fatal("Must specify --caches when using --standard-switch")
272
273    if options.standard_switch and options.repeat_switch:
274        fatal("Can't specify both --standard-switch and --repeat-switch")
275
276    if options.repeat_switch and options.take_checkpoints:
277        fatal("Can't specify both --repeat-switch and --take-checkpoints")
278
279    np = options.num_cpus
280    switch_cpus = None
281
282    if options.prog_interval:
283        for i in xrange(np):
284            testsys.cpu[i].progress_interval = options.prog_interval
285
286    if options.maxinsts:
287        for i in xrange(np):
288            testsys.cpu[i].max_insts_any_thread = options.maxinsts
289
290    if cpu_class:
291        switch_cpus = [cpu_class(switched_out=True, cpu_id=(i))
292                       for i in xrange(np)]
293
294        for i in xrange(np):
295            if options.fast_forward:
296                testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
297            switch_cpus[i].system =  testsys
298            switch_cpus[i].workload = testsys.cpu[i].workload
299            switch_cpus[i].clk_domain = testsys.cpu[i].clk_domain
300            # simulation period
301            if options.maxinsts:
302                switch_cpus[i].max_insts_any_thread = options.maxinsts
303            # Add checker cpu if selected
304            if options.checker:
305                switch_cpus[i].addCheckerCpu()
306
307        testsys.switch_cpus = switch_cpus
308        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
309
310    if options.repeat_switch:
311        switch_class = getCPUClass(options.cpu_type)[0]
312        if switch_class.require_caches() and \
313                not options.caches:
314            print "%s: Must be used with caches" % str(switch_class)
315            sys.exit(1)
316        if not switch_class.support_take_over():
317            print "%s: CPU switching not supported" % str(switch_class)
318            sys.exit(1)
319
320        repeat_switch_cpus = [switch_class(switched_out=True, \
321                                               cpu_id=(i)) for i in xrange(np)]
322
323        for i in xrange(np):
324            repeat_switch_cpus[i].system = testsys
325            repeat_switch_cpus[i].workload = testsys.cpu[i].workload
326            repeat_switch_cpus[i].clk_domain = testsys.cpu[i].clk_domain
327
328            if options.maxinsts:
329                repeat_switch_cpus[i].max_insts_any_thread = options.maxinsts
330
331            if options.checker:
332                repeat_switch_cpus[i].addCheckerCpu()
333
334        testsys.repeat_switch_cpus = repeat_switch_cpus
335
336        if cpu_class:
337            repeat_switch_cpu_list = [(switch_cpus[i], repeat_switch_cpus[i])
338                                      for i in xrange(np)]
339        else:
340            repeat_switch_cpu_list = [(testsys.cpu[i], repeat_switch_cpus[i])
341                                      for i in xrange(np)]
342
343    if options.standard_switch:
344        switch_cpus = [TimingSimpleCPU(switched_out=True, cpu_id=(i))
345                       for i in xrange(np)]
346        switch_cpus_1 = [DerivO3CPU(switched_out=True, cpu_id=(i))
347                        for i in xrange(np)]
348
349        for i in xrange(np):
350            switch_cpus[i].system =  testsys
351            switch_cpus_1[i].system =  testsys
352            switch_cpus[i].workload = testsys.cpu[i].workload
353            switch_cpus_1[i].workload = testsys.cpu[i].workload
354            switch_cpus[i].clk_domain = testsys.cpu[i].clk_domain
355            switch_cpus_1[i].clk_domain = testsys.cpu[i].clk_domain
356
357            # if restoring, make atomic cpu simulate only a few instructions
358            if options.checkpoint_restore != None:
359                testsys.cpu[i].max_insts_any_thread = 1
360            # Fast forward to specified location if we are not restoring
361            elif options.fast_forward:
362                testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
363            # Fast forward to a simpoint (warning: time consuming)
364            elif options.simpoint:
365                if testsys.cpu[i].workload[0].simpoint == 0:
366                    fatal('simpoint not found')
367                testsys.cpu[i].max_insts_any_thread = \
368                    testsys.cpu[i].workload[0].simpoint
369            # No distance specified, just switch
370            else:
371                testsys.cpu[i].max_insts_any_thread = 1
372
373            # warmup period
374            if options.warmup_insts:
375                switch_cpus[i].max_insts_any_thread =  options.warmup_insts
376
377            # simulation period
378            if options.maxinsts:
379                switch_cpus_1[i].max_insts_any_thread = options.maxinsts
380
381            # attach the checker cpu if selected
382            if options.checker:
383                switch_cpus[i].addCheckerCpu()
384                switch_cpus_1[i].addCheckerCpu()
385
386        testsys.switch_cpus = switch_cpus
387        testsys.switch_cpus_1 = switch_cpus_1
388        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
389        switch_cpu_list1 = [(switch_cpus[i], switch_cpus_1[i]) for i in xrange(np)]
390
391    # set the checkpoint in the cpu before m5.instantiate is called
392    if options.take_checkpoints != None and \
393           (options.simpoint or options.at_instruction):
394        offset = int(options.take_checkpoints)
395        # Set an instruction break point
396        if options.simpoint:
397            for i in xrange(np):
398                if testsys.cpu[i].workload[0].simpoint == 0:
399                    fatal('no simpoint for testsys.cpu[%d].workload[0]', i)
400                checkpoint_inst = int(testsys.cpu[i].workload[0].simpoint) + offset
401                testsys.cpu[i].max_insts_any_thread = checkpoint_inst
402                # used for output below
403                options.take_checkpoints = checkpoint_inst
404        else:
405            options.take_checkpoints = offset
406            # Set all test cpus with the right number of instructions
407            # for the upcoming simulation
408            for i in xrange(np):
409                testsys.cpu[i].max_insts_any_thread = offset
410
411    checkpoint_dir = None
412    if options.checkpoint_restore:
413        cpt_starttick, checkpoint_dir = findCptDir(options, cptdir, testsys)
414    m5.instantiate(checkpoint_dir)
415
416    # Handle the max tick settings now that tick frequency was resolved
417    # during system instantiation
418    # NOTE: the maxtick variable here is in absolute ticks, so it must
419    # include any simulated ticks before a checkpoint
420    explicit_maxticks = 0
421    maxtick_from_abs = m5.MaxTick
422    maxtick_from_rel = m5.MaxTick
423    maxtick_from_maxtime = m5.MaxTick
424    if options.abs_max_tick:
425        maxtick_from_abs = options.abs_max_tick
426        explicit_maxticks += 1
427    if options.rel_max_tick:
428        maxtick_from_rel = options.rel_max_tick
429        if options.checkpoint_restore:
430            # NOTE: this may need to be updated if checkpoints ever store
431            # the ticks per simulated second
432            maxtick_from_rel += cpt_starttick
433        explicit_maxticks += 1
434    if options.maxtime:
435        maxtick_from_maxtime = m5.ticks.fromSeconds(options.maxtime)
436        explicit_maxticks += 1
437    if explicit_maxticks > 1:
438        warn("Specified multiple of --abs-max-tick, --rel-max-tick, --maxtime."\
439             " Using least")
440    maxtick = min([maxtick_from_abs, maxtick_from_rel, maxtick_from_maxtime])
441
442    if options.checkpoint_restore != None and maxtick < cpt_starttick:
443        fatal("Bad maxtick (%d) specified: " \
444              "Checkpoint starts starts from tick: %d", maxtick, cpt_starttick)
445
446    if options.standard_switch or cpu_class:
447        if options.standard_switch:
448            print "Switch at instruction count:%s" % \
449                    str(testsys.cpu[0].max_insts_any_thread)
450            exit_event = m5.simulate()
451        elif cpu_class and options.fast_forward:
452            print "Switch at instruction count:%s" % \
453                    str(testsys.cpu[0].max_insts_any_thread)
454            exit_event = m5.simulate()
455        else:
456            print "Switch at curTick count:%s" % str(10000)
457            exit_event = m5.simulate(10000)
458        print "Switched CPUS @ tick %s" % (m5.curTick())
459
460        m5.switchCpus(testsys, switch_cpu_list)
461
462        if options.standard_switch:
463            print "Switch at instruction count:%d" % \
464                    (testsys.switch_cpus[0].max_insts_any_thread)
465
466            #warmup instruction count may have already been set
467            if options.warmup_insts:
468                exit_event = m5.simulate()
469            else:
470                exit_event = m5.simulate(options.standard_switch)
471            print "Switching CPUS @ tick %s" % (m5.curTick())
472            print "Simulation ends instruction count:%d" % \
473                    (testsys.switch_cpus_1[0].max_insts_any_thread)
474            m5.switchCpus(testsys, switch_cpu_list1)
475
476    # If we're taking and restoring checkpoints, use checkpoint_dir
477    # option only for finding the checkpoints to restore from.  This
478    # lets us test checkpointing by restoring from one set of
479    # checkpoints, generating a second set, and then comparing them.
480    if options.take_checkpoints and options.checkpoint_restore:
481        if m5.options.outdir:
482            cptdir = m5.options.outdir
483        else:
484            cptdir = getcwd()
485
486    if options.take_checkpoints != None :
487        # Checkpoints being taken via the command line at <when> and at
488        # subsequent periods of <period>.  Checkpoint instructions
489        # received from the benchmark running are ignored and skipped in
490        # favor of command line checkpoint instructions.
491        exit_event = scriptCheckpoints(options, maxtick, cptdir)
492    else:
493        if options.fast_forward:
494            m5.stats.reset()
495        print "**** REAL SIMULATION ****"
496
497        # If checkpoints are being taken, then the checkpoint instruction
498        # will occur in the benchmark code it self.
499        if options.repeat_switch and maxtick > options.repeat_switch:
500            exit_event = repeatSwitch(testsys, repeat_switch_cpu_list,
501                                      maxtick, options.repeat_switch)
502        else:
503            exit_event = benchCheckpoints(options, maxtick, cptdir)
504
505    print 'Exiting @ tick %i because %s' % (m5.curTick(), exit_event.getCause())
506    if options.checkpoint_at_end:
507        m5.checkpoint(joinpath(cptdir, "cpt.%d"))
508
509    if not m5.options.interactive:
510        sys.exit(exit_event.getCode())
511