Skip to content

Commit

Permalink
doc: dts: add help for undefined reference to __device_dts_ord_N
Browse files Browse the repository at this point in the history
This is an FAQ and we don't have a satisfactory page that describes
the current state of the world for how to troubleshoot. This leads
to frustrated users and duplicated support efforts. Try to improve
this by at least writing down what people have to do today.

If we ever improve the situation by adding a machine-readable map
between drivers, Kconfig options, and DT compatibles, we can hopefully
do better than this, but at least it's a start.

Signed-off-by: Martí Bolívar <[email protected]>
  • Loading branch information
mbolivar-nordic authored and carlescufi committed Apr 26, 2023
1 parent f3a8279 commit c625a0e
Showing 1 changed file with 177 additions and 0 deletions.
177 changes: 177 additions & 0 deletions doc/build/dts/troubleshooting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ 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
*****************************************

Expand All @@ -29,6 +31,181 @@ 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
Expand Down

0 comments on commit c625a0e

Please sign in to comment.