ac6-formation, un département d'Ac6 SAS
FR
FrançaisEnglish
 
go-up

ac6 ac6-formation

Devicetree sous Zephyr : overlays, bindings et lecture des erreurs de build

Le devicetree de Zephyr ressemble à celui de Linux et se comporte différemment sur le point le plus important : sous Linux, il est chargé à l'exécution par le noyau ; sous Zephyr, il est entièrement résolu à la compilation. Il ne reste rien du devicetree dans le binaire, seulement des constantes. Cette différence explique la quasi-totalité des surprises rencontrées au début.

La chaîne de résolution

Quand vous lancez west build, la séquence est la suivante.

Le fichier <board>.dts de la carte est lu, avec les .dtsi qu'il inclut, décrivant le SoC. Les overlays viennent ensuite s'appliquer par-dessus, dans un ordre précis. L'ensemble est aplati en un seul arbre, validé contre les bindings, puis transformé en devicetree_generated.h, un fichier de macros C.

Ce fichier vaut le coup d'être ouvert au moins une fois, dans build/zephyr/include/generated/zephyr/devicetree_generated.h. On y voit exactement ce que le compilateur verra, et beaucoup de questions se règlent en le lisant plutôt qu'en devinant.

La règle de découverte des overlays est la source d'erreur la plus classique, et elle n'est pas cumulative. Le système cherche dans cet ordre :

  1. socs/<SOC>_<qualificatifs>.overlay
  2. boards/<carte>.overlay
  3. boards/<carte>_<révision>.overlay
  4. <carte>.overlay
  5. app.overlay

Dès qu'un ou plusieurs fichiers sont trouvés à une étape, la recherche s'arrête. C'est le point que tout le monde comprend de travers : votre app.overlay sera purement ignoré si un boards/<carte>.overlay existe. Il n'est pas fusionné, il n'est pas écrasé, il n'est jamais lu, et aucun avertissement ne le signale.

Les variables CMake échappent à cette règle. DTC_OVERLAY_FILE remplace la liste découverte, et EXTRA_DTC_OVERLAY_FILE s'ajoute après, donc avec une précédence supérieure.

En cas de doute, build/zephyr/zephyr.dts contient l'arbre final après fusion. C'est la seule source de vérité.

Les bindings, la partie que Linux n'impose pas de la même manière

Un binding est un fichier YAML qui décrit quelles propriétés un compatible accepte, leur type, et lesquelles sont obligatoires. Sans binding correspondant, un nœud est silencieusement ignoré : il ne génère aucune macro, et votre DEVICE_DT_GET échoue à la compilation avec un message qui ne mentionne pas le devicetree.

C'est le premier réflexe à acquérir : si un nœud semble inexistant alors qu'il est bien dans le .dts, cherchez le binding avant tout le reste.

Un binding minimal ressemble à ceci :

description: Capteur de température maison
compatible: "acme,temp-sensor"
include: [sensor-device.yaml, i2c-device.yaml]
properties:
  sample-rate-hz:
    type: int
    required: true
  enable-gpios:
    type: phandle-array

Les bindings vivent dans dts/bindings/ de votre application ou d'un module. Le nom du fichier n'a aucune importance, c'est la valeur de compatible qui fait le lien.

Les macros, et le piège du statut

Chaque nœud activé produit un jeu de macros. Les plus utilisées :

#define MY_SENSOR DT_NODELABEL(temp0)

static const struct device *dev = DEVICE_DT_GET(MY_SENSOR);
static const uint32_t rate = DT_PROP(MY_SENSOR, sample_rate_hz);
static const struct gpio_dt_spec en = GPIO_DT_SPEC_GET(MY_SENSOR, enable_gpios);

Notez la transformation du nom de propriété : sample-rate-hz en devicetree devient sample_rate_hz en C. Les tirets deviennent des soulignés, tout passe en minuscules.

Le piège le plus courant concerne status. Un nœud dont le statut n'est pas "okay" existe dans l'arbre mais ne produit pas de macro exploitable. La vérification se fait ainsi :

#if !DT_NODE_HAS_STATUS(DT_NODELABEL(temp0), okay)
#error "temp0 est absent ou desactive dans le devicetree"
#endif

Ce #error explicite vaut infiniment mieux que le message de compilation par défaut, qui parle d'un identifiant introuvable sans jamais nommer le devicetree.

Écrire un overlay qui fonctionne

Pour ajouter un périphérique sur un bus existant, on cible le nœud du bus par son label et on lui ajoute un enfant :

&i2c1 {
    status = "okay";
    clock-frequency = <I2C_BITRATE_FAST>;

    temp0: temp-sensor@48 {
        compatible = "acme,temp-sensor";
        reg = <0x48>;
        sample-rate-hz = <10>;
        enable-gpios = <&gpioa 5 GPIO_ACTIVE_HIGH>;
        status = "okay";
    };
};

Trois points qui font échouer les débutants. L'adresse après l'arobase doit correspondre exactement à la première valeur de reg, sinon la validation échoue. Le bus parent doit lui aussi être en status = "okay" : activer l'enfant seul ne suffit pas. Et les macros comme GPIO_ACTIVE_HIGH exigent l'inclusion des en-têtes correspondants en tête d'overlay :

#include <zephyr/dt-bindings/gpio/gpio.h>

Lire les erreurs

MessageCause réelle
'DT_N_S_...' undeclaredLe nœud n'existe pas, ou son statut n'est pas okay
no bindings found for ...Aucun fichier YAML ne déclare ce compatible
'reg' is marked as requiredLe binding exige reg, absent du nœud
'unit-address' does not match 'reg'L'adresse après l'arobase diverge de reg
dtc: Warning: unit_address_vs_regMême cause, en avertissement seulement

La première ligne concentre la majorité des cas, et elle est trompeuse : le compilateur signale un identifiant C inconnu, jamais un problème de devicetree. Le réflexe correct est d'ouvrir build/zephyr/zephyr.dts et de vérifier que le nœud y figure avec le bon statut.

Les outils qui font gagner du temps

west build -t boards liste les cibles disponibles. Plus utile au quotidien, la cible de visualisation :

west build -t initlevels     # ordre d'initialisation des devices

Et surtout, la lecture directe de l'arbre final. Face à un comportement inattendu, build/zephyr/zephyr.dts répond à la question « qu'est-ce que le système a réellement compris de ma configuration », ce que ni le .dts de la carte ni l'overlay ne peuvent dire séparément.

Références

Pour aller plus loin

Ces mécanismes deviennent naturels une fois qu'on a écrit un binding, ajouté un périphérique sur une carte réelle, et débogué un nœud qui refusait d'apparaître. C'est ce que couvrent notre formation à la programmation avec Zephyr RTOS et notre cours sur West, le SDK et Kconfig.