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