blob: eddbfd18f41d55604a69e3d1928580c584e9afd7 [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
Manivannan Sadhasivam7ac767b2019-04-22 19:44:25 +053012import re
Leandro Pereiraca42e852016-12-08 12:55:45 -080013
14def read_until(line, fd, end):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010015 out = [line]
16 while True:
17 idx = line.find(end)
18 if idx < 0:
19 line = clean_line(fd.readline(), fd)
20 out.append(line)
21 else:
22 out.append(line[idx + len(end):])
23 return out
Leandro Pereiraca42e852016-12-08 12:55:45 -080024
25def remove_comment(line, fd):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010026 out = []
27 while True:
28 idx = line.find('/*')
29 if idx < 0:
30 idx = line.find('//')
31 if idx < 0:
32 out.append(line)
33 else:
34 out.append(line[:idx])
35 return ' '.join(out)
Leandro Pereiraca42e852016-12-08 12:55:45 -080036
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010037 out.append(line[:idx])
38 line = read_until(line[idx:], fd, '*/')[-1]
Leandro Pereiraca42e852016-12-08 12:55:45 -080039
40def clean_line(line, fd):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010041 return remove_comment(line, fd).strip()
Leandro Pereiraca42e852016-12-08 12:55:45 -080042
43def parse_node_name(line):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010044 line = line[:-1]
Leandro Pereiraca42e852016-12-08 12:55:45 -080045
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010046 if '@' in line:
47 line, addr = line.split('@')
48 else:
49 addr = None
Leandro Pereiraca42e852016-12-08 12:55:45 -080050
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010051 if ':' in line:
Erwan Gourioufa5d6ec2018-03-29 09:35:28 +020052 if len(line.split(':')) == 3:
53 alt_label, label, name = line.split(':')
54 else:
55 label, name = line.split(':')
56 alt_label = None
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010057 else:
58 name = line
59 label = None
Erwan Gourioufa5d6ec2018-03-29 09:35:28 +020060 alt_label = None
Leandro Pereiraca42e852016-12-08 12:55:45 -080061
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010062 if addr is None:
Erwan Gourioufa5d6ec2018-03-29 09:35:28 +020063 return label, name.strip(), None, None, None
Leandro Pereiraca42e852016-12-08 12:55:45 -080064
Erwan Gourioufa5d6ec2018-03-29 09:35:28 +020065 if alt_label is None:
66 return label, name.strip(), addr, int(addr, 16), None
67
68 return label, name.strip(), addr, int(addr, 16), alt_label
Leandro Pereiraca42e852016-12-08 12:55:45 -080069
70def parse_values_internal(value, start, end, separator):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010071 out = []
Leandro Pereiraca42e852016-12-08 12:55:45 -080072
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010073 inside = False
74 accum = []
75 for ch in value:
76 if not inside:
77 if ch == start:
78 inside = True
79 accum = []
80 else:
81 if ch == end:
82 inside = False
83 out.append(''.join(accum))
84 accum = []
85 else:
86 accum.append(ch)
Leandro Pereiraca42e852016-12-08 12:55:45 -080087
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010088 if separator == ' ':
89 out = [v.split() for v in out]
Leandro Pereiraca42e852016-12-08 12:55:45 -080090
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010091 if len(out) == 1:
92 return parse_value(out[0])
Leandro Pereiraca42e852016-12-08 12:55:45 -080093
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010094 return [parse_value(v) for v in out]
Leandro Pereiraca42e852016-12-08 12:55:45 -080095
96def parse_values(value, start, end, separator):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +010097 out = parse_values_internal(value, start, end, separator)
98 if isinstance(out, list) and \
99 all(isinstance(v, str) and len(v) == 1 and not v.isalpha() for v in out):
100 return bytearray(out)
101
102 return out
Leandro Pereiraca42e852016-12-08 12:55:45 -0800103
104def parse_value(value):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100105 if value == '':
106 return value
107
108 if isinstance(value, list):
109 out = [parse_value(v) for v in value]
110 return out[0] if len(out) == 1 else out
111
112 if value[0] == '<':
113 return parse_values(value, '<', '>', ' ')
114 if value[0] == '"':
115 return parse_values(value, '"', '"', ',')
116 if value[0] == '[':
Peter A. Bigot79765b82019-07-08 06:16:33 -0500117 return list(bytes.fromhex(value[1:value.find(']')]))
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100118
119 if value[0] == '&':
120 return {'ref': value[1:]}
121
122 if value[0].isdigit():
123 if value.startswith("0x"):
124 return int(value, 16)
125 if value[0] == '0':
126 return int(value, 8)
Manivannan Sadhasivam7ac767b2019-04-22 19:44:25 +0530127 # Match alpha numeric values
128 if re.match("\w", value):
129 return value
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100130 return int(value, 10)
131
Leandro Pereiraca42e852016-12-08 12:55:45 -0800132 return value
133
Leandro Pereiraca42e852016-12-08 12:55:45 -0800134def parse_property(property, fd):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100135 if '=' in property:
136 key, value = property.split('=', 1)
137 value = ' '.join(read_until(value, fd, ';')).strip()
138 if not value.endswith(';'):
139 raise SyntaxError("parse_property: missing semicolon: %s" % value)
140 return key.strip(), parse_value(value[:-1])
Leandro Pereiraca42e852016-12-08 12:55:45 -0800141
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100142 property = property.strip()
143 if not property.endswith(';'):
144 raise SyntaxError("parse_property: missing semicolon: %s" % property)
Leandro Pereiraca42e852016-12-08 12:55:45 -0800145
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100146 return property[:-1].strip(), True
Leandro Pereiraca42e852016-12-08 12:55:45 -0800147
148def build_node_name(name, addr):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100149 if addr is None:
150 return name
151 elif isinstance(addr, int):
152 return '%s@%x' % (name, addr)
Kumar Gala46e47552017-04-12 17:16:41 -0500153
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100154 return '%s@%s' % (name, addr.strip())
Leandro Pereiraca42e852016-12-08 12:55:45 -0800155
156def parse_node(line, fd):
Erwan Gourioufa5d6ec2018-03-29 09:35:28 +0200157 label, name, addr, numeric_addr, alt_label = parse_node_name(line)
Leandro Pereiraca42e852016-12-08 12:55:45 -0800158
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100159 node = {
160 'label': label,
161 'type': type,
162 'addr': numeric_addr,
163 'children': {},
164 'props': {},
165 'name': build_node_name(name, addr)
166 }
Erwan Gourioufa5d6ec2018-03-29 09:35:28 +0200167 if alt_label:
168 node['alt_name'] = alt_label
169
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100170 while True:
171 line = fd.readline()
172 if not line:
173 raise SyntaxError("parse_node: Missing } while parsing node")
Leandro Pereiraca42e852016-12-08 12:55:45 -0800174
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100175 line = clean_line(line, fd)
176 if not line:
177 continue
Leandro Pereiraca42e852016-12-08 12:55:45 -0800178
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100179 if line == "};":
180 break
Leandro Pereiraca42e852016-12-08 12:55:45 -0800181
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100182 if line.endswith('{'):
183 new_node = parse_node(line, fd)
184 node['children'][new_node['name']] = new_node
185 else:
186 key, value = parse_property(line, fd)
187 node['props'][key] = value
Leandro Pereiraca42e852016-12-08 12:55:45 -0800188
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100189 return node
Leandro Pereiraca42e852016-12-08 12:55:45 -0800190
191def parse_file(fd, ignore_dts_version=False):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100192 nodes = {}
193 has_v1_tag = False
194 while True:
195 line = fd.readline()
196 if not line:
197 break
Leandro Pereiraca42e852016-12-08 12:55:45 -0800198
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100199 line = clean_line(line, fd)
200 if not line:
201 continue
Leandro Pereiraca42e852016-12-08 12:55:45 -0800202
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100203 if line.startswith('/include/ '):
Ulf Magnusson399c04c2019-03-19 18:47:39 +0100204 _, filename = line.split()
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100205 with open(filename.strip()[1:-1], "r") as new_fd:
206 nodes.update(parse_file(new_fd, True))
207 elif line == '/dts-v1/;':
208 has_v1_tag = True
209 elif line.startswith('/memreserve/ ') and line.endswith(';'):
Ulf Magnusson399c04c2019-03-19 18:47:39 +0100210 _, start, end = line.split()
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100211 start = int(start, 16)
212 end = int(end[:-1], 16)
213 label = "reserved_memory_0x%x_0x%x" % (start, end)
214 nodes[label] = {
215 'type': 'memory',
216 'reg': [start, end],
217 'label': label,
218 'addr': start,
Ulf Magnusson0e8e92c2019-03-20 21:10:44 +0100219 'name': '<memreserve>'
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100220 }
221 elif line.endswith('{'):
222 if not has_v1_tag and not ignore_dts_version:
223 raise SyntaxError("parse_file: Missing /dts-v1/ tag")
Leandro Pereiraca42e852016-12-08 12:55:45 -0800224
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100225 new_node = parse_node(line, fd)
226 nodes[new_node['name']] = new_node
227 else:
228 raise SyntaxError("parse_file: Couldn't understand the line: %s" % line)
229 return nodes
Leandro Pereiraca42e852016-12-08 12:55:45 -0800230
231def dump_refs(name, value, indent=0):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100232 spaces = ' ' * indent
Leandro Pereiraca42e852016-12-08 12:55:45 -0800233
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100234 out = []
235 if isinstance(value, dict) and 'ref' in value:
236 out.append('%s\"%s\" -> \"%s\";' % (spaces, name, value['ref']))
237 elif isinstance(value, list):
238 for elem in value:
239 out.extend(dump_refs(name, elem, indent))
Leandro Pereiraca42e852016-12-08 12:55:45 -0800240
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100241 return out
Leandro Pereiraca42e852016-12-08 12:55:45 -0800242
243def dump_all_refs(name, props, indent=0):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100244 out = []
Ulf Magnusson399c04c2019-03-19 18:47:39 +0100245 for value in props.values():
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100246 out.extend(dump_refs(name, value, indent))
247 return out
Leandro Pereiraca42e852016-12-08 12:55:45 -0800248
249def next_subgraph(count=[0]):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100250 count[0] += 1
251 return 'subgraph cluster_%d' % count[0]
Leandro Pereiraca42e852016-12-08 12:55:45 -0800252
253def get_dot_node_name(node):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100254 name = node['name']
255 return name[1:] if name[0] == '&' else name
Leandro Pereiraca42e852016-12-08 12:55:45 -0800256
257def dump_to_dot(nodes, indent=0, start_string='digraph devicetree', name=None):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100258 spaces = ' ' * indent
Leandro Pereiraca42e852016-12-08 12:55:45 -0800259
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100260 print("%s%s {" % (spaces, start_string))
Leandro Pereiraca42e852016-12-08 12:55:45 -0800261
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100262 if name is not None:
263 print("%slabel = \"%s\";" % (spaces, name))
264 print("%s\"%s\";" % (spaces, name))
Leandro Pereiraca42e852016-12-08 12:55:45 -0800265
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100266 ref_list = []
Ulf Magnusson399c04c2019-03-19 18:47:39 +0100267 for node in nodes.values():
268 if node['children']:
269 refs = dump_to_dot(node['children'], indent + 1, next_subgraph(),
270 get_dot_node_name(node))
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100271 ref_list.extend(refs)
272 else:
Ulf Magnusson399c04c2019-03-19 18:47:39 +0100273 print("%s\"%s\";" % (spaces, get_dot_node_name(node)))
Leandro Pereiraca42e852016-12-08 12:55:45 -0800274
Ulf Magnusson399c04c2019-03-19 18:47:39 +0100275 for node in nodes.values():
276 refs = dump_all_refs(get_dot_node_name(node), node['props'], indent)
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100277 ref_list.extend(refs)
Leandro Pereiraca42e852016-12-08 12:55:45 -0800278
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100279 if start_string.startswith("digraph"):
280 print("%s%s" % (spaces, '\n'.join(ref_list)))
Leandro Pereiraca42e852016-12-08 12:55:45 -0800281
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100282 print("%s}" % spaces)
Leandro Pereiraca42e852016-12-08 12:55:45 -0800283
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100284 return ref_list
Leandro Pereiraca42e852016-12-08 12:55:45 -0800285
286def main(args):
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100287 if len(args) == 1:
288 print('Usage: %s filename.dts' % args[0])
289 return 1
Leandro Pereiraca42e852016-12-08 12:55:45 -0800290
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100291 if '--dot' in args:
292 formatter = dump_to_dot
293 args.remove('--dot')
294 else:
295 formatter = lambda nodes: pprint.pprint(nodes, indent=2)
Leandro Pereiraca42e852016-12-08 12:55:45 -0800296
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100297 with open(args[1], "r") as fd:
298 formatter(parse_file(fd))
Leandro Pereiraca42e852016-12-08 12:55:45 -0800299
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100300 return 0
Leandro Pereiraca42e852016-12-08 12:55:45 -0800301
302if __name__ == '__main__':
Tomasz Bursztykab34d1562018-03-07 15:45:46 +0100303 sys.exit(main(sys.argv))