zephyr/scripts/dts/testedtlib.py
Ulf Magnusson b97ed9e4b4 scripts: dts: Generalize handling of phandle-array types
Generating generic information for 'type: phandle-array' properties in
edtlib was difficult due to defining phandle-array as just a list of
phandles and numbers. To make sense of a phandle-array property like
'pwms', you have to know that #pwm-cells is expected to appear on
each referenced controller, and that the binding for the controller has
a #cells.

Because of this, handling of various 'type: phandle-array' properties
was previously hardcoded in edtlib and exposed through properties like
Node.pwms, instead of through the generic Node.props (though with a lot
of shared code).

In practice, it turns out that all 'type: phandle-array' properties in
Zephyr work exactly the same way: They all have names that end in -s,
the 's' is removed to derive the name of related properties, and they
all look up #cells in the binding for the controller, which gives names
to the data values.

Strengthen the definition of 'type: phandle-array' to mean a property
that works exactly like the existing phandle-array properties (which
also means requiring that the name ends in -s). This removes a ton of
hardcoding from edtlib and allows new 'type: phandle-array' properties
to be added without making any code changes.

If we ever need a property type that's a list of phandles and numbers
but that doesn't follow this scheme, then we could add a separate type
for it. We should check if the standard scheme is fine first though.

The only property type for which no information is generated is now
'compound'.

There's some inconsistency in how we generate identifiers for clocks
compared to other 'type: phandle-array' properties, so keep
special-casing them for now in gen_defines.py (see the comment in
write_clocks()).

This change also enabled a bunch of other simplifications, like reusing
the ControllerAndData class for interrupts.

Piggyback generalization of *-map properties so that they work for any
phandle-array properties. It's now possible to have things like
'io-channel-map', if you need to.

Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
2019-09-26 15:30:23 -07:00

207 lines
10 KiB
Python
Executable File

