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 hash:") 724 out_dt_define(f"{node.z_path_id}_HASH", node.hash) 725 726 out_comment("Node's dependency ordinal:") 727 out_dt_define(f"{node.z_path_id}_ORD", node.dep_ordinal) 728 out_dt_define(f"{node.z_path_id}_ORD_STR_SORTABLE", f"{node.dep_ordinal:0>5}") 729 730 out_comment("Ordinals for what this node depends on directly:") 731 out_dt_define(f"{node.z_path_id}_REQUIRES_ORDS", 732 fmt_dep_list(node.depends_on)) 733 734 out_comment("Ordinals for what depends directly on this node:") 735 out_dt_define(f"{node.z_path_id}_SUPPORTS_ORDS", 736 fmt_dep_list(node.required_by)) 737 738 739def prop2value(prop: edtlib.Property) -> edtlib.PropertyValType: 740 # Gets the macro value for property 'prop', if there is 741 # a single well-defined C rvalue that it can be represented as. 742 # Returns None if there isn't one. 743 744 if prop.type == "string": 745 return quote_str(prop.val) 746 747 if prop.type == "int": 748 return prop.val 749 750 if prop.type == "boolean": 751 return 1 if prop.val else 0 752 753 if prop.type in ["array", "uint8-array"]: 754 return list2init(f"{val} /* {hex(val)} */" for val in prop.val) 755 756 if prop.type == "string-array": 757 return list2init(quote_str(val) for val in prop.val) 758 759 # phandle, phandles, phandle-array, path, compound: nothing 760 return None 761 762 763def prop_len(prop: edtlib.Property) -> Optional[int]: 764 # Returns the property's length if and only if we should generate 765 # a _LEN macro for the property. Otherwise, returns None. 766 # 767 # The set of types handled here coincides with the allowable types 768 # that can be used with DT_PROP_LEN(). If you change this set, 769 # make sure to update the doxygen string for that macro, and make 770 # sure that DT_FOREACH_PROP_ELEM() works for the new types too. 771 # 772 # This deliberately excludes ranges, dma-ranges, reg and interrupts. 773 # While they have array type, their lengths as arrays are 774 # basically nonsense semantically due to #address-cells and 775 # #size-cells for "reg", #interrupt-cells for "interrupts" 776 # and #address-cells, #size-cells and the #address-cells from the 777 # parent node for "ranges" and "dma-ranges". 778 # 779 # We have special purpose macros for the number of register blocks 780 # / interrupt specifiers. Excluding them from this list means 781 # DT_PROP_LEN(node_id, ...) fails fast at the devicetree.h layer 782 # with a build error. This forces users to switch to the right 783 # macros. 784 785 if prop.type in ["phandle", "string"]: 786 # phandle is treated as a phandles of length 1. 787 # string is treated as a string-array of length 1. 788 return 1 789 790 if (prop.type in ["array", "uint8-array", "string-array", 791 "phandles", "phandle-array"] and 792 prop.name not in ["ranges", "dma-ranges", "reg", "interrupts"]): 793 return len(prop.val) 794 795 return None 796 797 798def phandle_macros(prop: edtlib.Property, macro: str) -> dict: 799 # Returns a dict of macros for phandle or phandles property 'prop'. 800 # 801 # The 'macro' argument is the N_<node-id>_P_<prop-id> bit. 802 # 803 # These are currently special because we can't serialize their 804 # values without using label properties, which we're trying to get 805 # away from needing in Zephyr. (Label properties are great for 806 # humans, but have drawbacks for code size and boot time.) 807 # 808 # The names look a bit weird to make it easier for devicetree.h 809 # to use the same macros for phandle, phandles, and phandle-array. 810 811 ret = {} 812 813 if prop.type == "phandle": 814 # A phandle is treated as a phandles with fixed length 1. 815 ret[f"{macro}"] = f"DT_{prop.val.z_path_id}" 816 ret[f"{macro}_IDX_0"] = f"DT_{prop.val.z_path_id}" 817 ret[f"{macro}_IDX_0_PH"] = f"DT_{prop.val.z_path_id}" 818 ret[f"{macro}_IDX_0_EXISTS"] = 1 819 elif prop.type == "phandles": 820 for i, node in enumerate(prop.val): 821 ret[f"{macro}_IDX_{i}"] = f"DT_{node.z_path_id}" 822 ret[f"{macro}_IDX_{i}_PH"] = f"DT_{node.z_path_id}" 823 ret[f"{macro}_IDX_{i}_EXISTS"] = 1 824 elif prop.type == "phandle-array": 825 for i, entry in enumerate(prop.val): 826 if entry is None: 827 # Unspecified element. The phandle-array at this index 828 # does not point at a ControllerAndData value, but 829 # subsequent indices in the array may. 830 ret[f"{macro}_IDX_{i}_EXISTS"] = 0 831 continue 832 833 ret.update(controller_and_data_macros(entry, i, macro)) 834 835 return ret 836 837 838def controller_and_data_macros(entry: edtlib.ControllerAndData, i: int, macro: str): 839 # Helper procedure used by phandle_macros(). 840 # 841 # Its purpose is to write the "controller" (i.e. label property of 842 # the phandle's node) and associated data macros for a 843 # ControllerAndData. 844 845 ret = {} 846 data = entry.data 847 848 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS 849 ret[f"{macro}_IDX_{i}_EXISTS"] = 1 850 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_PH 851 ret[f"{macro}_IDX_{i}_PH"] = f"DT_{entry.controller.z_path_id}" 852 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_VAL_<VAL> 853 for cell, val in data.items(): 854 ret[f"{macro}_IDX_{i}_VAL_{str2ident(cell)}"] = val 855 ret[f"{macro}_IDX_{i}_VAL_{str2ident(cell)}_EXISTS"] = 1 856 857 if not entry.name: 858 return ret 859 860 name = str2ident(entry.name) 861 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS 862 ret[f"{macro}_IDX_{i}_EXISTS"] = 1 863 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_NAME 864 ret[f"{macro}_IDX_{i}_NAME"] = quote_str(entry.name) 865 # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_PH 866 ret[f"{macro}_NAME_{name}_PH"] = f"DT_{entry.controller.z_path_id}" 867 # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_EXISTS 868 ret[f"{macro}_NAME_{name}_EXISTS"] = 1 869 # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_VAL_<VAL> 870 for cell, val in data.items(): 871 cell_ident = str2ident(cell) 872 ret[f"{macro}_NAME_{name}_VAL_{cell_ident}"] = ( 873 f"DT_{macro}_IDX_{i}_VAL_{cell_ident}") 874 ret[f"{macro}_NAME_{name}_VAL_{cell_ident}_EXISTS"] = 1 875 876 return ret 877 878 879def write_chosen(edt: edtlib.EDT): 880 # Tree-wide information such as chosen nodes is printed here. 881 882 out_comment("Chosen nodes\n") 883 chosen = {} 884 for name, node in edt.chosen_nodes.items(): 885 chosen[f"DT_CHOSEN_{str2ident(name)}"] = f"DT_{node.z_path_id}" 886 chosen[f"DT_CHOSEN_{str2ident(name)}_EXISTS"] = 1 887 max_len = max(map(len, chosen), default=0) 888 for macro, value in chosen.items(): 889 out_define(macro, value, width=max_len) 890 891 892def write_global_macros(edt: edtlib.EDT): 893 # Global or tree-wide information, such as number of instances 894 # with status "okay" for each compatible, is printed here. 895 896 897 out_comment("Macros for iterating over all nodes and enabled nodes") 898 out_dt_define("FOREACH_HELPER(fn)", 899 " ".join(f"fn(DT_{node.z_path_id})" for node in edt.nodes)) 900 out_dt_define("FOREACH_OKAY_HELPER(fn)", 901 " ".join(f"fn(DT_{node.z_path_id})" for node in edt.nodes 902 if node.status == "okay")) 903 out_dt_define("FOREACH_VARGS_HELPER(fn, ...)", 904 " ".join(f"fn(DT_{node.z_path_id}, __VA_ARGS__)" for node in edt.nodes)) 905 out_dt_define("FOREACH_OKAY_VARGS_HELPER(fn, ...)", 906 " ".join(f"fn(DT_{node.z_path_id}, __VA_ARGS__)" for node in edt.nodes 907 if node.status == "okay")) 908 909 n_okay_macros = {} 910 for_each_macros = {} 911 compat2buses = defaultdict(list) # just for "okay" nodes 912 for compat, okay_nodes in edt.compat2okay.items(): 913 for node in okay_nodes: 914 buses = node.on_buses 915 for bus in buses: 916 if bus is not None and bus not in compat2buses[compat]: 917 compat2buses[compat].append(bus) 918 919 ident = str2ident(compat) 920 n_okay_macros[f"DT_N_INST_{ident}_NUM_OKAY"] = len(okay_nodes) 921 922 # Helpers for non-INST for-each macros that take node 923 # identifiers as arguments. 924 for_each_macros[f"DT_FOREACH_OKAY_{ident}(fn)"] = ( 925 " ".join(f"fn(DT_{node.z_path_id})" 926 for node in okay_nodes)) 927 for_each_macros[f"DT_FOREACH_OKAY_VARGS_{ident}(fn, ...)"] = ( 928 " ".join(f"fn(DT_{node.z_path_id}, __VA_ARGS__)" 929 for node in okay_nodes)) 930 931 # Helpers for INST versions of for-each macros, which take 932 # instance numbers. We emit separate helpers for these because 933 # avoiding an intermediate node_id --> instance number 934 # conversion in the preprocessor helps to keep the macro 935 # expansions simpler. That hopefully eases debugging. 936 for_each_macros[f"DT_FOREACH_OKAY_INST_{ident}(fn)"] = ( 937 " ".join(f"fn({edt.compat2nodes[compat].index(node)})" 938 for node in okay_nodes)) 939 for_each_macros[f"DT_FOREACH_OKAY_INST_VARGS_{ident}(fn, ...)"] = ( 940 " ".join(f"fn({edt.compat2nodes[compat].index(node)}, __VA_ARGS__)" 941 for node in okay_nodes)) 942 943 for compat, nodes in edt.compat2nodes.items(): 944 for node in nodes: 945 if compat == "fixed-partitions": 946 for child in node.children.values(): 947 if "label" in child.props: 948 label = child.props["label"].val 949 macro = f"COMPAT_{str2ident(compat)}_LABEL_{str2ident(label)}" 950 val = f"DT_{child.z_path_id}" 951 952 out_dt_define(macro, val) 953 out_dt_define(macro + "_EXISTS", 1) 954 955 out_comment('Macros for compatibles with status "okay" nodes\n') 956 for compat, okay_nodes in edt.compat2okay.items(): 957 if okay_nodes: 958 out_define(f"DT_COMPAT_HAS_OKAY_{str2ident(compat)}", 1) 959 960 out_comment('Macros for status "okay" instances of each compatible\n') 961 for macro, value in n_okay_macros.items(): 962 out_define(macro, value) 963 for macro, value in for_each_macros.items(): 964 out_define(macro, value) 965 966 out_comment('Bus information for status "okay" nodes of each compatible\n') 967 for compat, buses in compat2buses.items(): 968 for bus in buses: 969 out_define( 970 f"DT_COMPAT_{str2ident(compat)}_BUS_{str2ident(bus)}", 1) 971 972 973def str2ident(s: str) -> str: 974 # Converts 's' to a form suitable for (part of) an identifier 975 976 return re.sub('[-,.@/+]', '_', s.lower()) 977 978 979def list2init(l: Iterable[str]) -> str: 980 # Converts 'l', a Python list (or iterable), to a C array initializer 981 982 return "{" + ", ".join(l) + "}" 983 984 985def out_dt_define( 986 macro: str, 987 val: str, 988 width: Optional[int] = None, 989 deprecation_msg: Optional[str] = None, 990) -> str: 991 # Writes "#define DT_<macro> <val>" to the header file 992 # 993 # The macro will be left-justified to 'width' characters if that 994 # is specified, and the value will follow immediately after in 995 # that case. Otherwise, this function decides how to add 996 # whitespace between 'macro' and 'val'. 997 # 998 # If a 'deprecation_msg' string is passed, the generated identifiers will 999 # generate a warning if used, via __WARN(<deprecation_msg>)). 1000 # 1001 # Returns the full generated macro for 'macro', with leading "DT_". 1002 ret = f"DT_{macro}" 1003 out_define(ret, val, width=width, deprecation_msg=deprecation_msg) 1004 return ret 1005 1006 1007def out_define( 1008 macro: str, 1009 val: str, 1010 width: Optional[int] = None, 1011 deprecation_msg: Optional[str] = None, 1012) -> None: 1013 # Helper for out_dt_define(). Outputs "#define <macro> <val>", 1014 # adds a deprecation message if given, and allocates whitespace 1015 # unless told not to. 1016 1017 warn = fr' __WARN("{deprecation_msg}")' if deprecation_msg else "" 1018 1019 if width: 1020 s = f"#define {macro.ljust(width)}{warn} {val}" 1021 else: 1022 s = f"#define {macro}{warn} {val}" 1023 1024 print(s, file=header_file) 1025 1026 1027def out_comment(s: str, blank_before=True) -> None: 1028 # Writes 's' as a comment to the header and configuration file. 's' is 1029 # allowed to have multiple lines. blank_before=True adds a blank line 1030 # before the comment. 1031 1032 if blank_before: 1033 print(file=header_file) 1034 1035 if "\n" in s: 1036 # Format multi-line comments like 1037 # 1038 # /* 1039 # * first line 1040 # * second line 1041 # * 1042 # * empty line before this line 1043 # */ 1044 res = ["/*"] 1045 for line in s.splitlines(): 1046 # Avoid an extra space after '*' for empty lines. They turn red in 1047 # Vim if space error checking is on, which is annoying. 1048 res.append(f" * {line}".rstrip()) 1049 res.append(" */") 1050 print("\n".join(res), file=header_file) 1051 else: 1052 # Format single-line comments like 1053 # 1054 # /* foo bar */ 1055 print(f"/* {s} */", file=header_file) 1056 1057ESCAPE_TABLE = str.maketrans( 1058 { 1059 "\n": "\\n", 1060 "\r": "\\r", 1061 "\\": "\\\\", 1062 '"': '\\"', 1063 } 1064) 1065 1066 1067def escape(s: str) -> str: 1068 # Backslash-escapes any double quotes, backslashes, and new lines in 's' 1069 1070 return s.translate(ESCAPE_TABLE) 1071 1072 1073def quote_str(s: str) -> str: 1074 # Puts quotes around 's' and escapes any double quotes and 1075 # backslashes within it 1076 1077 return f'"{escape(s)}"' 1078 1079 1080def escape_unquoted(s: str) -> str: 1081 # C macros cannot contain line breaks, so replace them with spaces. 1082 # Whitespace is used to separate preprocessor tokens, but it does not matter 1083 # which whitespace characters are used, so a line break and a space are 1084 # equivalent with regards to unquoted strings being used as C code. 1085 1086 return s.replace("\r", " ").replace("\n", " ") 1087 1088 1089def err(s: str) -> NoReturn: 1090 raise Exception(s) 1091 1092 1093if __name__ == "__main__": 1094 main() 1095