import argparse
import os
import pathlib
import pickle
import re
import sys
from collections import defaultdict
from typing import Iterable, NoReturn, Optional
import edtlib_logger
from devicetree import edtlib
def main():
global header_file
global flash_area_num
global board_id
global select_macros_dict
global board_id_var
global board_id_num
args = parse_args()
board_id = args.board_id + "_" if args.board_id else ""
print("board_id:" + board_id + "\n")
if board_id != "":
board_id_num = re.findall(r"\d+", board_id)[0]
board_id_var = "id"
select_macros_dict = {}
print("args.edt_pickle: ", args.edt_pickle)
print("args.header_out: ", args.header_out)
print("args.board_id: ", args.board_id)
edtlib_logger.setup_edtlib_logging()
with open(args.edt_pickle, "rb") as f:
edt = pickle.load(f)
flash_area_num = 0
with open(args.header_out, "w", encoding="utf-8") as header_file:
write_top_comment(edt)
write_utils()
sorted_nodes = sorted(edt.nodes, key=lambda node: node.dep_ordinal)
for node in sorted_nodes:
node.z_path_id = node_z_path_id(node)
regions = dict()
for node in sorted_nodes:
if "nuttx,memory-region" in node.props:
region = node.props["nuttx,memory-region"].val
if region in regions:
sys.exit(
f"ERROR: Duplicate 'nuttx,memory-region' ({region}) properties "
f"between {regions[region].path} and {node.path}"
)
regions[region] = node
for node in sorted_nodes:
write_node_comment(node)
out_comment("Node's full path:")
out_dt_define(f"{node.z_path_id}_PATH", f'"{escape(node.path)}"')
dynamic_macro_make("DT_NODE_PATH_DYNAMIC(id, node_id)", select_macros_dict)
out_comment("Node's name with unit-address:")
out_dt_define(f"{node.z_path_id}_FULL_NAME", f'"{escape(node.name)}"')
out_dt_define(
f"{node.z_path_id}_FULL_NAME_UNQUOTED", f"{escape(node.name)}"
)
out_dt_define(
f"{node.z_path_id}_FULL_NAME_TOKEN",
f"{edtlib.str_as_token(escape(node.name))}",
)
out_dt_define(
f"{node.z_path_id}_FULL_NAME_UPPER_TOKEN",
f"{edtlib.str_as_token(escape(node.name)).upper()}",
)
if node.parent is not None:
out_comment(f"Node parent ({node.parent.path}) identifier:")
out_dt_define(f"{node.z_path_id}_PARENT", f"DT_{node.parent.z_path_id}")
out_comment("Node's index in its parent's list of children:")
out_dt_define(
f"{node.z_path_id}_CHILD_IDX", node.parent.child_index(node)
)
out_comment("Helpers for dealing with node labels:")
out_dt_define(f"{node.z_path_id}_NODELABEL_NUM", len(node.labels))
out_dt_define(
f"{node.z_path_id}_FOREACH_NODELABEL(fn)",
" ".join(f"fn({nodelabel})" for nodelabel in node.labels),
)
out_dt_define(
f"{node.z_path_id}_FOREACH_NODELABEL_VARGS(fn, ...)",
" ".join(f"fn({nodelabel}, __VA_ARGS__)" for nodelabel in node.labels),
)
write_parent(node)
write_children(node)
write_dep_info(node)
write_idents_and_existence(node)
write_bus(node)
write_special_props(node)
write_vanilla_props(node)
write_chosen(edt)
write_global_macros(edt)
if board_id != "":
for key, (macro, val) in select_macros_dict.items():
out_dt_define(macro, val)
select_macros_dict[key] = f"{board_id}DT_{macro}"
with open(f"select_macros_{board_id_num}.pickle", "wb") as f:
pickle.dump(select_macros_dict, f)
def node_z_path_id(node: edtlib.Node) -> str:
components = ["N"]
if node.parent is not None:
components.extend(
f"S_{str2ident(component)}" for component in node.path.split("/")[1:]
)
return "_".join(components)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(allow_abbrev=False)
parser.add_argument("--header-out", required=True, help="path to write header to")
parser.add_argument(
"--edt-pickle", help="path to read pickled edtlib.EDT object from"
)
parser.add_argument(
"--board-id", default="", help="board identifier to use in generated header"
)
return parser.parse_args()
def write_top_comment(edt: edtlib.EDT) -> None:
s = f"""\
Generated by gen_defines.py
DTS input file:
{edt.dts_path}
Directories with bindings:
{", ".join(map(relativize, edt.bindings_dirs))}
Node dependency ordering (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_utils() -> None:
out_comment("Used to remove brackets from around a single argument")
out_define("DT_DEBRACKET_INTERNAL(...)", "__VA_ARGS__")
def write_node_comment(node: edtlib.Node) -> None:
s = f"""\
Devicetree node: {node.path}
Node identifier: DT_{node.z_path_id}
"""
if node.matching_compat:
if node.binding_path:
s += f"""
Binding (compatible = {node.matching_compat}):
{relativize(node.binding_path)}
"""
else:
s += f"""
Binding (compatible = {node.matching_compat}):
No yaml (bindings inferred from properties)
"""
if node.description:
s += (
"\n(Descriptions have moved to the Devicetree Bindings Index\n"
"in the documentation.)\n"
)
out_comment(s)
def relativize(path) -> Optional[str]:
zbase = os.getenv("NUTTX_BASE")
if zbase is None:
return path
try:
return str("$NUTTX_BASE" / pathlib.Path(path).relative_to(zbase))
except ValueError:
return path
def write_idents_and_existence(node: edtlib.Node) -> None:
idents = [f"N_ALIAS_{str2ident(alias)}" for alias in node.aliases]
for compat in node.compats:
instance_no = node.edt.compat2nodes[compat].index(node)
idents.append(f"N_INST_{instance_no}_{str2ident(compat)}")
idents.extend(f"N_NODELABEL_{str2ident(label)}" for label in node.labels)
out_comment("Existence and alternate IDs:")
out_dt_define(f"{node.z_path_id}_EXISTS", 1)
if idents:
maxlen = max(len(f"{board_id}DT_{ident}") for ident in idents)
for ident in idents:
out_dt_define(ident, f"{board_id}DT_{node.z_path_id}", width=maxlen)
def write_bus(node: edtlib.Node) -> None:
bus = node.bus_node
if not bus:
return
out_comment(f"Bus info (controller: '{bus.path}', type: '{node.on_buses}')")
for one_bus in node.on_buses:
out_dt_define(f"{node.z_path_id}_BUS_{str2ident(one_bus)}", 1)
out_dt_define(f"{node.z_path_id}_BUS", f"DT_{bus.z_path_id}")
def write_special_props(node: edtlib.Node) -> None:
out_comment("Macros for properties that are special in the specification:")
write_regs(node)
write_ranges(node)
write_interrupts(node)
write_compatibles(node)
write_status(node)
write_pinctrls(node)
write_fixed_partitions(node)
write_gpio_hogs(node)
def write_ranges(node: edtlib.Node) -> None:
idx_vals = []
path_id = node.z_path_id
if node.ranges is not None:
idx_vals.append((f"{path_id}_RANGES_NUM", len(node.ranges)))
for i, range in enumerate(node.ranges):
idx_vals.append((f"{path_id}_RANGES_IDX_{i}_EXISTS", 1))
if "pcie" in node.buses:
idx_vals.append((f"{path_id}_RANGES_IDX_{i}_VAL_CHILD_BUS_FLAGS_EXISTS", 1))
idx_macro = f"{path_id}_RANGES_IDX_{i}_VAL_CHILD_BUS_FLAGS"
idx_value = range.child_bus_addr >> ((range.child_bus_cells - 1) * 32)
idx_vals.append((idx_macro, f"{idx_value} /* {hex(idx_value)} */"))
if range.child_bus_addr is not None:
idx_macro = f"{path_id}_RANGES_IDX_{i}_VAL_CHILD_BUS_ADDRESS"
if "pcie" in node.buses:
idx_value = range.child_bus_addr & (
(1 << (range.child_bus_cells - 1) * 32) - 1
)
else:
idx_value = range.child_bus_addr
idx_vals.append((idx_macro, f"{idx_value} /* {hex(idx_value)} */"))
if range.parent_bus_addr is not None:
idx_macro = f"{path_id}_RANGES_IDX_{i}_VAL_PARENT_BUS_ADDRESS"
idx_vals.append(
(
idx_macro,
f"{range.parent_bus_addr} /* {hex(range.parent_bus_addr)} */",
)
)
if range.length is not None:
idx_macro = f"{path_id}_RANGES_IDX_{i}_VAL_LENGTH"
idx_vals.append((idx_macro, f"{range.length} /* {hex(range.length)} */"))
for macro, val in idx_vals:
out_dt_define(macro, val)
out_dt_define(
f"{path_id}_FOREACH_RANGE(fn)",
" ".join(
f"fn({board_id}DT_{path_id}, {i})" for i, range in enumerate(node.ranges)
),
)
def write_regs(node: edtlib.Node) -> None:
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)))
dynamic_macro_make("DT_NUM_REGS_DYNAMIC(id, node_id)", select_macros_dict)
for i, reg in enumerate(node.regs):
idx_vals.append((f"{path_id}_REG_IDX_{i}_EXISTS", 1))
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)} */"))
dynamic_macro_make(
"DT_REG_ADDR_BY_IDX_DYNAMIC(id, node_id, idx)",
select_macros_dict,
)
if reg.name:
name_vals.append((f"{path_id}_REG_NAME_{reg.name}_EXISTS", 1))
name_macro = f"{path_id}_REG_NAME_{reg.name}_VAL_ADDRESS"
name_vals.append((name_macro, f"{board_id}DT_{idx_macro}"))
dynamic_macro_make(
"DT_REG_ADDR_BY_NAME_DYNAMIC(id, node_id, name)",
select_macros_dict,
)
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)} */"))
dynamic_macro_make(
"DT_REG_SIZE_BY_IDX_DYNAMIC(id, node_id, idx)",
select_macros_dict,
)
if reg.name:
name_macro = f"{path_id}_REG_NAME_{reg.name}_VAL_SIZE"
name_vals.append((name_macro, f"{board_id}DT_{idx_macro}"))
dynamic_macro_make(
"DT_REG_SIZE_BY_NAME_DYNAMIC(id, node_id, name)",
select_macros_dict,
)
for macro, val in idx_vals:
out_dt_define(macro, val)
for macro, val in name_vals:
out_dt_define(macro, val)
out_dt_define(
f"{path_id}_FOREACH_REG(fn)",
" ".join(f"fn(DT_{path_id}, {i})" for i, reg in enumerate(node.regs)),
)
def write_interrupts(node: edtlib.Node) -> None:
def map_arm_gic_irq_type(irq, irq_num):
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:
return irq_num + 32
if irq_type == 1:
return irq_num + 16
err(f"Invalid interrupt type specified for {irq!r}")
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)
idx_vals.append((f"{path_id}_IRQ_IDX_{i}_EXISTS", 1))
idx_macro = f"{path_id}_IRQ_IDX_{i}_VAL_{name}"
idx_vals.append((idx_macro, cell_value))
idx_vals.append((idx_macro + "_EXISTS", 1))
dynamic_macro_make(
"DT_IRQN_BY_IDX_DYNAMIC(id, node_id, idx)", select_macros_dict
)
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))
idx_controller_macro = f"{path_id}_IRQ_IDX_{i}_CONTROLLER"
idx_controller_path = f"DT_{irq.controller.z_path_id}"
idx_vals.append((idx_controller_macro, idx_controller_path))
if irq.name:
name_controller_macro = (
f"{path_id}_IRQ_NAME_{str2ident(irq.name)}_CONTROLLER"
)
name_vals.append((name_controller_macro, f"DT_{idx_controller_macro}"))
irqs = []
while node.interrupts is not None and len(node.interrupts) > 0:
irq = node.interrupts[0]
irqs.append(irq)
if node == irq.controller:
break
node = irq.controller
idx_vals.append((f"{path_id}_IRQ_LEVEL", len(irqs)))
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: edtlib.Node) -> None:
for i, compat in enumerate(node.compats):
out_dt_define(f"{node.z_path_id}_COMPAT_MATCHES_{str2ident(compat)}", 1)
if node.edt.compat2vendor[compat]:
out_dt_define(f"{node.z_path_id}_COMPAT_VENDOR_IDX_{i}_EXISTS", 1)
out_dt_define(
f"{node.z_path_id}_COMPAT_VENDOR_IDX_{i}",
quote_str(node.edt.compat2vendor[compat]),
)
if node.edt.compat2model[compat]:
out_dt_define(f"{node.z_path_id}_COMPAT_MODEL_IDX_{i}_EXISTS", 1)
out_dt_define(
f"{node.z_path_id}_COMPAT_MODEL_IDX_{i}",
quote_str(node.edt.compat2model[compat]),
)
def write_parent(node: edtlib.Node) -> None:
def _visit_parent_node(node: edtlib.Node):
while node is not None:
yield node.parent
node = node.parent
out_dt_define(
f"{node.z_path_id}_FOREACH_ANCESTOR(fn)",
" ".join(
f"fn({board_id}DT_{parent.z_path_id})"
for parent in _visit_parent_node(node)
if parent is not None
),
)
def write_children(node: edtlib.Node) -> None:
out_comment("Helper macros for child nodes of this node.")
out_dt_define(f"{node.z_path_id}_CHILD_NUM", len(node.children))
ok_nodes_num = 0
for child in node.children.values():
if child.status == "okay":
ok_nodes_num = ok_nodes_num + 1
out_dt_define(f"{node.z_path_id}_CHILD_NUM_STATUS_OKAY", ok_nodes_num)
out_dt_define(
f"{node.z_path_id}_FOREACH_CHILD(fn)",
" ".join(
f"fn({board_id}DT_{child.z_path_id})" for child in node.children.values()
),
)
out_dt_define(
f"{node.z_path_id}_FOREACH_CHILD_SEP(fn, sep)",
" DT_DEBRACKET_INTERNAL sep ".join(
f"fn({board_id}DT_{child.z_path_id})" for child in node.children.values()
),
)
out_dt_define(
f"{node.z_path_id}_FOREACH_CHILD_VARGS(fn, ...)",
" ".join(
f"fn({board_id}DT_{child.z_path_id}, __VA_ARGS__)"
for child in node.children.values()
),
)
out_dt_define(
f"{node.z_path_id}_FOREACH_CHILD_SEP_VARGS(fn, sep, ...)",
" DT_DEBRACKET_INTERNAL sep ".join(
f"fn({board_id}DT_{child.z_path_id}, __VA_ARGS__)"
for child in node.children.values()
),
)
out_dt_define(
f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY(fn)",
" ".join(
f"fn({board_id}DT_{child.z_path_id})"
for child in node.children.values()
if child.status == "okay"
),
)
out_dt_define(
f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_SEP(fn, sep)",
" DT_DEBRACKET_INTERNAL sep ".join(
f"fn({board_id}DT_{child.z_path_id})"
for child in node.children.values()
if child.status == "okay"
),
)
out_dt_define(
f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_VARGS(fn, ...)",
" ".join(
f"fn({board_id}DT_{child.z_path_id}, __VA_ARGS__)"
for child in node.children.values()
if child.status == "okay"
),
)
out_dt_define(
f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_SEP_VARGS(fn, sep, ...)",
" DT_DEBRACKET_INTERNAL sep ".join(
f"fn({board_id}DT_{child.z_path_id}, __VA_ARGS__)"
for child in node.children.values()
if child.status == "okay"
),
)
dynamic_macro_make("DT_CHILD_NUM_DYNAMIC(id, node_id)", select_macros_dict)
dynamic_macro_make(
"DT_CHILD_NUM_STATUS_OKAY_DYNAMIC(id, node_id)", select_macros_dict
)
def write_status(node: edtlib.Node) -> None:
out_dt_define(f"{node.z_path_id}_STATUS_{str2ident(node.status)}", 1)
def write_pinctrls(node: edtlib.Node) -> None:
out_comment("Pin control (pinctrl-<i>, pinctrl-names) properties:")
out_dt_define(f"{node.z_path_id}_PINCTRL_NUM", len(node.pinctrls))
if not node.pinctrls:
return
for pc_idx, pinctrl in enumerate(node.pinctrls):
out_dt_define(f"{node.z_path_id}_PINCTRL_IDX_{pc_idx}_EXISTS", 1)
if not pinctrl.name:
continue
name = pinctrl.name_as_token
out_dt_define(f"{node.z_path_id}_PINCTRL_IDX_{pc_idx}_TOKEN", name)
out_dt_define(
f"{node.z_path_id}_PINCTRL_IDX_{pc_idx}_UPPER_TOKEN", name.upper()
)
out_dt_define(f"{node.z_path_id}_PINCTRL_NAME_{name}_EXISTS", 1)
out_dt_define(f"{node.z_path_id}_PINCTRL_NAME_{name}_IDX", pc_idx)
for idx, ph in enumerate(pinctrl.conf_nodes):
out_dt_define(
f"{node.z_path_id}_PINCTRL_NAME_{name}_IDX_{idx}_PH",
f"DT_{ph.z_path_id}",
)
def write_fixed_partitions(node: edtlib.Node) -> None:
if not (node.parent and "fixed-partitions" in node.parent.compats):
return
global flash_area_num
out_comment("fixed-partitions identifier:")
out_dt_define(f"{node.z_path_id}_PARTITION_ID", flash_area_num)
flash_area_num += 1
def write_gpio_hogs(node: edtlib.Node) -> None:
macro = f"{node.z_path_id}_GPIO_HOGS"
macro2val = {}
for i, entry in enumerate(node.gpio_hogs):
macro2val.update(controller_and_data_macros(entry, i, macro))
if macro2val:
out_comment("GPIO hog properties:")
out_dt_define(f"{macro}_EXISTS", 1)
out_dt_define(f"{macro}_NUM", len(node.gpio_hogs))
for macro, val in macro2val.items():
out_dt_define(macro, val)
def write_vanilla_props(node: edtlib.Node) -> None:
macro2val = {}
for prop_name, prop in node.props.items():
prop_id = str2ident(prop_name)
macro = f"{node.z_path_id}_P_{prop_id}"
val = prop2value(prop)
if val is not None:
macro2val[macro] = val
if prop.spec.type == "string":
macro2val.update(string_macros(macro, prop.val))
macro2val[f"{macro}_IDX_0"] = quote_str(prop.val)
macro2val[f"{macro}_IDX_0_EXISTS"] = 1
if prop.enum_indices is not None:
macro2val.update(enum_macros(prop, macro))
if "phandle" in prop.type:
macro2val.update(phandle_macros(prop, macro))
elif "array" in prop.type:
macro2val.update(array_macros(prop, macro))
plen = prop_len(prop)
if plen is not None:
macro2val[f"{macro}_FOREACH_PROP_ELEM(fn)"] = " \\\n\t".join(
f"fn({board_id}DT_{node.z_path_id}, {prop_id}, {i})"
for i in range(plen)
)
macro2val[f"{macro}_FOREACH_PROP_ELEM_SEP(fn, sep)"] = (
" DT_DEBRACKET_INTERNAL sep \\\n\t".join(
f"fn({board_id}DT_{node.z_path_id}, {prop_id}, {i})"
for i in range(plen)
)
)
macro2val[f"{macro}_FOREACH_PROP_ELEM_VARGS(fn, ...)"] = " \\\n\t".join(
f"fn({board_id}DT_{node.z_path_id}, {prop_id}, {i}, __VA_ARGS__)"
for i in range(plen)
)
macro2val[f"{macro}_FOREACH_PROP_ELEM_SEP_VARGS(fn, sep, ...)"] = (
" DT_DEBRACKET_INTERNAL sep \\\n\t".join(
f"fn({board_id}DT_{node.z_path_id}, {prop_id}, {i}, __VA_ARGS__)"
for i in range(plen)
)
)
macro2val[f"{macro}_LEN"] = plen
macro2val[f"{macro}_EXISTS"] = 1
dynamic_macro_make("DT_PROP_DYNAMIC(id, node_id, prop)", select_macros_dict)
dynamic_macro_make("DT_PROP_LEN_DYNAMIC(id, node_id, prop)", select_macros_dict)
dynamic_macro_make(
"DT_PROP_BY_IDX_DYNAMIC(id, node_id, prop, idx)", select_macros_dict
)
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 string_macros(macro: str, val: str):
as_token = edtlib.str_as_token(val)
return {
f"{macro}_STRING_UNQUOTED": escape_unquoted(val),
f"{macro}_STRING_TOKEN": as_token,
f"{macro}_STRING_UPPER_TOKEN": as_token.upper(),
}
def enum_macros(prop: edtlib.Property, macro: str):
spec = prop.spec
ret = {
f"{macro}_IDX_{i}_ENUM_IDX": index for i, index in enumerate(prop.enum_indices)
}
val = (
prop.val_as_tokens
if spec.enum_tokenizable
else (prop.val if isinstance(prop.val, list) else [prop.val])
)
for i, subval in enumerate(val):
ret[f"{macro}_IDX_{i}_EXISTS"] = 1
ret[f"{macro}_IDX_{i}_ENUM_VAL_{subval}_EXISTS"] = 1
return ret
def array_macros(prop: edtlib.Property, macro: str):
ret = {}
for i, subval in enumerate(prop.val):
ret[f"{macro}_IDX_{i}_EXISTS"] = 1
if isinstance(subval, str):
ret[f"{macro}_IDX_{i}"] = quote_str(subval)
ret.update(string_macros(f"{macro}_IDX_{i}", subval))
else:
ret[f"{macro}_IDX_{i}"] = subval
return ret
def write_dep_info(node: edtlib.Node) -> None:
def fmt_dep_list(dep_list):
if dep_list:
sorted_list = sorted(dep_list, key=lambda node: node.dep_ordinal)
return "\\\n\t" + " \\\n\t".join(
f"{n.dep_ordinal}, /* {n.path} */" for n in sorted_list
)
else:
return "/* nothing */"
out_comment("Node's hash:")
out_dt_define(f"{node.z_path_id}_HASH", node.hash)
out_comment("Node's dependency ordinal:")
out_dt_define(f"{node.z_path_id}_ORD", node.dep_ordinal)
out_dt_define(f"{node.z_path_id}_ORD_STR_SORTABLE", f"{node.dep_ordinal:0>5}")
out_comment("Ordinals for what this node depends on directly:")
out_dt_define(f"{node.z_path_id}_REQUIRES_ORDS", fmt_dep_list(node.depends_on))
out_comment("Ordinals for what depends directly on this node:")
out_dt_define(f"{node.z_path_id}_SUPPORTS_ORDS", fmt_dep_list(node.required_by))
def prop2value(prop: edtlib.Property) -> edtlib.PropertyValType:
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)
return None
def prop_len(prop: edtlib.Property) -> Optional[int]:
if prop.type in ["phandle", "string"]:
return 1
if prop.type in [
"array",
"uint8-array",
"string-array",
"phandles",
"phandle-array",
] and prop.name not in ["ranges", "dma-ranges", "reg", "interrupts"]:
return len(prop.val)
return None
def phandle_macros(prop: edtlib.Property, macro: str) -> dict:
ret = {}
if prop.type == "phandle":
ret[f"{macro}"] = f"{board_id}DT_{prop.val.z_path_id}"
ret[f"{macro}_IDX_0"] = f"{board_id}DT_{prop.val.z_path_id}"
ret[f"{macro}_IDX_0_PH"] = f"{board_id}DT_{prop.val.z_path_id}"
ret[f"{macro}_IDX_0_EXISTS"] = 1
elif prop.type == "phandles":
for i, node in enumerate(prop.val):
ret[f"{macro}_IDX_{i}"] = f"{board_id}DT_{node.z_path_id}"
ret[f"{macro}_IDX_{i}_PH"] = f"{board_id}DT_{node.z_path_id}"
ret[f"{macro}_IDX_{i}_EXISTS"] = 1
elif prop.type == "phandle-array":
for i, entry in enumerate(prop.val):
if entry is None:
ret[f"{macro}_IDX_{i}_EXISTS"] = 0
continue
ret.update(controller_and_data_macros(entry, i, macro))
return ret
def controller_and_data_macros(entry: edtlib.ControllerAndData, i: int, macro: str):
ret = {}
data = entry.data
ret[f"{macro}_IDX_{i}_EXISTS"] = 1
ret[f"{macro}_IDX_{i}_PH"] = f"DT_{entry.controller.z_path_id}"
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)
ret[f"{macro}_IDX_{i}_EXISTS"] = 1
ret[f"{macro}_IDX_{i}_NAME"] = quote_str(entry.name)
ret[f"{macro}_NAME_{name}_PH"] = f"DT_{entry.controller.z_path_id}"
ret[f"{macro}_NAME_{name}_EXISTS"] = 1
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: edtlib.EDT):
out_comment("Chosen nodes\n")
chosen = {}
for name, node in edt.chosen_nodes.items():
chosen[f"{board_id}DT_CHOSEN_{str2ident(name)}"] = (
f"{board_id}DT_{node.z_path_id}"
)
chosen[f"{board_id}DT_CHOSEN_{str2ident(name)}_EXISTS"] = 1
max_len = max(map(len, chosen), default=0)
for macro, value in chosen.items():
out_define(macro, value, width=max_len)
def write_global_macros(edt: edtlib.EDT):
out_comment("Macros for iterating over all nodes and enabled nodes")
out_dt_define(
"FOREACH_HELPER(fn)",
" ".join(f"fn({board_id}DT_{node.z_path_id})" for node in edt.nodes),
)
out_dt_define(
"FOREACH_OKAY_HELPER(fn)",
" ".join(
f"fn({board_id}DT_{node.z_path_id})"
for node in edt.nodes
if node.status == "okay"
),
)
out_dt_define(
"FOREACH_VARGS_HELPER(fn, ...)",
" ".join(
f"fn({board_id}DT_{node.z_path_id}, __VA_ARGS__)" for node in edt.nodes
),
)
out_dt_define(
"FOREACH_OKAY_VARGS_HELPER(fn, ...)",
" ".join(
f"fn({board_id}DT_{node.z_path_id}, __VA_ARGS__)"
for node in edt.nodes
if node.status == "okay"
),
)
n_okay_macros = {}
for_each_macros = {}
compat2buses = defaultdict(list)
for compat, okay_nodes in edt.compat2okay.items():
for node in okay_nodes:
buses = node.on_buses
for bus in buses:
if bus is not None and bus not in compat2buses[compat]:
compat2buses[compat].append(bus)
ident = str2ident(compat)
n_okay_macros[f"DT_N_INST_{ident}_NUM_OKAY"] = len(okay_nodes)
for_each_macros[f"{board_id}DT_FOREACH_OKAY_{ident}(fn)"] = " ".join(
f"fn({board_id}DT_{node.z_path_id})" for node in okay_nodes
)
for_each_macros[f"{board_id}DT_FOREACH_OKAY_VARGS_{ident}(fn, ...)"] = " ".join(
f"fn({board_id}DT_{node.z_path_id}, __VA_ARGS__)" for node in okay_nodes
)
for_each_macros[f"DT_FOREACH_OKAY_INST_{ident}(fn)"] = " ".join(
f"fn({edt.compat2nodes[compat].index(node)})" for node in okay_nodes
)
for_each_macros[f"DT_FOREACH_OKAY_INST_VARGS_{ident}(fn, ...)"] = " ".join(
f"fn({edt.compat2nodes[compat].index(node)}, __VA_ARGS__)"
for node in okay_nodes
)
for compat, nodes in edt.compat2nodes.items():
for node in nodes:
if compat == "fixed-partitions":
for child in node.children.values():
if "label" in child.props:
label = child.props["label"].val
macro = f"COMPAT_{str2ident(compat)}_LABEL_{str2ident(label)}"
val = f"DT_{child.z_path_id}"
out_dt_define(macro, val)
out_dt_define(macro + "_EXISTS", 1)
out_comment('Macros for compatibles with status "okay" nodes\n')
for compat, okay_nodes in edt.compat2okay.items():
if okay_nodes:
out_define(f"{board_id}DT_COMPAT_HAS_OKAY_{str2ident(compat)}", 1)
out_comment('Macros for status "okay" instances of each compatible\n')
for macro, value in n_okay_macros.items():
out_define(macro, value)
for macro, value in for_each_macros.items():
out_define(macro, value)
out_comment('Bus information for status "okay" nodes of each compatible\n')
for compat, buses in compat2buses.items():
for bus in buses:
out_define(f"DT_COMPAT_{str2ident(compat)}_BUS_{str2ident(bus)}", 1)
def str2ident(s: str) -> str:
return re.sub("[-,.@/+]", "_", s.lower())
def list2init(list: Iterable[str]) -> str:
return "{" + ", ".join(list) + "}"
def out_dt_define(
macro: str,
val: str,
width: Optional[int] = None,
deprecation_msg: Optional[str] = None,
) -> str:
ret = f"{board_id}DT_{macro}"
out_define(ret, val, width=width, deprecation_msg=deprecation_msg)
return ret
def out_define(
macro: str,
val: str,
width: Optional[int] = None,
deprecation_msg: Optional[str] = None,
) -> None:
warn = rf' __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: str, blank_before=True) -> None:
if blank_before:
print(file=header_file)
if "\n" in s:
res = ["/*"]
for line in s.splitlines():
res.append(f" * {line}".rstrip())
res.append(" */")
print("\n".join(res), file=header_file)
else:
print(f"/* {s} */", file=header_file)
ESCAPE_TABLE = str.maketrans(
{
"\n": "\\n",
"\r": "\\r",
"\\": "\\\\",
'"': '\\"',
}
)
def escape(s: str) -> str:
return s.translate(ESCAPE_TABLE)
def quote_str(s: str) -> str:
return f'"{escape(s)}"'
def escape_unquoted(s: str) -> str:
return s.replace("\r", " ").replace("\n", " ")
def err(s: str) -> NoReturn:
raise Exception(s)
def dynamic_macro_make(macro_name: str, select_dict: dict):
if board_id == "":
return
if macro_name in select_dict:
return
if macro_name == "DT_NODE_PATH_DYNAMIC(id, node_id)":
select_macros_name = "node_id_PATH_SELECT(id, node_id)"
select_statment = f"({board_id_var}) == {board_id_num} ? \
COND_CODE_1(IS_ENABLED(DT_CAT3({board_id}, node_id, _EXISTS)), \
(DT_CAT3({board_id}, node_id, _PATH)), (DT_NODE_DEFAULT))\n"
elif macro_name == "DT_NUM_REGS_DYNAMIC(id, node_id)":
select_macros_name = "node_id_REG_NUM_SELECT(id, node_id)"
select_statment = f"({board_id_var}) == {board_id_num} ? \
COND_CODE_1(IS_ENABLED(DT_CAT3({board_id}, node_id, _EXISTS)), \
(DT_CAT3({board_id}, node_id, _REG_NUM)), (DT_NODE_DEFAULT))\n"
elif macro_name == "DT_REG_ADDR_BY_IDX_DYNAMIC(id, node_id, idx)":
select_macros_name = "node_id_REG_IDX_idx_VAL_ADDRESS_SELECT(id, node_id, idx)"
select_statment = f"({board_id_var}) == {board_id_num} ? \
COND_CODE_1(IS_ENABLED(DT_CAT5({board_id}, node_id,_REG_IDX_, idx, _EXISTS)), \
(DT_CAT5({board_id}, node_id, _REG_IDX_, idx, _VAL_ADDRESS)), (DT_NODE_DEFAULT))\n"
elif macro_name == "DT_REG_ADDR_BY_NAME_DYNAMIC(id, node_id, name)":
select_macros_name = (
"node_id_REG_NAME_name_VAL_ADDRESS_SELECT(id, node_id, name)"
)
select_statment = f"({board_id_var}) == {board_id_num} ? \
COND_CODE_1(IS_ENABLED(DT_CAT5({board_id}, node_id,_REG_NAME_, name, _EXISTS)), \
(DT_CAT5({board_id}, node_id, _REG_NAME_, name, _VAL_ADDRESS)), (DT_NODE_DEFAULT))\n"
elif macro_name == "DT_REG_SIZE_BY_IDX_DYNAMIC(id, node_id, idx)":
select_macros_name = "node_id_REG_IDX_idx_VAL_SIZE_SELECT(id, node_id, idx)"
select_statment = f"({board_id_var}) == {board_id_num} ? \
COND_CODE_1(IS_ENABLED(DT_CAT5({board_id}, node_id,_REG_IDX_, idx, _EXISTS)), \
(DT_CAT5({board_id}, node_id, _REG_IDX_, idx, _VAL_SIZE)), (DT_NODE_DEFAULT))\n"
elif macro_name == "DT_REG_SIZE_BY_NAME_DYNAMIC(id, node_id, name)":
select_macros_name = "node_id_REG_NAME_name_VAL_SIZE_SELECT(id, node_id, name)"
select_statment = f"({board_id_var}) == {board_id_num} ? \
COND_CODE_1(IS_ENABLED(DT_CAT5({board_id}, node_id,_REG_NAME_, name, _EXISTS)), \
(DT_CAT5({board_id}, node_id, _REG_NAME_, name, _VAL_SIZE)), (DT_NODE_DEFAULT))\n"
elif macro_name == "DT_IRQN_BY_IDX_DYNAMIC(id, node_id, idx)":
select_macros_name = "node_id_IRQ_IDX_idx_VAL_irq_SELECT(id, node_id, idx)"
select_statment = f"({board_id_var}) == {board_id_num} ? \
COND_CODE_1(IS_ENABLED(DT_CAT5({board_id}, node_id, _IRQ_IDX_, idx, _EXISTS)), \
(DT_CAT6({board_id}, node_id, _IRQ_IDX_, idx, _VAL_, irq)), (DT_NODE_DEFAULT))\n"
elif macro_name == "DT_CHILD_NUM_DYNAMIC(id, node_id)":
select_macros_name = "node_id_CHILD_NUM_SELECT(id, node_id)"
select_statment = f"({board_id_var}) == {board_id_num} ? \
COND_CODE_1(IS_ENABLED(DT_CAT3({board_id}, node_id, _EXISTS)), \
(DT_CAT3({board_id}, node_id, _CHILD_NUM)), (DT_NODE_DEFAULT))\n"
elif macro_name == "DT_CHILD_NUM_STATUS_OKAY_DYNAMIC(id, node_id)":
select_macros_name = "node_id_CHILD_NUM_STATUS_OKAY_SELECT(id, node_id)"
select_statment = f"({board_id_var}) == {board_id_num} ? \
COND_CODE_1(IS_ENABLED(DT_CAT3({board_id}, node_id, _EXISTS)), \
(DT_CAT3({board_id}, node_id, _CHILD_NUM_STATUS_OKAY)), (DT_NODE_DEFAULT))\n"
elif macro_name == "DT_PROP_DYNAMIC(id, node_id, prop)":
select_macros_name = "node_id_P_prop_SELECT(id, node_id, prop)"
select_statment = f"({board_id_var}) == {board_id_num} ? \
COND_CODE_1(IS_ENABLED(DT_CAT5({board_id}, node_id, _P_, prop, _EXISTS)), \
(DT_CAT4({board_id}, node_id, _P_, prop)), (DT_NODE_DEFAULT))\n"
elif macro_name == "DT_PROP_LEN_DYNAMIC(id, node_id, prop)":
select_macros_name = "node_id_P_prop_LEN_SELECT(id, node_id, prop)"
select_statment = f"({board_id_var}) == {board_id_num} ? \
COND_CODE_1(IS_ENABLED(DT_CAT5({board_id}, node_id, _P_, prop, _EXISTS)), \
(DT_CAT5({board_id}, node_id, _P_, prop, _LEN)), (0))\n"
elif macro_name == "DT_PROP_BY_IDX_DYNAMIC(id, node_id, prop, idx)":
select_macros_name = "node_id_P_prop_BY_IDX_SELECT(id, node_id, prop, idx)"
select_statment = f"({board_id_var}) == {board_id_num} ? \
COND_CODE_1(IS_ENABLED(DT_CAT7({board_id}, node_id, _P_, prop, _IDX_, idx, _EXISTS)), \
(DT_CAT6({board_id}, node_id, _P_, prop, _IDX_, idx)), (0))\n"
select_macros_dict[macro_name] = (select_macros_name, select_statment)
if __name__ == "__main__":
main()