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
56 import m5
.ticks
as ticks
58 sim_object_classes_by_name
= {
59 cls
.__name
__: cls
for cls
in m5
.objects
.__dict
__.itervalues()
60 if inspect
.isclass(cls
) and issubclass(cls
, m5
.objects
.SimObject
) }
62 # Add some parsing functions to Param classes to handle reading in .ini
63 # file elements. This could be moved into src/python/m5/params.py if
64 # reading .ini files from Python proves to be useful
66 def no_parser(cls
, flags
, param
):
67 raise Exception('Can\'t parse string: %s for parameter'
68 ' class: %s' % (str(param
), cls
.__name
__))
70 def simple_parser(suffix
='', cast
=lambda i
: i
):
71 def body(cls
, flags
, param
):
72 return cls(cast(param
+ suffix
))
75 # def tick_parser(cast=m5.objects.Latency): # lambda i: i):
76 def tick_parser(cast
=lambda i
: i
):
77 def body(cls
, flags
, param
):
79 ret
= cls(cast(str(param
) + 't'))
83 def addr_range_parser(cls
, flags
, param
):
85 low
, high
= param
.split(':')
86 return m5
.objects
.AddrRange(long(low
), long(high
))
88 def memory_bandwidth_parser(cls
, flags
, param
):
89 # The string will be in tick/byte
90 # Convert to byte/tick
91 value
= 1.0 / float(param
)
93 value
= ticks
.fromSeconds(value
)
94 return cls('%fB/s' % value
)
96 # These parameters have trickier parsing from .ini files than might be
99 'Bool': simple_parser(),
100 'ParamValue': no_parser
,
101 'NumericParamValue': simple_parser(cast
=long),
102 'TickParamValue': tick_parser(),
103 'Frequency': tick_parser(cast
=m5
.objects
.Latency
),
104 'Current': simple_parser(suffix
='A'),
105 'Voltage': simple_parser(suffix
='V'),
106 'Enum': simple_parser(),
107 'MemorySize': simple_parser(suffix
='B'),
108 'MemorySize32': simple_parser(suffix
='B'),
109 'AddrRange': addr_range_parser
,
110 'String': simple_parser(),
111 'MemoryBandwidth': memory_bandwidth_parser
,
112 'Time': simple_parser(),
113 'EthernetAddr': simple_parser()
116 for name
, parser
in param_parsers
.iteritems():
117 setattr(m5
.params
.__dict
__[name
], 'parse_ini', classmethod(parser
))
119 class PortConnection(object):
120 """This class is similar to m5.params.PortRef but with just enough
121 information for ConfigManager"""
123 def __init__(self
, object_name
, port_name
, index
):
124 self
.object_name
= object_name
125 self
.port_name
= port_name
129 def from_string(cls
, str):
130 m
= re
.match('(.*)\.([^.\[]+)(\[(\d+)\])?', str)
131 object_name
, port_name
, whole_index
, index
= m
.groups()
132 if index
is not None:
137 return PortConnection(object_name
, port_name
, index
)
140 return '%s.%s[%d]' % (self
.object_name
, self
.port_name
, self
.index
)
142 def __cmp__(self
, right
):
143 return cmp((self
.object_name
, self
.port_name
, self
.index
),
144 (right
.object_name
, right
.port_name
, right
.index
))
147 """Convert any non list to a singleton list"""
148 if isinstance(v
, list):
153 class ConfigManager(object):
154 """Manager for parsing a Root configuration from a config file"""
155 def __init__(self
, config
):
157 self
.objects_by_name
= {}
158 self
.flags
= config
.get_flags()
160 def find_object(self
, object_name
):
161 """Find and configure (with just non-SimObject parameters)
164 if object_name
== 'Null':
167 if object_name
in self
.objects_by_name
:
168 return self
.objects_by_name
[object_name
]
170 object_type
= self
.config
.get_param(object_name
, 'type')
172 if object_type
not in sim_object_classes_by_name
:
173 raise Exception('No SimObject type %s is available to'
174 ' build: %s' % (object_type
, object_name
))
176 object_class
= sim_object_classes_by_name
[object_type
]
180 for param_name
, param
in object_class
._params
.iteritems():
181 if issubclass(param
.ptype
, m5
.params
.ParamValue
):
182 if isinstance(param
, m5
.params
.VectorParamDesc
):
183 param_values
= self
.config
.get_param_vector(object_name
,
186 param_value
= [ param
.ptype
.parse_ini(self
.flags
, value
)
187 for value
in param_values
]
189 param_value
= param
.ptype
.parse_ini(
190 self
.flags
, self
.config
.get_param(object_name
,
193 parsed_params
[param_name
] = param_value
195 obj
= object_class(**parsed_params
)
196 self
.objects_by_name
[object_name
] = obj
200 def fill_in_simobj_parameters(self
, object_name
, obj
):
201 """Fill in all references to other SimObjects in an objects
202 parameters. This relies on all referenced objects having been
205 if object_name
== 'Null':
208 for param_name
, param
in obj
.__class
__._params
.iteritems():
209 if issubclass(param
.ptype
, m5
.objects
.SimObject
):
210 if isinstance(param
, m5
.params
.VectorParamDesc
):
211 param_values
= self
.config
.get_param_vector(object_name
,
214 setattr(obj
, param_name
, [ self
.objects_by_name
[name
]
215 for name
in param_values
])
217 param_value
= self
.config
.get_param(object_name
,
220 if param_value
!= 'Null':
221 setattr(obj
, param_name
, self
.objects_by_name
[
226 def fill_in_children(self
, object_name
, obj
):
227 """Fill in the children of this object. This relies on all the
228 referenced objects having been created"""
230 children
= self
.config
.get_object_children(object_name
)
232 for child_name
, child_paths
in children
:
233 param
= obj
.__class
__._params
.get(child_name
, None)
235 if isinstance(child_paths
, list):
236 child_list
= [ self
.objects_by_name
[path
]
237 for path
in child_paths
]
239 child_list
= self
.objects_by_name
[child_paths
]
241 obj
.add_child(child_name
, child_list
)
243 for path
in to_list(child_paths
):
244 self
.fill_in_children(path
, self
.objects_by_name
[path
])
248 def parse_port_name(self
, port
):
249 """Parse the name of a port"""
251 m
= re
.match('(.*)\.([^.\[]+)(\[(\d+)\])?', port
)
252 peer
, peer_port
, whole_index
, index
= m
.groups()
253 if index
is not None:
258 return (peer
, self
.objects_by_name
[peer
], peer_port
, index
)
260 def gather_port_connections(self
, object_name
, obj
):
261 """Gather all the port-to-port connections from the named object.
262 Returns a list of (PortConnection, PortConnection) with unordered
263 (wrt. master/slave) connection information"""
265 if object_name
== 'Null':
269 for port_name
, port
in obj
.__class
__._ports
.iteritems():
270 # Assume that unnamed ports are unconnected
271 peers
= self
.config
.get_port_peers(object_name
, port_name
)
273 for index
, peer
in zip(xrange(0, len(peers
)), peers
):
274 parsed_ports
.append((
275 PortConnection(object_name
, port
.name
, index
),
276 PortConnection
.from_string(peer
)))
280 def bind_ports(self
, connections
):
281 """Bind all ports from the given connection list. Note that the
282 connection list *must* list all connections with both (slave,master)
283 and (master,slave) orderings"""
285 # Markup a dict of how many connections are made to each port.
286 # This will be used to check that the next-to-be-made connection
287 # has a suitable port index
288 port_bind_indices
= {}
289 for from_port
, to_port
in connections
:
291 (from_port
.object_name
, from_port
.port_name
)] = 0
293 def port_has_correct_index(port
):
294 return port_bind_indices
[
295 (port
.object_name
, port
.port_name
)] == port
.index
297 def increment_port_index(port
):
299 (port
.object_name
, port
.port_name
)] += 1
301 # Step through the sorted connections. Exactly one of
302 # each (slave,master) and (master,slave) pairs will be
303 # bindable because the connections are sorted.
304 # For example: port_bind_indices
305 # left right left right
306 # a.b[0] -> d.f[1] 0 0 X
307 # a.b[1] -> e.g 0 0 BIND!
308 # e.g -> a.b[1] 1 X 0
309 # d.f[0] -> f.h 0 0 BIND!
310 # d.f[1] -> a.b[0] 1 0 BIND!
311 connections_to_make
= []
312 for connection
in sorted(connections
):
313 from_port
, to_port
= connection
315 if (port_has_correct_index(from_port
) and
316 port_has_correct_index(to_port
)):
318 connections_to_make
.append((from_port
, to_port
))
320 increment_port_index(from_port
)
321 increment_port_index(to_port
)
323 # Exactly half of the connections (ie. all of them, one per
324 # direction) must now have been made
325 if (len(connections_to_make
) * 2) != len(connections
):
326 raise Exception('Port bindings can\'t be ordered')
328 # Actually do the binding
329 for from_port
, to_port
in connections_to_make
:
330 from_object
= self
.objects_by_name
[from_port
.object_name
]
331 to_object
= self
.objects_by_name
[to_port
.object_name
]
333 setattr(from_object
, from_port
.port_name
,
334 getattr(to_object
, to_port
.port_name
))
336 def find_all_objects(self
):
337 """Find and build all SimObjects from the config file and connect
338 their ports together as described. Does not instantiate system"""
340 # Build SimObjects for all sections of the config file
341 # populating not-SimObject-valued parameters
342 for object_name
in self
.config
.get_all_object_names():
343 self
.find_object(object_name
)
345 # Add children to objects in the hierarchy from root
346 self
.fill_in_children('root', self
.find_object('root'))
348 # Now fill in SimObject-valued parameters in the knowledge that
349 # this won't be interpreted as becoming the parent of objects
350 # which are already in the root hierarchy
351 for name
, obj
in self
.objects_by_name
.iteritems():
352 self
.fill_in_simobj_parameters(name
, obj
)
354 # Gather a list of all port-to-port connections
356 for name
, obj
in self
.objects_by_name
.iteritems():
357 connections
+= self
.gather_port_connections(name
, obj
)
359 # Find an acceptable order to bind those port connections and
361 self
.bind_ports(connections
)
363 class ConfigFile(object):
367 def load(self
, config_file
):
368 """Load the named config file"""
371 def get_all_object_names(self
):
372 """Get a list of all the SimObject paths in the configuration"""
375 def get_param(self
, object_name
, param_name
):
376 """Get a single param or SimObject reference from the configuration
380 def get_param_vector(self
, object_name
, param_name
):
381 """Get a vector param or vector of SimObject references from the
382 configuration as a list of strings"""
385 def get_object_children(self
, object_name
):
386 """Get a list of (name, paths) for each child of this object.
387 paths is either a single string object path or a list of object
391 def get_port_peers(self
, object_name
, port_name
):
392 """Get the list of connected port names (in the string form
393 object.port(\[index\])?) of the port object_name.port_name"""
396 class ConfigIniFile(ConfigFile
):
398 self
.parser
= ConfigParser
.ConfigParser()
400 def load(self
, config_file
):
401 self
.parser
.read(config_file
)
403 def get_all_object_names(self
):
404 return self
.parser
.sections()
406 def get_param(self
, object_name
, param_name
):
407 return self
.parser
.get(object_name
, param_name
)
409 def get_param_vector(self
, object_name
, param_name
):
410 return self
.parser
.get(object_name
, param_name
).split()
412 def get_object_children(self
, object_name
):
413 if self
.parser
.has_option(object_name
, 'children'):
414 children
= self
.parser
.get(object_name
, 'children')
415 child_names
= children
.split()
419 def make_path(child_name
):
420 if object_name
== 'root':
423 return '%s.%s' % (object_name
, child_name
)
425 return [ (name
, make_path(name
)) for name
in child_names
]
427 def get_port_peers(self
, object_name
, port_name
):
428 if self
.parser
.has_option(object_name
, port_name
):
429 peer_string
= self
.parser
.get(object_name
, port_name
)
430 return peer_string
.split()
434 class ConfigJsonFile(ConfigFile
):
438 def is_sim_object(self
, node
):
439 return isinstance(node
, dict) and 'path' in node
441 def find_all_objects(self
, node
):
442 if self
.is_sim_object(node
):
443 self
.object_dicts
[node
['path']] = node
445 if isinstance(node
, list):
447 self
.find_all_objects(elem
)
448 elif isinstance(node
, dict):
449 for elem
in node
.itervalues():
450 self
.find_all_objects(elem
)
452 def load(self
, config_file
):
453 root
= json
.load(open(config_file
, 'r'))
454 self
.object_dicts
= {}
455 self
.find_all_objects(root
)
457 def get_all_object_names(self
):
458 return sorted(self
.object_dicts
.keys())
460 def parse_param_string(self
, node
):
463 elif self
.is_sim_object(node
):
468 def get_param(self
, object_name
, param_name
):
469 obj
= self
.object_dicts
[object_name
]
471 return self
.parse_param_string(obj
[param_name
])
473 def get_param_vector(self
, object_name
, param_name
):
474 obj
= self
.object_dicts
[object_name
]
476 return [ self
.parse_param_string(p
) for p
in obj
[param_name
] ]
478 def get_object_children(self
, object_name
):
479 """It is difficult to tell which elements are children in the
480 JSON file as there is no explicit 'children' node. Take any
481 element which is a full SimObject description or a list of
482 SimObject descriptions. This will not work with a mixed list of
483 references and descriptions but that's a scenario that isn't
484 possible (very likely?) with gem5's binding/naming rules"""
485 obj
= self
.object_dicts
[object_name
]
488 for name
, node
in obj
.iteritems():
489 if self
.is_sim_object(node
):
490 children
.append((name
, node
['path']))
491 elif isinstance(node
, list) and node
!= [] and all([
492 self
.is_sim_object(e
) for e
in node
]):
493 children
.append((name
, [ e
['path'] for e
in node
]))
497 def get_port_peers(self
, object_name
, port_name
):
498 """Get the 'peer' element of any node with 'peer' and 'role'
500 obj
= self
.object_dicts
[object_name
]
503 if port_name
in obj
and 'peer' in obj
[port_name
] and \
504 'role' in obj
[port_name
]:
505 peers
= to_list(obj
[port_name
]['peer'])
509 parser
= argparse
.ArgumentParser()
511 parser
.add_argument('config_file', metavar
='config-file.ini',
512 help='.ini configuration file to load and run')
514 args
= parser
.parse_args(sys
.argv
[1:])
516 if args
.config_file
.endswith('.ini'):
517 config
= ConfigIniFile()
518 config
.load(args
.config_file
)
520 config
= ConfigJsonFile()
521 config
.load(args
.config_file
)
523 ticks
.fixGlobalFrequency()
525 mgr
= ConfigManager(config
)
527 mgr
.find_all_objects()
531 exit_event
= m5
.simulate()
532 print 'Exiting @ tick %i because %s' % (
533 m5
.curTick(), exit_event
.getCause())