diff --git a/cmake/dts.cmake b/cmake/dts.cmake index c4cdc891bc2..6dbd2acca05 100644 --- a/cmake/dts.cmake +++ b/cmake/dts.cmake @@ -4,16 +4,22 @@ file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/include/generated) # Zephyr code can configure itself based on a KConfig'uration with the # header file autoconf.h. There exists an analogous file devicetree_unfixed.h -# that allows configuration based on information encoded in DTS. +# that allows configuration based on information encoded in DTS, and a similar +# file with legacy contents called devicetree_unfixed_legacy.h. # -# Here we call on dtc, the gcc preprocessor, and -# scripts/dts/gen_legacy_defines.py to generate this header file at -# CMake configure-time. +# Here we call on dtc, the gcc preprocessor, +# scripts/dts/gen_defines.py, and scripts/dts/gen_legacy_defines.py to +# generate various DT-related files at CMake configure-time. # -# See ~/zephyr/doc/dts -set(DEVICETREE_UNFIXED_H ${PROJECT_BINARY_DIR}/include/generated/devicetree_unfixed.h) -set(DEVICETREE_CONF ${PROJECT_BINARY_DIR}/include/generated/devicetree.conf) -set(DTS_POST_CPP ${PROJECT_BINARY_DIR}/${BOARD}.dts.pre.tmp) +# The devicetree.conf file is still needed by some deprecated +# functions in kconfigfunctions.py. +# +# See the Devicetree user guide in the Zephyr documentation for details. +set(ZEPHYR_DTS ${PROJECT_BINARY_DIR}/zephyr.dts) +set(DEVICETREE_UNFIXED_H ${PROJECT_BINARY_DIR}/include/generated/devicetree_unfixed.h) +set(DEVICETREE_UNFIXED_LEGACY_H ${PROJECT_BINARY_DIR}/include/generated/devicetree_legacy_unfixed.h) +set(DEVICETREE_CONF ${PROJECT_BINARY_DIR}/include/generated/devicetree.conf) +set(DTS_POST_CPP ${PROJECT_BINARY_DIR}/${BOARD}.dts.pre.tmp) set_ifndef(DTS_SOURCE ${BOARD_DIR}/${BOARD}.dts) @@ -67,9 +73,9 @@ if(SUPPORTS_DTS) -include ${dts_file}) if(i EQUAL 0) - message(STATUS "Loading ${dts_file} as base") + message(STATUS "Found BOARD.dts: ${dts_file}") else() - message(STATUS "Overlaying ${dts_file}") + message(STATUS "Found devicetree overlay: ${dts_file}") endif() math(EXPR i "${i}+1") @@ -190,25 +196,49 @@ if(SUPPORTS_DTS) endif(DTC) # - # Run gen_legacy_defines.py to create a .conf file and a header file + # Run gen_defines.py to create a header file and zephyr.dts. # - set(CMD_NEW_EXTRACT ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/dts/gen_legacy_defines.py + set(CMD_EXTRACT ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/dts/gen_defines.py --dts ${BOARD}.dts.pre.tmp --dtc-flags '${EXTRA_DTC_FLAGS}' --bindings-dirs ${DTS_ROOT_BINDINGS} - --conf-out ${DEVICETREE_CONF} --header-out ${DEVICETREE_UNFIXED_H} - --dts-out ${PROJECT_BINARY_DIR}/zephyr.dts # As a debugging aid + --dts-out ${ZEPHYR_DTS} # As a debugging aid + ) + + # + # Run gen_legacy_defines.py to create a header file with legacy contents + # and a .conf file. + # + + set(CMD_LEGACY_EXTRACT ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/dts/gen_legacy_defines.py + --dts ${BOARD}.dts.pre.tmp + --dtc-flags '${EXTRA_DTC_FLAGS}' + --bindings-dirs ${DTS_ROOT_BINDINGS} + --header-out ${DEVICETREE_UNFIXED_LEGACY_H} + --conf-out ${DEVICETREE_CONF} ) execute_process( - COMMAND ${CMD_NEW_EXTRACT} + COMMAND ${CMD_EXTRACT} WORKING_DIRECTORY ${PROJECT_BINARY_DIR} RESULT_VARIABLE ret ) if(NOT "${ret}" STREQUAL "0") - message(FATAL_ERROR "new extractor failed with return code: ${ret}") + message(FATAL_ERROR "gen_defines.py failed with return code: ${ret}") + else() + message(STATUS "Generated zephyr.dts: ${ZEPHYR_DTS}") + message(STATUS "Generated devicetree_unfixed.h: ${DEVICETREE_UNFIXED_H}") + endif() + + execute_process( + COMMAND ${CMD_LEGACY_EXTRACT} + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + RESULT_VARIABLE ret + ) + if(NOT "${ret}" STREQUAL "0") + message(FATAL_ERROR "gen_legacy_defines.py failed with return code: ${ret}") endif() # A file that used to be generated by 'dtc'. zephyr.dts is the new @@ -218,4 +248,5 @@ if(SUPPORTS_DTS) else() file(WRITE ${DEVICETREE_UNFIXED_H} "/* WARNING. THIS FILE IS AUTO-GENERATED. DO NOT MODIFY! */") + file(WRITE ${DEVICETREE_UNFIXED_LEGACY_H} "/* WARNING. THIS FILE IS AUTO-GENERATED. DO NOT MODIFY! */") endif(SUPPORTS_DTS) diff --git a/include/devicetree.h b/include/devicetree.h index 4c7ee3450ab..040d48114d3 100644 --- a/include/devicetree.h +++ b/include/devicetree.h @@ -9,6 +9,7 @@ #define DEVICETREE_H #include +#include #include #endif /* DEVICETREE_H */ diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py new file mode 100755 index 00000000000..ac46b18351f --- /dev/null +++ b/scripts/dts/gen_defines.py @@ -0,0 +1,628 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2019 - 2020 Nordic Semiconductor ASA +# Copyright (c) 2019 Linaro Limited +# SPDX-License-Identifier: BSD-3-Clause + +# This script uses edtlib to generate a header file from a devicetree +# (.dts) file. Information from binding files in YAML format is used +# as well. +# +# Bindings are files that describe devicetree nodes. Devicetree nodes are +# usually mapped to bindings via their 'compatible = "..."' property. +# +# See Zephyr's Devicetree user guide for details. +# +# Note: Do not access private (_-prefixed) identifiers from edtlib here (and +# also note that edtlib is not meant to expose the dtlib API directly). +# Instead, think of what API you need, and add it as a public documented API in +# edtlib. This will keep this script simple. + +import argparse +import os +import pathlib +import re +import sys + +import edtlib + +def main(): + global header_file + + args = parse_args() + + try: + edt = edtlib.EDT(args.dts, args.bindings_dirs, + # Suppress this warning if it's suppressed in dtc + warn_reg_unit_address_mismatch= + "-Wno-simple_bus_reg" not in args.dtc_flags) + except edtlib.EDTError as e: + sys.exit(f"devicetree error: {e}") + + # Save merged DTS source, as a debugging aid + with open(args.dts_out, "w", encoding="utf-8") as f: + print(edt.dts_source, file=f) + + with open(args.header_out, "w", encoding="utf-8") as header_file: + write_top_comment(edt) + + for node in sorted(edt.nodes, key=lambda node: node.dep_ordinal): + node.z_path_id = "N_" + "_".join( + f"S_{str2ident(name)}" for name in node.path[1:].split("/")) + write_node_comment(node) + + if not node.enabled: + out_comment("No macros: node is disabled") + continue + if not node.matching_compat: + out_comment("No macros: node has no matching binding") + continue + + write_idents_and_existence(node) + write_bus(node) + write_special_props(node) + write_vanilla_props(node) + + write_chosen(edt) + write_inst_num(edt) + + +def parse_args(): + # Returns parsed command-line arguments + + parser = argparse.ArgumentParser() + parser.add_argument("--dts", required=True, help="DTS file") + parser.add_argument("--dtc-flags", + help="'dtc' devicetree compiler flags, some of which " + "might be respected here") + parser.add_argument("--bindings-dirs", nargs='+', required=True, + help="directory with bindings in YAML format, " + "we allow multiple") + parser.add_argument("--header-out", required=True, + help="path to write header to") + parser.add_argument("--dts-out", required=True, + help="path to write merged DTS source code to (e.g. " + "as a debugging aid)") + + return parser.parse_args() + + +def write_top_comment(edt): + # Writes an overview comment with misc. info at the top of the header and + # configuration file + + s = f"""\ +Generated by gen_defines.py + +DTS input file: + {edt.dts_path} + +Directories with bindings: + {", ".join(map(relativize, edt.bindings_dirs))} + +Nodes in dependency order (ordinal and path): +""" + + for scc in edt.scc_order(): + if len(scc) > 1: + err("cycle in devicetree involving " + + ", ".join(node.path for node in scc)) + s += f" {scc[0].dep_ordinal:<3} {scc[0].path}\n" + + s += """ +Definitions derived from these nodes in dependency order are next, +followed by /chosen nodes. +""" + + out_comment(s, blank_before=False) + + +def write_node_comment(node): + # Writes a comment describing 'node' to the header and configuration file + + s = f"""\ +Devicetree node: + {node.path} +""" + + if node.matching_compat: + s += f""" +Binding (compatible = {node.matching_compat}): + {relativize(node.binding_path)} +""" + s += f""" +Node's path identifier in this file: {node.z_path_id} +""" + + s += f"\nDependency Ordinal: {node.dep_ordinal}\n" + + if node.depends_on: + s += "\nRequires:\n" + for dep in node.depends_on: + s += f" {dep.dep_ordinal:<3} {dep.path}\n" + + if node.required_by: + s += "\nSupports:\n" + for req in node.required_by: + s += f" {req.dep_ordinal:<3} {req.path}\n" + + if node.description: + # Indent description by two spaces + s += "\nDescription:\n" + \ + "\n".join(" " + line for line in + node.description.splitlines()) + \ + "\n" + + out_comment(s) + + +def relativize(path): + # If 'path' is within $ZEPHYR_BASE, returns it relative to $ZEPHYR_BASE, + # with a "$ZEPHYR_BASE/..." hint at the start of the string. Otherwise, + # returns 'path' unchanged. + + zbase = os.getenv("ZEPHYR_BASE") + if zbase is None: + return path + + try: + return str("$ZEPHYR_BASE" / pathlib.Path(path).relative_to(zbase)) + except ValueError: + # Not within ZEPHYR_BASE + return path + + +def write_idents_and_existence(node): + # Writes macros related to the node's aliases, labels, etc., + # as well as existence flags. + + # Aliases + idents = [f"N_ALIAS_{str2ident(alias)}" for alias in node.aliases] + # Instances + for compat in node.compats: + if not node.enabled: + continue + instance_no = node.edt.compat2enabled[compat].index(node) + idents.append(f"N_INST_{instance_no}_{str2ident(compat)}") + # Node labels + idents.extend(f"N_NODELABEL_{str2ident(label)}" for label in node.labels) + + out_comment("Existence and alternate IDs:") + out_dt_define(node.z_path_id + "_EXISTS", 1) + + # Only determine maxlen if we have any idents + if idents: + maxlen = max(len("DT_" + ident) for ident in idents) + for ident in idents: + out_dt_define(ident, "DT_" + node.z_path_id, width=maxlen) + + +def write_bus(node): + # Macros about the node's bus controller, if there is one + + bus = node.bus_node + if not bus: + return + + if not bus.label: + err(f"missing 'label' property on bus node {bus!r}") + + out_comment(f"Bus info (controller: '{bus.path}', type: '{node.on_bus}')") + out_dt_define(f"{node.z_path_id}_BUS_{str2ident(node.on_bus)}", 1) + out_dt_define(f"{node.z_path_id}_BUS", f"DT_{bus.z_path_id}") + + +def write_special_props(node): + # Writes required macros for special case properties, when the + # data cannot otherwise be obtained from write_vanilla_props() + # results + + out_comment("Special property macros:") + + # Macros that are special to the devicetree specification + write_regs(node) + write_interrupts(node) + write_compatibles(node) + + +def write_regs(node): + # reg property: edtlib knows the right #address-cells and + # #size-cells, and can therefore pack the register base addresses + # and sizes correctly + + idx_vals = [] + name_vals = [] + path_id = node.z_path_id + + if node.regs is not None: + idx_vals.append((f"{path_id}_REG_NUM", len(node.regs))) + + for i, reg in enumerate(node.regs): + if reg.addr is not None: + idx_macro = f"{path_id}_REG_IDX_{i}_VAL_ADDRESS" + idx_vals.append((idx_macro, + f"{reg.addr} /* {hex(reg.addr)} */")) + if reg.name: + name_macro = f"{path_id}_REG_NAME_{reg.name}_VAL_ADDRESS" + name_vals.append((name_macro, f"DT_{idx_macro}")) + + if reg.size is not None: + idx_macro = f"{path_id}_REG_IDX_{i}_VAL_SIZE" + idx_vals.append((idx_macro, + f"{reg.size} /* {hex(reg.size)} */")) + if reg.name: + name_macro = f"{path_id}_REG_NAME_{reg.name}_VAL_SIZE" + name_vals.append((name_macro, f"DT_{idx_macro}")) + + for macro, val in idx_vals: + out_dt_define(macro, val) + for macro, val in name_vals: + out_dt_define(macro, val) + +def write_interrupts(node): + # interrupts property: we have some hard-coded logic for interrupt + # mapping here. + # + # TODO: can we push map_arm_gic_irq_type() and + # encode_zephyr_multi_level_irq() out of Python and into C with + # macro magic in devicetree.h? + + def map_arm_gic_irq_type(irq, irq_num): + # Maps ARM GIC IRQ (type)+(index) combo to linear IRQ number + if "type" not in irq.data: + err(f"Expected binding for {irq.controller!r} to have 'type' in " + "interrupt-cells") + irq_type = irq.data["type"] + + if irq_type == 0: # GIC_SPI + return irq_num + 32 + if irq_type == 1: # GIC_PPI + return irq_num + 16 + err(f"Invalid interrupt type specified for {irq!r}") + + def encode_zephyr_multi_level_irq(irq, irq_num): + # See doc/reference/kernel/other/interrupts.rst for details + # on how this encoding works + + irq_ctrl = irq.controller + # Look for interrupt controller parent until we have none + while irq_ctrl.interrupts: + irq_num = (irq_num + 1) << 8 + if "irq" not in irq_ctrl.interrupts[0].data: + err(f"Expected binding for {irq_ctrl!r} to have 'irq' in " + "interrupt-cells") + irq_num |= irq_ctrl.interrupts[0].data["irq"] + irq_ctrl = irq_ctrl.interrupts[0].controller + return irq_num + + idx_vals = [] + name_vals = [] + path_id = node.z_path_id + + if node.interrupts is not None: + idx_vals.append((f"{path_id}_IRQ_NUM", len(node.interrupts))) + + for i, irq in enumerate(node.interrupts): + for cell_name, cell_value in irq.data.items(): + name = str2ident(cell_name) + + if cell_name == "irq": + if "arm,gic" in irq.controller.compats: + cell_value = map_arm_gic_irq_type(irq, cell_value) + cell_value = encode_zephyr_multi_level_irq(irq, cell_value) + + idx_macro = f"{path_id}_IRQ_IDX_{i}_VAL_{name}" + idx_vals.append((idx_macro, cell_value)) + idx_vals.append((idx_macro + "_EXISTS", 1)) + if irq.name: + name_macro = \ + f"{path_id}_IRQ_NAME_{str2ident(irq.name)}_VAL_{name}" + name_vals.append((name_macro, f"DT_{idx_macro}")) + name_vals.append((name_macro + "_EXISTS", 1)) + + for macro, val in idx_vals: + out_dt_define(macro, val) + for macro, val in name_vals: + out_dt_define(macro, val) + + +def write_compatibles(node): + # Writes a macro for each of the node's compatibles. We don't care + # about whether edtlib / Zephyr's binding language recognizes + # them. The compatibles the node provides are what is important. + + for compat in node.compats: + out_dt_define( + f"{node.z_path_id}_COMPAT_MATCHES_{str2ident(compat)}", 1) + + +def write_vanilla_props(node): + # Writes macros for any and all properties defined in the + # "properties" section of the binding for the node. + # + # This does generate macros for special properties as well, like + # regs, etc. Just let that be rather than bothering to add + # never-ending amounts of special case code here to skip special + # properties. This function's macros can't conflict with + # write_special_props() macros, because they're in different + # namespaces. Special cases aren't special enough to break the rules. + + macro2val = {} + for prop_name, prop in node.props.items(): + macro = f"{node.z_path_id}_P_{str2ident(prop_name)}" + val = prop2value(prop) + if val is not None: + # DT_N__P_ + macro2val[macro] = val + + if prop.enum_index is not None: + # DT_N__P__ENUM_IDX + macro2val[macro + "_ENUM_IDX"] = prop.enum_index + + if "phandle" in prop.type: + macro2val.update(phandle_macros(prop, macro)) + elif "array" in prop.type: + # DT_N__P__IDX_ + for i, subval in enumerate(prop.val): + if isinstance(subval, str): + macro2val[macro + f"_IDX_{i}"] = quote_str(subval) + else: + macro2val[macro + f"_IDX_{i}"] = subval + + plen = prop_len(prop) + if plen is not None: + # DT_N__P__LEN + macro2val[macro + "_LEN"] = plen + + macro2val[f"{macro}_EXISTS"] = 1 + + if macro2val: + out_comment("Generic property macros:") + for macro, val in macro2val.items(): + out_dt_define(macro, val) + else: + out_comment("(No generic property macros)") + + +def prop2value(prop): + # Gets the macro value for property 'prop', if there is + # a single well-defined C rvalue that it can be represented as. + # Returns None if there isn't one. + + if prop.type == "string": + return quote_str(prop.val) + + if prop.type == "int": + return prop.val + + if prop.type == "boolean": + return 1 if prop.val else 0 + + if prop.type in ["array", "uint8-array"]: + return list2init(f"{val} /* {hex(val)} */" for val in prop.val) + + if prop.type == "string-array": + return list2init(quote_str(val) for val in prop.val) + + # phandle, phandles, phandle-array, path, compound: nothing + return None + + +def prop_len(prop): + # Returns the property's length if and only if we should generate + # a _LEN macro for the property. Otherwise, returns None. + # + # This deliberately excludes reg and interrupts. + # While they have array type, their lengths as arrays are + # basically nonsense semantically due to #address-cells and + # #size-cells for "reg" and #interrupt-cells for "interrupts". + # + # We have special purpose macros for the number of register blocks + # / interrupt specifiers. Excluding them from this list means + # DT_PROP_LEN(node_id, ...) fails fast at the devicetree.h layer + # with a build error. This forces users to switch to the right + # macros. + + if prop.type == "phandle": + return 1 + + if (prop.type in ["array", "uint8-array", "string-array", + "phandles", "phandle-array"] and + prop.name not in ["reg", "interrupts"]): + return len(prop.val) + + return None + + +def phandle_macros(prop, macro): + # Returns a dict of macros for phandle or phandles property 'prop'. + # + # The 'macro' argument is the N__P_ bit. + # + # These are currently special because we can't serialize their + # values without using label properties, which we're trying to get + # away from needing in Zephyr. (Label properties are great for + # humans, but have drawbacks for code size and boot time.) + # + # The names look a bit weird to make it easier for devicetree.h + # to use the same macros for phandle, phandles, and phandle-array. + + ret = {} + + if prop.type == "phandle": + # A phandle is treated as a phandles with fixed length 1. + ret[f"{macro}_IDX_0_PH"] = f"DT_{prop.val.z_path_id}" + elif prop.type == "phandles": + for i, node in enumerate(prop.val): + ret[f"{macro}_IDX_{i}_PH"] = f"DT_{node.z_path_id}" + elif prop.type == "phandle-array": + for i, entry in enumerate(prop.val): + ret.update(controller_and_data_macros(entry, i, macro)) + + return ret + + +def controller_and_data_macros(entry, i, macro): + # Helper procedure used by phandle_macros(). + # + # Its purpose is to write the "controller" (i.e. label property of + # the phandle's node) and associated data macros for a + # ControllerAndData. + + ret = {} + data = entry.data + + # DT_N__P__IDX__PH + ret[f"{macro}_IDX_{i}_PH"] = f"DT_{entry.controller.z_path_id}" + # DT_N__P__IDX__VAL_ + for cell, val in data.items(): + ret[f"{macro}_IDX_{i}_VAL_{str2ident(cell)}"] = val + ret[f"{macro}_IDX_{i}_VAL_{str2ident(cell)}_EXISTS"] = 1 + + if not entry.name: + return ret + + name = str2ident(entry.name) + # DT_N__P__IDX__NAME + ret[f"{macro}_IDX_{i}_NAME"] = quote_str(entry.name) + # DT_N__P__NAME__PH + ret[f"{macro}_NAME_{name}_PH"] = f"DT_{entry.controller.z_path_id}" + # DT_N__P__NAME__VAL_ + for cell, val in data.items(): + cell_ident = str2ident(cell) + ret[f"{macro}_NAME_{name}_VAL_{cell_ident}"] = \ + f"DT_{macro}_IDX_{i}_VAL_{cell_ident}" + ret[f"{macro}_NAME_{name}_VAL_{cell_ident}_EXISTS"] = 1 + + return ret + + +def write_chosen(edt): + # Tree-wide information such as chosen nodes is printed here. + + out_comment("Chosen nodes\n") + chosen = {} + for name, node in edt.chosen_nodes.items(): + chosen[f"DT_CHOSEN_{str2ident(name)}"] = f"DT_{node.z_path_id}" + chosen[f"DT_CHOSEN_{str2ident(name)}_EXISTS"] = 1 + max_len = max(map(len, chosen)) + for macro, value in chosen.items(): + out_define(macro, value, width=max_len) + + +def write_inst_num(edt): + # Tree-wide information such as number of instances is printed here. + + out_comment("Number of instances\n") + compat_list = [] + + # Walk the nodes to build which compats we need to generate for + for node in sorted(edt.nodes, key=lambda node: node.dep_ordinal): + if not node.enabled: + continue + if not node.matching_compat: + continue + for compat in node.compats: + if compat not in compat_list: + compat_list.append(compat) + + for compat in compat_list: + num_inst = len(edt.compat2enabled[compat]) + out_define(f"DT_N_INST_{str2ident(compat)}_NUM", num_inst) + + +def str2ident(s): + # Converts 's' to a form suitable for (part of) an identifier + + return re.sub('[-,.@/+]', '_', s.lower()) + + +def list2init(l): + # Converts 'l', a Python list (or iterable), to a C array initializer + + return "{" + ", ".join(l) + "}" + + +def out_dt_define(macro, val, width=None, deprecation_msg=None): + # Writes "#define DT_ " to the header file + # + # The macro will be left-justified to 'width' characters if that + # is specified, and the value will follow immediately after in + # that case. Otherwise, this function decides how to add + # whitespace between 'macro' and 'val'. + # + # If a 'deprecation_msg' string is passed, the generated identifiers will + # generate a warning if used, via __WARN()). + # + # Returns the full generated macro for 'macro', with leading "DT_". + ret = "DT_" + macro + out_define(ret, val, width=width, deprecation_msg=deprecation_msg) + return ret + + +def out_define(macro, val, width=None, deprecation_msg=None): + # Helper for out_dt_define(). Outputs "#define ", + # adds a deprecation message if given, and allocates whitespace + # unless told not to. + + warn = fr' __WARN("{deprecation_msg}")' if deprecation_msg else "" + + if width: + s = f"#define {macro.ljust(width)}{warn} {val}" + else: + s = f"#define {macro}{warn} {val}" + + print(s, file=header_file) + + +def out_comment(s, blank_before=True): + # Writes 's' as a comment to the header and configuration file. 's' is + # allowed to have multiple lines. blank_before=True adds a blank line + # before the comment. + + if blank_before: + print(file=header_file) + + if "\n" in s: + # Format multi-line comments like + # + # /* + # * first line + # * second line + # * + # * empty line before this line + # */ + res = ["/*"] + for line in s.splitlines(): + # Avoid an extra space after '*' for empty lines. They turn red in + # Vim if space error checking is on, which is annoying. + res.append(" *" if not line.strip() else " * " + line) + res.append(" */") + print("\n".join(res), file=header_file) + else: + # Format single-line comments like + # + # /* foo bar */ + print("/* " + s + " */", file=header_file) + + +def escape(s): + # Backslash-escapes any double quotes and backslashes in 's' + + # \ must be escaped before " to avoid double escaping + return s.replace("\\", "\\\\").replace('"', '\\"') + + +def quote_str(s): + # Puts quotes around 's' and escapes any double quotes and + # backslashes within it + + return f'"{escape(s)}"' + + +def err(s): + raise Exception(s) + + +if __name__ == "__main__": + main() diff --git a/scripts/dts/gen_legacy_defines.py b/scripts/dts/gen_legacy_defines.py index 7ed55ab3bf1..8cef9062ac2 100755 --- a/scripts/dts/gen_legacy_defines.py +++ b/scripts/dts/gen_legacy_defines.py @@ -4,19 +4,12 @@ # Copyright (c) 2019 Linaro Limited # SPDX-License-Identifier: BSD-3-Clause -# This script uses edtlib to generate a header file and a .conf file (both -# containing the same values) from a devicetree (.dts) file. Information from -# binding files in YAML format is used as well. +# This script is similar to gen_defines.py, but is for the legacy +# macro syntax used in e.g. Zephyr 2.2. # -# Bindings are files that describe devicetree nodes. Devicetree nodes are -# usually mapped to bindings via their 'compatible = "..."' property. -# -# See the docstring/comments at the top of edtlib.py for more information. -# -# Note: Do not access private (_-prefixed) identifiers from edtlib here (and -# also note that edtlib is not meant to expose the dtlib API directly). -# Instead, think of what API you need, and add it as a public documented API in -# edtlib. This will keep this script simple. +# It should be considered frozen code. New macro-related functionality +# should be done by modifying the macro namespaces managed by +# gen_defines.py. import argparse import os @@ -25,7 +18,6 @@ import sys import edtlib - def main(): global conf_file global header_file @@ -41,10 +33,6 @@ def main(): except edtlib.EDTError as e: sys.exit(f"devicetree error: {e}") - # Save merged DTS source, as a debugging aid - with open(args.dts_out, "w", encoding="utf-8") as f: - print(edt.dts_source, file=f) - conf_file = open(args.conf_out, "w", encoding="utf-8") header_file = open(args.header_out, "w", encoding="utf-8") flash_area_num = 0 @@ -84,8 +72,6 @@ def main(): conf_file.close() header_file.close() - print(f"Devicetree header saved to '{args.header_out}'") - def parse_args(): # Returns parsed command-line arguments @@ -102,9 +88,6 @@ def parse_args(): help="path to write header to") parser.add_argument("--conf-out", required=True, help="path to write configuration file to") - parser.add_argument("--dts-out", required=True, - help="path to write merged DTS source code to (e.g. " - "as a debugging aid)") return parser.parse_args() @@ -114,7 +97,7 @@ def write_top_comment(edt): # configuration file s = f"""\ -Generated by gen_defines.py +Generated by gen_legacy_defines.py DTS input file: {edt.dts_path}