read_config.py (12411:394fa4ecc018) read_config.py (12564:2778478ca882)
1# Copyright (c) 2014 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# 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.
23#
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.
35#
36# Author: Andrew Bardsley
37
38# This script allows .ini and .json system config file generated from a
39# previous gem5 run to be read in and instantiated.
40#
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
46# debugging.
47
1# Copyright (c) 2014 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# 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.
23#
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.
35#
36# Author: Andrew Bardsley
37
38# This script allows .ini and .json system config file generated from a
39# previous gem5 run to be read in and instantiated.
40#
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
46# debugging.
47
48from __future__ import print_function
49
48import argparse
49import ConfigParser
50import inspect
51import json
52import re
53import sys
54
55import m5
56import m5.ticks as ticks
57
58sim_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) }
61
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
65
66def no_parser(cls, flags, param):
67 raise Exception('Can\'t parse string: %s for parameter'
68 ' class: %s' % (str(param), cls.__name__))
69
70def simple_parser(suffix='', cast=lambda i: i):
71 def body(cls, flags, param):
72 return cls(cast(param + suffix))
73 return body
74
75# def tick_parser(cast=m5.objects.Latency): # lambda i: i):
76def tick_parser(cast=lambda i: i):
77 def body(cls, flags, param):
78 old_param = param
79 ret = cls(cast(str(param) + 't'))
80 return ret
81 return body
82
83def addr_range_parser(cls, flags, param):
84 sys.stdout.flush()
85 (low, high, intlv_high_bit, xor_high_bit,
86 intlv_bits, intlv_match) = param.split(':')
87 return m5.objects.AddrRange(
88 start=long(low), end=long(high),
89 intlvHighBit=long(intlv_high_bit), xorHighBit=long(xor_high_bit),
90 intlvBits=long(intlv_bits), intlvMatch=long(intlv_match))
91
92def memory_bandwidth_parser(cls, flags, param):
93 # The string will be in tick/byte
94 # Convert to byte/tick
95 value = 1.0 / float(param)
96 # Convert to byte/s
97 value = ticks.fromSeconds(value)
98 return cls('%fB/s' % value)
99
100# These parameters have trickier parsing from .ini files than might be
101# expected
102param_parsers = {
103 'Bool': simple_parser(),
104 'ParamValue': no_parser,
105 'NumericParamValue': simple_parser(cast=long),
106 'TickParamValue': tick_parser(),
107 'Frequency': tick_parser(cast=m5.objects.Latency),
108 'Current': simple_parser(suffix='A'),
109 'Voltage': simple_parser(suffix='V'),
110 'Enum': simple_parser(),
111 'MemorySize': simple_parser(suffix='B'),
112 'MemorySize32': simple_parser(suffix='B'),
113 'AddrRange': addr_range_parser,
114 'String': simple_parser(),
115 'MemoryBandwidth': memory_bandwidth_parser,
116 'Time': simple_parser(),
117 'EthernetAddr': simple_parser()
118 }
119
120for name, parser in param_parsers.iteritems():
121 setattr(m5.params.__dict__[name], 'parse_ini', classmethod(parser))
122
123class PortConnection(object):
124 """This class is similar to m5.params.PortRef but with just enough
125 information for ConfigManager"""
126
127 def __init__(self, object_name, port_name, index):
128 self.object_name = object_name
129 self.port_name = port_name
130 self.index = index
131
132 @classmethod
133 def from_string(cls, str):
134 m = re.match('(.*)\.([^.\[]+)(\[(\d+)\])?', str)
135 object_name, port_name, whole_index, index = m.groups()
136 if index is not None:
137 index = int(index)
138 else:
139 index = 0
140
141 return PortConnection(object_name, port_name, index)
142
143 def __str__(self):
144 return '%s.%s[%d]' % (self.object_name, self.port_name, self.index)
145
146 def __cmp__(self, right):
147 return cmp((self.object_name, self.port_name, self.index),
148 (right.object_name, right.port_name, right.index))
149
150def to_list(v):
151 """Convert any non list to a singleton list"""
152 if isinstance(v, list):
153 return v
154 else:
155 return [v]
156
157class ConfigManager(object):
158 """Manager for parsing a Root configuration from a config file"""
159 def __init__(self, config):
160 self.config = config
161 self.objects_by_name = {}
162 self.flags = config.get_flags()
163
164 def find_object(self, object_name):
165 """Find and configure (with just non-SimObject parameters)
166 a single object"""
167
168 if object_name == 'Null':
169 return NULL
170
171 if object_name in self.objects_by_name:
172 return self.objects_by_name[object_name]
173
174 object_type = self.config.get_param(object_name, 'type')
175
176 if object_type not in sim_object_classes_by_name:
177 raise Exception('No SimObject type %s is available to'
178 ' build: %s' % (object_type, object_name))
179
180 object_class = sim_object_classes_by_name[object_type]
181
182 parsed_params = {}
183
184 for param_name, param in object_class._params.iteritems():
185 if issubclass(param.ptype, m5.params.ParamValue):
186 if isinstance(param, m5.params.VectorParamDesc):
187 param_values = self.config.get_param_vector(object_name,
188 param_name)
189
190 param_value = [ param.ptype.parse_ini(self.flags, value)
191 for value in param_values ]
192 else:
193 param_value = param.ptype.parse_ini(
194 self.flags, self.config.get_param(object_name,
195 param_name))
196
197 parsed_params[param_name] = param_value
198
199 obj = object_class(**parsed_params)
200 self.objects_by_name[object_name] = obj
201
202 return obj
203
204 def fill_in_simobj_parameters(self, object_name, obj):
205 """Fill in all references to other SimObjects in an objects
206 parameters. This relies on all referenced objects having been
207 created"""
208
209 if object_name == 'Null':
210 return NULL
211
212 for param_name, param in obj.__class__._params.iteritems():
213 if issubclass(param.ptype, m5.objects.SimObject):
214 if isinstance(param, m5.params.VectorParamDesc):
215 param_values = self.config.get_param_vector(object_name,
216 param_name)
217
218 setattr(obj, param_name,
219 [ self.objects_by_name[name]
220 if name != 'Null' else m5.params.NULL
221 for name in param_values ])
222 else:
223 param_value = self.config.get_param(object_name,
224 param_name)
225
226 if param_value != 'Null':
227 setattr(obj, param_name, self.objects_by_name[
228 param_value])
229
230 return obj
231
232 def fill_in_children(self, object_name, obj):
233 """Fill in the children of this object. This relies on all the
234 referenced objects having been created"""
235
236 children = self.config.get_object_children(object_name)
237
238 for child_name, child_paths in children:
239 param = obj.__class__._params.get(child_name, None)
240 if child_name == 'Null':
241 continue
242
243 if isinstance(child_paths, list):
244 child_list = [ self.objects_by_name[path]
245 for path in child_paths ]
246 else:
247 child_list = self.objects_by_name[child_paths]
248
249 obj.add_child(child_name, child_list)
250
251 for path in to_list(child_paths):
252 self.fill_in_children(path, self.objects_by_name[path])
253
254 return obj
255
256 def parse_port_name(self, port):
257 """Parse the name of a port"""
258
259 m = re.match('(.*)\.([^.\[]+)(\[(\d+)\])?', port)
260 peer, peer_port, whole_index, index = m.groups()
261 if index is not None:
262 index = int(index)
263 else:
264 index = 0
265
266 return (peer, self.objects_by_name[peer], peer_port, index)
267
268 def gather_port_connections(self, object_name, obj):
269 """Gather all the port-to-port connections from the named object.
270 Returns a list of (PortConnection, PortConnection) with unordered
271 (wrt. master/slave) connection information"""
272
273 if object_name == 'Null':
274 return NULL
275
276 parsed_ports = []
277 for port_name, port in obj.__class__._ports.iteritems():
278 # Assume that unnamed ports are unconnected
279 peers = self.config.get_port_peers(object_name, port_name)
280
281 for index, peer in zip(xrange(0, len(peers)), peers):
282 parsed_ports.append((
283 PortConnection(object_name, port.name, index),
284 PortConnection.from_string(peer)))
285
286 return parsed_ports
287
288 def bind_ports(self, connections):
289 """Bind all ports from the given connection list. Note that the
290 connection list *must* list all connections with both (slave,master)
291 and (master,slave) orderings"""
292
293 # Markup a dict of how many connections are made to each port.
294 # This will be used to check that the next-to-be-made connection
295 # has a suitable port index
296 port_bind_indices = {}
297 for from_port, to_port in connections:
298 port_bind_indices[
299 (from_port.object_name, from_port.port_name)] = 0
300
301 def port_has_correct_index(port):
302 return port_bind_indices[
303 (port.object_name, port.port_name)] == port.index
304
305 def increment_port_index(port):
306 port_bind_indices[
307 (port.object_name, port.port_name)] += 1
308
309 # Step through the sorted connections. Exactly one of
310 # each (slave,master) and (master,slave) pairs will be
311 # bindable because the connections are sorted.
312 # For example: port_bind_indices
313 # left right left right
314 # a.b[0] -> d.f[1] 0 0 X
315 # a.b[1] -> e.g 0 0 BIND!
316 # e.g -> a.b[1] 1 X 0
317 # d.f[0] -> f.h 0 0 BIND!
318 # d.f[1] -> a.b[0] 1 0 BIND!
319 connections_to_make = []
320 for connection in sorted(connections):
321 from_port, to_port = connection
322
323 if (port_has_correct_index(from_port) and
324 port_has_correct_index(to_port)):
325
326 connections_to_make.append((from_port, to_port))
327
328 increment_port_index(from_port)
329 increment_port_index(to_port)
330
331 # Exactly half of the connections (ie. all of them, one per
332 # direction) must now have been made
333 if (len(connections_to_make) * 2) != len(connections):
334 raise Exception('Port bindings can\'t be ordered')
335
336 # Actually do the binding
337 for from_port, to_port in connections_to_make:
338 from_object = self.objects_by_name[from_port.object_name]
339 to_object = self.objects_by_name[to_port.object_name]
340
341 setattr(from_object, from_port.port_name,
342 getattr(to_object, to_port.port_name))
343
344 def find_all_objects(self):
345 """Find and build all SimObjects from the config file and connect
346 their ports together as described. Does not instantiate system"""
347
348 # Build SimObjects for all sections of the config file
349 # populating not-SimObject-valued parameters
350 for object_name in self.config.get_all_object_names():
351 self.find_object(object_name)
352
353 # Add children to objects in the hierarchy from root
354 self.fill_in_children('root', self.find_object('root'))
355
356 # Now fill in SimObject-valued parameters in the knowledge that
357 # this won't be interpreted as becoming the parent of objects
358 # which are already in the root hierarchy
359 for name, obj in self.objects_by_name.iteritems():
360 self.fill_in_simobj_parameters(name, obj)
361
362 # Gather a list of all port-to-port connections
363 connections = []
364 for name, obj in self.objects_by_name.iteritems():
365 connections += self.gather_port_connections(name, obj)
366
367 # Find an acceptable order to bind those port connections and
368 # bind them
369 self.bind_ports(connections)
370
371class ConfigFile(object):
372 def get_flags(self):
373 return set()
374
375 def load(self, config_file):
376 """Load the named config file"""
377 pass
378
379 def get_all_object_names(self):
380 """Get a list of all the SimObject paths in the configuration"""
381 pass
382
383 def get_param(self, object_name, param_name):
384 """Get a single param or SimObject reference from the configuration
385 as a string"""
386 pass
387
388 def get_param_vector(self, object_name, param_name):
389 """Get a vector param or vector of SimObject references from the
390 configuration as a list of strings"""
391 pass
392
393 def get_object_children(self, object_name):
394 """Get a list of (name, paths) for each child of this object.
395 paths is either a single string object path or a list of object
396 paths"""
397 pass
398
399 def get_port_peers(self, object_name, port_name):
400 """Get the list of connected port names (in the string form
401 object.port(\[index\])?) of the port object_name.port_name"""
402 pass
403
404class ConfigIniFile(ConfigFile):
405 def __init__(self):
406 self.parser = ConfigParser.ConfigParser()
407
408 def load(self, config_file):
409 self.parser.read(config_file)
410
411 def get_all_object_names(self):
412 return self.parser.sections()
413
414 def get_param(self, object_name, param_name):
415 return self.parser.get(object_name, param_name)
416
417 def get_param_vector(self, object_name, param_name):
418 return self.parser.get(object_name, param_name).split()
419
420 def get_object_children(self, object_name):
421 if self.parser.has_option(object_name, 'children'):
422 children = self.parser.get(object_name, 'children')
423 child_names = children.split()
424 else:
425 child_names = []
426
427 def make_path(child_name):
428 if object_name == 'root':
429 return child_name
430 else:
431 return '%s.%s' % (object_name, child_name)
432
433 return [ (name, make_path(name)) for name in child_names ]
434
435 def get_port_peers(self, object_name, port_name):
436 if self.parser.has_option(object_name, port_name):
437 peer_string = self.parser.get(object_name, port_name)
438 return peer_string.split()
439 else:
440 return []
441
442class ConfigJsonFile(ConfigFile):
443 def __init__(self):
444 pass
445
446 def is_sim_object(self, node):
447 return isinstance(node, dict) and 'path' in node
448
449 def find_all_objects(self, node):
450 if self.is_sim_object(node):
451 self.object_dicts[node['path']] = node
452
453 if isinstance(node, list):
454 for elem in node:
455 self.find_all_objects(elem)
456 elif isinstance(node, dict):
457 for elem in node.itervalues():
458 self.find_all_objects(elem)
459
460 def load(self, config_file):
461 root = json.load(open(config_file, 'r'))
462 self.object_dicts = {}
463 self.find_all_objects(root)
464
465 def get_all_object_names(self):
466 return sorted(self.object_dicts.keys())
467
468 def parse_param_string(self, node):
469 if node is None:
470 return "Null"
471 elif self.is_sim_object(node):
472 return node['path']
473 else:
474 return str(node)
475
476 def get_param(self, object_name, param_name):
477 obj = self.object_dicts[object_name]
478
479 return self.parse_param_string(obj[param_name])
480
481 def get_param_vector(self, object_name, param_name):
482 obj = self.object_dicts[object_name]
483
484 return [ self.parse_param_string(p) for p in obj[param_name] ]
485
486 def get_object_children(self, object_name):
487 """It is difficult to tell which elements are children in the
488 JSON file as there is no explicit 'children' node. Take any
489 element which is a full SimObject description or a list of
490 SimObject descriptions. This will not work with a mixed list of
491 references and descriptions but that's a scenario that isn't
492 possible (very likely?) with gem5's binding/naming rules"""
493 obj = self.object_dicts[object_name]
494
495 children = []
496 for name, node in obj.iteritems():
497 if self.is_sim_object(node):
498 children.append((name, node['path']))
499 elif isinstance(node, list) and node != [] and all([
500 self.is_sim_object(e) for e in node ]):
501 children.append((name, [ e['path'] for e in node ]))
502
503 return children
504
505 def get_port_peers(self, object_name, port_name):
506 """Get the 'peer' element of any node with 'peer' and 'role'
507 elements"""
508 obj = self.object_dicts[object_name]
509
510 peers = []
511 if port_name in obj and 'peer' in obj[port_name] and \
512 'role' in obj[port_name]:
513 peers = to_list(obj[port_name]['peer'])
514
515 return peers
516
517parser = argparse.ArgumentParser()
518
519parser.add_argument('config_file', metavar='config-file.ini',
520 help='.ini configuration file to load and run')
521parser.add_argument('--checkpoint-dir', type=str, default=None,
522 help='A checkpoint to directory to restore when starting '
523 'the simulation')
524
525args = parser.parse_args(sys.argv[1:])
526
527if args.config_file.endswith('.ini'):
528 config = ConfigIniFile()
529 config.load(args.config_file)
530else:
531 config = ConfigJsonFile()
532 config.load(args.config_file)
533
534ticks.fixGlobalFrequency()
535
536mgr = ConfigManager(config)
537
538mgr.find_all_objects()
539
540m5.instantiate(args.checkpoint_dir)
541
542exit_event = m5.simulate()
50import argparse
51import ConfigParser
52import inspect
53import json
54import re
55import sys
56
57import m5
58import m5.ticks as ticks
59
60sim_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) }
63
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
67
68def no_parser(cls, flags, param):
69 raise Exception('Can\'t parse string: %s for parameter'
70 ' class: %s' % (str(param), cls.__name__))
71
72def simple_parser(suffix='', cast=lambda i: i):
73 def body(cls, flags, param):
74 return cls(cast(param + suffix))
75 return body
76
77# def tick_parser(cast=m5.objects.Latency): # lambda i: i):
78def tick_parser(cast=lambda i: i):
79 def body(cls, flags, param):
80 old_param = param
81 ret = cls(cast(str(param) + 't'))
82 return ret
83 return body
84
85def addr_range_parser(cls, flags, param):
86 sys.stdout.flush()
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))
93
94def 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)
98 # Convert to byte/s
99 value = ticks.fromSeconds(value)
100 return cls('%fB/s' % value)
101
102# These parameters have trickier parsing from .ini files than might be
103# expected
104param_parsers = {
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()
120 }
121
122for name, parser in param_parsers.iteritems():
123 setattr(m5.params.__dict__[name], 'parse_ini', classmethod(parser))
124
125class PortConnection(object):
126 """This class is similar to m5.params.PortRef but with just enough
127 information for ConfigManager"""
128
129 def __init__(self, object_name, port_name, index):
130 self.object_name = object_name
131 self.port_name = port_name
132 self.index = index
133
134 @classmethod
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:
139 index = int(index)
140 else:
141 index = 0
142
143 return PortConnection(object_name, port_name, index)
144
145 def __str__(self):
146 return '%s.%s[%d]' % (self.object_name, self.port_name, self.index)
147
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))
151
152def to_list(v):
153 """Convert any non list to a singleton list"""
154 if isinstance(v, list):
155 return v
156 else:
157 return [v]
158
159class ConfigManager(object):
160 """Manager for parsing a Root configuration from a config file"""
161 def __init__(self, config):
162 self.config = config
163 self.objects_by_name = {}
164 self.flags = config.get_flags()
165
166 def find_object(self, object_name):
167 """Find and configure (with just non-SimObject parameters)
168 a single object"""
169
170 if object_name == 'Null':
171 return NULL
172
173 if object_name in self.objects_by_name:
174 return self.objects_by_name[object_name]
175
176 object_type = self.config.get_param(object_name, 'type')
177
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))
181
182 object_class = sim_object_classes_by_name[object_type]
183
184 parsed_params = {}
185
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,
190 param_name)
191
192 param_value = [ param.ptype.parse_ini(self.flags, value)
193 for value in param_values ]
194 else:
195 param_value = param.ptype.parse_ini(
196 self.flags, self.config.get_param(object_name,
197 param_name))
198
199 parsed_params[param_name] = param_value
200
201 obj = object_class(**parsed_params)
202 self.objects_by_name[object_name] = obj
203
204 return obj
205
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
209 created"""
210
211 if object_name == 'Null':
212 return NULL
213
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,
218 param_name)
219
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 ])
224 else:
225 param_value = self.config.get_param(object_name,
226 param_name)
227
228 if param_value != 'Null':
229 setattr(obj, param_name, self.objects_by_name[
230 param_value])
231
232 return obj
233
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"""
237
238 children = self.config.get_object_children(object_name)
239
240 for child_name, child_paths in children:
241 param = obj.__class__._params.get(child_name, None)
242 if child_name == 'Null':
243 continue
244
245 if isinstance(child_paths, list):
246 child_list = [ self.objects_by_name[path]
247 for path in child_paths ]
248 else:
249 child_list = self.objects_by_name[child_paths]
250
251 obj.add_child(child_name, child_list)
252
253 for path in to_list(child_paths):
254 self.fill_in_children(path, self.objects_by_name[path])
255
256 return obj
257
258 def parse_port_name(self, port):
259 """Parse the name of a port"""
260
261 m = re.match('(.*)\.([^.\[]+)(\[(\d+)\])?', port)
262 peer, peer_port, whole_index, index = m.groups()
263 if index is not None:
264 index = int(index)
265 else:
266 index = 0
267
268 return (peer, self.objects_by_name[peer], peer_port, index)
269
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"""
274
275 if object_name == 'Null':
276 return NULL
277
278 parsed_ports = []
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)
282
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)))
287
288 return parsed_ports
289
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"""
294
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:
300 port_bind_indices[
301 (from_port.object_name, from_port.port_name)] = 0
302
303 def port_has_correct_index(port):
304 return port_bind_indices[
305 (port.object_name, port.port_name)] == port.index
306
307 def increment_port_index(port):
308 port_bind_indices[
309 (port.object_name, port.port_name)] += 1
310
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
324
325 if (port_has_correct_index(from_port) and
326 port_has_correct_index(to_port)):
327
328 connections_to_make.append((from_port, to_port))
329
330 increment_port_index(from_port)
331 increment_port_index(to_port)
332
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')
337
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]
342
343 setattr(from_object, from_port.port_name,
344 getattr(to_object, to_port.port_name))
345
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"""
349
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)
354
355 # Add children to objects in the hierarchy from root
356 self.fill_in_children('root', self.find_object('root'))
357
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)
363
364 # Gather a list of all port-to-port connections
365 connections = []
366 for name, obj in self.objects_by_name.iteritems():
367 connections += self.gather_port_connections(name, obj)
368
369 # Find an acceptable order to bind those port connections and
370 # bind them
371 self.bind_ports(connections)
372
373class ConfigFile(object):
374 def get_flags(self):
375 return set()
376
377 def load(self, config_file):
378 """Load the named config file"""
379 pass
380
381 def get_all_object_names(self):
382 """Get a list of all the SimObject paths in the configuration"""
383 pass
384
385 def get_param(self, object_name, param_name):
386 """Get a single param or SimObject reference from the configuration
387 as a string"""
388 pass
389
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"""
393 pass
394
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
398 paths"""
399 pass
400
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"""
404 pass
405
406class ConfigIniFile(ConfigFile):
407 def __init__(self):
408 self.parser = ConfigParser.ConfigParser()
409
410 def load(self, config_file):
411 self.parser.read(config_file)
412
413 def get_all_object_names(self):
414 return self.parser.sections()
415
416 def get_param(self, object_name, param_name):
417 return self.parser.get(object_name, param_name)
418
419 def get_param_vector(self, object_name, param_name):
420 return self.parser.get(object_name, param_name).split()
421
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()
426 else:
427 child_names = []
428
429 def make_path(child_name):
430 if object_name == 'root':
431 return child_name
432 else:
433 return '%s.%s' % (object_name, child_name)
434
435 return [ (name, make_path(name)) for name in child_names ]
436
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()
441 else:
442 return []
443
444class ConfigJsonFile(ConfigFile):
445 def __init__(self):
446 pass
447
448 def is_sim_object(self, node):
449 return isinstance(node, dict) and 'path' in node
450
451 def find_all_objects(self, node):
452 if self.is_sim_object(node):
453 self.object_dicts[node['path']] = node
454
455 if isinstance(node, list):
456 for elem in node:
457 self.find_all_objects(elem)
458 elif isinstance(node, dict):
459 for elem in node.itervalues():
460 self.find_all_objects(elem)
461
462 def load(self, config_file):
463 root = json.load(open(config_file, 'r'))
464 self.object_dicts = {}
465 self.find_all_objects(root)
466
467 def get_all_object_names(self):
468 return sorted(self.object_dicts.keys())
469
470 def parse_param_string(self, node):
471 if node is None:
472 return "Null"
473 elif self.is_sim_object(node):
474 return node['path']
475 else:
476 return str(node)
477
478 def get_param(self, object_name, param_name):
479 obj = self.object_dicts[object_name]
480
481 return self.parse_param_string(obj[param_name])
482
483 def get_param_vector(self, object_name, param_name):
484 obj = self.object_dicts[object_name]
485
486 return [ self.parse_param_string(p) for p in obj[param_name] ]
487
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]
496
497 children = []
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 ]))
504
505 return children
506
507 def get_port_peers(self, object_name, port_name):
508 """Get the 'peer' element of any node with 'peer' and 'role'
509 elements"""
510 obj = self.object_dicts[object_name]
511
512 peers = []
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'])
516
517 return peers
518
519parser = argparse.ArgumentParser()
520
521parser.add_argument('config_file', metavar='config-file.ini',
522 help='.ini configuration file to load and run')
523parser.add_argument('--checkpoint-dir', type=str, default=None,
524 help='A checkpoint to directory to restore when starting '
525 'the simulation')
526
527args = parser.parse_args(sys.argv[1:])
528
529if args.config_file.endswith('.ini'):
530 config = ConfigIniFile()
531 config.load(args.config_file)
532else:
533 config = ConfigJsonFile()
534 config.load(args.config_file)
535
536ticks.fixGlobalFrequency()
537
538mgr = ConfigManager(config)
539
540mgr.find_all_objects()
541
542m5.instantiate(args.checkpoint_dir)
543
544exit_event = m5.simulate()
543print 'Exiting @ tick %i because %s' % (
544 m5.curTick(), exit_event.getCause())
545print('Exiting @ tick %i because %s' % (m5.curTick(), exit_event.getCause()))