blob: 521a3ad4b289638468b046d271292b31e14a7d4a [file] [log] [blame]
Anas Nashifed3d7c12017-04-10 08:53:23 -04001#!/usr/bin/env python3
Leandro Pereiraca42e852016-12-08 12:55:45 -08002#
3# Copyright (c) 2017 Intel Corporation
4#
Tomasz Bursztyka6a9ebdc2018-03-07 15:17:28 +01005# SPDX-License-Identifier: Apache-2.0
Leandro Pereiraca42e852016-12-08 12:55:45 -08006#
7
Tomasz Bursztykab34d1562018-03-07 15:45:46 +01008# vim: ai:ts=4:sw=4
9
Leandro Pereiraca42e852016-12-08 12:55:45 -080010import sys
11import pprint
12
13def read_until(line, fd, end):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010014 out = [line]
15 while True:
16 idx = line.find(end)
17 if idx < 0:
18 line = clean_line(fd.readline(), fd)
19 out.append(line)
20 else:
21 out.append(line[idx + len(end):])
22 return out
Leandro Pereiraca42e852016-12-08 12:55:45 -080023
24def remove_comment(line, fd):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010025 out = []
26 while True:
27 idx = line.find('/*')
28 if idx < 0:
29 idx = line.find('//')
30 if idx < 0:
31 out.append(line)
32 else:
33 out.append(line[:idx])
34 return ' '.join(out)
Leandro Pereiraca42e852016-12-08 12:55:45 -080035
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010036 out.append(line[:idx])
37 line = read_until(line[idx:], fd, '*/')[-1]
Leandro Pereiraca42e852016-12-08 12:55:45 -080038
39def clean_line(line, fd):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010040 return remove_comment(line, fd).strip()
Leandro Pereiraca42e852016-12-08 12:55:45 -080041
42def parse_node_name(line):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010043 line = line[:-1]
Leandro Pereiraca42e852016-12-08 12:55:45 -080044
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010045 if '@' in line:
46 line, addr = line.split('@')
47 else:
48 addr = None
Leandro Pereiraca42e852016-12-08 12:55:45 -080049
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010050 if ':' in line:
Erwan Gourioufa5d6ec2018-03-29 09:35:28 +020051 if len(line.split(':')) == 3:
52 alt_label, label, name = line.split(':')
53 else:
54 label, name = line.split(':')
55 alt_label = None
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010056 else:
57 name = line
58 label = None
Erwan Gourioufa5d6ec2018-03-29 09:35:28 +020059 alt_label = None
Leandro Pereiraca42e852016-12-08 12:55:45 -080060
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010061 if addr is None:
Erwan Gourioufa5d6ec2018-03-29 09:35:28 +020062 return label, name.strip(), None, None, None
Leandro Pereiraca42e852016-12-08 12:55:45 -080063
Erwan Gourioufa5d6ec2018-03-29 09:35:28 +020064 if alt_label is None:
65 return label, name.strip(), addr, int(addr, 16), None
66
67 return label, name.strip(), addr, int(addr, 16), alt_label
Leandro Pereiraca42e852016-12-08 12:55:45 -080068
69def parse_values_internal(value, start, end, separator):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010070 out = []
Leandro Pereiraca42e852016-12-08 12:55:45 -080071
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010072 inside = False
73 accum = []
74 for ch in value:
75 if not inside:
76 if ch == start:
77 inside = True
78 accum = []
79 else:
80 if ch == end:
81 inside = False
82 out.append(''.join(accum))
83 accum = []
84 else:
85 accum.append(ch)
Leandro Pereiraca42e852016-12-08 12:55:45 -080086
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010087 if separator == ' ':
88 out = [v.split() for v in out]
Leandro Pereiraca42e852016-12-08 12:55:45 -080089
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010090 if len(out) == 1:
91 return parse_value(out[0])
Leandro Pereiraca42e852016-12-08 12:55:45 -080092
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010093 return [parse_value(v) for v in out]
Leandro Pereiraca42e852016-12-08 12:55:45 -080094
95def parse_values(value, start, end, separator):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010096 out = parse_values_internal(value, start, end, separator)
97 if isinstance(out, list) and \
98 all(isinstance(v, str) and len(v) == 1 and not v.isalpha() for v in out):
99 return bytearray(out)
100
101 return out
Leandro Pereiraca42e852016-12-08 12:55:45 -0800102
103def parse_value(value):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100104 if value == '':
105 return value
106
107 if isinstance(value, list):
108 out = [parse_value(v) for v in value]
109 return out[0] if len(out) == 1 else out
110
111 if value[0] == '<':
112 return parse_values(value, '<', '>', ' ')
113 if value[0] == '"':
114 return parse_values(value, '"', '"', ',')
115 if value[0] == '[':
116 return parse_values(value, '[', ']', ' ')
117
118 if value[0] == '&':
119 return {'ref': value[1:]}
120
121 if value[0].isdigit():
122 if value.startswith("0x"):
123 return int(value, 16)
124 if value[0] == '0':
125 return int(value, 8)
126 return int(value, 10)
127
Leandro Pereiraca42e852016-12-08 12:55:45 -0800128 return value
129
Leandro Pereiraca42e852016-12-08 12:55:45 -0800130def parse_property(property, fd):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100131 if '=' in property:
132 key, value = property.split('=', 1)
133 value = ' '.join(read_until(value, fd, ';')).strip()
134 if not value.endswith(';'):
135 raise SyntaxError("parse_property: missing semicolon: %s" % value)
136 return key.strip(), parse_value(value[:-1])
Leandro Pereiraca42e852016-12-08 12:55:45 -0800137
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100138 property = property.strip()
139 if not property.endswith(';'):
140 raise SyntaxError("parse_property: missing semicolon: %s" % property)
Leandro Pereiraca42e852016-12-08 12:55:45 -0800141
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100142 return property[:-1].strip(), True
Leandro Pereiraca42e852016-12-08 12:55:45 -0800143
144def build_node_name(name, addr):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100145 if addr is None:
146 return name
147 elif isinstance(addr, int):
148 return '%s@%x' % (name, addr)
Kumar Gala46e47552017-04-12 17:16:41 -0500149
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100150 return '%s@%s' % (name, addr.strip())
Leandro Pereiraca42e852016-12-08 12:55:45 -0800151
152def parse_node(line, fd):
Erwan Gourioufa5d6ec2018-03-29 09:35:28 +0200153 label, name, addr, numeric_addr, alt_label = parse_node_name(line)
Leandro Pereiraca42e852016-12-08 12:55:45 -0800154
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100155 node = {
156 'label': label,
157 'type': type,
158 'addr': numeric_addr,
159 'children': {},
160 'props': {},
161 'name': build_node_name(name, addr)
162 }
Erwan Gourioufa5d6ec2018-03-29 09:35:28 +0200163 if alt_label:
164 node['alt_name'] = alt_label
165
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100166 while True:
167 line = fd.readline()
168 if not line:
169 raise SyntaxError("parse_node: Missing } while parsing node")
Leandro Pereiraca42e852016-12-08 12:55:45 -0800170
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100171 line = clean_line(line, fd)
172 if not line:
173 continue
Leandro Pereiraca42e852016-12-08 12:55:45 -0800174
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100175 if line == "};":
176 break
Leandro Pereiraca42e852016-12-08 12:55:45 -0800177
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100178 if line.endswith('{'):
179 new_node = parse_node(line, fd)
180 node['children'][new_node['name']] = new_node
181 else:
182 key, value = parse_property(line, fd)
183 node['props'][key] = value
Leandro Pereiraca42e852016-12-08 12:55:45 -0800184
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100185 return node
Leandro Pereiraca42e852016-12-08 12:55:45 -0800186
187def parse_file(fd, ignore_dts_version=False):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100188 nodes = {}
189 has_v1_tag = False
190 while True:
191 line = fd.readline()
192 if not line:
193 break
Leandro Pereiraca42e852016-12-08 12:55:45 -0800194
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100195 line = clean_line(line, fd)
196 if not line:
197 continue
Leandro Pereiraca42e852016-12-08 12:55:45 -0800198
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100199 if line.startswith('/include/ '):
200 tag, filename = line.split()
201 with open(filename.strip()[1:-1], "r") as new_fd:
202 nodes.update(parse_file(new_fd, True))
203 elif line == '/dts-v1/;':
204 has_v1_tag = True
205 elif line.startswith('/memreserve/ ') and line.endswith(';'):
206 tag, start, end = line.split()
207 start = int(start, 16)
208 end = int(end[:-1], 16)
209 label = "reserved_memory_0x%x_0x%x" % (start, end)
210 nodes[label] = {
211 'type': 'memory',
212 'reg': [start, end],
213 'label': label,
214 'addr': start,
215 'name': build_node_name(name, start)
216 }
217 elif line.endswith('{'):
218 if not has_v1_tag and not ignore_dts_version:
219 raise SyntaxError("parse_file: Missing /dts-v1/ tag")
Leandro Pereiraca42e852016-12-08 12:55:45 -0800220
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100221 new_node = parse_node(line, fd)
222 nodes[new_node['name']] = new_node
223 else:
224 raise SyntaxError("parse_file: Couldn't understand the line: %s" % line)
225 return nodes
Leandro Pereiraca42e852016-12-08 12:55:45 -0800226
227def dump_refs(name, value, indent=0):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100228 spaces = ' ' * indent
Leandro Pereiraca42e852016-12-08 12:55:45 -0800229
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100230 out = []
231 if isinstance(value, dict) and 'ref' in value:
232 out.append('%s\"%s\" -> \"%s\";' % (spaces, name, value['ref']))
233 elif isinstance(value, list):
234 for elem in value:
235 out.extend(dump_refs(name, elem, indent))
Leandro Pereiraca42e852016-12-08 12:55:45 -0800236
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100237 return out
Leandro Pereiraca42e852016-12-08 12:55:45 -0800238
239def dump_all_refs(name, props, indent=0):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100240 out = []
241 for key, value in props.items():
242 out.extend(dump_refs(name, value, indent))
243 return out
Leandro Pereiraca42e852016-12-08 12:55:45 -0800244
245def next_subgraph(count=[0]):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100246 count[0] += 1
247 return 'subgraph cluster_%d' % count[0]
Leandro Pereiraca42e852016-12-08 12:55:45 -0800248
249def get_dot_node_name(node):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100250 name = node['name']
251 return name[1:] if name[0] == '&' else name
Leandro Pereiraca42e852016-12-08 12:55:45 -0800252
253def dump_to_dot(nodes, indent=0, start_string='digraph devicetree', name=None):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100254 spaces = ' ' * indent
Leandro Pereiraca42e852016-12-08 12:55:45 -0800255
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100256 print("%s%s {" % (spaces, start_string))
Leandro Pereiraca42e852016-12-08 12:55:45 -0800257
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100258 if name is not None:
259 print("%slabel = \"%s\";" % (spaces, name))
260 print("%s\"%s\";" % (spaces, name))
Leandro Pereiraca42e852016-12-08 12:55:45 -0800261
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100262 ref_list = []
263 for key, value in nodes.items():
264 if value.get('children'):
265 refs = dump_to_dot(value['children'], indent + 1, next_subgraph(), get_dot_node_name(value))
266 ref_list.extend(refs)
267 else:
268 print("%s\"%s\";" % (spaces, get_dot_node_name(value)))
Leandro Pereiraca42e852016-12-08 12:55:45 -0800269
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100270 for key, value in nodes.items():
271 refs = dump_all_refs(get_dot_node_name(value), value.get('props', {}), indent)
272 ref_list.extend(refs)
Leandro Pereiraca42e852016-12-08 12:55:45 -0800273
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100274 if start_string.startswith("digraph"):
275 print("%s%s" % (spaces, '\n'.join(ref_list)))
Leandro Pereiraca42e852016-12-08 12:55:45 -0800276
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100277 print("%s}" % spaces)
Leandro Pereiraca42e852016-12-08 12:55:45 -0800278
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100279 return ref_list
Leandro Pereiraca42e852016-12-08 12:55:45 -0800280
281def main(args):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100282 if len(args) == 1:
283 print('Usage: %s filename.dts' % args[0])
284 return 1
Leandro Pereiraca42e852016-12-08 12:55:45 -0800285
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100286 if '--dot' in args:
287 formatter = dump_to_dot
288 args.remove('--dot')
289 else:
290 formatter = lambda nodes: pprint.pprint(nodes, indent=2)
Leandro Pereiraca42e852016-12-08 12:55:45 -0800291
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100292 with open(args[1], "r") as fd:
293 formatter(parse_file(fd))
Leandro Pereiraca42e852016-12-08 12:55:45 -0800294
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100295 return 0
Leandro Pereiraca42e852016-12-08 12:55:45 -0800296
297if __name__ == '__main__':
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100298 sys.exit(main(sys.argv))