363 lines
12 KiB
ReStructuredText
363 lines
12 KiB
ReStructuredText
.. _dt-trouble:
|
|
|
|
Troubleshooting devicetree
|
|
##########################
|
|
|
|
Here are some tips for fixing misbehaving devicetree related code.
|
|
|
|
See :ref:`dt-howtos` for other "HOWTO" style information.
|
|
|
|
.. _dt-trouble-try-pristine:
|
|
|
|
Try again with a pristine build directory
|
|
*****************************************
|
|
|
|
.. important:: Try this first, before doing anything else.
|
|
|
|
See :ref:`west-building-pristine` for examples, or just delete the build
|
|
directory completely and retry.
|
|
|
|
This is general advice which is especially applicable to debugging devicetree
|
|
issues, because the outputs are created during the CMake configuration phase,
|
|
and are not always regenerated when one of their inputs changes.
|
|
|
|
Make sure <devicetree.h> is included
|
|
************************************
|
|
|
|
Unlike Kconfig symbols, the :file:`devicetree.h` header must be included
|
|
explicitly.
|
|
|
|
Many Zephyr header files rely on information from devicetree, so including some
|
|
other API may transitively include :file:`devicetree.h`, but that's not
|
|
guaranteed.
|
|
|
|
undefined reference to ``__device_dts_ord_<N>``
|
|
***********************************************
|
|
|
|
This usually happens on a line like this:
|
|
|
|
.. code-block:: c
|
|
|
|
const struct device *dev = DEVICE_DT_GET(NODE_ID);
|
|
|
|
where ``NODE_ID`` is a valid :ref:`node identifier <dt-node-identifiers>`, but
|
|
no device driver has allocated a ``struct device`` for this devicetree node.
|
|
You thus get a linker error, because you're asking for a pointer to a device
|
|
that isn't defined.
|
|
|
|
To fix it, you need to make sure that:
|
|
|
|
1. The node is enabled: the node must have ``status = "okay";``.
|
|
|
|
(Recall that a missing ``status`` property means the same thing as ``status
|
|
= "okay";``; see :ref:`dt-important-props` for more information about
|
|
``status``).
|
|
|
|
2. A device driver responsible for allocating the ``struct device`` is enabled.
|
|
That is, the Kconfig option which makes the build system compile the driver
|
|
sources into your application needs to be set to ``y``.
|
|
|
|
(See :ref:`setting_configuration_values` for more information on setting
|
|
Kconfig options.)
|
|
|
|
Below, ``<build>`` means your build directory.
|
|
|
|
**Making sure the node is enabled**:
|
|
|
|
To find the devicetree node you need to check, use the number ``<N>`` from the
|
|
linker error. Look for this number in the list of nodes at the top of
|
|
:file:`<build>/zephyr/include/generated/devicetree_generated.h`. For example, if
|
|
``<N>`` is 15, and your :file:`devicetree_generated.h` file looks like this,
|
|
the node you are interested in is ``/soc/i2c@deadbeef``:
|
|
|
|
.. code-block:: none
|
|
|
|
/*
|
|
* Generated by gen_defines.py
|
|
*
|
|
* DTS input file:
|
|
* <build>/zephyr/zephyr.dts.pre
|
|
*
|
|
* Directories with bindings:
|
|
* $ZEPHYR_BASE/dts/bindings
|
|
*
|
|
* Node dependency ordering (ordinal and path):
|
|
* 0 /
|
|
* 1 /aliases
|
|
[...]
|
|
* 15 /soc/i2c@deadbeef
|
|
[...]
|
|
|
|
Now look for this node in :file:`<build>/zephyr/zephyr.dts`, which is the final
|
|
devicetree for your application build. (See :ref:`get-devicetree-outputs` for
|
|
information and examples.)
|
|
|
|
If the node has ``status = "disabled";`` in :file:`zephyr.dts`, then you need
|
|
to enable it by setting ``status = "okay";``, probably by using a devicetree
|
|
:ref:`overlay <set-devicetree-overlays>`. For example, if :file:`zephyr.dts`
|
|
looks like this:
|
|
|
|
.. code-block:: DTS
|
|
|
|
i2c0: i2c@deadbeef {
|
|
status = "disabled";
|
|
};
|
|
|
|
Then you should put this into your devicetree overlay and
|
|
:ref:`dt-trouble-try-pristine`:
|
|
|
|
.. code-block:: DTS
|
|
|
|
&i2c0 {
|
|
status = "okay";
|
|
};
|
|
|
|
Make sure that you see ``status = "okay";`` in :file:`zephyr.dts` after you
|
|
rebuild.
|
|
|
|
**Making sure the device driver is enabled**:
|
|
|
|
The first step is to figure out which device driver is responsible for handling
|
|
your devicetree node and allocating devices for it. To do this, you need to
|
|
start with the ``compatible`` property in your devicetree node, and find the
|
|
driver that allocates ``struct device`` instances for that compatible.
|
|
|
|
If you're not familiar with how devices are allocated from devicetree nodes
|
|
based on compatible properties, the ZDS 2021 talk `A deep dive into the Zephyr
|
|
2.5 device model`_ may be a useful place to start, along with the
|
|
:ref:`device_model_api` pages. See :ref:`dt-important-props` and the Devicetree
|
|
specification for more information about ``compatible``.
|
|
|
|
.. _A deep dive into the Zephyr 2.5 device model:
|
|
https://www.youtube.com/watch?v=sWaxQyIgEBY
|
|
|
|
There is currently no documentation for what device drivers exist and which
|
|
devicetree compatibles they are associated with. You will have to figure this
|
|
out by reading the source code:
|
|
|
|
- Look in :zephyr_file:`drivers` for the appropriate subdirectory that
|
|
corresponds to the API your device implements
|
|
- Look inside that directory for relevant files until you figure out what the
|
|
driver is, or realize there is no such driver.
|
|
|
|
Often, but not always, you can find the driver by looking for a file that sets
|
|
the ``DT_DRV_COMPAT`` macro to match your node's ``compatible`` property,
|
|
except lowercased and with special characters converted to underscores. For
|
|
example, if your node's compatible is ``vnd,foo-device``, look for a file with this
|
|
line:
|
|
|
|
.. code-block:: C
|
|
|
|
#define DT_DRV_COMPAT vnd_foo_device
|
|
|
|
.. important::
|
|
|
|
This **does not always work** since not all drivers use ``DT_DRV_COMPAT``.
|
|
|
|
If you find a driver, you next need to make sure the Kconfig option that
|
|
compiles it is enabled. (If you don't find a driver, and you are sure the
|
|
compatible property is correct, then you need to write a driver. Writing
|
|
drivers is outside the scope of this documentation page.)
|
|
|
|
Continuing the above example, if your devicetree node looks like this now:
|
|
|
|
.. code-block:: DTS
|
|
|
|
i2c0: i2c@deadbeef {
|
|
compatible = "nordic,nrf-twim";
|
|
status = "okay";
|
|
};
|
|
|
|
Then you would look inside of :zephyr_file:`drivers/i2c` for the driver file
|
|
that handles the compatible ``nordic,nrf-twim``. In this case, that is
|
|
:zephyr_file:`drivers/i2c/i2c_nrfx_twim.c`. Notice how even in cases where
|
|
``DT_DRV_COMPAT`` is not set, you can use information like driver file names as
|
|
clues.
|
|
|
|
Once you know the driver you want to enable, you need to make sure its Kconfig
|
|
option is set to ``y``. You can figure out which Kconfig option is needed by
|
|
looking for a line similar to this one in the :file:`CMakeLists.txt` file in
|
|
the drivers subdirectory. Continuing the above example,
|
|
:zephyr_file:`drivers/i2c/CMakeLists.txt` has a line that looks like this:
|
|
|
|
.. code-block:: cmake
|
|
|
|
zephyr_library_sources_ifdef(CONFIG_NRFX_TWIM i2c_nrfx_twim.c)
|
|
|
|
This means that :kconfig:option:`CONFIG_NRFX_TWIM` must be set to ``y`` in
|
|
:file:`<build>/zephyr/.config` file.
|
|
|
|
If your driver's Kconfig is not set to ``y``, you need to figure out what you
|
|
need to do to make that happen. Often, this will happen automatically as soon
|
|
as you enable the devicetree node. Otherwise, it is sometimes as simple as
|
|
adding a line like this to your application's :file:`prj.conf` file and then
|
|
making sure to :ref:`dt-trouble-try-pristine`:
|
|
|
|
.. code-block:: cfg
|
|
|
|
CONFIG_FOO=y
|
|
|
|
where ``CONFIG_FOO`` is the option that :file:`CMakeLists.txt` uses to decide
|
|
whether or not to compile the driver.
|
|
|
|
However, there may be other problems in your way, such as unmet Kconfig
|
|
dependencies that you also have to enable before you can enable your driver.
|
|
|
|
Consult the Kconfig file that defines ``CONFIG_FOO`` (for your value of
|
|
``FOO``) for more information.
|
|
|
|
.. _dt-use-the-right-names:
|
|
|
|
Make sure you're using the right names
|
|
**************************************
|
|
|
|
Remember that:
|
|
|
|
- In C/C++, devicetree names must be lowercased and special characters must be
|
|
converted to underscores. Zephyr's generated devicetree header has DTS names
|
|
converted in this way into the C tokens used by the preprocessor-based
|
|
``<devicetree.h>`` API.
|
|
- In overlays, use devicetree node and property names the same way they
|
|
would appear in any DTS file. Zephyr overlays are just DTS fragments.
|
|
|
|
For example, if you're trying to **get** the ``clock-frequency`` property of a
|
|
node with path ``/soc/i2c@12340000`` in a C/C++ file:
|
|
|
|
.. code-block:: c
|
|
|
|
/*
|
|
* foo.c: lowercase-and-underscores names
|
|
*/
|
|
|
|
/* Don't do this: */
|
|
#define MY_CLOCK_FREQ DT_PROP(DT_PATH(soc, i2c@1234000), clock-frequency)
|
|
/* ^ ^
|
|
* @ should be _ - should be _ */
|
|
|
|
/* Do this instead: */
|
|
#define MY_CLOCK_FREQ DT_PROP(DT_PATH(soc, i2c_1234000), clock_frequency)
|
|
/* ^ ^ */
|
|
|
|
And if you're trying to **set** that property in a devicetree overlay:
|
|
|
|
.. code-block:: none
|
|
|
|
/*
|
|
* foo.overlay: DTS names with special characters, etc.
|
|
*/
|
|
|
|
/* Don't do this; you'll get devicetree errors. */
|
|
&{/soc/i2c_12340000/} {
|
|
clock_frequency = <115200>;
|
|
};
|
|
|
|
/* Do this instead. Overlays are just DTS fragments. */
|
|
&{/soc/i2c@12340000/} {
|
|
clock-frequency = <115200>;
|
|
};
|
|
|
|
Look at the preprocessor output
|
|
*******************************
|
|
|
|
To save preprocessor output files, enable the
|
|
:kconfig:option:`CONFIG_COMPILER_SAVE_TEMPS` option. For example, to build
|
|
:ref:`hello_world` with west with this option set, use:
|
|
|
|
.. code-block:: sh
|
|
|
|
west build -b BOARD samples/hello_world -- -DCONFIG_COMPILER_SAVE_TEMPS=y
|
|
|
|
This will create a preprocessor output file named :file:`foo.c.i` in the build
|
|
directory for each source file :file:`foo.c`.
|
|
|
|
You can then search for the file in the build directory to see what your
|
|
devicetree macros expanded to. For example, on macOS and Linux, using ``find``
|
|
to find :file:`main.c.i`:
|
|
|
|
.. code-block:: sh
|
|
|
|
$ find build -name main.c.i
|
|
build/CMakeFiles/app.dir/src/main.c.i
|
|
|
|
It's usually easiest to run a style formatter on the results before opening
|
|
them. For example, to use ``clang-format`` to reformat the file in place:
|
|
|
|
.. code-block:: sh
|
|
|
|
clang-format -i build/CMakeFiles/app.dir/src/main.c.i
|
|
|
|
You can then open the file in your favorite editor to view the final C results
|
|
after preprocessing.
|
|
|
|
Do not track macro expansion
|
|
****************************
|
|
|
|
Compiler messages for devicetree errors can sometimes be very long. This
|
|
typically happens when the compiler prints a message for every step of a
|
|
complex macro expansion that has several intermediate expansion steps.
|
|
|
|
To prevent the compiler from doing this, you can disable the
|
|
:kconfig:option:`CONFIG_COMPILER_TRACK_MACRO_EXPANSION` option. This typically
|
|
reduces the output to one message per error.
|
|
|
|
For example, to build :ref:`hello_world` with west and this option disabled,
|
|
use:
|
|
|
|
.. code-block:: sh
|
|
|
|
west build -b BOARD samples/hello_world -- -DCONFIG_COMPILER_TRACK_MACRO_EXPANSION=n
|
|
|
|
Validate properties
|
|
*******************
|
|
|
|
If you're getting a compile error reading a node property, check your node
|
|
identifier and property. For example, if you get a build error on a line that
|
|
looks like this:
|
|
|
|
.. code-block:: c
|
|
|
|
int baud_rate = DT_PROP(DT_NODELABEL(my_serial), current_speed);
|
|
|
|
Try checking the node by adding this to the file and recompiling:
|
|
|
|
.. code-block:: c
|
|
|
|
#if !DT_NODE_EXISTS(DT_NODELABEL(my_serial))
|
|
#error "whoops"
|
|
#endif
|
|
|
|
If you see the "whoops" error message when you rebuild, the node identifier
|
|
isn't referring to a valid node. :ref:`get-devicetree-outputs` and debug from
|
|
there.
|
|
|
|
Some hints for what to check next if you don't see the "whoops" error message:
|
|
|
|
- did you :ref:`dt-use-the-right-names`?
|
|
- does the :ref:`property exist <dt-checking-property-exists>`?
|
|
- does the node have a :ref:`matching binding <dt-bindings>`?
|
|
- does the binding define the property?
|
|
|
|
.. _missing-dt-binding:
|
|
|
|
Check for missing bindings
|
|
**************************
|
|
|
|
See :ref:`dt-bindings` for information about bindings, and
|
|
:ref:`devicetree_binding_index` for information on bindings built into Zephyr.
|
|
|
|
If the build fails to :ref:`dts-find-binding` for a node, then either the
|
|
node's ``compatible`` property is not defined, or its value has no matching
|
|
binding. If the property is set, check for typos in its name. In a devicetree
|
|
source file, ``compatible`` should look like ``"vnd,some-device"`` --
|
|
:ref:`dt-use-the-right-names`.
|
|
|
|
If your binding file is not under :file:`zephyr/dts`, you may need to set
|
|
:ref:`DTS_ROOT <dts_root>`; see :ref:`dt-where-bindings-are-located`.
|
|
|
|
Errors with DT_INST_() APIs
|
|
***************************
|
|
|
|
If you're using an API like :c:func:`DT_INST_PROP`, you must define
|
|
``DT_DRV_COMPAT`` to the lowercase-and-underscores version of the compatible
|
|
you are interested in. See :ref:`dt-create-devices-inst`.
|