1 # Copyright (c) 2014 ARM Limited
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.
13 # Redistribution and use in source and binary forms, with or without
14 # modification, are permitted provided that the following conditions are
15 # met: redistributions of source code must retain the above copyright
16 # notice, this list of conditions and the following disclaimer;
17 # redistributions in binary form must reproduce the above copyright
18 # notice, this list of conditions and the following disclaimer in the
19 # documentation and/or other materials provided with the distribution;
20 # neither the name of the copyright holders nor the names of its
21 # contributors may be used to endorse or promote products derived from
22 # this software without specific prior written permission.
24 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 # Author: Andrew Bardsley
38 # This script allows .ini and .json system config file generated from a
39 # previous gem5 run to be read in and instantiated.
41 # This may be useful as a way of allowing variant run scripts (say,
42 # with more complicated than usual checkpointing/stats dumping/
43 # simulation control) to read pre-described systems from config scripts
44 # with better system-description capabilities. Splitting scripts
45 # between system construction and run control may allow better
48 from __future__
import print_function
58 import m5
.ticks
as ticks
60 sim_object_classes_by_name
= {
61 cls
.__name
__: cls
for cls
in m5
.objects
.__dict
__.itervalues()
62 if inspect
.isclass(cls
) and issubclass(cls
, m5
.objects
.SimObject
) }
64 # Add some parsing functions to Param classes to handle reading in .ini
65 # file elements. This could be moved into src/python/m5/params.py if
66 # reading .ini files from Python proves to be useful
68 def no_parser(cls
, flags
, param
):
69 raise Exception('Can\'t parse string: %s for parameter'
70 ' class: %s' % (str(param
), cls
.__name
__))
72 def simple_parser(suffix
='', cast
=lambda i
: i
):
73 def body(cls
, flags
, param
):
74 return cls(cast(param
+ suffix
))
77 # def tick_parser(cast=m5.objects.Latency): # lambda i: i):
78 def tick_parser(cast
=lambda i
: i
):
79 def body(cls
, flags
, param
):
81 ret
= cls(cast(str(param
) + 't'))
85 def addr_range_parser(cls
, flags
, param
):
87 (low
, high
, intlv_high_bit
, xor_high_bit
,
88 intlv_bits
, intlv_match
) = param
.split(':')
89 return m5
.objects
.AddrRange(
90 start
=long(low
), end
=long(high
),
91 intlvHighBit
=long(intlv_high_bit
), xorHighBit
=long(xor_high_bit
),
92 intlvBits
=long(intlv_bits
), intlvMatch
=long(intlv_match
))
94 def memory_bandwidth_parser(cls
, flags
, param
):
95 # The string will be in tick/byte
96 # Convert to byte/tick
97 value
= 1.0 / float(param
)
99 value
= ticks
.fromSeconds(value
)
100 return cls('%fB/s' % value
)
102 # These parameters have trickier parsing from .ini files than might be
105 'Bool': simple_parser(),
106 'ParamValue': no_parser
,
107 'NumericParamValue': simple_parser(cast
=long),
108 'TickParamValue': tick_parser(),
109 'Frequency': tick_parser(cast
=m5
.objects
.Latency
),
110 'Current': simple_parser(suffix
='A'),
111 'Voltage': simple_parser(suffix
='V'),
112 'Enum': simple_parser(),
113 'MemorySize': simple_parser(suffix
='B'),
114 'MemorySize32': simple_parser(suffix
='B'),
115 'AddrRange': addr_range_parser
,
116 'String': simple_parser(),
117 'MemoryBandwidth': memory_bandwidth_parser
,
118 'Time': simple_parser(),
119 'EthernetAddr': simple_parser()
122 for name
, parser
in param_parsers
.iteritems():
123 setattr(m5
.params
.__dict
__[name
], 'parse_ini', classmethod(parser
))
125 class PortConnection(object):
126 """This class is similar to m5.params.PortRef but with just enough
127 information for ConfigManager"""
129 def __init__(self
, object_name
, port_name
, index
):
130 self
.object_name
= object_name
131 self
.port_name
= port_name
135 def from_string(cls
, str):
136 m
= re
.match('(.*)\.([^.\[]+)(\[(\d+)\])?', str)
137 object_name
, port_name
, whole_index
, index
= m
.groups()
138 if index
is not None:
143 return PortConnection(object_name
, port_name
, index
)
146 return '%s.%s[%d]' % (self
.object_name
, self
.port_name
, self
.index
)
148 def __cmp__(self
, right
):
149 return cmp((self
.object_name
, self
.port_name
, self
.index
),
150 (right
.object_name
, right
.port_name
, right
.index
))
153 """Convert any non list to a singleton list"""
154 if isinstance(v
, list):
159 class ConfigManager(object):
160 """Manager for parsing a Root configuration from a config file"""
161 def __init__(self
, config
):
163 self
.objects_by_name
= {}
164 self
.flags
= config
.get_flags()
166 def find_object(self
, object_name
):
167 """Find and configure (with just non-SimObject parameters)
170 if object_name
== 'Null':
173 if object_name
in self
.objects_by_name
:
174 return self
.objects_by_name
[object_name
]
176 object_type
= self
.config
.get_param(object_name
, 'type')
178 if object_type
not in sim_object_classes_by_name
:
179 raise Exception('No SimObject type %s is available to'
180 ' build: %s' % (object_type
, object_name
))
182 object_class
= sim_object_classes_by_name
[object_type
]
186 for param_name
, param
in object_class
._params
.iteritems():
187 if issubclass(param
.ptype
, m5
.params
.ParamValue
):
188 if isinstance(param
, m5
.params
.VectorParamDesc
):
189 param_values
= self
.config
.get_param_vector(object_name
,
192 param_value
= [ param
.ptype
.parse_ini(self
.flags
, value
)
193 for value
in param_values
]
195 param_value
= param
.ptype
.parse_ini(
196 self
.flags
, self
.config
.get_param(object_name
,
199 parsed_params
[param_name
] = param_value
201 obj
= object_class(**parsed_params
)
202 self
.objects_by_name
[object_name
] = obj
206 def fill_in_simobj_parameters(self
, object_name
, obj
):
207 """Fill in all references to other SimObjects in an objects
208 parameters. This relies on all referenced objects having been
211 if object_name
== 'Null':
214 for param_name
, param
in obj
.__class
__._params
.iteritems():
215 if issubclass(param
.ptype
, m5
.objects
.SimObject
):
216 if isinstance(param
, m5
.params
.VectorParamDesc
):
217 param_values
= self
.config
.get_param_vector(object_name
,
220 setattr(obj
, param_name
,
221 [ self
.objects_by_name
[name
]
222 if name
!= 'Null' else m5
.params
.NULL
223 for name
in param_values
])
225 param_value
= self
.config
.get_param(object_name
,
228 if param_value
!= 'Null':
229 setattr(obj
, param_name
, self
.objects_by_name
[
234 def fill_in_children(self
, object_name
, obj
):
235 """Fill in the children of this object. This relies on all the
236 referenced objects having been created"""
238 children
= self
.config
.get_object_children(object_name
)
240 for child_name
, child_paths
in children
:
241 param
= obj
.__class
__._params
.get(child_name
, None)
242 if child_name
== 'Null':
245 if isinstance(child_paths
, list):
246 child_list
= [ self
.objects_by_name
[path
]
247 for path
in child_paths
]
249 child_list
= self
.objects_by_name
[child_paths
]
251 obj
.add_child(child_name
, child_list
)
253 for path
in to_list(child_paths
):
254 self
.fill_in_children(path
, self
.objects_by_name
[path
])
258 def parse_port_name(self
, port
):
259 """Parse the name of a port"""
261 m
= re
.match('(.*)\.([^.\[]+)(\[(\d+)\])?', port
)
262 peer
, peer_port
, whole_index
, index
= m
.groups()
263 if index
is not None:
268 return (peer
, self
.objects_by_name
[peer
], peer_port
, index
)
270 def gather_port_connections(self
, object_name
, obj
):
271 """Gather all the port-to-port connections from the named object.
272 Returns a list of (PortConnection, PortConnection) with unordered
273 (wrt. master/slave) connection information"""
275 if object_name
== 'Null':
279 for port_name
, port
in obj
.__class
__._ports
.iteritems():
280 # Assume that unnamed ports are unconnected
281 peers
= self
.config
.get_port_peers(object_name
, port_name
)
283 for index
, peer
in zip(xrange(0, len(peers
)), peers
):
284 parsed_ports
.append((
285 PortConnection(object_name
, port
.name
, index
),
286 PortConnection
.from_string(peer
)))
290 def bind_ports(self
, connections
):
291 """Bind all ports from the given connection list. Note that the
292 connection list *must* list all connections with both (slave,master)
293 and (master,slave) orderings"""
295 # Markup a dict of how many connections are made to each port.
296 # This will be used to check that the next-to-be-made connection
297 # has a suitable port index
298 port_bind_indices
= {}
299 for from_port
, to_port
in connections
:
301 (from_port
.object_name
, from_port
.port_name
)] = 0
303 def port_has_correct_index(port
):
304 return port_bind_indices
[
305 (port
.object_name
, port
.port_name
)] == port
.index
307 def increment_port_index(port
):
309 (port
.object_name
, port
.port_name
)] += 1
311 # Step through the sorted connections. Exactly one of
312 # each (slave,master) and (master,slave) pairs will be
313 # bindable because the connections are sorted.
314 # For example: port_bind_indices
315 # left right left right
316 # a.b[0] -> d.f[1] 0 0 X
317 # a.b[1] -> e.g 0 0 BIND!
318 # e.g -> a.b[1] 1 X 0
319 # d.f[0] -> f.h 0 0 BIND!
320 # d.f[1] -> a.b[0] 1 0 BIND!
321 connections_to_make
= []
322 for connection
in sorted(connections
):
323 from_port
, to_port
= connection
325 if (port_has_correct_index(from_port
) and
326 port_has_correct_index(to_port
)):
328 connections_to_make
.append((from_port
, to_port
))
330 increment_port_index(from_port
)
331 increment_port_index(to_port
)
333 # Exactly half of the connections (ie. all of them, one per
334 # direction) must now have been made
335 if (len(connections_to_make
) * 2) != len(connections
):
336 raise Exception('Port bindings can\'t be ordered')
338 # Actually do the binding
339 for from_port
, to_port
in connections_to_make
:
340 from_object
= self
.objects_by_name
[from_port
.object_name
]
341 to_object
= self
.objects_by_name
[to_port
.object_name
]
343 setattr(from_object
, from_port
.port_name
,
344 getattr(to_object
, to_port
.port_name
))
346 def find_all_objects(self
):
347 """Find and build all SimObjects from the config file and connect
348 their ports together as described. Does not instantiate system"""
350 # Build SimObjects for all sections of the config file
351 # populating not-SimObject-valued parameters
352 for object_name
in self
.config
.get_all_object_names():
353 self
.find_object(object_name
)
355 # Add children to objects in the hierarchy from root
356 self
.fill_in_children('root', self
.find_object('root'))
358 # Now fill in SimObject-valued parameters in the knowledge that
359 # this won't be interpreted as becoming the parent of objects
360 # which are already in the root hierarchy
361 for name
, obj
in self
.objects_by_name
.iteritems():
362 self
.fill_in_simobj_parameters(name
, obj
)
364 # Gather a list of all port-to-port connections
366 for name
, obj
in self
.objects_by_name
.iteritems():
367 connections
+= self
.gather_port_connections(name
, obj
)
369 # Find an acceptable order to bind those port connections and
371 self
.bind_ports(connections
)
373 class ConfigFile(object):
377 def load(self
, config_file
):
378 """Load the named config file"""
381 def get_all_object_names(self
):
382 """Get a list of all the SimObject paths in the configuration"""
385 def get_param(self
, object_name
, param_name
):
386 """Get a single param or SimObject reference from the configuration
390 def get_param_vector(self
, object_name
, param_name
):
391 """Get a vector param or vector of SimObject references from the
392 configuration as a list of strings"""
395 def get_object_children(self
, object_name
):
396 """Get a list of (name, paths) for each child of this object.
397 paths is either a single string object path or a list of object
401 def get_port_peers(self
, object_name
, port_name
):
402 """Get the list of connected port names (in the string form
403 object.port(\[index\])?) of the port object_name.port_name"""
406 class ConfigIniFile(ConfigFile
):
408 self
.parser
= ConfigParser
.ConfigParser()
410 def load(self
, config_file
):
411 self
.parser
.read(config_file
)
413 def get_all_object_names(self
):
414 return self
.parser
.sections()
416 def get_param(self
, object_name
, param_name
):
417 return self
.parser
.get(object_name
, param_name
)
419 def get_param_vector(self
, object_name
, param_name
):
420 return self
.parser
.get(object_name
, param_name
).split()
422 def get_object_children(self
, object_name
):
423 if self
.parser
.has_option(object_name
, 'children'):
424 children
= self
.parser
.get(object_name
, 'children')
425 child_names
= children
.split()
429 def make_path(child_name
):
430 if object_name
== 'root':
433 return '%s.%s' % (object_name
, child_name
)
435 return [ (name
, make_path(name
)) for name
in child_names
]
437 def get_port_peers(self
, object_name
, port_name
):
438 if self
.parser
.has_option(object_name
, port_name
):
439 peer_string
= self
.parser
.get(object_name
, port_name
)
440 return peer_string
.split()
444 class ConfigJsonFile(ConfigFile
):
448 def is_sim_object(self
, node
):
449 return isinstance(node
, dict) and 'path' in node
451 def find_all_objects(self
, node
):
452 if self
.is_sim_object(node
):
453 self
.object_dicts
[node
['path']] = node
455 if isinstance(node
, list):
457 self
.find_all_objects(elem
)
458 elif isinstance(node
, dict):
459 for elem
in node
.itervalues():
460 self
.find_all_objects(elem
)
462 def load(self
, config_file
):
463 root
= json
.load(open(config_file
, 'r'))
464 self
.object_dicts
= {}
465 self
.find_all_objects(root
)
467 def get_all_object_names(self
):
468 return sorted(self
.object_dicts
.keys())
470 def parse_param_string(self
, node
):
473 elif self
.is_sim_object(node
):
478 def get_param(self
, object_name
, param_name
):
479 obj
= self
.object_dicts
[object_name
]
481 return self
.parse_param_string(obj
[param_name
])
483 def get_param_vector(self
, object_name
, param_name
):
484 obj
= self
.object_dicts
[object_name
]
486 return [ self
.parse_param_string(p
) for p
in obj
[param_name
] ]
488 def get_object_children(self
, object_name
):
489 """It is difficult to tell which elements are children in the
490 JSON file as there is no explicit 'children' node. Take any
491 element which is a full SimObject description or a list of
492 SimObject descriptions. This will not work with a mixed list of
493 references and descriptions but that's a scenario that isn't
494 possible (very likely?) with gem5's binding/naming rules"""
495 obj
= self
.object_dicts
[object_name
]
498 for name
, node
in obj
.iteritems():
499 if self
.is_sim_object(node
):
500 children
.append((name
, node
['path']))
501 elif isinstance(node
, list) and node
!= [] and all([
502 self
.is_sim_object(e
) for e
in node
]):
503 children
.append((name
, [ e
['path'] for e
in node
]))
507 def get_port_peers(self
, object_name
, port_name
):
508 """Get the 'peer' element of any node with 'peer' and 'role'
510 obj
= self
.object_dicts
[object_name
]
513 if port_name
in obj
and 'peer' in obj
[port_name
] and \
514 'role' in obj
[port_name
]:
515 peers
= to_list(obj
[port_name
]['peer'])
519 parser
= argparse
.ArgumentParser()
521 parser
.add_argument('config_file', metavar
='config-file.ini',
522 help='.ini configuration file to load and run')
523 parser
.add_argument('--checkpoint-dir', type=str, default
=None,
524 help='A checkpoint to directory to restore when starting '
527 args
= parser
.parse_args(sys
.argv
[1:])
529 if args
.config_file
.endswith('.ini'):
530 config
= ConfigIniFile()
531 config
.load(args
.config_file
)
533 config
= ConfigJsonFile()
534 config
.load(args
.config_file
)
536 ticks
.fixGlobalFrequency()
538 mgr
= ConfigManager(config
)
540 mgr
.find_all_objects()
542 m5
.instantiate(args
.checkpoint_dir
)
544 exit_event
= m5
.simulate()
545 print('Exiting @ tick %i because %s' % (m5
.curTick(), exit_event
.getCause()))