169 lines
6.9 KiB
Python
Executable File
169 lines
6.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# Copyright (c) 2017 Intel Corporation
|
|
# Copyright (c) 2020 Nordic Semiconductor NA
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
"""Translate generic handles into ones optimized for the application.
|
|
|
|
Immutable device data includes information about dependencies,
|
|
e.g. that a particular sensor is controlled through a specific I2C bus
|
|
and that it signals event on a pin on a specific GPIO controller.
|
|
This information is encoded in the first-pass binary using identifiers
|
|
derived from the devicetree. This script extracts those identifiers
|
|
and replaces them with ones optimized for use with the devices
|
|
actually present.
|
|
|
|
For example the sensor might have a first-pass handle defined by its
|
|
devicetree ordinal 52, with the I2C driver having ordinal 24 and the
|
|
GPIO controller ordinal 14. The runtime ordinal is the index of the
|
|
corresponding device in the static devicetree array, which might be 6,
|
|
5, and 3, respectively.
|
|
|
|
The output is a C source file that provides alternative definitions
|
|
for the array contents referenced from the immutable device objects.
|
|
In the final link these definitions supersede the ones in the
|
|
driver-specific object file.
|
|
"""
|
|
|
|
import sys
|
|
import argparse
|
|
import os
|
|
import pickle
|
|
|
|
from elf_parser import ZephyrElf
|
|
|
|
# This is needed to load edt.pickle files.
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..',
|
|
'dts', 'python-devicetree', 'src'))
|
|
|
|
def parse_args():
|
|
global args
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description=__doc__,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False)
|
|
|
|
parser.add_argument("-k", "--kernel", required=True,
|
|
help="Input zephyr ELF binary")
|
|
parser.add_argument("--dynamic-deps", action="store_true",
|
|
help="Indicates if device dependencies are dynamic")
|
|
parser.add_argument("-d", "--num-dynamic-devices", required=False, default=0,
|
|
type=int, help="Input number of dynamic devices allowed")
|
|
parser.add_argument("-o", "--output-source", required=True,
|
|
help="Output source file")
|
|
parser.add_argument("-g", "--output-graphviz",
|
|
help="Output file for graphviz dependency graph")
|
|
parser.add_argument("-z", "--zephyr-base",
|
|
help="Path to current Zephyr base. If this argument \
|
|
is not provided the environment will be checked for \
|
|
the ZEPHYR_BASE environment variable.")
|
|
parser.add_argument("-s", "--start-symbol", required=True,
|
|
help="Symbol name of the section which contains the \
|
|
devices. The symbol name must point to the first \
|
|
device in that section.")
|
|
|
|
args = parser.parse_args()
|
|
|
|
ZEPHYR_BASE = args.zephyr_base or os.getenv("ZEPHYR_BASE")
|
|
|
|
if ZEPHYR_BASE is None:
|
|
sys.exit("-z / --zephyr-base not provided. Please provide "
|
|
"--zephyr-base or set ZEPHYR_BASE in environment")
|
|
|
|
sys.path.insert(0, os.path.join(ZEPHYR_BASE, "scripts/dts"))
|
|
|
|
def c_handle_comment(dev, handles):
|
|
def dev_path_str(dev):
|
|
return dev.edt_node and dev.edt_node.path or dev.sym.name
|
|
lines = [
|
|
'',
|
|
'/* {:d} : {:s}:'.format(dev.handle, (dev_path_str(dev))),
|
|
]
|
|
if len(handles["depends"]) > 0:
|
|
lines.append(' * Direct Dependencies:')
|
|
for dep in handles["depends"]:
|
|
lines.append(' * - {:s}'.format(dev_path_str(dep)))
|
|
if len(handles["injected"]) > 0:
|
|
lines.append(' * Injected Dependencies:')
|
|
for dep in handles["injected"]:
|
|
lines.append(' * - {:s}'.format(dev_path_str(dep)))
|
|
if len(handles["supports"]) > 0:
|
|
lines.append(' * Supported:')
|
|
for sup in handles["supports"]:
|
|
lines.append(' * - {:s}'.format(dev_path_str(sup)))
|
|
lines.append(' */')
|
|
return lines
|
|
|
|
def c_handle_array(dev, handles, dynamic_deps, extra_support_handles=0):
|
|
handles = [
|
|
*[str(d.handle) for d in handles["depends"]],
|
|
'Z_DEVICE_DEPS_SEP',
|
|
*[str(d.handle) for d in handles["injected"]],
|
|
'Z_DEVICE_DEPS_SEP',
|
|
*[str(d.handle) for d in handles["supports"]],
|
|
*(extra_support_handles * ['DEVICE_HANDLE_NULL']),
|
|
'Z_DEVICE_DEPS_ENDS',
|
|
]
|
|
ctype = (
|
|
'{:s}Z_DECL_ALIGN(device_handle_t) '
|
|
'__attribute__((__section__(".__device_deps_pass2")))'
|
|
).format('const ' if not dynamic_deps else '')
|
|
return [
|
|
# The `extern` line pretends this was first declared in some .h
|
|
# file to silence "should it be static?" warnings in some
|
|
# compilers and static analyzers.
|
|
'extern {:s} {:s}[{:d}];'.format(ctype, dev.ordinals.sym.name, len(handles)),
|
|
ctype,
|
|
'{:s}[] = {{ {:s} }};'.format(dev.ordinals.sym.name, ', '.join(handles)),
|
|
]
|
|
|
|
def main():
|
|
parse_args()
|
|
|
|
edtser = os.path.join(os.path.split(args.kernel)[0], "edt.pickle")
|
|
with open(edtser, 'rb') as f:
|
|
edt = pickle.load(f)
|
|
|
|
parsed_elf = ZephyrElf(args.kernel, edt, args.start_symbol)
|
|
if parsed_elf.relocatable:
|
|
# While relocatable elf files will load cleanly, the pointers pulled from
|
|
# the symbol table are invalid (as expected, because the structures have not
|
|
# yet been allocated addresses). Fixing this will require iterating over
|
|
# the relocation sections to find the symbols those pointers will end up
|
|
# referring to.
|
|
sys.exit('Relocatable elf files are not yet supported')
|
|
|
|
if args.output_graphviz:
|
|
# Try and output the dependency tree
|
|
try:
|
|
dot = parsed_elf.device_dependency_graph('Device dependency graph', args.kernel)
|
|
with open(args.output_graphviz, 'w') as f:
|
|
f.write(dot.source)
|
|
except ImportError:
|
|
pass
|
|
|
|
with open(args.output_source, "w") as fp:
|
|
fp.write('#include <zephyr/device.h>\n')
|
|
fp.write('#include <zephyr/toolchain.h>\n')
|
|
for dev in parsed_elf.devices:
|
|
# The device handle are collected up in a set, which has no
|
|
# specified order. Sort each sub-category of device handle types
|
|
# separately, so that the generated C array is reproducible across
|
|
# builds.
|
|
sorted_handles = {
|
|
"depends": sorted(dev.devs_depends_on, key=lambda d: d.handle),
|
|
"injected": sorted(dev.devs_depends_on_injected, key=lambda d: d.handle),
|
|
"supports": sorted(dev.devs_supports, key=lambda d: d.handle),
|
|
}
|
|
extra_sups = args.num_dynamic_devices if dev.pm and dev.pm.is_power_domain else 0
|
|
lines = c_handle_comment(dev, sorted_handles)
|
|
lines.extend(
|
|
c_handle_array(dev, sorted_handles, args.dynamic_deps, extra_sups)
|
|
)
|
|
lines.extend([''])
|
|
fp.write('\n'.join(lines))
|
|
|
|
if __name__ == "__main__":
|
|
main()
|