blob: 2216d8ee59ee749c644dea38fa7542d06d930017 [file] [log] [blame]
Martí Bolívardc85edd2020-02-28 15:26:52 -08001#!/usr/bin/env python3
2
3# Copyright (c) 2019 - 2020 Nordic Semiconductor ASA
4# Copyright (c) 2019 Linaro Limited
5# SPDX-License-Identifier: BSD-3-Clause
6
7# This script uses edtlib to generate a header file from a devicetree
8# (.dts) file. Information from binding files in YAML format is used
9# as well.
10#
11# Bindings are files that describe devicetree nodes. Devicetree nodes are
12# usually mapped to bindings via their 'compatible = "..."' property.
13#
14# See Zephyr's Devicetree user guide for details.
15#
16# Note: Do not access private (_-prefixed) identifiers from edtlib here (and
17# also note that edtlib is not meant to expose the dtlib API directly).
18# Instead, think of what API you need, and add it as a public documented API in
19# edtlib. This will keep this script simple.
20
21import argparse
Martí Bolívara3fae2f2020-03-25 14:18:27 -070022from collections import defaultdict
Martí Bolívardc85edd2020-02-28 15:26:52 -080023import os
24import pathlib
25import re
26import sys
27
28import edtlib
29
30def main():
31 global header_file
32
33 args = parse_args()
34
35 try:
36 edt = edtlib.EDT(args.dts, args.bindings_dirs,
37 # Suppress this warning if it's suppressed in dtc
38 warn_reg_unit_address_mismatch=
Kumar Galabc48f1c2020-05-01 12:33:00 -050039 "-Wno-simple_bus_reg" not in args.dtc_flags,
Martí Bolívar7e0eed92020-05-06 11:23:07 -070040 default_prop_types=True)
Martí Bolívardc85edd2020-02-28 15:26:52 -080041 except edtlib.EDTError as e:
42 sys.exit(f"devicetree error: {e}")
43
44 # Save merged DTS source, as a debugging aid
45 with open(args.dts_out, "w", encoding="utf-8") as f:
46 print(edt.dts_source, file=f)
47
Martí Bolívare96ca542020-05-07 12:07:02 -070048 # The raw index into edt.compat2nodes[compat] is used for node
49 # instance numbering within a compatible.
50 #
51 # As a way to satisfy people's intuitions about instance numbers,
52 # though, we sort this list so enabled instances come first.
53 #
54 # This might look like a hack, but it keeps drivers and
55 # applications which don't use instance numbers carefully working
56 # as expected, since e.g. instance number 0 is always the
57 # singleton instance if there's just one enabled node of a
58 # particular compatible.
59 #
60 # This doesn't violate any devicetree.h API guarantees about
61 # instance ordering, since we make no promises that instance
62 # numbers are stable across builds.
63 for compat, nodes in edt.compat2nodes.items():
64 edt.compat2nodes[compat] = sorted(
65 nodes, key=lambda node: 0 if node.status == "okay" else 1)
66
Martí Bolívardc85edd2020-02-28 15:26:52 -080067 with open(args.header_out, "w", encoding="utf-8") as header_file:
68 write_top_comment(edt)
69
Dominik Ermelba8b74d2020-04-17 06:32:28 +000070 # populate all z_path_id first so any children references will
71 # work correctly.
Martí Bolívardc85edd2020-02-28 15:26:52 -080072 for node in sorted(edt.nodes, key=lambda node: node.dep_ordinal):
Martí Bolívar186bace2020-04-08 15:02:18 -070073 node.z_path_id = node_z_path_id(node)
Dominik Ermelba8b74d2020-04-17 06:32:28 +000074
75 for node in sorted(edt.nodes, key=lambda node: node.dep_ordinal):
Martí Bolívardc85edd2020-02-28 15:26:52 -080076 write_node_comment(node)
77
Martí Bolívar6e273432020-04-08 15:04:15 -070078 if node.parent is not None:
79 out_comment(f"Node parent ({node.parent.path}) identifier:")
80 out_dt_define(f"{node.z_path_id}_PARENT",
81 f"DT_{node.parent.z_path_id}")
82
Dominik Ermelba8b74d2020-04-17 06:32:28 +000083 write_child_functions(node)
Martí Bolívardc85edd2020-02-28 15:26:52 -080084 write_idents_and_existence(node)
85 write_bus(node)
86 write_special_props(node)
87 write_vanilla_props(node)
88
89 write_chosen(edt)
Martí Bolívara3fae2f2020-03-25 14:18:27 -070090 write_global_compat_info(edt)
Martí Bolívardc85edd2020-02-28 15:26:52 -080091
92
Martí Bolívar186bace2020-04-08 15:02:18 -070093def node_z_path_id(node):
94 # Return the node specific bit of the node's path identifier:
95 #
96 # - the root node's path "/" has path identifier "N"
97 # - "/foo" has "N_S_foo"
98 # - "/foo/bar" has "N_S_foo_S_bar"
99 # - "/foo/bar@123" has "N_S_foo_S_bar_123"
100 #
101 # This is used throughout this file to generate macros related to
102 # the node.
103
104 components = ["N"]
105 if node.parent is not None:
106 components.extend(f"S_{str2ident(component)}" for component in
107 node.path.split("/")[1:])
108
109 return "_".join(components)
110
Martí Bolívardc85edd2020-02-28 15:26:52 -0800111def parse_args():
112 # Returns parsed command-line arguments
113
114 parser = argparse.ArgumentParser()
115 parser.add_argument("--dts", required=True, help="DTS file")
116 parser.add_argument("--dtc-flags",
117 help="'dtc' devicetree compiler flags, some of which "
118 "might be respected here")
119 parser.add_argument("--bindings-dirs", nargs='+', required=True,
120 help="directory with bindings in YAML format, "
121 "we allow multiple")
122 parser.add_argument("--header-out", required=True,
123 help="path to write header to")
124 parser.add_argument("--dts-out", required=True,
125 help="path to write merged DTS source code to (e.g. "
126 "as a debugging aid)")
127
128 return parser.parse_args()
129
130
131def write_top_comment(edt):
132 # Writes an overview comment with misc. info at the top of the header and
133 # configuration file
134
135 s = f"""\
136Generated by gen_defines.py
137
138DTS input file:
139 {edt.dts_path}
140
141Directories with bindings:
142 {", ".join(map(relativize, edt.bindings_dirs))}
143
144Nodes in dependency order (ordinal and path):
145"""
146
147 for scc in edt.scc_order():
148 if len(scc) > 1:
149 err("cycle in devicetree involving "
150 + ", ".join(node.path for node in scc))
151 s += f" {scc[0].dep_ordinal:<3} {scc[0].path}\n"
152
153 s += """
154Definitions derived from these nodes in dependency order are next,
155followed by /chosen nodes.
156"""
157
158 out_comment(s, blank_before=False)
159
160
161def write_node_comment(node):
162 # Writes a comment describing 'node' to the header and configuration file
163
164 s = f"""\
Martí Bolívarb6e6ba02020-04-08 15:09:46 -0700165Devicetree node: {node.path}
166
167Node's generated path identifier: DT_{node.z_path_id}
Martí Bolívardc85edd2020-02-28 15:26:52 -0800168"""
169
170 if node.matching_compat:
171 s += f"""
172Binding (compatible = {node.matching_compat}):
173 {relativize(node.binding_path)}
174"""
Martí Bolívardc85edd2020-02-28 15:26:52 -0800175
176 s += f"\nDependency Ordinal: {node.dep_ordinal}\n"
177
178 if node.depends_on:
179 s += "\nRequires:\n"
180 for dep in node.depends_on:
181 s += f" {dep.dep_ordinal:<3} {dep.path}\n"
182
183 if node.required_by:
184 s += "\nSupports:\n"
185 for req in node.required_by:
186 s += f" {req.dep_ordinal:<3} {req.path}\n"
187
188 if node.description:
189 # Indent description by two spaces
190 s += "\nDescription:\n" + \
191 "\n".join(" " + line for line in
192 node.description.splitlines()) + \
193 "\n"
194
195 out_comment(s)
196
197
198def relativize(path):
199 # If 'path' is within $ZEPHYR_BASE, returns it relative to $ZEPHYR_BASE,
200 # with a "$ZEPHYR_BASE/..." hint at the start of the string. Otherwise,
201 # returns 'path' unchanged.
202
203 zbase = os.getenv("ZEPHYR_BASE")
204 if zbase is None:
205 return path
206
207 try:
208 return str("$ZEPHYR_BASE" / pathlib.Path(path).relative_to(zbase))
209 except ValueError:
210 # Not within ZEPHYR_BASE
211 return path
212
213
214def write_idents_and_existence(node):
215 # Writes macros related to the node's aliases, labels, etc.,
216 # as well as existence flags.
217
218 # Aliases
219 idents = [f"N_ALIAS_{str2ident(alias)}" for alias in node.aliases]
220 # Instances
221 for compat in node.compats:
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700222 instance_no = node.edt.compat2nodes[compat].index(node)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800223 idents.append(f"N_INST_{instance_no}_{str2ident(compat)}")
224 # Node labels
225 idents.extend(f"N_NODELABEL_{str2ident(label)}" for label in node.labels)
226
227 out_comment("Existence and alternate IDs:")
228 out_dt_define(node.z_path_id + "_EXISTS", 1)
229
230 # Only determine maxlen if we have any idents
231 if idents:
232 maxlen = max(len("DT_" + ident) for ident in idents)
233 for ident in idents:
234 out_dt_define(ident, "DT_" + node.z_path_id, width=maxlen)
235
236
237def write_bus(node):
238 # Macros about the node's bus controller, if there is one
239
240 bus = node.bus_node
241 if not bus:
242 return
243
244 if not bus.label:
245 err(f"missing 'label' property on bus node {bus!r}")
246
247 out_comment(f"Bus info (controller: '{bus.path}', type: '{node.on_bus}')")
248 out_dt_define(f"{node.z_path_id}_BUS_{str2ident(node.on_bus)}", 1)
249 out_dt_define(f"{node.z_path_id}_BUS", f"DT_{bus.z_path_id}")
250
251
252def write_special_props(node):
253 # Writes required macros for special case properties, when the
254 # data cannot otherwise be obtained from write_vanilla_props()
255 # results
256
257 out_comment("Special property macros:")
258
259 # Macros that are special to the devicetree specification
260 write_regs(node)
261 write_interrupts(node)
262 write_compatibles(node)
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700263 write_status(node)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800264
265
266def write_regs(node):
267 # reg property: edtlib knows the right #address-cells and
268 # #size-cells, and can therefore pack the register base addresses
269 # and sizes correctly
270
271 idx_vals = []
272 name_vals = []
273 path_id = node.z_path_id
274
275 if node.regs is not None:
276 idx_vals.append((f"{path_id}_REG_NUM", len(node.regs)))
277
278 for i, reg in enumerate(node.regs):
Kumar Gala4e2ad002020-04-14 14:27:20 -0500279 idx_vals.append((f"{path_id}_REG_IDX_{i}_EXISTS", 1))
Martí Bolívardc85edd2020-02-28 15:26:52 -0800280 if reg.addr is not None:
281 idx_macro = f"{path_id}_REG_IDX_{i}_VAL_ADDRESS"
282 idx_vals.append((idx_macro,
283 f"{reg.addr} /* {hex(reg.addr)} */"))
284 if reg.name:
285 name_macro = f"{path_id}_REG_NAME_{reg.name}_VAL_ADDRESS"
286 name_vals.append((name_macro, f"DT_{idx_macro}"))
287
288 if reg.size is not None:
289 idx_macro = f"{path_id}_REG_IDX_{i}_VAL_SIZE"
290 idx_vals.append((idx_macro,
291 f"{reg.size} /* {hex(reg.size)} */"))
292 if reg.name:
293 name_macro = f"{path_id}_REG_NAME_{reg.name}_VAL_SIZE"
294 name_vals.append((name_macro, f"DT_{idx_macro}"))
295
296 for macro, val in idx_vals:
297 out_dt_define(macro, val)
298 for macro, val in name_vals:
299 out_dt_define(macro, val)
300
301def write_interrupts(node):
302 # interrupts property: we have some hard-coded logic for interrupt
303 # mapping here.
304 #
305 # TODO: can we push map_arm_gic_irq_type() and
306 # encode_zephyr_multi_level_irq() out of Python and into C with
307 # macro magic in devicetree.h?
308
309 def map_arm_gic_irq_type(irq, irq_num):
310 # Maps ARM GIC IRQ (type)+(index) combo to linear IRQ number
311 if "type" not in irq.data:
312 err(f"Expected binding for {irq.controller!r} to have 'type' in "
313 "interrupt-cells")
314 irq_type = irq.data["type"]
315
316 if irq_type == 0: # GIC_SPI
317 return irq_num + 32
318 if irq_type == 1: # GIC_PPI
319 return irq_num + 16
320 err(f"Invalid interrupt type specified for {irq!r}")
321
322 def encode_zephyr_multi_level_irq(irq, irq_num):
323 # See doc/reference/kernel/other/interrupts.rst for details
324 # on how this encoding works
325
326 irq_ctrl = irq.controller
327 # Look for interrupt controller parent until we have none
328 while irq_ctrl.interrupts:
329 irq_num = (irq_num + 1) << 8
330 if "irq" not in irq_ctrl.interrupts[0].data:
331 err(f"Expected binding for {irq_ctrl!r} to have 'irq' in "
332 "interrupt-cells")
333 irq_num |= irq_ctrl.interrupts[0].data["irq"]
334 irq_ctrl = irq_ctrl.interrupts[0].controller
335 return irq_num
336
337 idx_vals = []
338 name_vals = []
339 path_id = node.z_path_id
340
341 if node.interrupts is not None:
342 idx_vals.append((f"{path_id}_IRQ_NUM", len(node.interrupts)))
343
344 for i, irq in enumerate(node.interrupts):
345 for cell_name, cell_value in irq.data.items():
346 name = str2ident(cell_name)
347
348 if cell_name == "irq":
349 if "arm,gic" in irq.controller.compats:
350 cell_value = map_arm_gic_irq_type(irq, cell_value)
351 cell_value = encode_zephyr_multi_level_irq(irq, cell_value)
352
Kumar Gala4e2ad002020-04-14 14:27:20 -0500353 idx_vals.append((f"{path_id}_IRQ_IDX_{i}_EXISTS", 1))
Martí Bolívardc85edd2020-02-28 15:26:52 -0800354 idx_macro = f"{path_id}_IRQ_IDX_{i}_VAL_{name}"
355 idx_vals.append((idx_macro, cell_value))
356 idx_vals.append((idx_macro + "_EXISTS", 1))
357 if irq.name:
358 name_macro = \
359 f"{path_id}_IRQ_NAME_{str2ident(irq.name)}_VAL_{name}"
360 name_vals.append((name_macro, f"DT_{idx_macro}"))
361 name_vals.append((name_macro + "_EXISTS", 1))
362
363 for macro, val in idx_vals:
364 out_dt_define(macro, val)
365 for macro, val in name_vals:
366 out_dt_define(macro, val)
367
368
369def write_compatibles(node):
370 # Writes a macro for each of the node's compatibles. We don't care
371 # about whether edtlib / Zephyr's binding language recognizes
372 # them. The compatibles the node provides are what is important.
373
374 for compat in node.compats:
375 out_dt_define(
376 f"{node.z_path_id}_COMPAT_MATCHES_{str2ident(compat)}", 1)
377
378
Dominik Ermelba8b74d2020-04-17 06:32:28 +0000379def write_child_functions(node):
380 # Writes macro that are helpers that will call a macro/function
381 # for each child node.
382
383 if node.children:
384 functions = ''
385 # For each subnode with non-empty list of labels add the
386 # first (non-alias) label to the string of ', ' separated
387 # list of labels.
388 for subnode in node.children.values():
389 functions = functions + f"fn(DT_{subnode.z_path_id}) "
390
391 if functions:
392 macro = f"{node.z_path_id}_FOREACH_CHILD(fn)"
393 out_dt_define(macro, functions)
394
395
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700396def write_status(node):
397 out_dt_define(f"{node.z_path_id}_STATUS_{str2ident(node.status)}", 1)
398
399
Martí Bolívardc85edd2020-02-28 15:26:52 -0800400def write_vanilla_props(node):
401 # Writes macros for any and all properties defined in the
402 # "properties" section of the binding for the node.
403 #
404 # This does generate macros for special properties as well, like
405 # regs, etc. Just let that be rather than bothering to add
406 # never-ending amounts of special case code here to skip special
407 # properties. This function's macros can't conflict with
408 # write_special_props() macros, because they're in different
409 # namespaces. Special cases aren't special enough to break the rules.
410
411 macro2val = {}
412 for prop_name, prop in node.props.items():
413 macro = f"{node.z_path_id}_P_{str2ident(prop_name)}"
414 val = prop2value(prop)
415 if val is not None:
416 # DT_N_<node-id>_P_<prop-id>
417 macro2val[macro] = val
418
419 if prop.enum_index is not None:
420 # DT_N_<node-id>_P_<prop-id>_ENUM_IDX
421 macro2val[macro + "_ENUM_IDX"] = prop.enum_index
422
423 if "phandle" in prop.type:
424 macro2val.update(phandle_macros(prop, macro))
425 elif "array" in prop.type:
426 # DT_N_<node-id>_P_<prop-id>_IDX_<i>
427 for i, subval in enumerate(prop.val):
428 if isinstance(subval, str):
429 macro2val[macro + f"_IDX_{i}"] = quote_str(subval)
430 else:
431 macro2val[macro + f"_IDX_{i}"] = subval
432
433 plen = prop_len(prop)
434 if plen is not None:
435 # DT_N_<node-id>_P_<prop-id>_LEN
436 macro2val[macro + "_LEN"] = plen
437
438 macro2val[f"{macro}_EXISTS"] = 1
439
440 if macro2val:
441 out_comment("Generic property macros:")
442 for macro, val in macro2val.items():
443 out_dt_define(macro, val)
444 else:
445 out_comment("(No generic property macros)")
446
447
448def prop2value(prop):
449 # Gets the macro value for property 'prop', if there is
450 # a single well-defined C rvalue that it can be represented as.
451 # Returns None if there isn't one.
452
453 if prop.type == "string":
454 return quote_str(prop.val)
455
456 if prop.type == "int":
457 return prop.val
458
459 if prop.type == "boolean":
460 return 1 if prop.val else 0
461
462 if prop.type in ["array", "uint8-array"]:
463 return list2init(f"{val} /* {hex(val)} */" for val in prop.val)
464
465 if prop.type == "string-array":
466 return list2init(quote_str(val) for val in prop.val)
467
468 # phandle, phandles, phandle-array, path, compound: nothing
469 return None
470
471
472def prop_len(prop):
473 # Returns the property's length if and only if we should generate
474 # a _LEN macro for the property. Otherwise, returns None.
475 #
476 # This deliberately excludes reg and interrupts.
477 # While they have array type, their lengths as arrays are
478 # basically nonsense semantically due to #address-cells and
479 # #size-cells for "reg" and #interrupt-cells for "interrupts".
480 #
481 # We have special purpose macros for the number of register blocks
482 # / interrupt specifiers. Excluding them from this list means
483 # DT_PROP_LEN(node_id, ...) fails fast at the devicetree.h layer
484 # with a build error. This forces users to switch to the right
485 # macros.
486
487 if prop.type == "phandle":
488 return 1
489
490 if (prop.type in ["array", "uint8-array", "string-array",
491 "phandles", "phandle-array"] and
492 prop.name not in ["reg", "interrupts"]):
493 return len(prop.val)
494
495 return None
496
497
498def phandle_macros(prop, macro):
499 # Returns a dict of macros for phandle or phandles property 'prop'.
500 #
501 # The 'macro' argument is the N_<node-id>_P_<prop-id> bit.
502 #
503 # These are currently special because we can't serialize their
504 # values without using label properties, which we're trying to get
505 # away from needing in Zephyr. (Label properties are great for
506 # humans, but have drawbacks for code size and boot time.)
507 #
508 # The names look a bit weird to make it easier for devicetree.h
509 # to use the same macros for phandle, phandles, and phandle-array.
510
511 ret = {}
512
513 if prop.type == "phandle":
514 # A phandle is treated as a phandles with fixed length 1.
515 ret[f"{macro}_IDX_0_PH"] = f"DT_{prop.val.z_path_id}"
516 elif prop.type == "phandles":
517 for i, node in enumerate(prop.val):
518 ret[f"{macro}_IDX_{i}_PH"] = f"DT_{node.z_path_id}"
519 elif prop.type == "phandle-array":
520 for i, entry in enumerate(prop.val):
521 ret.update(controller_and_data_macros(entry, i, macro))
522
523 return ret
524
525
526def controller_and_data_macros(entry, i, macro):
527 # Helper procedure used by phandle_macros().
528 #
529 # Its purpose is to write the "controller" (i.e. label property of
530 # the phandle's node) and associated data macros for a
531 # ControllerAndData.
532
533 ret = {}
534 data = entry.data
535
536 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_PH
537 ret[f"{macro}_IDX_{i}_PH"] = f"DT_{entry.controller.z_path_id}"
538 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_VAL_<VAL>
539 for cell, val in data.items():
540 ret[f"{macro}_IDX_{i}_VAL_{str2ident(cell)}"] = val
541 ret[f"{macro}_IDX_{i}_VAL_{str2ident(cell)}_EXISTS"] = 1
542
543 if not entry.name:
544 return ret
545
546 name = str2ident(entry.name)
Erwan Gouriou6c8617a2020-04-06 14:56:11 +0200547 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS
548 ret[f"{macro}_IDX_{i}_EXISTS"] = 1
Martí Bolívardc85edd2020-02-28 15:26:52 -0800549 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_NAME
550 ret[f"{macro}_IDX_{i}_NAME"] = quote_str(entry.name)
551 # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_PH
552 ret[f"{macro}_NAME_{name}_PH"] = f"DT_{entry.controller.z_path_id}"
Erwan Gouriou6c8617a2020-04-06 14:56:11 +0200553 # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_EXISTS
554 ret[f"{macro}_NAME_{name}_EXISTS"] = 1
Martí Bolívardc85edd2020-02-28 15:26:52 -0800555 # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_VAL_<VAL>
556 for cell, val in data.items():
557 cell_ident = str2ident(cell)
558 ret[f"{macro}_NAME_{name}_VAL_{cell_ident}"] = \
559 f"DT_{macro}_IDX_{i}_VAL_{cell_ident}"
560 ret[f"{macro}_NAME_{name}_VAL_{cell_ident}_EXISTS"] = 1
561
562 return ret
563
564
565def write_chosen(edt):
566 # Tree-wide information such as chosen nodes is printed here.
567
568 out_comment("Chosen nodes\n")
569 chosen = {}
570 for name, node in edt.chosen_nodes.items():
571 chosen[f"DT_CHOSEN_{str2ident(name)}"] = f"DT_{node.z_path_id}"
572 chosen[f"DT_CHOSEN_{str2ident(name)}_EXISTS"] = 1
Kumar Gala299bfd02020-03-25 15:32:58 -0500573 max_len = max(map(len, chosen), default=0)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800574 for macro, value in chosen.items():
575 out_define(macro, value, width=max_len)
576
577
Martí Bolívara3fae2f2020-03-25 14:18:27 -0700578def write_global_compat_info(edt):
579 # Tree-wide information related to each compatible, such as number
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700580 # of instances with status "okay", is printed here.
Martí Bolívardc85edd2020-02-28 15:26:52 -0800581
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700582 n_okay_macros = {}
583 for_each_macros = {}
584 compat2buses = defaultdict(list) # just for "okay" nodes
585 for compat, okay_nodes in edt.compat2okay.items():
586 for node in okay_nodes:
Martí Bolívara3fae2f2020-03-25 14:18:27 -0700587 bus = node.on_bus
588 if bus is not None and bus not in compat2buses[compat]:
589 compat2buses[compat].append(bus)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800590
Martí Bolívar63d55292020-04-06 15:13:53 -0700591 ident = str2ident(compat)
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700592 n_okay_macros[f"DT_N_INST_{ident}_NUM_OKAY"] = len(okay_nodes)
593 for_each_macros[f"DT_FOREACH_OKAY_INST_{ident}(fn)"] = \
594 " ".join(f"fn({edt.compat2nodes[compat].index(node)})"
595 for node in okay_nodes)
596
597 out_comment('Macros for compatibles with status "okay" nodes\n')
598 for compat, okay_nodes in edt.compat2okay.items():
599 if okay_nodes:
600 out_define(f"DT_COMPAT_HAS_OKAY_{str2ident(compat)}", 1)
601
602 out_comment('Macros for status "okay" instances of each compatible\n')
603 for macro, value in n_okay_macros.items():
Martí Bolívar63d55292020-04-06 15:13:53 -0700604 out_define(macro, value)
605 for macro, value in for_each_macros.items():
606 out_define(macro, value)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800607
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700608 out_comment('Bus information for status "okay" nodes of each compatible\n')
Martí Bolívara3fae2f2020-03-25 14:18:27 -0700609 for compat, buses in compat2buses.items():
610 for bus in buses:
611 out_define(
612 f"DT_COMPAT_{str2ident(compat)}_BUS_{str2ident(bus)}", 1)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800613
614def str2ident(s):
615 # Converts 's' to a form suitable for (part of) an identifier
616
617 return re.sub('[-,.@/+]', '_', s.lower())
618
619
620def list2init(l):
621 # Converts 'l', a Python list (or iterable), to a C array initializer
622
623 return "{" + ", ".join(l) + "}"
624
625
626def out_dt_define(macro, val, width=None, deprecation_msg=None):
627 # Writes "#define DT_<macro> <val>" to the header file
628 #
629 # The macro will be left-justified to 'width' characters if that
630 # is specified, and the value will follow immediately after in
631 # that case. Otherwise, this function decides how to add
632 # whitespace between 'macro' and 'val'.
633 #
634 # If a 'deprecation_msg' string is passed, the generated identifiers will
635 # generate a warning if used, via __WARN(<deprecation_msg>)).
636 #
637 # Returns the full generated macro for 'macro', with leading "DT_".
638 ret = "DT_" + macro
639 out_define(ret, val, width=width, deprecation_msg=deprecation_msg)
640 return ret
641
642
643def out_define(macro, val, width=None, deprecation_msg=None):
644 # Helper for out_dt_define(). Outputs "#define <macro> <val>",
645 # adds a deprecation message if given, and allocates whitespace
646 # unless told not to.
647
648 warn = fr' __WARN("{deprecation_msg}")' if deprecation_msg else ""
649
650 if width:
651 s = f"#define {macro.ljust(width)}{warn} {val}"
652 else:
653 s = f"#define {macro}{warn} {val}"
654
655 print(s, file=header_file)
656
657
658def out_comment(s, blank_before=True):
659 # Writes 's' as a comment to the header and configuration file. 's' is
660 # allowed to have multiple lines. blank_before=True adds a blank line
661 # before the comment.
662
663 if blank_before:
664 print(file=header_file)
665
666 if "\n" in s:
667 # Format multi-line comments like
668 #
669 # /*
670 # * first line
671 # * second line
672 # *
673 # * empty line before this line
674 # */
675 res = ["/*"]
676 for line in s.splitlines():
677 # Avoid an extra space after '*' for empty lines. They turn red in
678 # Vim if space error checking is on, which is annoying.
679 res.append(" *" if not line.strip() else " * " + line)
680 res.append(" */")
681 print("\n".join(res), file=header_file)
682 else:
683 # Format single-line comments like
684 #
685 # /* foo bar */
686 print("/* " + s + " */", file=header_file)
687
688
689def escape(s):
690 # Backslash-escapes any double quotes and backslashes in 's'
691
692 # \ must be escaped before " to avoid double escaping
693 return s.replace("\\", "\\\\").replace('"', '\\"')
694
695
696def quote_str(s):
697 # Puts quotes around 's' and escapes any double quotes and
698 # backslashes within it
699
700 return f'"{escape(s)}"'
701
702
703def err(s):
704 raise Exception(s)
705
706
707if __name__ == "__main__":
708 main()