| .. _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:: none |
| |
| 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`. |