diff options
-rwxr-xr-x | scripts/devicetree.py | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/scripts/devicetree.py b/scripts/devicetree.py new file mode 100755 index 000000000..c74dc90ff --- /dev/null +++ b/scripts/devicetree.py @@ -0,0 +1,290 @@ +#!/usr/bin/python +# +# Copyright (c) 2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import sys +import pprint + +def read_until(line, fd, end): + out = [line] + while True: + idx = line.find(end) + if idx < 0: + line = clean_line(fd.readline(), fd) + out.append(line) + else: + out.append(line[idx + len(end):]) + return out + +def remove_comment(line, fd): + out = [] + while True: + idx = line.find('/*') + if idx < 0: + idx = line.find('//') + if idx < 0: + out.append(line) + else: + out.append(line[:idx]) + return ' '.join(out) + + out.append(line[:idx]) + line = read_until(line[idx:], fd, '*/')[-1] + +def clean_line(line, fd): + return remove_comment(line, fd).strip() + +def parse_node_name(line): + line = line[:-1] + + if '@' in line: + line, addr = line.split('@') + else: + addr = None + + if ':' in line: + label, name = line.split(':') + else: + name = line + label = None + + if addr is None: + return label, name.strip(), None + + return label, name.strip(), int(addr, 16) + +def parse_values_internal(value, start, end, separator): + out = [] + + inside = False + accum = [] + for ch in value: + if not inside: + if ch == start: + inside = True + accum = [] + else: + if ch == end: + inside = False + out.append(''.join(accum)) + accum = [] + else: + accum.append(ch) + + if separator == ' ': + out = [v.split() for v in out] + + if len(out) == 1: + return parse_value(out[0]) + + return [parse_value(v) for v in out] + +def parse_values(value, start, end, separator): + out = parse_values_internal(value, start, end, separator) + if isinstance(out, list) and all(isinstance(v, str) and len(v) == 1 and not v.isalpha() for v in out): + return bytearray(out) + return out + +def parse_value(value): + if value == '': + return value + + if isinstance(value, list): + out = [parse_value(v) for v in value] + return out[0] if len(out) == 1 else out + + if value[0] == '<': + return parse_values(value, '<', '>', ' ') + if value[0] == '"': + return parse_values(value, '"', '"', ',') + if value[0] == '[': + return parse_values(value, '[', ']', ' ') + + if value[0] == '&': + return {'ref': value[1:]} + + if value[0].isdigit(): + if value.startswith("0x"): + return int(value, 16) + if value[0] == '0': + return int(value, 8) + return int(value, 10) + + return value + +def parse_property(property, fd): + if '=' in property: + key, value = property.split('=', 1) + value = ' '.join(read_until(value, fd, ';')).strip() + if not value.endswith(';'): + raise SyntaxError("parse_property: missing semicolon: %s" % value) + return key.strip(), parse_value(value[:-1]) + + property = property.strip() + if not property.endswith(';'): + raise SyntaxError("parse_property: missing semicolon: %s" % property) + + return property[:-1].strip(), True + +def build_node_name(name, addr): + if addr is None: + return name + return '%s@%x' % (name, addr) + +def parse_node(line, fd): + label, name, addr = parse_node_name(line) + + node = { + 'label': label, + 'type': type, + 'addr': addr, + 'children': {}, + 'props': {}, + 'name': build_node_name(name, addr) + } + while True: + line = fd.readline() + if not line: + raise SyntaxError("parse_node: Missing } while parsing node") + + line = clean_line(line, fd) + if not line: + continue + + if line == "};": + break + + if line.endswith('{'): + new_node = parse_node(line, fd) + node['children'][new_node['name']] = new_node + else: + key, value = parse_property(line, fd) + node['props'][key] = value + + return node + +def parse_file(fd, ignore_dts_version=False): + nodes = {} + has_v1_tag = False + while True: + line = fd.readline() + if not line: + break + + line = clean_line(line, fd) + if not line: + continue + + if line.startswith('/include/ '): + tag, filename = line.split() + with open(filename.strip()[1:-1], "r") as new_fd: + nodes.update(parse_file(new_fd, True)) + elif line == '/dts-v1/;': + has_v1_tag = True + elif line.startswith('/memreserve/ ') and line.endswith(';'): + tag, start, end = line.split() + start = int(start, 16) + end = int(end[:-1], 16) + label = "reserved_memory_0x%x_0x%x" % (start, end) + nodes[label] = { + 'type': 'memory', + 'reg': [start, end], + 'label': label, + 'addr': start, + 'name': build_node_name(name, start) + } + elif line.endswith('{'): + if not has_v1_tag and not ignore_dts_version: + raise SyntaxError("parse_file: Missing /dts-v1/ tag") + + new_node = parse_node(line, fd) + nodes[new_node['name']] = new_node + else: + raise SyntaxError("parse_file: Couldn't understand the line: %s" % line) + return nodes + +def dump_refs(name, value, indent=0): + spaces = ' ' * indent + + out = [] + if isinstance(value, dict) and 'ref' in value: + out.append('%s\"%s\" -> \"%s\";' % (spaces, name, value['ref'])) + elif isinstance(value, list): + for elem in value: + out.extend(dump_refs(name, elem, indent)) + + return out + +def dump_all_refs(name, props, indent=0): + out = [] + for key, value in props.items(): + out.extend(dump_refs(name, value, indent)) + return out + +def next_subgraph(count=[0]): + count[0] += 1 + return 'subgraph cluster_%d' % count[0] + +def get_dot_node_name(node): + name = node['name'] + return name[1:] if name[0] == '&' else name + +def dump_to_dot(nodes, indent=0, start_string='digraph devicetree', name=None): + spaces = ' ' * indent + + print("%s%s {" % (spaces, start_string)) + + if name is not None: + print("%slabel = \"%s\";" % (spaces, name)) + print("%s\"%s\";" % (spaces, name)) + + ref_list = [] + for key, value in nodes.items(): + if value.get('children'): + refs = dump_to_dot(value['children'], indent + 1, next_subgraph(), get_dot_node_name(value)) + ref_list.extend(refs) + else: + print("%s\"%s\";" % (spaces, get_dot_node_name(value))) + + for key, value in nodes.items(): + refs = dump_all_refs(get_dot_node_name(value), value.get('props', {}), indent) + ref_list.extend(refs) + + if start_string.startswith("digraph"): + print("%s%s" % (spaces, '\n'.join(ref_list))) + + print("%s}" % spaces) + + return ref_list + +def main(args): + if len(args) == 1: + print('Usage: %s filename.dts' % args[0]) + return 1 + + if '--dot' in args: + formatter = dump_to_dot + args.remove('--dot') + else: + formatter = lambda nodes: pprint.pprint(nodes, indent=2) + + with open(args[1], "r") as fd: + formatter(parse_file(fd)) + + return 0 + +if __name__ == '__main__': + sys.exit(main(sys.argv)) |