#!/usr/bin/env python3
# Copyright (c) 2019 Nordic Semiconductor ASA
# SPDX-License-Identifier: BSD-3-Clause
import sys
import edtlib
# Test suite for edtlib.py. Run it directly as an executable, in this
# directory:
#
# $ ./testedtlib.py
#
# test.dts is the test file. test-bindings/ has bindings. The tests mostly use
# string comparisons via the various __repr__() methods.
def run():
"""
Runs all edtlib tests. Immediately exits with status 1 and a message on
stderr on test suite failures.
"""
def fail(msg):
sys.exit("test failed: " + msg)
def verify_eq(actual, expected):
if actual != expected:
# Put values on separate lines to make it easy to spot differences
fail("not equal (expected value last):\n'{}'\n'{}'"
.format(actual, expected))
def verify_streq(actual, expected):
verify_eq(str(actual), expected)
edt = edtlib.EDT("test.dts", ["test-bindings"])
#
# Test interrupts
#
verify_streq(edt.get_node("/interrupt-parent-test/node").interrupts,
"[<ControllerAndData, name: foo, controller: <Node /interrupt-parent-test/controller in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: {'one': 1, 'two': 2, 'three': 3}>, <ControllerAndData, name: bar, controller: <Node /interrupt-parent-test/controller in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: {'one': 4, 'two': 5, 'three': 6}>]")
verify_streq(edt.get_node("/interrupts-extended-test/node").interrupts,
"[<ControllerAndData, controller: <Node /interrupts-extended-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, data: {'one': 1}>, <ControllerAndData, controller: <Node /interrupts-extended-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: {'one': 2, 'two': 3}>, <ControllerAndData, controller: <Node /interrupts-extended-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: {'one': 4, 'two': 5, 'three': 6}>]")
verify_streq(edt.get_node("/interrupt-map-test/node@0").interrupts,
"[<ControllerAndData, controller: <Node /interrupt-map-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, data: {'one': 0}>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: {'one': 0, 'two': 1}>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: {'one': 0, 'two': 0, 'three': 2}>]")
verify_streq(edt.get_node("/interrupt-map-test/node@1").interrupts,
"[<ControllerAndData, controller: <Node /interrupt-map-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, data: {'one': 3}>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: {'one': 0, 'two': 4}>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: {'one': 0, 'two': 0, 'three': 5}>]")
verify_streq(edt.get_node("/interrupt-map-bitops-test/node@70000000E").interrupts,
"[<ControllerAndData, controller: <Node /interrupt-map-bitops-test/controller in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: {'one': 3, 'two': 2}>]")
#
# Test 'reg'
#
verify_streq(edt.get_node("/reg-zero-address-cells/node").regs,
"[<Register, addr: 0x0, size: 0x1>, <Register, addr: 0x0, size: 0x2>]")
verify_streq(edt.get_node("/reg-zero-size-cells/node").regs,
"[<Register, addr: 0x1, size: 0x0>, <Register, addr: 0x2, size: 0x0>]")
verify_streq(edt.get_node("/reg-ranges/parent/node").regs,
"[<Register, addr: 0x5, size: 0x1>, <Register, addr: 0xe0000000f, size: 0x1>, <Register, addr: 0xc0000000e, size: 0x1>, <Register, addr: 0xc0000000d, size: 0x1>, <Register, addr: 0xa0000000b, size: 0x1>, <Register, addr: 0x0, size: 0x1>]")
verify_streq(edt.get_node("/reg-nested-ranges/grandparent/parent/node").regs,
"[<Register, addr: 0x30000000200000001, size: 0x1>]")
#
# Test Node.parent and Node.children
#
verify_eq(edt.get_node("/").parent, None)
verify_streq(edt.get_node("/parent/child-1").parent,
"<Node /parent in 'test.dts', no binding>")
verify_streq(edt.get_node("/parent/child-2/grandchild").parent,
"<Node /parent/child-2 in 'test.dts', no binding>")
verify_streq(edt.get_node("/parent").children,
"{'child-1': <Node /parent/child-1 in 'test.dts', no binding>, 'child-2': <Node /parent/child-2 in 'test.dts', no binding>}")
verify_eq(edt.get_node("/parent/child-1").children, {})
#
# Test 'include:' and the legacy 'inherits: !include ...'
#
verify_streq(edt.get_node("/binding-include").description,
"Parent binding")
verify_streq(edt.get_node("/binding-include").props,
"{'foo': <Property, name: foo, type: int, value: 0>, 'bar': <Property, name: bar, type: int, value: 1>, 'baz': <Property, name: baz, type: int, value: 2>, 'qaz': <Property, name: qaz, type: int, value: 3>}")
#
# Test 'child/parent-bus:'
#
verify_streq(edt.get_node("/buses/foo-bus/node").binding_path,
"test-bindings/device-on-foo-bus.yaml")
verify_streq(edt.get_node("/buses/bar-bus/node").binding_path,
"test-bindings/device-on-bar-bus.yaml")
#
# Test 'child-binding:'
#
child1 = edt.get_node("/child-binding/child-1")
child2 = edt.get_node("/child-binding/child-2")
grandchild = edt.get_node("/child-binding/child-1/grandchild")
verify_streq(child1.binding_path, "test-bindings/child-binding.yaml")
verify_streq(child1.description, "child node")
verify_streq(child1.props, "{'child-prop': <Property, name: child-prop, type: int, value: 1>}")
verify_streq(child2.binding_path, "test-bindings/child-binding.yaml")
verify_streq(child2.description, "child node")
verify_streq(child2.props, "{'child-prop': <Property, name: child-prop, type: int, value: 3>}")
verify_streq(grandchild.binding_path, "test-bindings/child-binding.yaml")
verify_streq(grandchild.description, "grandchild node")
verify_streq(grandchild.props, "{'grandchild-prop': <Property, name: grandchild-prop, type: int, value: 2>}")
#
# Test deprecated 'sub-node:' key (replaced with 'child-binding:')
#
verify_streq(edt.get_node("/deprecated/sub-node").props,
"{'child-prop': <Property, name: child-prop, type: int, value: 3>}")
#
# Test Node.props (derived from DT and 'properties:' in the binding)
#
verify_streq(edt.get_node("/props").props["int"],
"<Property, name: int, type: int, value: 1>")
verify_streq(edt.get_node("/props").props["existent-boolean"],
"<Property, name: existent-boolean, type: boolean, value: True>")
verify_streq(edt.get_node("/props").props["nonexistent-boolean"],
"<Property, name: nonexistent-boolean, type: boolean, value: False>")
verify_streq(edt.get_node("/props").props["array"],
"<Property, name: array, type: array, value: [1, 2, 3]>")
verify_streq(edt.get_node("/props").props["uint8-array"],
r"<Property, name: uint8-array, type: uint8-array, value: b'\x124'>")
verify_streq(edt.get_node("/props").props["string"],
"<Property, name: string, type: string, value: 'foo'>")
verify_streq(edt.get_node("/props").props["string-array"],
"<Property, name: string-array, type: string-array, value: ['foo', 'bar', 'baz']>")
verify_streq(edt.get_node("/props").props["phandle-ref"],
"<Property, name: phandle-ref, type: phandle, value: <Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>>")
verify_streq(edt.get_node("/props").props["phandle-refs"],
"<Property, name: phandle-refs, type: phandles, value: [<Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>, <Node /props/ctrl-2 in 'test.dts', binding test-bindings/phandle-array-controller-2.yaml>]>")
verify_streq(edt.get_node("/props").props["phandle-array-foos"],
"<Property, name: phandle-array-foos, type: phandle-array, value: [<ControllerAndData, controller: <Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>, data: {'one': 1}>, <ControllerAndData, controller: <Node /props/ctrl-2 in 'test.dts', binding test-bindings/phandle-array-controller-2.yaml>, data: {'one': 2, 'two': 3}>]>")
verify_streq(edt.get_node("/props").props["foo-gpios"],
"<Property, name: foo-gpios, type: phandle-array, value: [<ControllerAndData, controller: <Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>, data: {'one': 1}>]>")
#
# Test <prefix>-map, via gpio-map (the most common case)
#
verify_streq(edt.get_node("/gpio-map/source").props["foo-gpios"],
"<Property, name: foo-gpios, type: phandle-array, value: [<ControllerAndData, controller: <Node /gpio-map/destination in 'test.dts', binding test-bindings/gpio-dst.yaml>, data: {'val': 6}>, <ControllerAndData, controller: <Node /gpio-map/destination in 'test.dts', binding test-bindings/gpio-dst.yaml>, data: {'val': 5}>]>")
#
# Test property default values given in bindings
#
verify_streq(edt.get_node("/defaults").props,
r"{'int': <Property, name: int, type: int, value: 123>, 'array': <Property, name: array, type: array, value: [1, 2, 3]>, 'uint8-array': <Property, name: uint8-array, type: uint8-array, value: b'\x89\xab\xcd'>, 'string': <Property, name: string, type: string, value: 'hello'>, 'string-array': <Property, name: string-array, type: string-array, value: ['hello', 'there']>, 'default-not-used': <Property, name: default-not-used, type: int, value: 234>}")
#
# Test having multiple directories with bindings, with a different .dts file
#
edt = edtlib.EDT("test-multidir.dts", ["test-bindings", "test-bindings-2"])
verify_streq(edt.get_node("/in-dir-1").binding_path,
"test-bindings/multidir.yaml")
verify_streq(edt.get_node("/in-dir-2").binding_path,
"test-bindings-2/multidir.yaml")
print("all tests passed")
if __name__ == "__main__":
run()