Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion cmake/modules/dts.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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 <zephyr/devicetree.h>
Expand Down Expand Up @@ -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}
)

Expand All @@ -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.
Expand Down
114 changes: 113 additions & 1 deletion scripts/dts/gen_edt.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
# edtlib. This will keep this script simple.

import argparse
import json
import os
import pickle
import sys
Expand Down Expand Up @@ -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
Expand All @@ -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")
Expand All @@ -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)

Expand Down
Loading