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 child_unit_addrs = {} 488 for child in node.children.values(): 489 # Provide a way to query child nodes 490 if (addr := child.unit_addr) is not None: 491 child_unit_addrs.setdefault(addr, []).append(child) 492 493 for addr, children in child_unit_addrs.items(): 494 if len(children) != 1: 495 # Duplicate unit addresses for different children, skip 496 continue 497 498 out_dt_define(f"{node.z_path_id}_CHILD_UNIT_ADDR_INT_{addr}", f"DT_{children[0].z_path_id}") 499 500 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD(fn)", 501 " ".join(f"fn(DT_{child.z_path_id})" for child in 502 node.children.values())) 503 504 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_SEP(fn, sep)", 505 " DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id})" 506 for child in node.children.values())) 507 508 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_VARGS(fn, ...)", 509 " ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)" 510 for child in node.children.values())) 511 512 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_SEP_VARGS(fn, sep, ...)", 513 " DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)" 514 for child in node.children.values())) 515 516 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY(fn)", 517 " ".join(f"fn(DT_{child.z_path_id})" 518 for child in node.children.values() if child.status == "okay")) 519 520 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_SEP(fn, sep)", 521 " DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id})" 522 for child in node.children.values() if child.status == "okay")) 523 524 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_VARGS(fn, ...)", 525 " ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)" 526 for child in node.children.values() if child.status == "okay")) 527 528 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_SEP_VARGS(fn, sep, ...)", 529 " DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)" 530 for child in node.children.values() if child.status == "okay")) 531 532 533def write_status(node: edtlib.Node) -> None: 534 out_dt_define(f"{node.z_path_id}_STATUS_{str2ident(node.status)}", 1) 535 536 537def write_pinctrls(node: edtlib.Node) -> None: 538 # Write special macros for pinctrl-<index> and pinctrl-names properties. 539 540 out_comment("Pin control (pinctrl-<i>, pinctrl-names) properties:") 541 542 out_dt_define(f"{node.z_path_id}_PINCTRL_NUM", len(node.pinctrls)) 543 544 if not node.pinctrls: 545 return 546 547 for pc_idx, pinctrl in enumerate(node.pinctrls): 548 out_dt_define(f"{node.z_path_id}_PINCTRL_IDX_{pc_idx}_EXISTS", 1) 549 550 if not pinctrl.name: 551 continue 552 553 name = pinctrl.name_as_token 554 555 # Below we rely on the fact that edtlib ensures the 556 # pinctrl-<pc_idx> properties are contiguous, start from 0, 557 # and contain only phandles. 558 out_dt_define(f"{node.z_path_id}_PINCTRL_IDX_{pc_idx}_TOKEN", name) 559 out_dt_define(f"{node.z_path_id}_PINCTRL_IDX_{pc_idx}_UPPER_TOKEN", name.upper()) 560 out_dt_define(f"{node.z_path_id}_PINCTRL_NAME_{name}_EXISTS", 1) 561 out_dt_define(f"{node.z_path_id}_PINCTRL_NAME_{name}_IDX", pc_idx) 562 for idx, ph in enumerate(pinctrl.conf_nodes): 563 out_dt_define(f"{node.z_path_id}_PINCTRL_NAME_{name}_IDX_{idx}_PH", 564 f"DT_{ph.z_path_id}") 565 566 567def write_fixed_partitions(node: edtlib.Node) -> None: 568 # Macros for child nodes of each fixed-partitions node. 569 570 if not (node.parent and ("fixed-partitions" in node.parent.compats or "fixed-subpartitions" in node.parent.compats)): 571 return 572 573 global flash_area_num 574 out_comment("fixed-partitions identifier:") 575 out_dt_define(f"{node.z_path_id}_PARTITION_ID", flash_area_num) 576 flash_area_num += 1 577 578 579def write_gpio_hogs(node: edtlib.Node) -> None: 580 # Write special macros for gpio-hog node properties. 581 582 macro = f"{node.z_path_id}_GPIO_HOGS" 583 macro2val = {} 584 for i, entry in enumerate(node.gpio_hogs): 585 macro2val.update(controller_and_data_macros(entry, i, macro, "")) 586 587 if macro2val: 588 out_comment("GPIO hog properties:") 589 out_dt_define(f"{macro}_EXISTS", 1) 590 out_dt_define(f"{macro}_NUM", len(node.gpio_hogs)) 591 for macro, val in macro2val.items(): 592 out_dt_define(macro, val) 593 594 595def write_vanilla_props(node: edtlib.Node) -> None: 596 # Writes macros for any and all properties defined in the 597 # "properties" section of the binding for the node. 598 # 599 # This does generate macros for special properties as well, like 600 # regs, etc. Just let that be rather than bothering to add 601 # never-ending amounts of special case code here to skip special 602 # properties. This function's macros can't conflict with 603 # write_special_props() macros, because they're in different 604 # namespaces. Special cases aren't special enough to break the rules. 605 606 macro2val = {} 607 for prop_name, prop in node.props.items(): 608 prop_id = str2ident(prop_name) 609 macro = f"{node.z_path_id}_P_{prop_id}" 610 val = prop2value(prop) 611 if val is not None: 612 # DT_N_<node-id>_P_<prop-id> 613 macro2val[macro] = val 614 615 if prop.spec.type == 'string': 616 macro2val.update(string_macros(macro, prop.val)) 617 # DT_N_<node-id>_P_<prop-id>_IDX_0: 618 # DT_N_<node-id>_P_<prop-id>_IDX_0_EXISTS: 619 # Allows treating the string like a degenerate case of a 620 # string-array of length 1. 621 macro2val[f"{macro}_IDX_0"] = quote_str(prop.val) 622 macro2val[f"{macro}_IDX_0_EXISTS"] = 1 623 624 if prop.enum_indices is not None: 625 macro2val.update(enum_macros(prop, macro)) 626 627 if "phandle" in prop.type: 628 macro2val.update(phandle_macros(prop, macro)) 629 elif "array" in prop.type: 630 macro2val.update(array_macros(prop, macro)) 631 632 plen = prop_len(prop) 633 if plen is not None: 634 # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM 635 macro2val[f"{macro}_FOREACH_PROP_ELEM(fn)"] = ( 636 ' \\\n\t'.join(f'fn(DT_{node.z_path_id}, {prop_id}, {i})' 637 for i in range(plen))) 638 639 # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_SEP 640 macro2val[f"{macro}_FOREACH_PROP_ELEM_SEP(fn, sep)"] = ( 641 ' DT_DEBRACKET_INTERNAL sep \\\n\t'.join( 642 f'fn(DT_{node.z_path_id}, {prop_id}, {i})' 643 for i in range(plen))) 644 645 # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_VARGS 646 macro2val[f"{macro}_FOREACH_PROP_ELEM_VARGS(fn, ...)"] = ( 647 ' \\\n\t'.join( 648 f'fn(DT_{node.z_path_id}, {prop_id}, {i}, __VA_ARGS__)' 649 for i in range(plen))) 650 651 # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_SEP_VARGS 652 macro2val[f"{macro}_FOREACH_PROP_ELEM_SEP_VARGS(fn, sep, ...)"] = ( 653 ' DT_DEBRACKET_INTERNAL sep \\\n\t'.join( 654 f'fn(DT_{node.z_path_id}, {prop_id}, {i}, __VA_ARGS__)' 655 for i in range(plen))) 656 657 # DT_N_<node-id>_P_<prop-id>_LEN 658 macro2val[f"{macro}_LEN"] = plen 659 660 # DT_N_<node-id>_P_<prop-id>_EXISTS 661 macro2val[f"{macro}_EXISTS"] = 1 662 663 if macro2val: 664 out_comment("Generic property macros:") 665 for macro, val in macro2val.items(): 666 out_dt_define(macro, val) 667 else: 668 out_comment("(No generic property macros)") 669 670 671def string_macros(macro: str, val: str): 672 # Returns a dict of macros for a string 'val'. 673 # The 'macro' argument is the N_<node-id>_P_<prop-id>... part. 674 675 as_token = edtlib.str_as_token(val) 676 return { 677 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_STRING_UNQUOTED 678 f"{macro}_STRING_UNQUOTED": escape_unquoted(val), 679 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_STRING_TOKEN 680 f"{macro}_STRING_TOKEN": as_token, 681 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_STRING_UPPER_TOKEN 682 f"{macro}_STRING_UPPER_TOKEN": as_token.upper()} 683 684 685def enum_macros(prop: edtlib.Property, macro: str): 686 # Returns a dict of macros for property 'prop' with a defined enum in their dt-binding. 687 # The 'macro' argument is the N_<node-id>_P_<prop-id> part. 688 689 spec = prop.spec 690 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_ENUM_IDX 691 ret = {f"{macro}_IDX_{i}_ENUM_IDX": index for i, index in enumerate(prop.enum_indices)} 692 val = prop.val_as_tokens if spec.enum_tokenizable else (prop.val if isinstance(prop.val, list) else [prop.val]) 693 694 for i, subval in enumerate(val): 695 # make sure the subval is a formated right. 696 if isinstance(subval, str): 697 subval = str2ident(subval) 698 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS 699 ret[f"{macro}_IDX_{i}_EXISTS"] = 1 700 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_ENUM_VAL_<val>_EXISTS 1 701 ret[f"{macro}_IDX_{i}_ENUM_VAL_{subval}_EXISTS"] = 1 702 # DT_N_<node-id>_P_<prop-id>_ENUM_VAL_<val>_EXISTS 1 703 ret[f"{macro}_ENUM_VAL_{subval}_EXISTS"] = 1 704 705 return ret 706 707 708def array_macros(prop: edtlib.Property, macro: str): 709 # Returns a dict of macros for array property 'prop'. 710 # The 'macro' argument is the N_<node-id>_P_<prop-id> part. 711 712 ret = {} 713 for i, subval in enumerate(prop.val): 714 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS 715 ret[f"{macro}_IDX_{i}_EXISTS"] = 1 716 717 # DT_N_<node-id>_P_<prop-id>_IDX_<i> 718 if isinstance(subval, str): 719 ret[f"{macro}_IDX_{i}"] = quote_str(subval) 720 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_STRING_... 721 ret.update(string_macros(f"{macro}_IDX_{i}", subval)) 722 else: 723 ret[f"{macro}_IDX_{i}"] = subval 724 725 return ret 726 727 728def write_dep_info(node: edtlib.Node) -> None: 729 # Write dependency-related information about the node. 730 731 def fmt_dep_list(dep_list): 732 if dep_list: 733 # Sort the list by dependency ordinal for predictability. 734 sorted_list = sorted(dep_list, key=lambda node: node.dep_ordinal) 735 return ("\\\n\t" + " \\\n\t" 736 .join(f"{n.dep_ordinal}, /* {n.path} */" 737 for n in sorted_list)) 738 else: 739 return "/* nothing */" 740 741 out_comment("Node's hash:") 742 out_dt_define(f"{node.z_path_id}_HASH", node.hash) 743 744 out_comment("Node's dependency ordinal:") 745 out_dt_define(f"{node.z_path_id}_ORD", node.dep_ordinal) 746 out_dt_define(f"{node.z_path_id}_ORD_STR_SORTABLE", f"{node.dep_ordinal:0>5}") 747 748 out_comment("Ordinals for what this node depends on directly:") 749 out_dt_define(f"{node.z_path_id}_REQUIRES_ORDS", 750 fmt_dep_list(node.depends_on)) 751 752 out_comment("Ordinals for what depends directly on this node:") 753 out_dt_define(f"{node.z_path_id}_SUPPORTS_ORDS", 754 fmt_dep_list(node.required_by)) 755 756 757def prop2value(prop: edtlib.Property) -> edtlib.PropertyValType: 758 # Gets the macro value for property 'prop', if there is 759 # a single well-defined C rvalue that it can be represented as. 760 # Returns None if there isn't one. 761 762 if prop.type == "string": 763 return quote_str(prop.val) 764 765 if prop.type == "int": 766 return prop.val 767 768 if prop.type == "boolean": 769 return 1 if prop.val else 0 770 771 if prop.type in ["array", "uint8-array"]: 772 return list2init(f"{val} /* {hex(val)} */" for val in prop.val) 773 774 if prop.type == "string-array": 775 return list2init(quote_str(val) for val in prop.val) 776 777 # phandle, phandles, phandle-array, path, compound: nothing 778 return None 779 780 781def prop_len(prop: edtlib.Property) -> Optional[int]: 782 # Returns the property's length if and only if we should generate 783 # a _LEN macro for the property. Otherwise, returns None. 784 # 785 # The set of types handled here coincides with the allowable types 786 # that can be used with DT_PROP_LEN(). If you change this set, 787 # make sure to update the doxygen string for that macro, and make 788 # sure that DT_FOREACH_PROP_ELEM() works for the new types too. 789 # 790 # This deliberately excludes ranges, dma-ranges, reg and interrupts. 791 # While they have array type, their lengths as arrays are 792 # basically nonsense semantically due to #address-cells and 793 # #size-cells for "reg", #interrupt-cells for "interrupts" 794 # and #address-cells, #size-cells and the #address-cells from the 795 # parent node for "ranges" and "dma-ranges". 796 # 797 # We have special purpose macros for the number of register blocks 798 # / interrupt specifiers. Excluding them from this list means 799 # DT_PROP_LEN(node_id, ...) fails fast at the devicetree.h layer 800 # with a build error. This forces users to switch to the right 801 # macros. 802 803 if prop.type in ["phandle", "string"]: 804 # phandle is treated as a phandles of length 1. 805 # string is treated as a string-array of length 1. 806 return 1 807 808 if (prop.type in ["array", "uint8-array", "string-array", 809 "phandles", "phandle-array"] and 810 prop.name not in ["ranges", "dma-ranges", "reg", "interrupts"]): 811 return len(prop.val) 812 813 return None 814 815 816def phandle_macros(prop: edtlib.Property, macro: str) -> dict: 817 # Returns a dict of macros for phandle or phandles property 'prop'. 818 # 819 # The 'macro' argument is the N_<node-id>_P_<prop-id> bit. 820 # 821 # These are currently special because we can't serialize their 822 # values without using label properties, which we're trying to get 823 # away from needing in Zephyr. (Label properties are great for 824 # humans, but have drawbacks for code size and boot time.) 825 # 826 # The names look a bit weird to make it easier for devicetree.h 827 # to use the same macros for phandle, phandles, and phandle-array. 828 829 ret = {} 830 831 if prop.type == "phandle": 832 # A phandle is treated as a phandles with fixed length 1. 833 ret[f"{macro}"] = f"DT_{prop.val.z_path_id}" 834 ret[f"{macro}_IDX_0"] = f"DT_{prop.val.z_path_id}" 835 ret[f"{macro}_IDX_0_PH"] = f"DT_{prop.val.z_path_id}" 836 ret[f"{macro}_IDX_0_EXISTS"] = 1 837 elif prop.type == "phandles": 838 for i, node in enumerate(prop.val): 839 ret[f"{macro}_IDX_{i}"] = f"DT_{node.z_path_id}" 840 ret[f"{macro}_IDX_{i}_PH"] = f"DT_{node.z_path_id}" 841 ret[f"{macro}_IDX_{i}_EXISTS"] = 1 842 elif prop.type == "phandle-array": 843 for i, entry in enumerate(prop.val): 844 if entry is None: 845 # Unspecified element. The phandle-array at this index 846 # does not point at a ControllerAndData value, but 847 # subsequent indices in the array may. 848 ret[f"{macro}_IDX_{i}_EXISTS"] = 0 849 continue 850 851 ret.update(controller_and_data_macros(entry, i, macro, prop.name)) 852 853 return ret 854 855 856def controller_and_data_macros(entry: edtlib.ControllerAndData, i: int, macro: str, pname: str): 857 # Helper procedure used by phandle_macros(). 858 # 859 # Its purpose is to write the "controller" (i.e. label property of 860 # the phandle's node) and associated data macros for a 861 # ControllerAndData. 862 863 ret = {} 864 data = entry.data 865 node = entry.node 866 pname = edtlib.str_as_token(str2ident(pname)) 867 868 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS 869 ret[f"{macro}_IDX_{i}_EXISTS"] = 1 870 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_PH 871 ret[f"{macro}_IDX_{i}_PH"] = f"DT_{entry.controller.z_path_id}" 872 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_VAL_<VAL> 873 for cell, val in data.items(): 874 ret[f"{macro}_IDX_{i}_VAL_{str2ident(cell)}"] = val 875 ret[f"{macro}_IDX_{i}_VAL_{str2ident(cell)}_EXISTS"] = 1 876 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS 877 ret[f"{macro}_IDX_{i}_EXISTS"] = 1 878 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_FOREACH_CELL 879 ret[f"{macro}_IDX_{i}_FOREACH_CELL(fn)"] = ( 880 ' \\\n\t'.join(f'fn(DT_{node.z_path_id}, {pname}, {i}, {cell})' 881 for cell in data)) 882 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_FOREACH_CELL_SEP 883 ret[f"{macro}_IDX_{i}_FOREACH_CELL_SEP(fn, sep)"] = ( 884 ' DT_DEBRACKET_INTERNAL sep \\\n\t'.join( 885 f'fn(DT_{node.z_path_id}, {pname}, {i}, {cell})' 886 for cell in data)) 887 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_NUM_CELLS 888 ret[f"{macro}_IDX_{i}_NUM_CELLS"] = len(data) 889 890 if not entry.name: 891 return ret 892 893 name = str2ident(entry.name) 894 895 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_NAME 896 ret[f"{macro}_IDX_{i}_NAME"] = edtlib.str_as_token(name) 897 # DT_N_<node-id>_P_<prop-id>_NAME_<name>_IDX 898 ret[f"{macro}_NAME_{name}_IDX"] = i 899 # DT_N_<node-id>_P_<prop-id>_NAME_<name>_FOREACH_CELL 900 ret[f"{macro}_NAME_{name}_FOREACH_CELL(fn)"] = ( 901 ' \\\n\t'.join(f'fn(DT_{node.z_path_id}, {pname}, {name}, {cell})' 902 for cell in data)) 903 # DT_N_<node-id>_P_<prop-id>_NAME_<name>_FOREACH_CELL_SEP 904 ret[f"{macro}_NAME_{name}_FOREACH_CELL_SEP(fn, sep)"] = ( 905 ' DT_DEBRACKET_INTERNAL sep \\\n\t'.join( 906 f'fn(DT_{node.z_path_id}, {pname}, {name}, {cell})' 907 for cell in data)) 908 # DT_N_<node-id>_P_<prop-id>_NAME_<name>_NUM_CELLS 909 ret[f"{macro}_NAME_{name}_NUM_CELLS"] = len(data) 910 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_NAME 911 ret[f"{macro}_IDX_{i}_NAME"] = quote_str(entry.name) 912 # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_PH 913 ret[f"{macro}_NAME_{name}_PH"] = f"DT_{entry.controller.z_path_id}" 914 # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_EXISTS 915 ret[f"{macro}_NAME_{name}_EXISTS"] = 1 916 # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_VAL_<VAL> 917 for cell, val in data.items(): 918 cell_ident = str2ident(cell) 919 ret[f"{macro}_NAME_{name}_VAL_{cell_ident}"] = ( 920 f"DT_{macro}_IDX_{i}_VAL_{cell_ident}") 921 ret[f"{macro}_NAME_{name}_VAL_{cell_ident}_EXISTS"] = 1 922 923 return ret 924 925 926def write_chosen(edt: edtlib.EDT): 927 # Tree-wide information such as chosen nodes is printed here. 928 929 out_comment("Chosen nodes\n") 930 chosen = {} 931 for name, node in edt.chosen_nodes.items(): 932 chosen[f"DT_CHOSEN_{str2ident(name)}"] = f"DT_{node.z_path_id}" 933 chosen[f"DT_CHOSEN_{str2ident(name)}_EXISTS"] = 1 934 max_len = max(map(len, chosen), default=0) 935 for macro, value in chosen.items(): 936 out_define(macro, value, width=max_len) 937 938 939def write_global_macros(edt: edtlib.EDT): 940 # Global or tree-wide information, such as number of instances 941 # with status "okay" for each compatible, is printed here. 942 943 944 out_comment("Macros for iterating over all nodes and enabled nodes") 945 out_dt_define("FOREACH_HELPER(fn)", 946 " ".join(f"fn(DT_{node.z_path_id})" for node in edt.nodes)) 947 out_dt_define("FOREACH_OKAY_HELPER(fn)", 948 " ".join(f"fn(DT_{node.z_path_id})" for node in edt.nodes 949 if node.status == "okay")) 950 out_dt_define("FOREACH_VARGS_HELPER(fn, ...)", 951 " ".join(f"fn(DT_{node.z_path_id}, __VA_ARGS__)" for node in edt.nodes)) 952 out_dt_define("FOREACH_OKAY_VARGS_HELPER(fn, ...)", 953 " ".join(f"fn(DT_{node.z_path_id}, __VA_ARGS__)" for node in edt.nodes 954 if node.status == "okay")) 955 956 n_okay_macros = {} 957 for_each_macros = {} 958 compat2buses = defaultdict(list) # just for "okay" nodes 959 for compat, okay_nodes in edt.compat2okay.items(): 960 for node in okay_nodes: 961 buses = node.on_buses 962 for bus in buses: 963 if bus is not None and bus not in compat2buses[compat]: 964 compat2buses[compat].append(bus) 965 966 ident = str2ident(compat) 967 n_okay_macros[f"DT_N_INST_{ident}_NUM_OKAY"] = len(okay_nodes) 968 969 # Helpers for non-INST for-each macros that take node 970 # identifiers as arguments. 971 for_each_macros[f"DT_FOREACH_OKAY_{ident}(fn)"] = ( 972 " ".join(f"fn(DT_{node.z_path_id})" 973 for node in okay_nodes)) 974 for_each_macros[f"DT_FOREACH_OKAY_VARGS_{ident}(fn, ...)"] = ( 975 " ".join(f"fn(DT_{node.z_path_id}, __VA_ARGS__)" 976 for node in okay_nodes)) 977 978 # Helpers for INST versions of for-each macros, which take 979 # instance numbers. We emit separate helpers for these because 980 # avoiding an intermediate node_id --> instance number 981 # conversion in the preprocessor helps to keep the macro 982 # expansions simpler. That hopefully eases debugging. 983 for_each_macros[f"DT_FOREACH_OKAY_INST_{ident}(fn)"] = ( 984 " ".join(f"fn({edt.compat2nodes[compat].index(node)})" 985 for node in okay_nodes)) 986 for_each_macros[f"DT_FOREACH_OKAY_INST_VARGS_{ident}(fn, ...)"] = ( 987 " ".join(f"fn({edt.compat2nodes[compat].index(node)}, __VA_ARGS__)" 988 for node in okay_nodes)) 989 990 for compat, nodes in edt.compat2nodes.items(): 991 for node in nodes: 992 if compat == "fixed-partitions": 993 for child in node.children.values(): 994 if "label" in child.props: 995 label = child.props["label"].val 996 macro = f"COMPAT_{str2ident(compat)}_LABEL_{str2ident(label)}" 997 val = f"DT_{child.z_path_id}" 998 999 out_dt_define(macro, val) 1000 out_dt_define(macro + "_EXISTS", 1) 1001 1002 out_comment('Macros for compatibles with status "okay" nodes\n') 1003 for compat, okay_nodes in edt.compat2okay.items(): 1004 if okay_nodes: 1005 out_define(f"DT_COMPAT_HAS_OKAY_{str2ident(compat)}", 1) 1006 1007 out_comment('Macros for status "okay" instances of each compatible\n') 1008 for macro, value in n_okay_macros.items(): 1009 out_define(macro, value) 1010 for macro, value in for_each_macros.items(): 1011 out_define(macro, value) 1012 1013 out_comment('Bus information for status "okay" nodes of each compatible\n') 1014 for compat, buses in compat2buses.items(): 1015 for bus in buses: 1016 out_define( 1017 f"DT_COMPAT_{str2ident(compat)}_BUS_{str2ident(bus)}", 1) 1018 1019 1020def str2ident(s: str) -> str: 1021 # Converts 's' to a form suitable for (part of) an identifier 1022 1023 return re.sub('[-,.@/+]', '_', s.lower()) 1024 1025 1026def list2init(l: Iterable[str]) -> str: 1027 # Converts 'l', a Python list (or iterable), to a C array initializer 1028 1029 return "{" + ", ".join(l) + "}" 1030 1031 1032def out_dt_define( 1033 macro: str, 1034 val: str, 1035 width: Optional[int] = None, 1036 deprecation_msg: Optional[str] = None, 1037) -> str: 1038 # Writes "#define DT_<macro> <val>" to the header file 1039 # 1040 # The macro will be left-justified to 'width' characters if that 1041 # is specified, and the value will follow immediately after in 1042 # that case. Otherwise, this function decides how to add 1043 # whitespace between 'macro' and 'val'. 1044 # 1045 # If a 'deprecation_msg' string is passed, the generated identifiers will 1046 # generate a warning if used, via __WARN(<deprecation_msg>)). 1047 # 1048 # Returns the full generated macro for 'macro', with leading "DT_". 1049 ret = f"DT_{macro}" 1050 out_define(ret, val, width=width, deprecation_msg=deprecation_msg) 1051 return ret 1052 1053 1054def out_define( 1055 macro: str, 1056 val: str, 1057 width: Optional[int] = None, 1058 deprecation_msg: Optional[str] = None, 1059) -> None: 1060 # Helper for out_dt_define(). Outputs "#define <macro> <val>", 1061 # adds a deprecation message if given, and allocates whitespace 1062 # unless told not to. 1063 1064 warn = fr' __WARN("{deprecation_msg}")' if deprecation_msg else "" 1065 1066 if width: 1067 s = f"#define {macro.ljust(width)}{warn} {val}" 1068 else: 1069 s = f"#define {macro}{warn} {val}" 1070 1071 print(s, file=header_file) 1072 1073 1074def out_comment(s: str, blank_before=True) -> None: 1075 # Writes 's' as a comment to the header and configuration file. 's' is 1076 # allowed to have multiple lines. blank_before=True adds a blank line 1077 # before the comment. 1078 1079 if blank_before: 1080 print(file=header_file) 1081 1082 if "\n" in s: 1083 # Format multi-line comments like 1084 # 1085 # /* 1086 # * first line 1087 # * second line 1088 # * 1089 # * empty line before this line 1090 # */ 1091 res = ["/*"] 1092 for line in s.splitlines(): 1093 # Avoid an extra space after '*' for empty lines. They turn red in 1094 # Vim if space error checking is on, which is annoying. 1095 res.append(f" * {line}".rstrip()) 1096 res.append(" */") 1097 print("\n".join(res), file=header_file) 1098 else: 1099 # Format single-line comments like 1100 # 1101 # /* foo bar */ 1102 print(f"/* {s} */", file=header_file) 1103 1104ESCAPE_TABLE = str.maketrans( 1105 { 1106 "\n": "\\n", 1107 "\r": "\\r", 1108 "\\": "\\\\", 1109 '"': '\\"', 1110 } 1111) 1112 1113 1114def escape(s: str) -> str: 1115 # Backslash-escapes any double quotes, backslashes, and new lines in 's' 1116 1117 return s.translate(ESCAPE_TABLE) 1118 1119 1120def quote_str(s: str) -> str: 1121 # Puts quotes around 's' and escapes any double quotes and 1122 # backslashes within it 1123 1124 return f'"{escape(s)}"' 1125 1126 1127def escape_unquoted(s: str) -> str: 1128 # C macros cannot contain line breaks, so replace them with spaces. 1129 # Whitespace is used to separate preprocessor tokens, but it does not matter 1130 # which whitespace characters are used, so a line break and a space are 1131 # equivalent with regards to unquoted strings being used as C code. 1132 1133 return s.replace("\r", " ").replace("\n", " ") 1134 1135 1136def err(s: str) -> NoReturn: 1137 raise Exception(s) 1138 1139 1140if __name__ == "__main__": 1141 main() 1142