From c4f0c60db84aab11ad67593e15165c8b832d01a1 Mon Sep 17 00:00:00 2001 From: Luca Burelli Date: Thu, 9 Jan 2025 11:45:23 +0100 Subject: [PATCH] edtlib: add "hash" attribute to nodes Add a new "hash" attribute to all Devicetree EDT nodes. The hash is calculated on the full path of the node; this means that its value remains stable across rebuilds. The hash is checked for uniqueness among nodes in the same EDT. This computed token is then added to `devicetree_generated.h` and made accessible to Zephyr code via a new DT_HASH(node_id) macro. Signed-off-by: Luca Burelli --- include/zephyr/devicetree.h | 10 ++++++++ scripts/dts/gen_defines.py | 3 +++ .../src/devicetree/edtlib.py | 24 +++++++++++++++++++ .../python-devicetree/tests/test_edtlib.py | 10 ++++++++ 4 files changed, 47 insertions(+) diff --git a/include/zephyr/devicetree.h b/include/zephyr/devicetree.h index de4903471cbf..b3eee1ced960 100644 --- a/include/zephyr/devicetree.h +++ b/include/zephyr/devicetree.h @@ -242,6 +242,16 @@ */ #define DT_HAS_ALIAS(alias_name) DT_NODE_EXISTS(DT_ALIAS(alias_name)) +/** + * @brief Get the hash associated with a DT node + * + * Get the hash for the specified node_id. The hash is calculated on the + * full devicetree path of the node. + * @param node_id node identifier + * @return hash value as a preprocessor token + */ +#define DT_HASH(node_id) DT_CAT(node_id, _HASH) + /** * @brief Get a node identifier for an instance of a compatible * diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py index eb9fc5e8ce02..99aac7d720ce 100755 --- a/scripts/dts/gen_defines.py +++ b/scripts/dts/gen_defines.py @@ -720,6 +720,9 @@ def fmt_dep_list(dep_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}") diff --git a/scripts/dts/python-devicetree/src/devicetree/edtlib.py b/scripts/dts/python-devicetree/src/devicetree/edtlib.py index 752e15dc81d4..9b35890db9e1 100644 --- a/scripts/dts/python-devicetree/src/devicetree/edtlib.py +++ b/scripts/dts/python-devicetree/src/devicetree/edtlib.py @@ -72,6 +72,8 @@ from dataclasses import dataclass from typing import (Any, Callable, Iterable, NoReturn, Optional, TYPE_CHECKING, Union) +import base64 +import hashlib import logging import os import re @@ -90,6 +92,14 @@ from devicetree.grutils import Graph from devicetree._private import _slice_helper +def _compute_hash(path: str) -> str: + # Calculates the hash associated with the node's full path. + hasher = hashlib.sha256() + hasher.update(path.encode()) + hash = base64.b64encode(hasher.digest()).decode() + # replace invalid chars and drop padding + return hash.translate(hash.maketrans("+/", "__", "=")) + # # Public classes # @@ -912,6 +922,11 @@ class Node: The ordinal is defined for all Nodes, and is unique among nodes in its EDT 'nodes' list. + hash: + A hashed value of the devicetree path of the node. This is defined for + all Nodes, and is checked for uniqueness among nodes in its EDT 'nodes' + list. + required_by: A list with the nodes that directly depend on the node @@ -1027,6 +1042,7 @@ def __init__( self.interrupts: list[ControllerAndData] = [] self.pinctrls: list[PinCtrl] = [] self.bus_node = self._bus_node(support_fixed_partitions_on_any_bus) + self.hash: str = _compute_hash(dt_node.path) self._init_binding() self._init_regs() @@ -2270,10 +2286,18 @@ def _init_nodes(self) -> None: # Creates a list of edtlib.Node objects from the dtlib.Node objects, in # self.nodes + hash2node: dict[str, Node] = {} + for dt_node in self._dt.node_iter(): # Warning: We depend on parent Nodes being created before their # children. This is guaranteed by node_iter(). node = Node(dt_node, self, self._fixed_partitions_no_bus) + + if node.hash in hash2node: + _err(f"hash collision between '{node.path}' and " + f"'{hash2node[node.hash].path}'") + hash2node[node.hash] = node + self.nodes.append(node) self._node2enode[dt_node] = node diff --git a/scripts/dts/python-devicetree/tests/test_edtlib.py b/scripts/dts/python-devicetree/tests/test_edtlib.py index 30fbc7c3fc36..222f54fd8694 100644 --- a/scripts/dts/python-devicetree/tests/test_edtlib.py +++ b/scripts/dts/python-devicetree/tests/test_edtlib.py @@ -241,6 +241,16 @@ def test_hierarchy(): assert edt.get_node("/parent/child-1").children == {} +def test_hashes(): + '''Test Node.hash on hierarchy nodes''' + with from_here(): + edt = edtlib.EDT("test.dts", ["test-bindings"]) + + assert edt.get_node("/").hash == "il7asoJjJEMhngUeSt4tHVu8Zxx4EFG_FDeJfL3_oPE" + assert edt.get_node("/parent").hash == "38lte65vn2I4YtFxqM921ZudsWz2sWGcCxAYf5EsKEQ" + assert edt.get_node("/parent/child-1").hash == "sv1LnIlXN4B1lRFuZL4WHWu9yfzRUhmY_kwWqkKr1Og" + assert edt.get_node("/parent/child-2").hash == "ayvZczwhPy5g_cnGHKwwWICJ7uD0gMOHAU06nNILOsI" + def test_child_index(): '''Test Node.child_index.''' with from_here():