Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | # Copyright (c) 2019 - 2020 Nordic Semiconductor ASA |
| 4 | # Copyright (c) 2019 Linaro Limited |
Benedikt Schmidt | fe3287a | 2024-09-09 11:18:56 +0200 | [diff] [blame] | 5 | # Copyright (c) 2024 SILA Embedded Solutions GmbH |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 6 | # SPDX-License-Identifier: BSD-3-Clause |
| 7 | |
Benedikt Schmidt | fe3287a | 2024-09-09 11:18:56 +0200 | [diff] [blame] | 8 | # This script uses edtlib to generate a header file from a pickled |
| 9 | # edt file. |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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 | |
| 16 | import argparse |
Martí Bolívar | a3fae2f | 2020-03-25 14:18:27 -0700 | [diff] [blame] | 17 | from collections import defaultdict |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 18 | import os |
| 19 | import pathlib |
Martí Bolívar | 533f451 | 2020-07-01 10:43:43 -0700 | [diff] [blame] | 20 | import pickle |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 21 | import re |
| 22 | import sys |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 23 | from typing import Iterable, NoReturn, Optional |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 24 | |
Jordan Yates | 8e4107f | 2022-04-30 21:13:52 +1000 | [diff] [blame] | 25 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'python-devicetree', |
| 26 | 'src')) |
Martí Bolívar | 5332847 | 2021-03-26 16:18:58 -0700 | [diff] [blame] | 27 | |
Benedikt Schmidt | fe3287a | 2024-09-09 11:18:56 +0200 | [diff] [blame] | 28 | import edtlib_logger |
Martí Bolívar | 5332847 | 2021-03-26 16:18:58 -0700 | [diff] [blame] | 29 | from devicetree import edtlib |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 30 | |
Martí Bolívar | 0985849 | 2020-12-08 09:41:49 -0800 | [diff] [blame] | 31 | |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 32 | def main(): |
| 33 | global header_file |
Kumar Gala | bd97378 | 2020-05-06 20:54:29 -0500 | [diff] [blame] | 34 | global flash_area_num |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 35 | |
| 36 | args = parse_args() |
| 37 | |
Benedikt Schmidt | fe3287a | 2024-09-09 11:18:56 +0200 | [diff] [blame] | 38 | edtlib_logger.setup_edtlib_logging() |
Martí Bolívar | 0985849 | 2020-12-08 09:41:49 -0800 | [diff] [blame] | 39 | |
Benedikt Schmidt | fe3287a | 2024-09-09 11:18:56 +0200 | [diff] [blame] | 40 | with open(args.edt_pickle, 'rb') as f: |
| 41 | edt = pickle.load(f) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 42 | |
Kumar Gala | bd97378 | 2020-05-06 20:54:29 -0500 | [diff] [blame] | 43 | flash_area_num = 0 |
| 44 | |
Martí Bolívar | 533f451 | 2020-07-01 10:43:43 -0700 | [diff] [blame] | 45 | # Create the generated header. |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 46 | with open(args.header_out, "w", encoding="utf-8") as header_file: |
| 47 | write_top_comment(edt) |
| 48 | |
Gerard Marull-Paretas | d77f4e6 | 2022-07-05 16:50:36 +0200 | [diff] [blame] | 49 | write_utils() |
| 50 | |
Florian Grandel | 945925b | 2024-09-06 10:17:26 +0200 | [diff] [blame] | 51 | sorted_nodes = sorted(edt.nodes, key=lambda node: node.dep_ordinal) |
| 52 | |
Dominik Ermel | ba8b74d | 2020-04-17 06:32:28 +0000 | [diff] [blame] | 53 | # populate all z_path_id first so any children references will |
| 54 | # work correctly. |
Florian Grandel | 945925b | 2024-09-06 10:17:26 +0200 | [diff] [blame] | 55 | for node in sorted_nodes: |
Martí Bolívar | 186bace | 2020-04-08 15:02:18 -0700 | [diff] [blame] | 56 | node.z_path_id = node_z_path_id(node) |
Dominik Ermel | ba8b74d | 2020-04-17 06:32:28 +0000 | [diff] [blame] | 57 | |
Jordan Yates | 9c98d4f | 2021-07-28 20:01:16 +1000 | [diff] [blame] | 58 | # Check to see if we have duplicate "zephyr,memory-region" property values. |
| 59 | regions = dict() |
Florian Grandel | 945925b | 2024-09-06 10:17:26 +0200 | [diff] [blame] | 60 | for node in sorted_nodes: |
Jordan Yates | 9c98d4f | 2021-07-28 20:01:16 +1000 | [diff] [blame] | 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 | |
Florian Grandel | 945925b | 2024-09-06 10:17:26 +0200 | [diff] [blame] | 68 | for node in sorted_nodes: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 69 | write_node_comment(node) |
| 70 | |
Kumar Gala | 270a05f | 2021-02-24 11:28:21 -0600 | [diff] [blame] | 71 | out_comment("Node's full path:") |
Martí Bolívar | 00ffc7e | 2020-12-13 12:27:04 -0800 | [diff] [blame] | 72 | out_dt_define(f"{node.z_path_id}_PATH", f'"{escape(node.path)}"') |
| 73 | |
Kumar Gala | 4aac908 | 2021-02-24 10:44:07 -0600 | [diff] [blame] | 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)}"') |
TOKITA Hiroshi | 767d1ce | 2024-09-12 22:58:53 +0900 | [diff] [blame] | 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()}') |
Kumar Gala | 4aac908 | 2021-02-24 10:44:07 -0600 | [diff] [blame] | 83 | |
Martí Bolívar | 6e27343 | 2020-04-08 15:04:15 -0700 | [diff] [blame] | 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 | |
Martí Bolívar | 50f9b3c | 2022-03-23 13:41:09 -0700 | [diff] [blame] | 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 | |
Martí Bolívar | 74abb2b | 2024-04-24 19:22:42 -0600 | [diff] [blame] | 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 | |
James Roy | 4553a21 | 2024-11-14 21:10:00 +0800 | [diff] [blame] | 100 | write_parent(node) |
Martí Bolívar | 355cc01 | 2022-03-23 13:26:24 -0700 | [diff] [blame] | 101 | write_children(node) |
Martí Bolívar | 305379e | 2020-06-08 14:59:19 -0700 | [diff] [blame] | 102 | write_dep_info(node) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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) |
Martí Bolívar | 190197e | 2022-07-20 13:10:33 -0700 | [diff] [blame] | 109 | write_global_macros(edt) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 110 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 111 | |
| 112 | def node_z_path_id(node: edtlib.Node) -> str: |
Martí Bolívar | 186bace | 2020-04-08 15:02:18 -0700 | [diff] [blame] | 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 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 130 | |
| 131 | def parse_args() -> argparse.Namespace: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 132 | # Returns parsed command-line arguments |
| 133 | |
Jamie McCrae | ec70444 | 2023-01-04 16:08:36 +0000 | [diff] [blame] | 134 | parser = argparse.ArgumentParser(allow_abbrev=False) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 135 | parser.add_argument("--header-out", required=True, |
| 136 | help="path to write header to") |
Benedikt Schmidt | fe3287a | 2024-09-09 11:18:56 +0200 | [diff] [blame] | 137 | parser.add_argument("--edt-pickle", |
| 138 | help="path to read pickled edtlib.EDT object from") |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 139 | |
| 140 | return parser.parse_args() |
| 141 | |
| 142 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 143 | def write_top_comment(edt: edtlib.EDT) -> None: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 144 | # Writes an overview comment with misc. info at the top of the header and |
| 145 | # configuration file |
| 146 | |
| 147 | s = f"""\ |
| 148 | Generated by gen_defines.py |
| 149 | |
| 150 | DTS input file: |
| 151 | {edt.dts_path} |
| 152 | |
| 153 | Directories with bindings: |
| 154 | {", ".join(map(relativize, edt.bindings_dirs))} |
| 155 | |
Martí Bolívar | 305379e | 2020-06-08 14:59:19 -0700 | [diff] [blame] | 156 | Node dependency ordering (ordinal and path): |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 157 | """ |
| 158 | |
Martí Bolívar | b6db201 | 2020-08-24 13:33:53 -0700 | [diff] [blame] | 159 | for scc in edt.scc_order: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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 += """ |
| 166 | Definitions derived from these nodes in dependency order are next, |
| 167 | followed by /chosen nodes. |
| 168 | """ |
| 169 | |
| 170 | out_comment(s, blank_before=False) |
| 171 | |
| 172 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 173 | def write_utils() -> None: |
Gerard Marull-Paretas | d77f4e6 | 2022-07-05 16:50:36 +0200 | [diff] [blame] | 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 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 180 | def write_node_comment(node: edtlib.Node) -> None: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 181 | # Writes a comment describing 'node' to the header and configuration file |
| 182 | |
| 183 | s = f"""\ |
Martí Bolívar | b6e6ba0 | 2020-04-08 15:09:46 -0700 | [diff] [blame] | 184 | Devicetree node: {node.path} |
| 185 | |
Martí Bolívar | 305379e | 2020-06-08 14:59:19 -0700 | [diff] [blame] | 186 | Node identifier: DT_{node.z_path_id} |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 187 | """ |
| 188 | |
| 189 | if node.matching_compat: |
Peter Bigot | 932532e | 2020-09-02 05:05:19 -0500 | [diff] [blame] | 190 | if node.binding_path: |
| 191 | s += f""" |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 192 | Binding (compatible = {node.matching_compat}): |
| 193 | {relativize(node.binding_path)} |
| 194 | """ |
Peter Bigot | 932532e | 2020-09-02 05:05:19 -0500 | [diff] [blame] | 195 | else: |
| 196 | s += f""" |
| 197 | Binding (compatible = {node.matching_compat}): |
| 198 | No yaml (bindings inferred from properties) |
| 199 | """ |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 200 | |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 201 | if node.description: |
Martí Bolívar | f7d33f2 | 2020-10-30 17:45:27 -0700 | [diff] [blame] | 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") |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 215 | |
| 216 | out_comment(s) |
| 217 | |
| 218 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 219 | def relativize(path) -> Optional[str]: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 235 | def write_idents_and_existence(node: edtlib.Node) -> None: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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: |
Martí Bolívar | 7e0eed9 | 2020-05-06 11:23:07 -0700 | [diff] [blame] | 243 | instance_no = node.edt.compat2nodes[compat].index(node) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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:") |
James Roy | ab959d5 | 2024-12-05 21:10:53 +0800 | [diff] [blame] | 249 | out_dt_define(f"{node.z_path_id}_EXISTS", 1) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 250 | |
| 251 | # Only determine maxlen if we have any idents |
| 252 | if idents: |
James Roy | ab959d5 | 2024-12-05 21:10:53 +0800 | [diff] [blame] | 253 | maxlen = max(len(f"DT_{ident}") for ident in idents) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 254 | for ident in idents: |
James Roy | ab959d5 | 2024-12-05 21:10:53 +0800 | [diff] [blame] | 255 | out_dt_define(ident, f"DT_{node.z_path_id}", width=maxlen) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 256 | |
| 257 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 258 | def write_bus(node: edtlib.Node) -> None: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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 | |
Daniel Leung | 418c915 | 2022-08-26 10:52:32 -0700 | [diff] [blame] | 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 | |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 270 | out_dt_define(f"{node.z_path_id}_BUS", f"DT_{bus.z_path_id}") |
| 271 | |
| 272 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 273 | def write_special_props(node: edtlib.Node) -> None: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 274 | # Writes required macros for special case properties, when the |
| 275 | # data cannot otherwise be obtained from write_vanilla_props() |
| 276 | # results |
| 277 | |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 278 | # Macros that are special to the devicetree specification |
Martí Bolívar | 7f69a03 | 2021-08-11 15:14:51 -0700 | [diff] [blame] | 279 | out_comment("Macros for properties that are special in the specification:") |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 280 | write_regs(node) |
Neil Armstrong | 1e8f0f3 | 2021-06-24 10:14:05 +0200 | [diff] [blame] | 281 | write_ranges(node) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 282 | write_interrupts(node) |
| 283 | write_compatibles(node) |
Martí Bolívar | 7e0eed9 | 2020-05-06 11:23:07 -0700 | [diff] [blame] | 284 | write_status(node) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 285 | |
Martí Bolívar | 7f69a03 | 2021-08-11 15:14:51 -0700 | [diff] [blame] | 286 | # Macros that are special to bindings inherited from Linux, which |
| 287 | # we can't capture with the current bindings language. |
Martí Bolívar | 9df0493 | 2021-08-11 15:43:24 -0700 | [diff] [blame] | 288 | write_pinctrls(node) |
Martí Bolívar | 7f69a03 | 2021-08-11 15:14:51 -0700 | [diff] [blame] | 289 | write_fixed_partitions(node) |
Henrik Brix Andersen | 2881915 | 2023-01-13 11:32:27 +0100 | [diff] [blame] | 290 | write_gpio_hogs(node) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 291 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 292 | |
| 293 | def write_ranges(node: edtlib.Node) -> None: |
Neil Armstrong | 1e8f0f3 | 2021-06-24 10:14:05 +0200 | [diff] [blame] | 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 | |
Daniel Leung | 418c915 | 2022-08-26 10:52:32 -0700 | [diff] [blame] | 307 | if "pcie" in node.buses: |
Neil Armstrong | 1e8f0f3 | 2021-06-24 10:14:05 +0200 | [diff] [blame] | 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" |
Daniel Leung | 418c915 | 2022-08-26 10:52:32 -0700 | [diff] [blame] | 315 | if "pcie" in node.buses: |
Neil Armstrong | 1e8f0f3 | 2021-06-24 10:14:05 +0200 | [diff] [blame] | 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 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 336 | |
| 337 | def write_regs(node: edtlib.Node) -> None: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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): |
Kumar Gala | 4e2ad00 | 2020-04-14 14:27:20 -0500 | [diff] [blame] | 350 | idx_vals.append((f"{path_id}_REG_IDX_{i}_EXISTS", 1)) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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: |
Fin Maaß | fb8b30d | 2024-05-28 11:56:42 +0200 | [diff] [blame] | 356 | name_vals.append((f"{path_id}_REG_NAME_{reg.name}_EXISTS", 1)) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 373 | |
| 374 | def write_interrupts(node: edtlib.Node) -> None: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 375 | # interrupts property: we have some hard-coded logic for interrupt |
| 376 | # mapping here. |
| 377 | # |
Yong Cong Sin | df2c068 | 2023-10-02 12:42:24 +0800 | [diff] [blame] | 378 | # TODO: can we push map_arm_gic_irq_type() out of Python and into C with |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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 | |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 408 | |
Kumar Gala | 4e2ad00 | 2020-04-14 14:27:20 -0500 | [diff] [blame] | 409 | idx_vals.append((f"{path_id}_IRQ_IDX_{i}_EXISTS", 1)) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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: |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 414 | name_macro = ( |
| 415 | f"{path_id}_IRQ_NAME_{str2ident(irq.name)}_VAL_{name}") |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 416 | name_vals.append((name_macro, f"DT_{idx_macro}")) |
| 417 | name_vals.append((name_macro + "_EXISTS", 1)) |
| 418 | |
Bjarki Arge Andreasen | 08d6ff0 | 2023-11-26 11:34:06 +0100 | [diff] [blame] | 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 | |
Yong Cong Sin | 450a66f | 2023-12-22 16:20:10 +0800 | [diff] [blame] | 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 | |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 442 | def write_compatibles(node: edtlib.Node) -> None: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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 | |
Maureen Helm | 5b5aa6e | 2022-08-24 13:44:51 -0500 | [diff] [blame] | 447 | for i, compat in enumerate(node.compats): |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 448 | out_dt_define( |
| 449 | f"{node.z_path_id}_COMPAT_MATCHES_{str2ident(compat)}", 1) |
| 450 | |
Maureen Helm | 5b5aa6e | 2022-08-24 13:44:51 -0500 | [diff] [blame] | 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])) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 455 | |
Maureen Helm | e73c363 | 2022-09-07 17:17:18 -0500 | [diff] [blame] | 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 | |
James Roy | 4553a21 | 2024-11-14 21:10:00 +0800 | [diff] [blame] | 461 | def 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)) |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 472 | |
| 473 | def write_children(node: edtlib.Node) -> None: |
Martí Bolívar | 355cc01 | 2022-03-23 13:26:24 -0700 | [diff] [blame] | 474 | # Writes helper macros for dealing with node's children. |
Dominik Ermel | ba8b74d | 2020-04-17 06:32:28 +0000 | [diff] [blame] | 475 | |
Martí Bolívar | 7b2a728 | 2022-07-08 11:04:46 -0700 | [diff] [blame] | 476 | out_comment("Helper macros for child nodes of this node.") |
| 477 | |
Swift Tian | 5871ff0 | 2024-04-25 18:52:22 +0800 | [diff] [blame] | 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 | |
Kumar Gala | 4a5a90a | 2020-05-08 12:25:25 -0500 | [diff] [blame] | 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())) |
Dominik Ermel | ba8b74d | 2020-04-17 06:32:28 +0000 | [diff] [blame] | 490 | |
Gerard Marull-Paretas | fff9ecb | 2022-07-05 16:52:36 +0200 | [diff] [blame] | 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 | |
Arvin Farahmand | d0b9c03 | 2021-05-06 11:19:29 -0400 | [diff] [blame] | 495 | out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_VARGS(fn, ...)", |
Gerard Marull-Paretas | fff9ecb | 2022-07-05 16:52:36 +0200 | [diff] [blame] | 496 | " ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)" |
| 497 | for child in node.children.values())) |
Dominik Ermel | ba8b74d | 2020-04-17 06:32:28 +0000 | [diff] [blame] | 498 | |
Gerard Marull-Paretas | fff9ecb | 2022-07-05 16:52:36 +0200 | [diff] [blame] | 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())) |
Hou Zhiqiang | 0700a24 | 2021-04-26 16:22:38 +0800 | [diff] [blame] | 502 | |
Gerard Marull-Paretas | fff9ecb | 2022-07-05 16:52:36 +0200 | [diff] [blame] | 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 | |
Arvin Farahmand | d0b9c03 | 2021-05-06 11:19:29 -0400 | [diff] [blame] | 511 | out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_VARGS(fn, ...)", |
Gerard Marull-Paretas | fff9ecb | 2022-07-05 16:52:36 +0200 | [diff] [blame] | 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")) |
Hou Zhiqiang | 0700a24 | 2021-04-26 16:22:38 +0800 | [diff] [blame] | 518 | |
| 519 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 520 | def write_status(node: edtlib.Node) -> None: |
Martí Bolívar | 7e0eed9 | 2020-05-06 11:23:07 -0700 | [diff] [blame] | 521 | out_dt_define(f"{node.z_path_id}_STATUS_{str2ident(node.status)}", 1) |
| 522 | |
| 523 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 524 | def write_pinctrls(node: edtlib.Node) -> None: |
Martí Bolívar | 9df0493 | 2021-08-11 15:43:24 -0700 | [diff] [blame] | 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 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 554 | def write_fixed_partitions(node: edtlib.Node) -> None: |
Martí Bolívar | 7f69a03 | 2021-08-11 15:14:51 -0700 | [diff] [blame] | 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 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 566 | def write_gpio_hogs(node: edtlib.Node) -> None: |
Henrik Brix Andersen | 2881915 | 2023-01-13 11:32:27 +0100 | [diff] [blame] | 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 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 581 | |
| 582 | def write_vanilla_props(node: edtlib.Node) -> None: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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(): |
Martí Bolívar | 9c229a4 | 2021-04-14 15:26:42 -0700 | [diff] [blame] | 595 | prop_id = str2ident(prop_name) |
| 596 | macro = f"{node.z_path_id}_P_{prop_id}" |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 597 | val = prop2value(prop) |
| 598 | if val is not None: |
| 599 | # DT_N_<node-id>_P_<prop-id> |
| 600 | macro2val[macro] = val |
| 601 | |
Carlo Caione | f4db14f | 2021-05-17 17:24:27 +0200 | [diff] [blame] | 602 | if prop.spec.type == 'string': |
Joel Hirsbrunner | 8b02bc9 | 2024-10-04 21:29:37 +0200 | [diff] [blame] | 603 | macro2val.update(string_macros(macro, prop.val)) |
Martí Bolívar | 0c29e07 | 2023-05-06 14:42:15 -0700 | [diff] [blame] | 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. |
James Roy | ab959d5 | 2024-12-05 21:10:53 +0800 | [diff] [blame] | 608 | macro2val[f"{macro}_IDX_0"] = quote_str(prop.val) |
| 609 | macro2val[f"{macro}_IDX_0_EXISTS"] = 1 |
Carlo Caione | f4db14f | 2021-05-17 17:24:27 +0200 | [diff] [blame] | 610 | |
Joel Hirsbrunner | 8b02bc9 | 2024-10-04 21:29:37 +0200 | [diff] [blame] | 611 | if prop.enum_indices is not None: |
| 612 | macro2val.update(enum_macros(prop, macro)) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 613 | |
| 614 | if "phandle" in prop.type: |
| 615 | macro2val.update(phandle_macros(prop, macro)) |
| 616 | elif "array" in prop.type: |
Joel Hirsbrunner | 8b02bc9 | 2024-10-04 21:29:37 +0200 | [diff] [blame] | 617 | macro2val.update(array_macros(prop, macro)) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 618 | |
Martí Bolívar | 0c29e07 | 2023-05-06 14:42:15 -0700 | [diff] [blame] | 619 | plen = prop_len(prop) |
| 620 | if plen is not None: |
Martí Bolívar | 9c229a4 | 2021-04-14 15:26:42 -0700 | [diff] [blame] | 621 | # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 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))) |
Gerard Marull-Paretas | fdea3c9 | 2022-09-06 15:31:15 +0200 | [diff] [blame] | 625 | |
Martí Bolívar | 5204369 | 2023-05-05 15:30:38 -0700 | [diff] [blame] | 626 | # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_SEP |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 627 | macro2val[f"{macro}_FOREACH_PROP_ELEM_SEP(fn, sep)"] = ( |
Gerard Marull-Paretas | fdea3c9 | 2022-09-06 15:31:15 +0200 | [diff] [blame] | 628 | ' DT_DEBRACKET_INTERNAL sep \\\n\t'.join( |
| 629 | f'fn(DT_{node.z_path_id}, {prop_id}, {i})' |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 630 | for i in range(plen))) |
Martí Bolívar | 9c229a4 | 2021-04-14 15:26:42 -0700 | [diff] [blame] | 631 | |
Martí Bolívar | 5204369 | 2023-05-05 15:30:38 -0700 | [diff] [blame] | 632 | # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_VARGS |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 633 | macro2val[f"{macro}_FOREACH_PROP_ELEM_VARGS(fn, ...)"] = ( |
Gerard Marull-Paretas | fdea3c9 | 2022-09-06 15:31:15 +0200 | [diff] [blame] | 634 | ' \\\n\t'.join( |
| 635 | f'fn(DT_{node.z_path_id}, {prop_id}, {i}, __VA_ARGS__)' |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 636 | for i in range(plen))) |
Gerard Marull-Paretas | fdea3c9 | 2022-09-06 15:31:15 +0200 | [diff] [blame] | 637 | |
Martí Bolívar | 5204369 | 2023-05-05 15:30:38 -0700 | [diff] [blame] | 638 | # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_SEP_VARGS |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 639 | macro2val[f"{macro}_FOREACH_PROP_ELEM_SEP_VARGS(fn, sep, ...)"] = ( |
Gerard Marull-Paretas | fdea3c9 | 2022-09-06 15:31:15 +0200 | [diff] [blame] | 640 | ' DT_DEBRACKET_INTERNAL sep \\\n\t'.join( |
| 641 | f'fn(DT_{node.z_path_id}, {prop_id}, {i}, __VA_ARGS__)' |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 642 | for i in range(plen))) |
Arvin Farahmand | d0b9c03 | 2021-05-06 11:19:29 -0400 | [diff] [blame] | 643 | |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 644 | # DT_N_<node-id>_P_<prop-id>_LEN |
James Roy | ab959d5 | 2024-12-05 21:10:53 +0800 | [diff] [blame] | 645 | macro2val[f"{macro}_LEN"] = plen |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 646 | |
Martí Bolívar | 5204369 | 2023-05-05 15:30:38 -0700 | [diff] [blame] | 647 | # DT_N_<node-id>_P_<prop-id>_EXISTS |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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 | |
Joel Hirsbrunner | 8b02bc9 | 2024-10-04 21:29:37 +0200 | [diff] [blame] | 658 | def 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 | |
| 672 | def 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 |
James Roy | ab959d5 | 2024-12-05 21:10:53 +0800 | [diff] [blame] | 683 | ret[f"{macro}_IDX_{i}_EXISTS"] = 1 |
Joel Hirsbrunner | 8b02bc9 | 2024-10-04 21:29:37 +0200 | [diff] [blame] | 684 | # DT_N_<node-id>_P_<prop-id>_IDX_<i>_ENUM_VAL_<val>_EXISTS 1 |
James Roy | ab959d5 | 2024-12-05 21:10:53 +0800 | [diff] [blame] | 685 | ret[f"{macro}_IDX_{i}_ENUM_VAL_{subval}_EXISTS"] = 1 |
Joel Hirsbrunner | 8b02bc9 | 2024-10-04 21:29:37 +0200 | [diff] [blame] | 686 | |
| 687 | return ret |
| 688 | |
| 689 | |
| 690 | def 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 |
James Roy | ab959d5 | 2024-12-05 21:10:53 +0800 | [diff] [blame] | 697 | ret[f"{macro}_IDX_{i}_EXISTS"] = 1 |
Joel Hirsbrunner | 8b02bc9 | 2024-10-04 21:29:37 +0200 | [diff] [blame] | 698 | |
| 699 | # DT_N_<node-id>_P_<prop-id>_IDX_<i> |
| 700 | if isinstance(subval, str): |
James Roy | ab959d5 | 2024-12-05 21:10:53 +0800 | [diff] [blame] | 701 | ret[f"{macro}_IDX_{i}"] = quote_str(subval) |
Joel Hirsbrunner | 8b02bc9 | 2024-10-04 21:29:37 +0200 | [diff] [blame] | 702 | # DT_N_<node-id>_P_<prop-id>_IDX_<i>_STRING_... |
James Roy | ab959d5 | 2024-12-05 21:10:53 +0800 | [diff] [blame] | 703 | ret.update(string_macros(f"{macro}_IDX_{i}", subval)) |
Joel Hirsbrunner | 8b02bc9 | 2024-10-04 21:29:37 +0200 | [diff] [blame] | 704 | else: |
James Roy | ab959d5 | 2024-12-05 21:10:53 +0800 | [diff] [blame] | 705 | ret[f"{macro}_IDX_{i}"] = subval |
Joel Hirsbrunner | 8b02bc9 | 2024-10-04 21:29:37 +0200 | [diff] [blame] | 706 | |
| 707 | return ret |
| 708 | |
| 709 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 710 | def write_dep_info(node: edtlib.Node) -> None: |
Martí Bolívar | 305379e | 2020-06-08 14:59:19 -0700 | [diff] [blame] | 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) |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 717 | return ("\\\n\t" + " \\\n\t" |
| 718 | .join(f"{n.dep_ordinal}, /* {n.path} */" |
| 719 | for n in sorted_list)) |
Martí Bolívar | 305379e | 2020-06-08 14:59:19 -0700 | [diff] [blame] | 720 | else: |
| 721 | return "/* nothing */" |
| 722 | |
Luca Burelli | 16d71d0 | 2025-01-09 11:45:23 +0100 | [diff] [blame] | 723 | out_comment("Node's hash:") |
| 724 | out_dt_define(f"{node.z_path_id}_HASH", node.hash) |
| 725 | |
Martí Bolívar | 305379e | 2020-06-08 14:59:19 -0700 | [diff] [blame] | 726 | out_comment("Node's dependency ordinal:") |
| 727 | out_dt_define(f"{node.z_path_id}_ORD", node.dep_ordinal) |
Jordan Yates | b6e0341 | 2023-07-15 21:47:02 +1000 | [diff] [blame] | 728 | out_dt_define(f"{node.z_path_id}_ORD_STR_SORTABLE", f"{node.dep_ordinal:0>5}") |
Martí Bolívar | 305379e | 2020-06-08 14:59:19 -0700 | [diff] [blame] | 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 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 739 | def prop2value(prop: edtlib.Property) -> edtlib.PropertyValType: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 763 | def prop_len(prop: edtlib.Property) -> Optional[int]: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 764 | # Returns the property's length if and only if we should generate |
| 765 | # a _LEN macro for the property. Otherwise, returns None. |
| 766 | # |
Martí Bolívar | 0c29e07 | 2023-05-06 14:42:15 -0700 | [diff] [blame] | 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 | # |
Neil Armstrong | 1e8f0f3 | 2021-06-24 10:14:05 +0200 | [diff] [blame] | 772 | # This deliberately excludes ranges, dma-ranges, reg and interrupts. |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 773 | # While they have array type, their lengths as arrays are |
| 774 | # basically nonsense semantically due to #address-cells and |
Neil Armstrong | 1e8f0f3 | 2021-06-24 10:14:05 +0200 | [diff] [blame] | 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". |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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 | |
Martí Bolívar | 8aa83f6 | 2023-05-05 15:48:53 -0700 | [diff] [blame] | 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. |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 788 | return 1 |
| 789 | |
| 790 | if (prop.type in ["array", "uint8-array", "string-array", |
| 791 | "phandles", "phandle-array"] and |
Neil Armstrong | 1e8f0f3 | 2021-06-24 10:14:05 +0200 | [diff] [blame] | 792 | prop.name not in ["ranges", "dma-ranges", "reg", "interrupts"]): |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 793 | return len(prop.val) |
| 794 | |
| 795 | return None |
| 796 | |
| 797 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 798 | def phandle_macros(prop: edtlib.Property, macro: str) -> dict: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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. |
Kumar Gala | 7b9fbcd | 2021-08-12 14:57:49 -0500 | [diff] [blame] | 815 | ret[f"{macro}"] = f"DT_{prop.val.z_path_id}" |
| 816 | ret[f"{macro}_IDX_0"] = f"DT_{prop.val.z_path_id}" |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 817 | ret[f"{macro}_IDX_0_PH"] = f"DT_{prop.val.z_path_id}" |
Martí Bolívar | ffc0312 | 2020-11-12 20:27:20 -0800 | [diff] [blame] | 818 | ret[f"{macro}_IDX_0_EXISTS"] = 1 |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 819 | elif prop.type == "phandles": |
| 820 | for i, node in enumerate(prop.val): |
Kumar Gala | 7b9fbcd | 2021-08-12 14:57:49 -0500 | [diff] [blame] | 821 | ret[f"{macro}_IDX_{i}"] = f"DT_{node.z_path_id}" |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 822 | ret[f"{macro}_IDX_{i}_PH"] = f"DT_{node.z_path_id}" |
Martí Bolívar | ffc0312 | 2020-11-12 20:27:20 -0800 | [diff] [blame] | 823 | ret[f"{macro}_IDX_{i}_EXISTS"] = 1 |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 824 | elif prop.type == "phandle-array": |
| 825 | for i, entry in enumerate(prop.val): |
Martí Bolívar | 38ede5a | 2020-12-17 14:12:01 -0800 | [diff] [blame] | 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 | |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 833 | ret.update(controller_and_data_macros(entry, i, macro)) |
| 834 | |
| 835 | return ret |
| 836 | |
| 837 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 838 | def controller_and_data_macros(entry: edtlib.ControllerAndData, i: int, macro: str): |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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 | |
Martí Bolívar | ffc0312 | 2020-11-12 20:27:20 -0800 | [diff] [blame] | 848 | # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS |
| 849 | ret[f"{macro}_IDX_{i}_EXISTS"] = 1 |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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) |
Erwan Gouriou | 6c8617a | 2020-04-06 14:56:11 +0200 | [diff] [blame] | 861 | # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS |
| 862 | ret[f"{macro}_IDX_{i}_EXISTS"] = 1 |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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}" |
Erwan Gouriou | 6c8617a | 2020-04-06 14:56:11 +0200 | [diff] [blame] | 867 | # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_EXISTS |
| 868 | ret[f"{macro}_NAME_{name}_EXISTS"] = 1 |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 869 | # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_VAL_<VAL> |
| 870 | for cell, val in data.items(): |
| 871 | cell_ident = str2ident(cell) |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 872 | ret[f"{macro}_NAME_{name}_VAL_{cell_ident}"] = ( |
| 873 | f"DT_{macro}_IDX_{i}_VAL_{cell_ident}") |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 874 | ret[f"{macro}_NAME_{name}_VAL_{cell_ident}_EXISTS"] = 1 |
| 875 | |
| 876 | return ret |
| 877 | |
| 878 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 879 | def write_chosen(edt: edtlib.EDT): |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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 |
Kumar Gala | 299bfd0 | 2020-03-25 15:32:58 -0500 | [diff] [blame] | 887 | max_len = max(map(len, chosen), default=0) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 888 | for macro, value in chosen.items(): |
| 889 | out_define(macro, value, width=max_len) |
| 890 | |
| 891 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 892 | def write_global_macros(edt: edtlib.EDT): |
Martí Bolívar | 190197e | 2022-07-20 13:10:33 -0700 | [diff] [blame] | 893 | # Global or tree-wide information, such as number of instances |
| 894 | # with status "okay" for each compatible, is printed here. |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 895 | |
Martí Bolívar | f0d11f7 | 2022-07-20 13:15:56 -0700 | [diff] [blame] | 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")) |
Carlo Caione | 935268e | 2023-07-04 17:50:12 +0200 | [diff] [blame] | 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")) |
Martí Bolívar | f0d11f7 | 2022-07-20 13:15:56 -0700 | [diff] [blame] | 908 | |
Martí Bolívar | 7e0eed9 | 2020-05-06 11:23:07 -0700 | [diff] [blame] | 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: |
Daniel Leung | 418c915 | 2022-08-26 10:52:32 -0700 | [diff] [blame] | 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) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 918 | |
Martí Bolívar | 63d5529 | 2020-04-06 15:13:53 -0700 | [diff] [blame] | 919 | ident = str2ident(compat) |
Martí Bolívar | 7e0eed9 | 2020-05-06 11:23:07 -0700 | [diff] [blame] | 920 | n_okay_macros[f"DT_N_INST_{ident}_NUM_OKAY"] = len(okay_nodes) |
Martí Bolívar | e7d42ff | 2021-08-05 15:24:50 -0700 | [diff] [blame] | 921 | |
| 922 | # Helpers for non-INST for-each macros that take node |
| 923 | # identifiers as arguments. |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 924 | for_each_macros[f"DT_FOREACH_OKAY_{ident}(fn)"] = ( |
Martí Bolívar | e7d42ff | 2021-08-05 15:24:50 -0700 | [diff] [blame] | 925 | " ".join(f"fn(DT_{node.z_path_id})" |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 926 | for node in okay_nodes)) |
| 927 | for_each_macros[f"DT_FOREACH_OKAY_VARGS_{ident}(fn, ...)"] = ( |
Martí Bolívar | e7d42ff | 2021-08-05 15:24:50 -0700 | [diff] [blame] | 928 | " ".join(f"fn(DT_{node.z_path_id}, __VA_ARGS__)" |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 929 | for node in okay_nodes)) |
Martí Bolívar | e7d42ff | 2021-08-05 15:24:50 -0700 | [diff] [blame] | 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. |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 936 | for_each_macros[f"DT_FOREACH_OKAY_INST_{ident}(fn)"] = ( |
Martí Bolívar | 7e0eed9 | 2020-05-06 11:23:07 -0700 | [diff] [blame] | 937 | " ".join(f"fn({edt.compat2nodes[compat].index(node)})" |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 938 | for node in okay_nodes)) |
| 939 | for_each_macros[f"DT_FOREACH_OKAY_INST_VARGS_{ident}(fn, ...)"] = ( |
Arvin Farahmand | d0b9c03 | 2021-05-06 11:19:29 -0400 | [diff] [blame] | 940 | " ".join(f"fn({edt.compat2nodes[compat].index(node)}, __VA_ARGS__)" |
James Roy | 802eac7 | 2024-12-28 21:23:31 +0800 | [diff] [blame] | 941 | for node in okay_nodes)) |
Arvin Farahmand | d0b9c03 | 2021-05-06 11:19:29 -0400 | [diff] [blame] | 942 | |
Kumar Gala | bd97378 | 2020-05-06 20:54:29 -0500 | [diff] [blame] | 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 | |
Martí Bolívar | 7e0eed9 | 2020-05-06 11:23:07 -0700 | [diff] [blame] | 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(): |
Martí Bolívar | 63d5529 | 2020-04-06 15:13:53 -0700 | [diff] [blame] | 962 | out_define(macro, value) |
| 963 | for macro, value in for_each_macros.items(): |
| 964 | out_define(macro, value) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 965 | |
Martí Bolívar | 7e0eed9 | 2020-05-06 11:23:07 -0700 | [diff] [blame] | 966 | out_comment('Bus information for status "okay" nodes of each compatible\n') |
Martí Bolívar | a3fae2f | 2020-03-25 14:18:27 -0700 | [diff] [blame] | 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) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 971 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 972 | |
| 973 | def str2ident(s: str) -> str: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 974 | # Converts 's' to a form suitable for (part of) an identifier |
| 975 | |
| 976 | return re.sub('[-,.@/+]', '_', s.lower()) |
| 977 | |
| 978 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 979 | def list2init(l: Iterable[str]) -> str: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 980 | # Converts 'l', a Python list (or iterable), to a C array initializer |
| 981 | |
| 982 | return "{" + ", ".join(l) + "}" |
| 983 | |
| 984 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 985 | def out_dt_define( |
| 986 | macro: str, |
| 987 | val: str, |
| 988 | width: Optional[int] = None, |
| 989 | deprecation_msg: Optional[str] = None, |
| 990 | ) -> str: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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_". |
James Roy | ab959d5 | 2024-12-05 21:10:53 +0800 | [diff] [blame] | 1002 | ret = f"DT_{macro}" |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 1003 | out_define(ret, val, width=width, deprecation_msg=deprecation_msg) |
| 1004 | return ret |
| 1005 | |
| 1006 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 1007 | def out_define( |
| 1008 | macro: str, |
| 1009 | val: str, |
| 1010 | width: Optional[int] = None, |
| 1011 | deprecation_msg: Optional[str] = None, |
| 1012 | ) -> None: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 1027 | def out_comment(s: str, blank_before=True) -> None: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 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. |
James Roy | ab959d5 | 2024-12-05 21:10:53 +0800 | [diff] [blame] | 1048 | res.append(f" * {line}".rstrip()) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 1049 | res.append(" */") |
| 1050 | print("\n".join(res), file=header_file) |
| 1051 | else: |
| 1052 | # Format single-line comments like |
| 1053 | # |
| 1054 | # /* foo bar */ |
James Roy | ab959d5 | 2024-12-05 21:10:53 +0800 | [diff] [blame] | 1055 | print(f"/* {s} */", file=header_file) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 1056 | |
Joel Spadin | 6edefd8 | 2024-09-14 20:17:32 -0500 | [diff] [blame] | 1057 | ESCAPE_TABLE = str.maketrans( |
| 1058 | { |
| 1059 | "\n": "\\n", |
| 1060 | "\r": "\\r", |
| 1061 | "\\": "\\\\", |
| 1062 | '"': '\\"', |
| 1063 | } |
| 1064 | ) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 1065 | |
Joel Spadin | 6edefd8 | 2024-09-14 20:17:32 -0500 | [diff] [blame] | 1066 | |
| 1067 | def escape(s: str) -> str: |
| 1068 | # Backslash-escapes any double quotes, backslashes, and new lines in 's' |
| 1069 | |
| 1070 | return s.translate(ESCAPE_TABLE) |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 1071 | |
| 1072 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 1073 | def quote_str(s: str) -> str: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 1074 | # Puts quotes around 's' and escapes any double quotes and |
| 1075 | # backslashes within it |
| 1076 | |
| 1077 | return f'"{escape(s)}"' |
| 1078 | |
| 1079 | |
Joel Spadin | 6edefd8 | 2024-09-14 20:17:32 -0500 | [diff] [blame] | 1080 | def 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 | |
Florian Grandel | de846c7 | 2024-09-06 10:15:55 +0200 | [diff] [blame] | 1089 | def err(s: str) -> NoReturn: |
Martí Bolívar | dc85edd | 2020-02-28 15:26:52 -0800 | [diff] [blame] | 1090 | raise Exception(s) |
| 1091 | |
| 1092 | |
| 1093 | if __name__ == "__main__": |
| 1094 | main() |