diff --git a/cmake/modules/dts.cmake b/cmake/modules/dts.cmake index 1822d89fc4a56..34b64b9583c71 100644 --- a/cmake/modules/dts.cmake +++ b/cmake/modules/dts.cmake @@ -109,6 +109,8 @@ set(GEN_EDT_SCRIPT ${DT_SCRIPTS}/gen_edt.py) set(GEN_DEFINES_SCRIPT ${DT_SCRIPTS}/gen_defines.py) # The edtlib.EDT object in pickle format. set(EDT_PICKLE ${PROJECT_BINARY_DIR}/edt.pickle) +# The edtlib.EDT object in json format. +set(EDT_JSON ${PROJECT_BINARY_DIR}/edt.json) # The generated file containing the final DTS, for debugging. set(ZEPHYR_DTS ${PROJECT_BINARY_DIR}/zephyr.dts) # The generated C header needed by @@ -300,6 +302,7 @@ set(CMD_GEN_EDT ${PYTHON_EXECUTABLE} ${GEN_EDT_SCRIPT} --workspace-dir ${GEN_EDT_WORKSPACE_DIR} --dts-out ${ZEPHYR_DTS}.new # for debugging and dtc --edt-pickle-out ${EDT_PICKLE}.new +--edt-json-out ${EDT_JSON}.new ${EXTRA_GEN_EDT_ARGS} ) @@ -310,9 +313,11 @@ execute_process( ) zephyr_file_copy(${ZEPHYR_DTS}.new ${ZEPHYR_DTS} ONLY_IF_DIFFERENT) zephyr_file_copy(${EDT_PICKLE}.new ${EDT_PICKLE} ONLY_IF_DIFFERENT) -file(REMOVE ${ZEPHYR_DTS}.new ${EDT_PICKLE}.new) +zephyr_file_copy(${EDT_JSON}.new ${EDT_JSON} ONLY_IF_DIFFERENT) +file(REMOVE ${ZEPHYR_DTS}.new ${EDT_PICKLE}.new ${EDT_JSON}.new) message(STATUS "Generated zephyr.dts: ${ZEPHYR_DTS}") message(STATUS "Generated pickled edt: ${EDT_PICKLE}") +message(STATUS "Generated json edt: ${EDT_JSON}") # # Run GEN_DEFINES_SCRIPT. diff --git a/scripts/dts/gen_edt.py b/scripts/dts/gen_edt.py index 07fec5969eb2f..aea70262afdb3 100755 --- a/scripts/dts/gen_edt.py +++ b/scripts/dts/gen_edt.py @@ -20,6 +20,7 @@ # edtlib. This will keep this script simple. import argparse +import json import os import pickle import sys @@ -59,7 +60,7 @@ def main(): print(edt.dts_source, file=f) write_pickled_edt(edt, args.edt_pickle_out) - + write_json_metainfo_edt(edt, args.edt_json_out) def parse_args() -> argparse.Namespace: # Returns parsed command-line arguments @@ -80,6 +81,8 @@ def parse_args() -> argparse.Namespace: "as a debugging aid)") parser.add_argument("--edt-pickle-out", help="path to write pickled edtlib.EDT object to", required=True) + parser.add_argument("--edt-json-out", + help="path to write json edtlib.EDT object to", required=True) parser.add_argument("--vendor-prefixes", action='append', default=[], help="vendor-prefixes.txt path; used for validation; " "may be given multiple times") @@ -106,6 +109,115 @@ def write_pickled_edt(edt: edtlib.EDT, out_file: str) -> None: pickle.dump(edt, f, protocol=4) +def reg_to_dict(reg: edtlib.Register) -> dict: + return { + 'name': reg.name, + 'addr': f'{hex(reg.addr) if reg.addr is not None else hex(0)}', + 'size': f'{hex(reg.size) if reg.size is not None else hex(0)}', + } if reg is not None else {} + + +def range_to_dict(rangx: edtlib.Range) -> dict: + return { + 'child_bus_cells': rangx.child_bus_cells, + 'child_bus_addr': rangx.child_bus_addr, + 'parent_bus_cells': rangx.parent_bus_cells, + 'parent_bus_addr': rangx.parent_bus_addr, + 'length_cells': rangx.length_cells, + 'length': rangx.length, + } if rangx is not None else {} + + +def prop_to_dict(prop_name: str, prop: edtlib.Property, cache=None) -> dict: + def propspec_to_dict(spec: edtlib.PropertySpec) -> dict: + return { + 'path': spec.path, + 'type': spec.type, + 'enum': spec.enum, + 'const': spec.const, + 'default': spec.default, + 'required': spec.required + } + + def propval_as(val: edtlib.PropertyValType): + if isinstance(val, int): + return val + if isinstance(val, str): + return val + if isinstance(val, edtlib.Node): + return node_to_dict(val, cache) + if isinstance(val, bytes): + return list(val) + if isinstance(val, list): + if all(isinstance(v, int) for v in val): + return val + if all(isinstance(v, str) for v in val): + return val + if all(isinstance(v, edtlib.Node) or v is None for v in val): + return [node_to_dict(n, cache) for n in val] + if all(isinstance(v, edtlib.ControllerAndData) or v is None for v in val): + return [ctrl_to_dict(c, cache) for c in val] + + return { + prop_name: { + 'spec': propspec_to_dict(prop.spec), + 'val': propval_as(prop.val), + 'type': prop.type + }} if prop is not None else {} + +def pinctrl_to_dict(pinctrl: edtlib.PinCtrl, cache=None) -> dict: + return { + 'name': pinctrl.name, + 'conf_nodes': [node_to_dict(p, cache) for p in pinctrl.conf_nodes] + } if pinctrl is not None else {} + + +def ctrl_to_dict(ctrl: edtlib.ControllerAndData, cache=None) -> dict: + return { + 'name': ctrl.name, + 'basename': ctrl.basename, + 'data': ctrl.data, + 'controller': node_to_dict(ctrl.controller, cache) + } if ctrl is not None else {} + + +def node_to_dict(node: edtlib.Node, cache=None) -> dict: + if cache is None: + cache = set() + + # Avoid deep recursion + if node is not None and id(node) in cache: + return {'node_ref': node.path} + cache.add(id(node)) + + return { + 'name': node.name, + 'compats': node.compats, + 'path': node.path, + 'labels': node.labels, + 'aliases': node.aliases, + 'status': node.status, + 'dep_ordinal': node.dep_ordinal, + 'hash': node.hash, + 'ranges': [range_to_dict(r) for r in node.ranges], + 'regs': [reg_to_dict(r) for r in node.regs], + 'props': [prop_to_dict(p_name, p, cache) for p_name, p in node.props.items()], + 'interrupts': [ctrl_to_dict(i, cache) for i in node.interrupts], + 'pinctrls': [pinctrl_to_dict(p, cache) for p in node.pinctrls] + } if node is not None else {} + + +def write_json_metainfo_edt(edt: edtlib.EDT, out_file: str) -> None: + # Writes the edt object in pickle format to out_file. + + nodes = {} + + with open(out_file, 'w') as f: + nodes['nodes'] = [node_to_dict(node) for node in edt.nodes if node is not None] + # Sorry, I was supported before Python 2.6 + json.dump(nodes, f) + + def err(s: str) -> NoReturn: raise Exception(s)