1#!/usr/bin/env python3 2 3# Copyright (c) 2019 - 2020 Nordic Semiconductor ASA 4# Copyright (c) 2019 Linaro Limited 5# Copyright (c) 2024 SILA Embedded Solutions GmbH 6# SPDX-License-Identifier: BSD-3-Clause 7 8# This script uses edtlib to generate a header file from a pickled 9# edt file. 10# 11# Note: Do not access private (_-prefixed) identifiers from edtlib here (and 12# also note that edtlib is not meant to expose the dtlib API directly). 13# Instead, think of what API you need, and add it as a public documented API in 14# edtlib. This will keep this script simple. 15 16import argparse 17from collections import defaultdict 18import os 19import pathlib 20import pickle 21import re 22import sys 23from typing import Iterable, NoReturn, Optional 24 25sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'python-devicetree', 26 'src')) 27 28import edtlib_logger 29from devicetree import edtlib 30 31 32def main(): 33 global header_file 34 global flash_area_num 35 36 args = parse_args() 37 38 edtlib_logger.setup_edtlib_logging() 39 40 with open(args.edt_pickle, 'rb') as f: 41 edt = pickle.load(f) 42 43 flash_area_num = 0 44 45 # Create the generated header. 46 with open(args.header_out, "w", encoding="utf-8") as header_file: 47 write_top_comment(edt) 48 49 write_utils() 50 51 sorted_nodes = sorted(edt.nodes, key=lambda node: node.dep_ordinal) 52 53 # populate all z_path_id first so any children references will 54 # work correctly. 55 for node in sorted_nodes: 56 node.z_path_id = node_z_path_id(node) 57 58 # Check to see if we have duplicate "zephyr,memory-region" property values. 59 regions = dict() 60 for node in sorted_nodes: 61 if 'zephyr,memory-region' in node.props: 62 region = node.props['zephyr,memory-region'].val 63 if region in regions: 64 sys.exit(f"ERROR: Duplicate 'zephyr,memory-region' ({region}) properties " 65 f"between {regions[region].path} and {node.path}") 66 regions[region] = node 67 68 for node in sorted_nodes: 69 write_node_comment(node) 70 71 out_comment("Node's full path:") 72 out_dt_define(f"{node.z_path_id}_PATH", f'"{escape(node.path)}"') 73 74 out_comment("Node's name with unit-address:") 75 out_dt_define(f"{node.z_path_id}_FULL_NAME", 76 f'"{escape(node.name)}"') 77 out_dt_define(f"{node.z_path_id}_FULL_NAME_UNQUOTED", 78 f'{escape(node.name)}') 79 out_dt_define(f"{node.z_path_id}_FULL_NAME_TOKEN", 80 f'{edtlib.str_as_token(escape(node.name))}') 81 out_dt_define(f"{node.z_path_id}_FULL_NAME_UPPER_TOKEN", 82 f'{edtlib.str_as_token(escape(node.name)).upper()}') 83 84 if node.parent is not None: 85 out_comment(f"Node parent ({node.parent.path}) identifier:") 86 out_dt_define(f"{node.z_path_id}_PARENT", 87 f"DT_{node.parent.z_path_id}") 88 89 out_comment(f"Node's index in its parent's list of children:") 90 out_dt_define(f"{node.z_path_id}_CHILD_IDX", 91 node.parent.child_index(node)) 92 93 out_comment("Helpers for dealing with node labels:") 94 out_dt_define(f"{node.z_path_id}_NODELABEL_NUM", len(node.labels)) 95 out_dt_define(f"{node.z_path_id}_FOREACH_NODELABEL(fn)", 96 " ".join(f"fn({nodelabel})" for nodelabel in node.labels)) 97 out_dt_define(f"{node.z_path_id}_FOREACH_NODELABEL_VARGS(fn, ...)", 98 " ".join(f"fn({nodelabel}, __VA_ARGS__)" for nodelabel in node.labels)) 99 100 write_parent(node) 101 write_children(node) 102 write_dep_info(node) 103 write_idents_and_existence(node) 104 write_bus(node) 105 write_special_props(node) 106 write_vanilla_props(node) 107 108 write_chosen(edt) 109 write_global_macros(edt) 110 111 112def node_z_path_id(node: edtlib.Node) -> str: 113 # Return the node specific bit of the node's path identifier: 114 # 115 # - the root node's path "/" has path identifier "N" 116 # - "/foo" has "N_S_foo" 117 # - "/foo/bar" has "N_S_foo_S_bar" 118 # - "/foo/bar@123" has "N_S_foo_S_bar_123" 119 # 120 # This is used throughout this file to generate macros related to 121 # the node. 122 123 components = ["N"] 124 if node.parent is not None: 125 components.extend(f"S_{str2ident(component)}" for component in 126 node.path.split("/")[1:]) 127 128 return "_".join(components) 129 130 131def parse_args() -> argparse.Namespace: 132 # Returns parsed command-line arguments 133 134 parser = argparse.ArgumentParser(allow_abbrev=False) 135 parser.add_argument("--header-out", required=True, 136 help="path to write header to") 137 parser.add_argument("--edt-pickle", 138 help="path to read pickled edtlib.EDT object from") 139 140 return parser.parse_args() 141 142 143def write_top_comment(edt: edtlib.EDT) -> None: 144 # Writes an overview comment with misc. info at the top of the header and 145 # configuration file 146 147 s = f"""\ 148Generated by gen_defines.py 149 150DTS input file: 151 {edt.dts_path} 152 153Directories with bindings: 154 {", ".join(map(relativize, edt.bindings_dirs))} 155 156Node dependency ordering (ordinal and path): 157""" 158 159 for scc in edt.scc_order: 160 if len(scc) > 1: 161 err("cycle in devicetree involving " 162 + ", ".join(node.path for node in scc)) 163 s += f" {scc[0].dep_ordinal:<3} {scc[0].path}\n" 164 165 s += """ 166Definitions derived from these nodes in dependency order are next, 167followed by /chosen nodes. 168""" 169 170 out_comment(s, blank_before=False) 171 172 173def write_utils() -> None: 174 # Writes utility macros 175 176 out_comment("Used to remove brackets from around a single argument") 177 out_define("DT_DEBRACKET_INTERNAL(...)", "__VA_ARGS__") 178 179 180def write_node_comment(node: edtlib.Node) -> None: 181 # Writes a comment describing 'node' to the header and configuration file 182 183 s = f"""\ 184Devicetree node: {node.path} 185 186Node identifier: DT_{node.z_path_id} 187""" 188 189 if node.matching_compat: 190 if node.binding_path: 191 s += f""" 192Binding (compatible = {node.matching_compat}): 193 {relativize(node.binding_path)} 194""" 195 else: 196 s += f""" 197Binding (compatible = {node.matching_compat}): 198 No yaml (bindings inferred from properties) 199""" 200 201 if node.description: 202 # We used to put descriptions in the generated file, but 203 # devicetree bindings now have pages in the HTML 204 # documentation. Let users who are accustomed to digging 205 # around in the generated file where to find the descriptions 206 # now. 207 # 208 # Keeping them here would mean that the descriptions 209 # themselves couldn't contain C multi-line comments, which is 210 # inconvenient when we want to do things like quote snippets 211 # of .dtsi files within the descriptions, or otherwise 212 # include the string "*/". 213 s += ("\n(Descriptions have moved to the Devicetree Bindings Index\n" 214 "in the documentation.)\n") 215 216 out_comment(s) 217 218 219def relativize(path) -> Optional[str]: 220 # If 'path' is within $ZEPHYR_BASE, returns it relative to $ZEPHYR_BASE, 221 # with a "$ZEPHYR_BASE/..." hint at the start of the string. Otherwise, 222 # returns 'path' unchanged. 223 224 zbase = os.getenv("ZEPHYR_BASE") 225 if zbase is None: 226 return path 227 228 try: 229 return str("$ZEPHYR_BASE" / pathlib.Path(path).relative_to(zbase)) 230 except ValueError: 231 # Not within ZEPHYR_BASE 232 return path 233 234 235def write_idents_and_existence(node: edtlib.Node) -> None: 236 # Writes macros related to the node's aliases, labels, etc., 237 # as well as existence flags. 238 239 # Aliases 240 idents = [f"N_ALIAS_{str2ident(alias)}" for alias in node.aliases] 241 # Instances 242 for compat in node.compats: 243 instance_no = node.edt.compat2nodes[compat].index(node) 244 idents.append(f"N_INST_{instance_no}_{str2ident(compat)}") 245 # Node labels 246 idents.extend(f"N_NODELABEL_{str2ident(label)}" for label in node.labels) 247 248 out_comment("Existence and alternate IDs:") 249 out_dt_define(f"{node.z_path_id}_EXISTS", 1) 250 251 # Only determine maxlen if we have any idents 252 if idents: 253 maxlen = max(len(f"DT_{ident}") for ident in idents) 254 for ident in idents: 255 out_dt_define(ident, f"DT_{node.z_path_id}", width=maxlen) 256 257 258def write_bus(node: edtlib.Node) -> None: 259 # Macros about the node's bus controller, if there is one 260 261 bus = node.bus_node 262 if not bus: 263 return 264 265 out_comment(f"Bus info (controller: '{bus.path}', type: '{node.on_buses}')") 266 267 for one_bus in node.on_buses: 268 out_dt_define(f"{node.z_path_id}_BUS_{str2ident(one_bus)}", 1) 269 270 out_dt_define(f"{node.z_path_id}_BUS", f"DT_{bus.z_path_id}") 271 272 273def write_special_props(node: edtlib.Node) -> None: 274 # Writes required macros for special case properties, when the 275 # data cannot otherwise be obtained from write_vanilla_props() 276 # results 277 278 # Macros that are special to the devicetree specification 279 out_comment("Macros for properties that are special in the specification:") 280 write_regs(node) 281 write_ranges(node) 282 write_interrupts(node) 283 write_compatibles(node) 284 write_status(node) 285 286 # Macros that are special to bindings inherited from Linux, which 287 # we can't capture with the current bindings language. 288 write_pinctrls(node) 289 write_fixed_partitions(node) 290 write_gpio_hogs(node) 291 292 293def write_ranges(node: edtlib.Node) -> None: 294 # ranges property: edtlib knows the right #address-cells and 295 # #size-cells of parent and child, and can therefore pack the 296 # child & parent addresses and sizes correctly 297 298 idx_vals = [] 299 path_id = node.z_path_id 300 301 if node.ranges is not None: 302 idx_vals.append((f"{path_id}_RANGES_NUM", len(node.ranges))) 303 304 for i,range in enumerate(node.ranges): 305 idx_vals.append((f"{path_id}_RANGES_IDX_{i}_EXISTS", 1)) 306 307 if "pcie" in node.buses: 308 idx_vals.append((f"{path_id}_RANGES_IDX_{i}_VAL_CHILD_BUS_FLAGS_EXISTS", 1)) 309 idx_macro = f"{path_id}_RANGES_IDX_{i}_VAL_CHILD_BUS_FLAGS" 310 idx_value = range.child_bus_addr >> ((range.child_bus_cells - 1) * 32) 311 idx_vals.append((idx_macro, 312 f"{idx_value} /* {hex(idx_value)} */")) 313 if range.child_bus_addr is not None: 314 idx_macro = f"{path_id}_RANGES_IDX_{i}_VAL_CHILD_BUS_ADDRESS" 315 if "pcie" in node.buses: 316 idx_value = range.child_bus_addr & ((1 << (range.child_bus_cells - 1) * 32) - 1) 317 else: 318 idx_value = range.child_bus_addr 319 idx_vals.append((idx_macro, 320 f"{idx_value} /* {hex(idx_value)} */")) 321 if range.parent_bus_addr is not None: 322 idx_macro = f"{path_id}_RANGES_IDX_{i}_VAL_PARENT_BUS_ADDRESS" 323 idx_vals.append((idx_macro, 324 f"{range.parent_bus_addr} /* {hex(range.parent_bus_addr)} */")) 325 if range.length is not None: 326 idx_macro = f"{path_id}_RANGES_IDX_{i}_VAL_LENGTH" 327 idx_vals.append((idx_macro, 328 f"{range.length} /* {hex(range.length)} */")) 329 330 for macro, val in idx_vals: 331 out_dt_define(macro, val) 332 333 out_dt_define(f"{path_id}_FOREACH_RANGE(fn)", 334 " ".join(f"fn(DT_{path_id}, {i})" for i,range in enumerate(node.ranges))) 335 336 337def write_regs(node: edtlib.Node) -> None: 338 # reg property: edtlib knows the right #address-cells and 339 # #size-cells, and can therefore pack the register base addresses 340 # and sizes correctly 341 342 idx_vals = [] 343 name_vals = [] 344 path_id = node.z_path_id 345 346 if node.regs is not None: 347 idx_vals.append((f"{path_id}_REG_NUM", len(node.regs))) 348 349 for i, reg in enumerate(node.regs): 350 idx_vals.append((f"{path_id}_REG_IDX_{i}_EXISTS", 1)) 351 if reg.addr is not None: 352 idx_macro = f"{path_id}_REG_IDX_{i}_VAL_ADDRESS" 353 idx_vals.append((idx_macro, 354 f"{reg.addr} /* {hex(reg.addr)} */")) 355 if reg.name: 356 name_vals.append((f"{path_id}_REG_NAME_{reg.name}_EXISTS", 1)) 357 name_macro = f"{path_id}_REG_NAME_{reg.name}_VAL_ADDRESS" 358 name_vals.append((name_macro, f"DT_{idx_macro}")) 359 360 if reg.size is not None: 361 idx_macro = f"{path_id}_REG_IDX_{i}_VAL_SIZE" 362 idx_vals.append((idx_macro, 363 f"{reg.size} /* {hex(reg.size)} */")) 364 if reg.name: 365 name_macro = f"{path_id}_REG_NAME_{reg.name}_VAL_SIZE" 366 name_vals.append((name_macro, f"DT_{idx_macro}")) 367 368 for macro, val in idx_vals: 369 out_dt_define(macro, val) 370 for macro, val in name_vals: 371 out_dt_define(macro, val) 372 373 374def write_interrupts(node: edtlib.Node) -> None: 375 # interrupts property: we have some hard-coded logic for interrupt 376 # mapping here. 377 # 378 # TODO: can we push map_arm_gic_irq_type() out of Python and into C with 379 # macro magic in devicetree.h? 380 381 def map_arm_gic_irq_type(irq, irq_num): 382 # Maps ARM GIC IRQ (type)+(index) combo to linear IRQ number 383 if "type" not in irq.data: 384 err(f"Expected binding for {irq.controller!r} to have 'type' in " 385 "interrupt-cells") 386 irq_type = irq.data["type"] 387 388 if irq_type == 0: # GIC_SPI 389 return irq_num + 32 390 if irq_type == 1: # GIC_PPI 391 return irq_num + 16 392 err(f"Invalid interrupt type specified for {irq!r}") 393 394 idx_vals = [] 395 name_vals = [] 396 path_id = node.z_path_id 397 398 if node.interrupts is not None: 399 idx_vals.append((f"{path_id}_IRQ_NUM", len(node.interrupts))) 400 401 for i, irq in enumerate(node.interrupts): 402 for cell_name, cell_value in irq.data.items(): 403 name = str2ident(cell_name) 404 405 if cell_name == "irq": 406 if "arm,gic" in irq.controller.compats: 407 cell_value = map_arm_gic_irq_type(irq, cell_value) 408 409 idx_vals.append((f"{path_id}_IRQ_IDX_{i}_EXISTS", 1)) 410 idx_macro = f"{path_id}_IRQ_IDX_{i}_VAL_{name}" 411 idx_vals.append((idx_macro, cell_value)) 412 idx_vals.append((idx_macro + "_EXISTS", 1)) 413 if irq.name: 414 name_macro = ( 415 f"{path_id}_IRQ_NAME_{str2ident(irq.name)}_VAL_{name}") 416 name_vals.append((name_macro, f"DT_{idx_macro}")) 417 name_vals.append((name_macro + "_EXISTS", 1)) 418 419 idx_controller_macro = f"{path_id}_IRQ_IDX_{i}_CONTROLLER" 420 idx_controller_path = f"DT_{irq.controller.z_path_id}" 421 idx_vals.append((idx_controller_macro, idx_controller_path)) 422 if irq.name: 423 name_controller_macro = f"{path_id}_IRQ_NAME_{str2ident(irq.name)}_CONTROLLER" 424 name_vals.append((name_controller_macro, f"DT_{idx_controller_macro}")) 425 426 # Interrupt controller info 427 irqs = [] 428 while node.interrupts is not None and len(node.interrupts) > 0: 429 irq = node.interrupts[0] 430 irqs.append(irq) 431 if node == irq.controller: 432 break 433 node = irq.controller 434 idx_vals.append((f"{path_id}_IRQ_LEVEL", len(irqs))) 435 436 for macro, val in idx_vals: 437 out_dt_define(macro, val) 438 for macro, val in name_vals: 439 out_dt_define(macro, val) 440 441 442def write_compatibles(node: edtlib.Node) -> None: 443 # Writes a macro for each of the node's compatibles. We don't care 444 # about whether edtlib / Zephyr's binding language recognizes 445 # them. The compatibles the node provides are what is important. 446 447 for i, compat in enumerate(node.compats): 448 out_dt_define( 449 f"{node.z_path_id}_COMPAT_MATCHES_{str2ident(compat)}", 1) 450 451 if node.edt.compat2vendor[compat]: 452 out_dt_define(f"{node.z_path_id}_COMPAT_VENDOR_IDX_{i}_EXISTS", 1) 453 out_dt_define(f"{node.z_path_id}_COMPAT_VENDOR_IDX_{i}", 454 quote_str(node.edt.compat2vendor[compat])) 455 456 if node.edt.compat2model[compat]: 457 out_dt_define(f"{node.z_path_id}_COMPAT_MODEL_IDX_{i}_EXISTS", 1) 458 out_dt_define(f"{node.z_path_id}_COMPAT_MODEL_IDX_{i}", 459 quote_str(node.edt.compat2model[compat])) 460 461def write_parent(node: edtlib.Node) -> None: 462 # Visit all parent nodes. 463 def _visit_parent_node(node: edtlib.Node): 464 while node is not None: 465 yield node.parent 466 node = node.parent 467 468 # Writes helper macros for dealing with node's parent. 469 out_dt_define(f"{node.z_path_id}_FOREACH_ANCESTOR(fn)", 470 " ".join(f"fn(DT_{parent.z_path_id})" for parent in 471 _visit_parent_node(node) if parent is not None)) 472 473def write_children(node: edtlib.Node) -> None: 474 # Writes helper macros for dealing with node's children. 475 476 out_comment("Helper macros for child nodes of this node.") 477 478 out_dt_define(f"{node.z_path_id}_CHILD_NUM", len(node.children)) 479 480 ok_nodes_num = 0 481 for child in node.children.values(): 482 if child.status == "okay": 483 ok_nodes_num = ok_nodes_num + 1 484 485 out_dt_define(f"{node.z_path_id}_CHILD_NUM_STATUS_OKAY", ok_nodes_num) 486 487 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD(fn)", 488 " ".join(f"fn(DT_{child.z_path_id})" for child in 489 node.children.values())) 490 491 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_SEP(fn, sep)", 492 " DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id})" 493 for child in node.children.values())) 494 495 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_VARGS(fn, ...)", 496 " ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)" 497 for child in node.children.values())) 498 499 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_SEP_VARGS(fn, sep, ...)", 500 " DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)" 501 for child in node.children.values())) 502 503 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY(fn)", 504 " ".join(f"fn(DT_{child.z_path_id})" 505 for child in node.children.values() if child.status == "okay")) 506 507 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_SEP(fn, sep)", 508 " DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id})" 509 for child in node.children.values() if child.status == "okay")) 510 511 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_VARGS(fn, ...)", 512 " ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)" 513 for child in node.children.values() if child.status == "okay")) 514 515 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_SEP_VARGS(fn, sep, ...)", 516 " DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)" 517 for child in node.children.values() if child.status == "okay")) 518 519 520def write_status(node: edtlib.Node) -> None: 521 out_dt_define(f"{node.z_path_id}_STATUS_{str2ident(node.status)}", 1) 522 523 524def write_pinctrls(node: edtlib.Node) -> None: 525 # Write special macros for pinctrl-<index> and pinctrl-names properties. 526 527 out_comment("Pin control (pinctrl-<i>, pinctrl-names) properties:") 528 529 out_dt_define(f"{node.z_path_id}_PINCTRL_NUM", len(node.pinctrls)) 530 531 if not node.pinctrls: 532 return 533 534 for pc_idx, pinctrl in enumerate(node.pinctrls): 535 out_dt_define(f"{node.z_path_id}_PINCTRL_IDX_{pc_idx}_EXISTS", 1) 536 537 if not pinctrl.name: 538 continue 539 540 name = pinctrl.name_as_token 541 542 # Below we rely on the fact that edtlib ensures the 543 # pinctrl-<pc_idx> properties are contiguous, start from 0, 544 # and contain only phandles. 545 out_dt_define(f"{node.z_path_id}_PINCTRL_IDX_{pc_idx}_TOKEN", name) 546 out_dt_define(f"{node.z_path_id}_PINCTRL_IDX_{pc_idx}_UPPER_TOKEN", name.upper()) 547 out_dt_define(f"{node.z_path_id}_PINCTRL_NAME_{name}_EXISTS", 1) 548 out_dt_define(f"{node.z_path_id}_PINCTRL_NAME_{name}_IDX", pc_idx) 549 for idx, ph in enumerate(pinctrl.conf_nodes): 550 out_dt_define(f"{node.z_path_id}_PINCTRL_NAME_{name}_IDX_{idx}_PH", 551 f"DT_{ph.z_path_id}") 552 553 554def write_fixed_partitions(node: edtlib.Node) -> None: 555 # Macros for child nodes of each fixed-partitions node. 556 557 if not (node.parent and "fixed-partitions" in node.parent.compats): 558 return 559 560 global flash_area_num 561 out_comment("fixed-partitions identifier:") 562 out_dt_define(f"{node.z_path_id}_PARTITION_ID", flash_area_num) 563 flash_area_num += 1 564 565 566def write_gpio_hogs(node: edtlib.Node) -> None: 567 # Write special macros for gpio-hog node properties. 568 569 macro = f"{node.z_path_id}_GPIO_HOGS" 570 macro2val = {} 571 for i, entry in enumerate(node.gpio_hogs): 572 macro2val.update(controller_and_data_macros(entry, i, macro)) 573 574 if macro2val: 575 out_comment("GPIO hog properties:") 576 out_dt_define(f"{macro}_EXISTS", 1) 577 out_dt_define(f"{macro}_NUM", len(node.gpio_hogs)) 578 for macro, val in macro2val.items(): 579 out_dt_define(macro, val) 580 581 582def write_vanilla_props(node: edtlib.Node) -> None: 583 # Writes macros for any and all properties defined in the 584 # "properties" section of the binding for the node. 585 # 586 # This does generate macros for special properties as well, like 587 # regs, etc. Just let that be rather than bothering to add 588 # never-ending amounts of special case code here to skip special 589 # properties. This function's macros can't conflict with 590 # write_special_props() macros, because they're in different 591 # namespaces. Special cases aren't special enough to break the rules. 592 593 macro2val = {} 594 for prop_name, prop in node.props.items(): 595 prop_id = str2ident(prop_name) 596 macro = f"{node.z_path_id}_P_{prop_id}" 597 val = prop2value(prop) 598 if val is not None: 599 # DT_N_<node-id>_P_<prop-id> 600 macro2val[macro] = val 601 602 if prop.spec.type == 'string': 603 macro2val.update(string_macros(macro, prop.val)) 604 # DT_N_<node-id>_P_<prop-id>_IDX_0: 605 # DT_N_<node-id>_P_<prop-id>_IDX_0_EXISTS: 606 # Allows treating the string like a degenerate case of a 607 # string-array of length 1. 608 macro2val[f"{macro}_IDX_0"] = quote_str(prop.val) 609 macro2val[f"{macro}_IDX_0_EXISTS"] = 1 610 611 if prop.enum_indices is not None: 612 macro2val.update(enum_macros(prop, macro)) 613 614 if "phandle" in prop.type: 615 macro2val.update(phandle_macros(prop, macro)) 616 elif "array" in prop.type: 617 macro2val.update(array_macros(prop, macro)) 618 619 plen = prop_len(prop) 620 if plen is not None: 621 # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM 622 macro2val[f"{macro}_FOREACH_PROP_ELEM(fn)"] = ( 623 ' \\\n\t'.join(f'fn(DT_{node.z_path_id}, {prop_id}, {i})' 624 for i in range(plen))) 625 626 # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_SEP 627 macro2val[f"{macro}_FOREACH_PROP_ELEM_SEP(fn, sep)"] = ( 628 ' DT_DEBRACKET_INTERNAL sep \\\n\t'.join( 629 f'fn(DT_{node.z_path_id}, {prop_id}, {i})' 630 for i in range(plen))) 631 632 # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_VARGS 633 macro2val[f"{macro}_FOREACH_PROP_ELEM_VARGS(fn, ...)"] = ( 634 ' \\\n\t'.join( 635 f'fn(DT_{node.z_path_id}, {prop_id}, {i}, __VA_ARGS__)' 636 for i in range(plen))) 637 638 # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_SEP_VARGS 639 macro2val[f"{macro}_FOREACH_PROP_ELEM_SEP_VARGS(fn, sep, ...)"] = ( 640 ' DT_DEBRACKET_INTERNAL sep \\\n\t'.join( 641 f'fn(DT_{node.z_path_id}, {prop_id}, {i}, __VA_ARGS__)' 642 for i in range(plen))) 643 644 # DT_N_<node-id>_P_<prop-id>_LEN 645 macro2val[f"{macro}_LEN"] = plen 646 647 # DT_N_<node-id>_P_<prop-id>_EXISTS 648 macro2val[f"{macro}_EXISTS"] = 1 649 650 if macro2val: 651 out_comment("Generic property macros:") 652 for macro, val in macro2val.items(): 653 out_dt_define(macro, val) 654 else: 655 out_comment("(No generic property macros)") 656 657 658def string_macros(macro: str, val: str): 659 # Returns a dict of macros for a string 'val'. 660 # The 'macro' argument is the N_<node-id>_P_<prop-id>... part. 661 662 as_token = edtlib.str_as_token(val) 663 return { 664 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_STRING_UNQUOTED 665 f"{macro}_STRING_UNQUOTED": escape_unquoted(val), 666 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_STRING_TOKEN 667 f"{macro}_STRING_TOKEN": as_token, 668 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_STRING_UPPER_TOKEN 669 f"{macro}_STRING_UPPER_TOKEN": as_token.upper()} 670 671 672def enum_macros(prop: edtlib.Property, macro: str): 673 # Returns a dict of macros for property 'prop' with a defined enum in their dt-binding. 674 # The 'macro' argument is the N_<node-id>_P_<prop-id> part. 675 676 spec = prop.spec 677 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_ENUM_IDX 678 ret = {f"{macro}_IDX_{i}_ENUM_IDX": index for i, index in enumerate(prop.enum_indices)} 679 val = prop.val_as_tokens if spec.enum_tokenizable else (prop.val if isinstance(prop.val, list) else [prop.val]) 680 681 for i, subval in enumerate(val): 682 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS 683 ret[f"{macro}_IDX_{i}_EXISTS"] = 1 684 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_ENUM_VAL_<val>_EXISTS 1 685 ret[f"{macro}_IDX_{i}_ENUM_VAL_{subval}_EXISTS"] = 1 686 687 return ret 688 689 690def array_macros(prop: edtlib.Property, macro: str): 691 # Returns a dict of macros for array property 'prop'. 692 # The 'macro' argument is the N_<node-id>_P_<prop-id> part. 693 694 ret = {} 695 for i, subval in enumerate(prop.val): 696 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS 697 ret[f"{macro}_IDX_{i}_EXISTS"] = 1 698 699 # DT_N_<node-id>_P_<prop-id>_IDX_<i> 700 if isinstance(subval, str): 701 ret[f"{macro}_IDX_{i}"] = quote_str(subval) 702 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_STRING_... 703 ret.update(string_macros(f"{macro}_IDX_{i}", subval)) 704 else: 705 ret[f"{macro}_IDX_{i}"] = subval 706 707 return ret 708 709 710def write_dep_info(node: edtlib.Node) -> None: 711 # Write dependency-related information about the node. 712 713 def fmt_dep_list(dep_list): 714 if dep_list: 715 # Sort the list by dependency ordinal for predictability. 716 sorted_list = sorted(dep_list, key=lambda node: node.dep_ordinal) 717 return ("\\\n\t" + " \\\n\t" 718 .join(f"{n.dep_ordinal}, /* {n.path} */" 719 for n in sorted_list)) 720 else: 721 return "/* nothing */" 722 723 out_comment("Node's dependency ordinal:") 724 out_dt_define(f"{node.z_path_id}_ORD", node.dep_ordinal) 725 out_dt_define(f"{node.z_path_id}_ORD_STR_SORTABLE", f"{node.dep_ordinal:0>5}") 726 727 out_comment("Ordinals for what this node depends on directly:") 728 out_dt_define(f"{node.z_path_id}_REQUIRES_ORDS", 729 fmt_dep_list(node.depends_on)) 730 731 out_comment("Ordinals for what depends directly on this node:") 732 out_dt_define(f"{node.z_path_id}_SUPPORTS_ORDS", 733 fmt_dep_list(node.required_by)) 734 735 736def prop2value(prop: edtlib.Property) -> edtlib.PropertyValType: 737 # Gets the macro value for property 'prop', if there is 738 # a single well-defined C rvalue that it can be represented as. 739 # Returns None if there isn't one. 740 741 if prop.type == "string": 742 return quote_str(prop.val) 743 744 if prop.type == "int": 745 return prop.val 746 747 if prop.type == "boolean": 748 return 1 if prop.val else 0 749 750 if prop.type in ["array", "uint8-array"]: 751 return list2init(f"{val} /* {hex(val)} */" for val in prop.val) 752 753 if prop.type == "string-array": 754 return list2init(quote_str(val) for val in prop.val) 755 756 # phandle, phandles, phandle-array, path, compound: nothing 757 return None 758 759 760def prop_len(prop: edtlib.Property) -> Optional[int]: 761 # Returns the property's length if and only if we should generate 762 # a _LEN macro for the property. Otherwise, returns None. 763 # 764 # The set of types handled here coincides with the allowable types 765 # that can be used with DT_PROP_LEN(). If you change this set, 766 # make sure to update the doxygen string for that macro, and make 767 # sure that DT_FOREACH_PROP_ELEM() works for the new types too. 768 # 769 # This deliberately excludes ranges, dma-ranges, reg and interrupts. 770 # While they have array type, their lengths as arrays are 771 # basically nonsense semantically due to #address-cells and 772 # #size-cells for "reg", #interrupt-cells for "interrupts" 773 # and #address-cells, #size-cells and the #address-cells from the 774 # parent node for "ranges" and "dma-ranges". 775 # 776 # We have special purpose macros for the number of register blocks 777 # / interrupt specifiers. Excluding them from this list means 778 # DT_PROP_LEN(node_id, ...) fails fast at the devicetree.h layer 779 # with a build error. This forces users to switch to the right 780 # macros. 781 782 if prop.type in ["phandle", "string"]: 783 # phandle is treated as a phandles of length 1. 784 # string is treated as a string-array of length 1. 785 return 1 786 787 if (prop.type in ["array", "uint8-array", "string-array", 788 "phandles", "phandle-array"] and 789 prop.name not in ["ranges", "dma-ranges", "reg", "interrupts"]): 790 return len(prop.val) 791 792 return None 793 794 795def phandle_macros(prop: edtlib.Property, macro: str) -> dict: 796 # Returns a dict of macros for phandle or phandles property 'prop'. 797 # 798 # The 'macro' argument is the N_<node-id>_P_<prop-id> bit. 799 # 800 # These are currently special because we can't serialize their 801 # values without using label properties, which we're trying to get 802 # away from needing in Zephyr. (Label properties are great for 803 # humans, but have drawbacks for code size and boot time.) 804 # 805 # The names look a bit weird to make it easier for devicetree.h 806 # to use the same macros for phandle, phandles, and phandle-array. 807 808 ret = {} 809 810 if prop.type == "phandle": 811 # A phandle is treated as a phandles with fixed length 1. 812 ret[f"{macro}"] = f"DT_{prop.val.z_path_id}" 813 ret[f"{macro}_IDX_0"] = f"DT_{prop.val.z_path_id}" 814 ret[f"{macro}_IDX_0_PH"] = f"DT_{prop.val.z_path_id}" 815 ret[f"{macro}_IDX_0_EXISTS"] = 1 816 elif prop.type == "phandles": 817 for i, node in enumerate(prop.val): 818 ret[f"{macro}_IDX_{i}"] = f"DT_{node.z_path_id}" 819 ret[f"{macro}_IDX_{i}_PH"] = f"DT_{node.z_path_id}" 820 ret[f"{macro}_IDX_{i}_EXISTS"] = 1 821 elif prop.type == "phandle-array": 822 for i, entry in enumerate(prop.val): 823 if entry is None: 824 # Unspecified element. The phandle-array at this index 825 # does not point at a ControllerAndData value, but 826 # subsequent indices in the array may. 827 ret[f"{macro}_IDX_{i}_EXISTS"] = 0 828 continue 829 830 ret.update(controller_and_data_macros(entry, i, macro)) 831 832 return ret 833 834 835def controller_and_data_macros(entry: edtlib.ControllerAndData, i: int, macro: str): 836 # Helper procedure used by phandle_macros(). 837 # 838 # Its purpose is to write the "controller" (i.e. label property of 839 # the phandle's node) and associated data macros for a 840 # ControllerAndData. 841 842 ret = {} 843 data = entry.data 844 845 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS 846 ret[f"{macro}_IDX_{i}_EXISTS"] = 1 847 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_PH 848 ret[f"{macro}_IDX_{i}_PH"] = f"DT_{entry.controller.z_path_id}" 849 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_VAL_<VAL> 850 for cell, val in data.items(): 851 ret[f"{macro}_IDX_{i}_VAL_{str2ident(cell)}"] = val 852 ret[f"{macro}_IDX_{i}_VAL_{str2ident(cell)}_EXISTS"] = 1 853 854 if not entry.name: 855 return ret 856 857 name = str2ident(entry.name) 858 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS 859 ret[f"{macro}_IDX_{i}_EXISTS"] = 1 860 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_NAME 861 ret[f"{macro}_IDX_{i}_NAME"] = quote_str(entry.name) 862 # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_PH 863 ret[f"{macro}_NAME_{name}_PH"] = f"DT_{entry.controller.z_path_id}" 864 # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_EXISTS 865 ret[f"{macro}_NAME_{name}_EXISTS"] = 1 866 # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_VAL_<VAL> 867 for cell, val in data.items(): 868 cell_ident = str2ident(cell) 869 ret[f"{macro}_NAME_{name}_VAL_{cell_ident}"] = ( 870 f"DT_{macro}_IDX_{i}_VAL_{cell_ident}") 871 ret[f"{macro}_NAME_{name}_VAL_{cell_ident}_EXISTS"] = 1 872 873 return ret 874 875 876def write_chosen(edt: edtlib.EDT): 877 # Tree-wide information such as chosen nodes is printed here. 878 879 out_comment("Chosen nodes\n") 880 chosen = {} 881 for name, node in edt.chosen_nodes.items(): 882 chosen[f"DT_CHOSEN_{str2ident(name)}"] = f"DT_{node.z_path_id}" 883 chosen[f"DT_CHOSEN_{str2ident(name)}_EXISTS"] = 1 884 max_len = max(map(len, chosen), default=0) 885 for macro, value in chosen.items(): 886 out_define(macro, value, width=max_len) 887 888 889def write_global_macros(edt: edtlib.EDT): 890 # Global or tree-wide information, such as number of instances 891 # with status "okay" for each compatible, is printed here. 892 893 894 out_comment("Macros for iterating over all nodes and enabled nodes") 895 out_dt_define("FOREACH_HELPER(fn)", 896 " ".join(f"fn(DT_{node.z_path_id})" for node in edt.nodes)) 897 out_dt_define("FOREACH_OKAY_HELPER(fn)", 898 " ".join(f"fn(DT_{node.z_path_id})" for node in edt.nodes 899 if node.status == "okay")) 900 out_dt_define("FOREACH_VARGS_HELPER(fn, ...)", 901 " ".join(f"fn(DT_{node.z_path_id}, __VA_ARGS__)" for node in edt.nodes)) 902 out_dt_define("FOREACH_OKAY_VARGS_HELPER(fn, ...)", 903 " ".join(f"fn(DT_{node.z_path_id}, __VA_ARGS__)" for node in edt.nodes 904 if node.status == "okay")) 905 906 n_okay_macros = {} 907 for_each_macros = {} 908 compat2buses = defaultdict(list) # just for "okay" nodes 909 for compat, okay_nodes in edt.compat2okay.items(): 910 for node in okay_nodes: 911 buses = node.on_buses 912 for bus in buses: 913 if bus is not None and bus not in compat2buses[compat]: 914 compat2buses[compat].append(bus) 915 916 ident = str2ident(compat) 917 n_okay_macros[f"DT_N_INST_{ident}_NUM_OKAY"] = len(okay_nodes) 918 919 # Helpers for non-INST for-each macros that take node 920 # identifiers as arguments. 921 for_each_macros[f"DT_FOREACH_OKAY_{ident}(fn)"] = ( 922 " ".join(f"fn(DT_{node.z_path_id})" 923 for node in okay_nodes)) 924 for_each_macros[f"DT_FOREACH_OKAY_VARGS_{ident}(fn, ...)"] = ( 925 " ".join(f"fn(DT_{node.z_path_id}, __VA_ARGS__)" 926 for node in okay_nodes)) 927 928 # Helpers for INST versions of for-each macros, which take 929 # instance numbers. We emit separate helpers for these because 930 # avoiding an intermediate node_id --> instance number 931 # conversion in the preprocessor helps to keep the macro 932 # expansions simpler. That hopefully eases debugging. 933 for_each_macros[f"DT_FOREACH_OKAY_INST_{ident}(fn)"] = ( 934 " ".join(f"fn({edt.compat2nodes[compat].index(node)})" 935 for node in okay_nodes)) 936 for_each_macros[f"DT_FOREACH_OKAY_INST_VARGS_{ident}(fn, ...)"] = ( 937 " ".join(f"fn({edt.compat2nodes[compat].index(node)}, __VA_ARGS__)" 938 for node in okay_nodes)) 939 940 for compat, nodes in edt.compat2nodes.items(): 941 for node in nodes: 942 if compat == "fixed-partitions": 943 for child in node.children.values(): 944 if "label" in child.props: 945 label = child.props["label"].val 946 macro = f"COMPAT_{str2ident(compat)}_LABEL_{str2ident(label)}" 947 val = f"DT_{child.z_path_id}" 948 949 out_dt_define(macro, val) 950 out_dt_define(macro + "_EXISTS", 1) 951 952 out_comment('Macros for compatibles with status "okay" nodes\n') 953 for compat, okay_nodes in edt.compat2okay.items(): 954 if okay_nodes: 955 out_define(f"DT_COMPAT_HAS_OKAY_{str2ident(compat)}", 1) 956 957 out_comment('Macros for status "okay" instances of each compatible\n') 958 for macro, value in n_okay_macros.items(): 959 out_define(macro, value) 960 for macro, value in for_each_macros.items(): 961 out_define(macro, value) 962 963 out_comment('Bus information for status "okay" nodes of each compatible\n') 964 for compat, buses in compat2buses.items(): 965 for bus in buses: 966 out_define( 967 f"DT_COMPAT_{str2ident(compat)}_BUS_{str2ident(bus)}", 1) 968 969 970def str2ident(s: str) -> str: 971 # Converts 's' to a form suitable for (part of) an identifier 972 973 return re.sub('[-,.@/+]', '_', s.lower()) 974 975 976def list2init(l: Iterable[str]) -> str: 977 # Converts 'l', a Python list (or iterable), to a C array initializer 978 979 return "{" + ", ".join(l) + "}" 980 981 982def out_dt_define( 983 macro: str, 984 val: str, 985 width: Optional[int] = None, 986 deprecation_msg: Optional[str] = None, 987) -> str: 988 # Writes "#define DT_<macro> <val>" to the header file 989 # 990 # The macro will be left-justified to 'width' characters if that 991 # is specified, and the value will follow immediately after in 992 # that case. Otherwise, this function decides how to add 993 # whitespace between 'macro' and 'val'. 994 # 995 # If a 'deprecation_msg' string is passed, the generated identifiers will 996 # generate a warning if used, via __WARN(<deprecation_msg>)). 997 # 998 # Returns the full generated macro for 'macro', with leading "DT_". 999 ret = f"DT_{macro}" 1000 out_define(ret, val, width=width, deprecation_msg=deprecation_msg) 1001 return ret 1002 1003 1004def out_define( 1005 macro: str, 1006 val: str, 1007 width: Optional[int] = None, 1008 deprecation_msg: Optional[str] = None, 1009) -> None: 1010 # Helper for out_dt_define(). Outputs "#define <macro> <val>", 1011 # adds a deprecation message if given, and allocates whitespace 1012 # unless told not to. 1013 1014 warn = fr' __WARN("{deprecation_msg}")' if deprecation_msg else "" 1015 1016 if width: 1017 s = f"#define {macro.ljust(width)}{warn} {val}" 1018 else: 1019 s = f"#define {macro}{warn} {val}" 1020 1021 print(s, file=header_file) 1022 1023 1024def out_comment(s: str, blank_before=True) -> None: 1025 # Writes 's' as a comment to the header and configuration file. 's' is 1026 # allowed to have multiple lines. blank_before=True adds a blank line 1027 # before the comment. 1028 1029 if blank_before: 1030 print(file=header_file) 1031 1032 if "\n" in s: 1033 # Format multi-line comments like 1034 # 1035 # /* 1036 # * first line 1037 # * second line 1038 # * 1039 # * empty line before this line 1040 # */ 1041 res = ["/*"] 1042 for line in s.splitlines(): 1043 # Avoid an extra space after '*' for empty lines. They turn red in 1044 # Vim if space error checking is on, which is annoying. 1045 res.append(f" * {line}".rstrip()) 1046 res.append(" */") 1047 print("\n".join(res), file=header_file) 1048 else: 1049 # Format single-line comments like 1050 # 1051 # /* foo bar */ 1052 print(f"/* {s} */", file=header_file) 1053 1054ESCAPE_TABLE = str.maketrans( 1055 { 1056 "\n": "\\n", 1057 "\r": "\\r", 1058 "\\": "\\\\", 1059 '"': '\\"', 1060 } 1061) 1062 1063 1064def escape(s: str) -> str: 1065 # Backslash-escapes any double quotes, backslashes, and new lines in 's' 1066 1067 return s.translate(ESCAPE_TABLE) 1068 1069 1070def quote_str(s: str) -> str: 1071 # Puts quotes around 's' and escapes any double quotes and 1072 # backslashes within it 1073 1074 return f'"{escape(s)}"' 1075 1076 1077def escape_unquoted(s: str) -> str: 1078 # C macros cannot contain line breaks, so replace them with spaces. 1079 # Whitespace is used to separate preprocessor tokens, but it does not matter 1080 # which whitespace characters are used, so a line break and a space are 1081 # equivalent with regards to unquoted strings being used as C code. 1082 1083 return s.replace("\r", " ").replace("\n", " ") 1084 1085 1086def err(s: str) -> NoReturn: 1087 raise Exception(s) 1088 1089 1090if __name__ == "__main__": 1091 main() 1092