zephyr/scripts/dts/devicetree.py
Ulf Magnusson d26cb1fb55 scripts/dts: Remove redundant and confusing dict.get() calls
dict.get(key) signals to people reading the code that you're not sure
whether the key exists. It returns None if it doesn't.

When the key is known to exist, dict[key] should be used. This also
helps catch bugs by raising an exception if the key is missing.

Similarly, whether a key in a dict should be tested with

    if key in dict:

instead of with

    if dict.get(key):

The second version signals that you both want to make sure that the
exists and that it's truthy (e.g., non-empty). That's confusing if a
simple existence check was meant.

There seems to be a bug in output_keyvalue_lines() where

    fd.write("%s=%s\n" % (entry, defs[node].get(a)))

can end up writing '...=None'. Removing the .get() makes it throw an
exception instead. Keep the .get() for now and don't attempt to fix the
bug.

Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
2019-02-07 10:39:37 +01:00

299 lines
7.8 KiB
Python
Executable File

#!/usr/bin/env python3
#
# Copyright (c) 2017 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
# vim: ai:ts=4:sw=4
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:
if len(line.split(':')) == 3:
alt_label, label, name = line.split(':')
else:
label, name = line.split(':')
alt_label = None
else:
name = line
label = None
alt_label = None
if addr is None:
return label, name.strip(), None, None, None
if alt_label is None:
return label, name.strip(), addr, int(addr, 16), None
return label, name.strip(), addr, int(addr, 16), alt_label
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
elif isinstance(addr, int):
return '%s@%x' % (name, addr)
return '%s@%s' % (name, addr.strip())
def parse_node(line, fd):
label, name, addr, numeric_addr, alt_label = parse_node_name(line)
node = {
'label': label,
'type': type,
'addr': numeric_addr,
'children': {},
'props': {},
'name': build_node_name(name, addr)
}
if alt_label:
node['alt_name'] = alt_label
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['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['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))