Simulation.py revision 9665:6dbdeee787cc
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
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, maxtick, 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    It also sets the value of the maximum tick value till which the simulation
122    will run.
123    """
124
125    from os.path import isdir, exists
126    from os import listdir
127    import re
128
129    if not isdir(cptdir):
130        fatal("checkpoint dir %s does not exist!", cptdir)
131
132    if options.at_instruction or options.simpoint:
133        inst = options.checkpoint_restore
134        if options.simpoint:
135            # assume workload 0 has the simpoint
136            if testsys.cpu[0].workload[0].simpoint == 0:
137                fatal('Unable to find simpoint')
138            inst += int(testsys.cpu[0].workload[0].simpoint)
139
140        checkpoint_dir = joinpath(cptdir, "cpt.%s.%s" % (options.bench, inst))
141        if not exists(checkpoint_dir):
142            fatal("Unable to find checkpoint directory %s", checkpoint_dir)
143    else:
144        dirs = listdir(cptdir)
145        expr = re.compile('cpt\.([0-9]*)')
146        cpts = []
147        for dir in dirs:
148            match = expr.match(dir)
149            if match:
150                cpts.append(match.group(1))
151
152        cpts.sort(lambda a,b: cmp(long(a), long(b)))
153
154        cpt_num = options.checkpoint_restore
155        if cpt_num > len(cpts):
156            fatal('Checkpoint %d not found', cpt_num)
157
158        maxtick = maxtick - int(cpts[cpt_num - 1])
159        checkpoint_dir = joinpath(cptdir, "cpt.%s" % cpts[cpt_num - 1])
160
161    return maxtick, checkpoint_dir
162
163def scriptCheckpoints(options, maxtick, cptdir):
164    if options.at_instruction or options.simpoint:
165        checkpoint_inst = int(options.take_checkpoints)
166
167        # maintain correct offset if we restored from some instruction
168        if options.checkpoint_restore != None:
169            checkpoint_inst += options.checkpoint_restore
170
171        print "Creating checkpoint at inst:%d" % (checkpoint_inst)
172        exit_event = m5.simulate()
173        exit_cause = exit_event.getCause()
174        print "exit cause = %s" % exit_cause
175
176        # skip checkpoint instructions should they exist
177        while exit_cause == "checkpoint":
178            exit_event = m5.simulate()
179            exit_cause = exit_event.getCause()
180
181        if exit_cause == "a thread reached the max instruction count":
182            m5.checkpoint(joinpath(cptdir, "cpt.%s.%d" % \
183                    (options.bench, checkpoint_inst)))
184            print "Checkpoint written."
185
186    else:
187        when, period = options.take_checkpoints.split(",", 1)
188        when = int(when)
189        period = int(period)
190        num_checkpoints = 0
191
192        exit_event = m5.simulate(when - m5.curTick())
193        exit_cause = exit_event.getCause()
194        while exit_cause == "checkpoint":
195            exit_event = m5.simulate(when - m5.curTick())
196            exit_cause = exit_event.getCause()
197
198        if exit_cause == "simulate() limit reached":
199            m5.checkpoint(joinpath(cptdir, "cpt.%d"))
200            num_checkpoints += 1
201
202        sim_ticks = when
203        max_checkpoints = options.max_checkpoints
204
205        while num_checkpoints < max_checkpoints and \
206                exit_cause == "simulate() limit reached":
207            if (sim_ticks + period) > maxtick:
208                exit_event = m5.simulate(maxtick - sim_ticks)
209                exit_cause = exit_event.getCause()
210                break
211            else:
212                exit_event = m5.simulate(period)
213                exit_cause = exit_event.getCause()
214                sim_ticks += period
215                while exit_event.getCause() == "checkpoint":
216                    exit_event = m5.simulate(sim_ticks - m5.curTick())
217                if exit_event.getCause() == "simulate() limit reached":
218                    m5.checkpoint(joinpath(cptdir, "cpt.%d"))
219                    num_checkpoints += 1
220
221    return exit_event
222
223def benchCheckpoints(options, maxtick, cptdir):
224    exit_event = m5.simulate(maxtick - m5.curTick())
225    exit_cause = exit_event.getCause()
226
227    num_checkpoints = 0
228    max_checkpoints = options.max_checkpoints
229
230    while exit_cause == "checkpoint":
231        m5.checkpoint(joinpath(cptdir, "cpt.%d"))
232        num_checkpoints += 1
233        if num_checkpoints == max_checkpoints:
234            exit_cause = "maximum %d checkpoints dropped" % max_checkpoints
235            break
236
237        exit_event = m5.simulate(maxtick - m5.curTick())
238        exit_cause = exit_event.getCause()
239
240    return exit_event
241
242def repeatSwitch(testsys, repeat_switch_cpu_list, maxtick, switch_freq):
243    print "starting switch loop"
244    while True:
245        exit_event = m5.simulate(switch_freq)
246        exit_cause = exit_event.getCause()
247
248        if exit_cause != "simulate() limit reached":
249            return exit_event
250
251        m5.switchCpus(testsys, repeat_switch_cpu_list)
252
253        tmp_cpu_list = []
254        for old_cpu, new_cpu in repeat_switch_cpu_list:
255            tmp_cpu_list.append((new_cpu, old_cpu))
256        repeat_switch_cpu_list = tmp_cpu_list
257
258        if (maxtick - m5.curTick()) <= switch_freq:
259            exit_event = m5.simulate(maxtick - m5.curTick())
260            return exit_event
261
262def run(options, root, testsys, cpu_class):
263    if options.maxtick:
264        maxtick = options.maxtick
265    elif options.maxtime:
266        simtime = m5.ticks.seconds(simtime)
267        print "simulating for: ", simtime
268        maxtick = simtime
269    else:
270        maxtick = m5.MaxTick
271
272    if options.checkpoint_dir:
273        cptdir = options.checkpoint_dir
274    elif m5.options.outdir:
275        cptdir = m5.options.outdir
276    else:
277        cptdir = getcwd()
278
279    if options.fast_forward and options.checkpoint_restore != None:
280        fatal("Can't specify both --fast-forward and --checkpoint-restore")
281
282    if options.standard_switch and not options.caches:
283        fatal("Must specify --caches when using --standard-switch")
284
285    if options.standard_switch and options.repeat_switch:
286        fatal("Can't specify both --standard-switch and --repeat-switch")
287
288    if options.repeat_switch and options.take_checkpoints:
289        fatal("Can't specify both --repeat-switch and --take-checkpoints")
290
291    np = options.num_cpus
292    switch_cpus = None
293
294    if options.prog_interval:
295        for i in xrange(np):
296            testsys.cpu[i].progress_interval = options.prog_interval
297
298    if options.maxinsts:
299        for i in xrange(np):
300            testsys.cpu[i].max_insts_any_thread = options.maxinsts
301
302    if cpu_class:
303        switch_cpus = [cpu_class(switched_out=True, cpu_id=(i))
304                       for i in xrange(np)]
305
306        for i in xrange(np):
307            if options.fast_forward:
308                testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
309            switch_cpus[i].system =  testsys
310            switch_cpus[i].workload = testsys.cpu[i].workload
311            switch_cpus[i].clock = testsys.cpu[i].clock
312            # simulation period
313            if options.maxinsts:
314                switch_cpus[i].max_insts_any_thread = options.maxinsts
315            # Add checker cpu if selected
316            if options.checker:
317                switch_cpus[i].addCheckerCpu()
318
319        testsys.switch_cpus = switch_cpus
320        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
321
322    if options.repeat_switch:
323        switch_class = getCPUClass(options.cpu_type)[0]
324        if switch_class.require_caches() and \
325                not options.caches:
326            print "%s: Must be used with caches" % str(switch_class)
327            sys.exit(1)
328        if not switch_class.support_take_over():
329            print "%s: CPU switching not supported" % str(switch_class)
330            sys.exit(1)
331
332        repeat_switch_cpus = [switch_class(switched_out=True, \
333                                               cpu_id=(i)) for i in xrange(np)]
334
335        for i in xrange(np):
336            repeat_switch_cpus[i].system = testsys
337            repeat_switch_cpus[i].workload = testsys.cpu[i].workload
338            repeat_switch_cpus[i].clock = testsys.cpu[i].clock
339
340            if options.maxinsts:
341                repeat_switch_cpus[i].max_insts_any_thread = options.maxinsts
342
343            if options.checker:
344                repeat_switch_cpus[i].addCheckerCpu()
345
346        testsys.repeat_switch_cpus = repeat_switch_cpus
347
348        if cpu_class:
349            repeat_switch_cpu_list = [(switch_cpus[i], repeat_switch_cpus[i])
350                                      for i in xrange(np)]
351        else:
352            repeat_switch_cpu_list = [(testsys.cpu[i], repeat_switch_cpus[i])
353                                      for i in xrange(np)]
354
355    if options.standard_switch:
356        switch_cpus = [TimingSimpleCPU(switched_out=True, cpu_id=(i))
357                       for i in xrange(np)]
358        switch_cpus_1 = [DerivO3CPU(switched_out=True, cpu_id=(i))
359                        for i in xrange(np)]
360
361        for i in xrange(np):
362            switch_cpus[i].system =  testsys
363            switch_cpus_1[i].system =  testsys
364            switch_cpus[i].workload = testsys.cpu[i].workload
365            switch_cpus_1[i].workload = testsys.cpu[i].workload
366            switch_cpus[i].clock = testsys.cpu[i].clock
367            switch_cpus_1[i].clock = testsys.cpu[i].clock
368
369            # if restoring, make atomic cpu simulate only a few instructions
370            if options.checkpoint_restore != None:
371                testsys.cpu[i].max_insts_any_thread = 1
372            # Fast forward to specified location if we are not restoring
373            elif options.fast_forward:
374                testsys.cpu[i].max_insts_any_thread = int(options.fast_forward)
375            # Fast forward to a simpoint (warning: time consuming)
376            elif options.simpoint:
377                if testsys.cpu[i].workload[0].simpoint == 0:
378                    fatal('simpoint not found')
379                testsys.cpu[i].max_insts_any_thread = \
380                    testsys.cpu[i].workload[0].simpoint
381            # No distance specified, just switch
382            else:
383                testsys.cpu[i].max_insts_any_thread = 1
384
385            # warmup period
386            if options.warmup_insts:
387                switch_cpus[i].max_insts_any_thread =  options.warmup_insts
388
389            # simulation period
390            if options.maxinsts:
391                switch_cpus_1[i].max_insts_any_thread = options.maxinsts
392
393            # attach the checker cpu if selected
394            if options.checker:
395                switch_cpus[i].addCheckerCpu()
396                switch_cpus_1[i].addCheckerCpu()
397
398        testsys.switch_cpus = switch_cpus
399        testsys.switch_cpus_1 = switch_cpus_1
400        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in xrange(np)]
401        switch_cpu_list1 = [(switch_cpus[i], switch_cpus_1[i]) for i in xrange(np)]
402
403    # set the checkpoint in the cpu before m5.instantiate is called
404    if options.take_checkpoints != None and \
405           (options.simpoint or options.at_instruction):
406        offset = int(options.take_checkpoints)
407        # Set an instruction break point
408        if options.simpoint:
409            for i in xrange(np):
410                if testsys.cpu[i].workload[0].simpoint == 0:
411                    fatal('no simpoint for testsys.cpu[%d].workload[0]', i)
412                checkpoint_inst = int(testsys.cpu[i].workload[0].simpoint) + offset
413                testsys.cpu[i].max_insts_any_thread = checkpoint_inst
414                # used for output below
415                options.take_checkpoints = checkpoint_inst
416        else:
417            options.take_checkpoints = offset
418            # Set all test cpus with the right number of instructions
419            # for the upcoming simulation
420            for i in xrange(np):
421                testsys.cpu[i].max_insts_any_thread = offset
422
423    checkpoint_dir = None
424    if options.checkpoint_restore != None:
425        maxtick, checkpoint_dir = findCptDir(options, maxtick, cptdir, testsys)
426    m5.instantiate(checkpoint_dir)
427
428    if options.standard_switch or cpu_class:
429        if options.standard_switch:
430            print "Switch at instruction count:%s" % \
431                    str(testsys.cpu[0].max_insts_any_thread)
432            exit_event = m5.simulate()
433        elif cpu_class and options.fast_forward:
434            print "Switch at instruction count:%s" % \
435                    str(testsys.cpu[0].max_insts_any_thread)
436            exit_event = m5.simulate()
437        else:
438            print "Switch at curTick count:%s" % str(10000)
439            exit_event = m5.simulate(10000)
440        print "Switched CPUS @ tick %s" % (m5.curTick())
441
442        m5.switchCpus(testsys, switch_cpu_list)
443
444        if options.standard_switch:
445            print "Switch at instruction count:%d" % \
446                    (testsys.switch_cpus[0].max_insts_any_thread)
447
448            #warmup instruction count may have already been set
449            if options.warmup_insts:
450                exit_event = m5.simulate()
451            else:
452                exit_event = m5.simulate(options.standard_switch)
453            print "Switching CPUS @ tick %s" % (m5.curTick())
454            print "Simulation ends instruction count:%d" % \
455                    (testsys.switch_cpus_1[0].max_insts_any_thread)
456            m5.switchCpus(testsys, switch_cpu_list1)
457
458    # If we're taking and restoring checkpoints, use checkpoint_dir
459    # option only for finding the checkpoints to restore from.  This
460    # lets us test checkpointing by restoring from one set of
461    # checkpoints, generating a second set, and then comparing them.
462    if options.take_checkpoints and options.checkpoint_restore:
463        if m5.options.outdir:
464            cptdir = m5.options.outdir
465        else:
466            cptdir = getcwd()
467
468    if options.take_checkpoints != None :
469        # Checkpoints being taken via the command line at <when> and at
470        # subsequent periods of <period>.  Checkpoint instructions
471        # received from the benchmark running are ignored and skipped in
472        # favor of command line checkpoint instructions.
473        exit_event = scriptCheckpoints(options, maxtick, cptdir)
474    else:
475        if options.fast_forward:
476            m5.stats.reset()
477        print "**** REAL SIMULATION ****"
478
479        # If checkpoints are being taken, then the checkpoint instruction
480        # will occur in the benchmark code it self.
481        if options.repeat_switch and maxtick > options.repeat_switch:
482            exit_event = repeatSwitch(testsys, repeat_switch_cpu_list,
483                                      maxtick, options.repeat_switch)
484        else:
485            exit_event = benchCheckpoints(options, maxtick, cptdir)
486
487    print 'Exiting @ tick %i because %s' % (m5.curTick(), exit_event.getCause())
488    if options.checkpoint_at_end:
489        m5.checkpoint(joinpath(cptdir, "cpt.%d"))
490
491    if not m5.options.interactive:
492        sys.exit(exit_event.getCode())
493