Publié le 24 juillet 2015 - par

Un point sur le Device Tree

BigTree_250pxLes derniers noyaux et firmware du Raspberry Pi, y compris Raspbian et NOOBS, utilisent maintenant par défaut Device Tree (Arborescence Matérielle) pour gérer certaines allocations des ressources et les chargements de modules. L’objectif de ce changement est de diminuer le problème d’accès concurrent aux ressources du système par plusieurs pilotes, et de permettre la configuration automatique des modules HAT.

Les Device Tree et le Raspberry Pi

Ce billet s’adresse plutôt à des utilisateurs avancés
qui sont amenés à intervenir au niveau du système,
et à utiliser les bus externes I2C, I2C et SPI.
<== C’est une traduction d’un article paru sur raspberrypi.org ==>

Depuis quelques années, un important changement a eu lieu dans le support de l’architecture ARM du noyau Linux : le passage à une description du matériel appelée le Device Tree. Tout développeur souhaitant aujourd’hui porter le noyau Linux sur une plateforme ARM (que ce soit une nouvelle carte ou un nouveau processeur) doit désormais comprendre ce nouveau mécanisme et l’utiliser: c’est devenu une connaissance indispensable du développeur Linux embarqué.

L’implémentation actuelle n’est pas un système d’arborescence des périphériques pur – il y a encore du code qui s’occupe des composants présents sur la carte – mais les interfaces externes (I2C, I2S spi) et les périphériques audio qui les utilisent doivent maintenant être créés à l’aide d’un Blob d’Arborescence Matérielle (DTB = Device Tree Blob) transmis au noyau par le loader (start.elf). (voir la page 11 de Device Tree for the Dummies)

device_tree_for_dummies_600px

Le principal impact de l’utilisation de l’Arborescence Matérielle est de changer d’approche. On part d’une situation où tout est validé, une liste noire (blacklist) permettant d’éliminer les modules inutiles. Ici rien n’est validé, sauf les modules indiqués par la DTB. Si vous voulez continuer à utiliser des interfaces externes ainsi que les périphériques qui s’y rattachent, vous allez devoir ajouter de nouveaux paramètres à votre fichier config.txt. La partie 3 de cet article donne des informations plus détaillées, en attendant voici quelques exemples :

# Uncomment some or all of these to enable the optional hardware interfaces
#dtparam=i2c_arm=on
#dtparam=i2s=on
#dtparam=spi=on

# Uncomment one of these lines to enable an audio interface
#dtoverlay=hifiberry-amp
#dtoverlay=hifiberry-dac
#dtoverlay=hifiberry-dacplus
#dtoverlay=hifiberry-digi
#dtoverlay=iqaudio-dac
#dtoverlay=iqaudio-dacplus

# Uncomment this to enable the lirc-rpi module
#dtoverlay=lirc-rpi

# Uncomment this to override the defaults for the lirc-rpi module
#dtparam=gpio_out_pin=16
#dtparam=gpio_in_pin=17
#dtparam=gpio_in_pull=down

Partie 1 : Les Arborescences Matérielles (DT)

Une Arborscence Matérielle (DT = Device Tree) est une façon de décrire le matériel présent dans un système. Il doit inclure le nom de la CPU (microprocesseur) de base, sa configuration mémoire et les périphériques (internes et externes) qui y sont reliés. Une DT ne doit pas être utilisée pour décrire le logiciel, même si en énumérant les modules matériels elle provoque habituellement le chargement des pilotes pour ces modules. Il peut être utile de rappeler que les DT sont censées être neutres par rapport à l’OS, donc tout ce qui est spécifique à Linux n’a probablement pas lieu de s’y trouver…

Les DT représentent la configuration matérielle sous la forme d’une hiérarchie de nœuds. Chaque nœud peut contenir des propriétés ainsi que des sous-noeuds. Les propriétés sont appelées tableaux d’octets, et peuvent contenir des chaînes, des nombres (big-endian), des séquences arbitraires d’octets… Par analogie avec un système de fichiers, les nœuds seraient les répertoires et les propriétés seraient les fichiers. Les emplacements des nœuds et des propriétés au sein de l’arborescence peuvent être décrits à l’aide d’un chemin, avec des barres obliques comme séparateurs et une seule barre oblique (/) pour indiquer la racine.

arboresence_Linux

Syntaxe de base des DT

[Cette section emprunte beaucoup à devicetree.org]

Les Arborescences Matérielles sont généralement écrites sous forme de texte, appelés Source de l’Arborscence Matérielle (DTS = Device Tree Source) et stockées dans des fichiers ayant un suffixe .dts.

DTS est une syntaxe C-like, avec des accolades pour le regroupement et des points-virgules à la fin de chaque ligne. ATTENTION /!\. DTS nécessite des points-virgules après les accolades fermantes – pensez plutôt aux structures du C plutôt qu’aux fonctions.

Le format binaire compilé est appelé Arborescence Matérielle Aplatie (FDT = Flattened Device Tree) ou Blob d’Arborescence Matérielle (DTB = Device Tree Blob), et est stocké dans des fichiers .dtb.

Voici un exemple d’arborescence simple au format .dts :

/dts-v1/;
/include/ "common.dtsi";

/ {
    node1 {
        a-string-property = "A string";
        a-string-list-property = "first string", "second string";
        a-byte-data-property = [0x01 0x23 0x34 0x56];
        cousin: child-node1 {
            first-child-property;
            second-child-property = ;
            a-string-property = "Hello, world";
        };
        child-node2 {
        };
    };
    node2 {
        an-empty-property;
        a-cell-property = < 1 2 3 4>; /* chaque nombre (cell) est un uint32 */
        child-node1 {
            my-cousin = <&cousin>;
        };
    };
};

/node2 {
    another-property-for-node2;
};

Cet arbre contient

  • Une liste d’Item
  • Un en-tête obligatoire — / dts-v1 /.
  • L’inclusion d’un autre fichier DTS, classiquement appelé *.dtsi, analogue à un fichier d’en-tête .h en C – voir ci dessous « Un aparté sur /include/ ».
  • Un seul nœud racine : /
  • Deux nœuds enfants : node1 et node2 .
  • Des enfants pour node1: child-node1 et child-node2 .
  • Un label (étiquette) ( cousin ) et une référence à ce label ( &cousin ) – voir les labels et références ci-dessous.
  • Un tas de propriétés dispersés à travers l’arborescence.
  • Un nœud répété ( /node2 ) – voir ci dessous « Un aparté sur /include/ ».

Les propriétés sont de simples paires clévaleurvaleur peut être vide ou contenir un flux d’octets quelconque. Bien que les types de données ne soient pas encodées dans la structure de données, il y a quelques types de données fondamentales qui peuvent être utilisés dans un fichier source d’Arborescence Matérielle.

Les chaînes de caractères (terminées par NUL) sont indiqués par des guillemets :

string-property = "a string";

Les «cellules» sont des entiers sur 32 bits délimités par < et > :

cell-property = <0xbeef 123 0xabcd1234>;

Des données sous forme d’octets arbitraires sont délimitée par des crochets, et saisies en hexadécimal :

binary-property = [01 23 45 67 89 ab cd ef];

Les données de types différentes peuvent être concaténées en utilisant une virgule :

mixed-property = "a string", [01 23 45 67], <0x12345678>;

Les virgules sont également utilisé pour créer des listes de chaînes :

string-list = "red fish", "blue fish";

Un aparté sur /include/

La directive /include/ provoque une inclusion de texte simple, un peu comme la directive #include de C, mais une caractéristique du compilateur d’Arborescence Matérielle conduit à différents modes d’utilisation. Étant donné que les nœuds sont nommés, éventuellement avec des chemins absolus, il est possible pour le même nœud apparaisse deux fois dans un fichier DTS (et ses inclusions). Lorsque cela arrive, les nœuds et les propriétés sont combinés, écrasant les propriétés (les dernières valeurs remplacent les valeurs précédentes).

Dans l’exemple ci-dessus, la deuxième apparition de /node2 fait qu’une nouvelle propriété va être ajoutée à l’original :

/node2 {
    an-empty-property;
    a-cell-property = <1 2 3 4>; /* chaque nombre (cell) est un uint32 */
    another-property-for-node2;
    child-node1 {
        my-cousin = <&cousin>;
    };
};

 

Il est ainsi possible pour un .dtsi de remplacer (ou de fournir des valeurs par défaut) à différents endroits d’une arborescence.

Labels et Références

Il est souvent nécessaire pour une partie de l’arbre de se référer à une autre partie, et il y a quatre façons de le faire :

Chemin sous forme de chaîne de caractères

Les chemins doivent être explicites, par analogie à un système de fichiers – /soc/i2S@7e203000 est le chemin complet vers le périphérique I2S dans un BCM2835 et un BCM2836. Notez que même s’il est facile de construire un chemin d’accès à une propriété (/soc/i2S@7e203000/état – voilà, c’est fait), les API standard ne le font pas ; vous trouvez tout d’abord un nœud, puis vous choisissez les propriétés de ce nœud.

phandles

Un phandle est un entier de 32 bits unique attribué à un nœud dans sa propriété phandle. (Pour des raisons historiques, vous verrez aussi un doublon, correspondant à linux,phandle). Les phandle sont numérotés de manière séquentielle à partir de 1 – 0 est pas un phandle valable – et sont généralement attribués par le compilateur DT quand il rencontre une référence à un nœud dans un contexte d’entiers, généralement sous la forme d’un label (voir ci-dessous). Les références aux nœuds à l’aide phandles sont simplement encodées en tant que la valeur de cet entier (cell) ; il n’y a pas de balisage pour indiquer qu’ils doivent être interprétés comme phandles – c’est défini au niveau de l’application.

Labels

Tout comme une étiquette en C donne un nom à un endroit dans le code, un label – ou étiquette – de DT attribue un nom à un noeud dans la hiérarchie. Le compilateur prend les références aux labels et les convertit en chemins lorsqu’il est utilisé dans un contexte de chaîne ( &node ) et en phandles un contexte d’entiers ( <&node> ) ; les étiquettes d’origine ne figurent pas dans la sortie compilée. Notez que les étiquettes ne contiennent pas de structure – ce ne sont que des repères dans un espace de noms global et plat.

Alias

Les Alias sont similaires aux étiquettes, sauf qu’ils apparaissent dans la sortie FDT comme une espèce d’index. Ils sont stockés en tant que propriétés du nœud /aliases avec chaque propriété reliant un nom d’alias à un chemin sous forme de chaîne de caractères. Bien que le nœud aliases apparaisse dans la source, les chaînes de chemin apparaissent généralement comme des références aux labels ( &node ) plutôt que d’être écrites en toutes lettres. Les APIs DT qui résolvent une chaîne de chemin en nœud lisent généralement le premier caractère du chemin, et traitent les chemins qui ne commencent pas par un slash comme les alias qui doivent d’abord être convertis en chemin en utilisant la table /aliases .

Sémantique de l’Arborescence Matérielle

Comment construire une Arborescence Matérielle – la meilleure façon est encore de récupérer la configuration de certains matériels – est un sujet vaste et complexe. Il y a beaucoup de ressources disponibles, dont certaines sont énumérées ci-dessous, et ce document ne veut pas en être un autre. Mais il y a certaines choses qui méritent d’être abordées.

Les propriétés compatible sont le lien entre la description du matériel et le logiciel du pilote. Quand un OS rencontre un nœud avec une propriété compatible il consulte sa base de données de pilotes de périphériques pour trouver celui qui convient le mieux. Sous Linux cela aboutit habituellement au chargement automatique du module, à condition qu’il ait été correctement désigné et qu’il ne figure pas sur la liste noire (blacklist).

La propriété status indique si un périphérique est activé ou désactivé. Si status vaut ok, okay ou est absent, alors le périphérique est activé. Sinon status doit être disabled, ce qui signifie que ce que vous pensez que cela signifie 😉 le périphérique est désactivé. Il peut être utile de placer des périphériques dans un fichier .dtsi avec un status défini sur disabled. Une configuration dérivée peut alors inclure ce .dtsi et définir l’état des périphériques nécessaires à okay.

Voici quelques liens vers des articles sur l’écriture de l’Arborescence Matérielle :

Partie 2 : Overlays d’Arborescence Matérielle

Un SoC moderne (System-on-Chip) est un circuit très compliqué – une arborescence de ce circuit complet pourrait nécessiter plusieurs centaines de lignes. Si on va un peu plus loin et qu’on place le SoC sur une carte avec d’autres composants, ça ne fait qu’aggraver les choses. Pour que tout cela reste gérable, surtout si certains périphériques partagent des composants, il est logique de mettre les éléments communs dans les fichiers .dtsi qui seront éventuellement inclus dans plusieurs fichiers .dts.

Mais quand un système comme LE Raspberry Pi peut aussi accueillir des cartes d’extension, comme les cartes HAT, le problème ne fait qu’empirer ! En fin de compte, chaque configuration possible nécessite une Arborescence Matérielle pour la décrire, mais une fois que vous avez pris en compte les différents modèles de base (modèles A, B, A+ et B+, Pi2) et les gadgets qui ne nécessitent que l’utilisation de quelques broches du GPIO et qui peuvent coexister, le nombre de combinaisons commence à croître très rapidement.

Ce qu’il faut c’est une façon de décrire ces composants optionnels en utilisant des Arborescences Matérielles (DT) partielles, puis d’être capable de construire une arborescence complète en prenant un DT de base et en lui ajoutant un certain nombre d’éléments optionnels. Eh bien c’est possible, et ces éléments optionnels sont appelés « overlays« .

Fragments

Un overlay d’Arborescence Matérielle (DT) comprend un certain nombre de fragments, dont chacun cible un nœud  (et ses sous-nœuds). Bien que le concept semble assez simple, la syntaxe parait plutôt étrange au premier abord :

// Enable the i2s interface
/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2708";

    fragment@0 {
        target = <&i2s>;
        __overlay__ {
            status = "okay";
        };
    };
};

La chaîne compatible identifie ce point comme étant le bcm2708, qui est l’architecture de base de cette partie du BCM2835. Pour le BCM2836 vous pouvez utiliser une chaîne compatible de type « BRCM, bcm2709 », mais à moins que vous ne cibliez les caractéristiques propres des CPU ARM, les deux architectures devraient être équivalentes, donc utiliser « BRCM, bcm2708 » peut convenir. Puis vient le premier (et dans ce cas seulement) fragment. Les fragments sont numérotés de manière séquentielle à partir de zéro. Le non respect de ceci peut faire que certains de vos fragments voir la totalité peuvent manquer.

Chaque fragment est constitué de deux parties – une propriété  target  , identifiant le nœud auquel appliquer l’overlay et l’ __overlay__ lui-même, dont le corps est ajouté au nœud cible. L’exemple ci-dessus peut être interprété comme s’il avait été écrit comme ceci :

/dts-v1/;

/ {
    compatible = "brcm,bcm2708";
};

&i2s {
    status = "okay";
};

L’effet de la fusion de cet overlay avec l’Arborescence Matérielle de base  du Raspberry Pi (bcm2708rpibplus.dtb, par exemple), à condition que l’overlay soit chargé en second, serait d’activer l’interface I2S en passant son statut en okay. Mais si vous essayez de compiler cet overlay en utilisant :

dtc -I dts -O dtb -o 2nd-overlay.dtb 2nd-overlay.dts

Vous allez obtenir une erreur :

Label or path i2s not found

Cela n’est pas étonnant, car il n’y a aucune référence au fichier .dtb de base ou .dts permettant au compilateur de trouver l’étiquette i2S.

Essayez à nouveau, cette fois avec l’exemple d’origine :

dtc -I dts -O dtb -o 1st-overlay.dtb 1st-overlay.dts

vous aurez une ou deux erreurs :

Si dtc retourne une erreur à propos de la troisième ligne, alors il n’a pas les extensions requises pour que l’overlay fonctionne. La directive  /plugin/ signale au compilateur qu’il doit générer des informations de lien pour que les symboles non résolus puissent être corrigées plus tard.

Pour installer un dtc appropriés sur un Raspberry Pi, tapez :

sudo apt-get install device-tree-compiler

Sur les autres plateformes, vous avez deux options : si vous téléchargez les sources du noyau depuis le github de raspberrypi et faites  make ARCH=arm dtbs vous obtiendrez un dtc utilisable dans le répertoire scripts/dtc. Sinon, suivez ces étapes dans un répertoire utilisable :

wget -c https://raw.githubusercontent.com/RobertCNelson/tools/master/pkgs/dtc.sh
chmod +x dtc.sh
./dtc.sh

Remarque : Ce script va télécharger la source principale, appliquer quelques patches, puis compiler et installer. Vous pouvez modifier dtc.sh avant de l’exécuter pour modifier le chemin de téléchargement (actuellement ~/ git/dtc) et le chemin d’installation (/usr/local/bin).

Si vous voyez   Reference to non-existent node or label « i2s »   tout ce que vous avez à faire est de modifier la ligne de commande pour indiquer au compilateur d’autoriser les symboles non résolus, en ajoutant @ :

dtc -@ -I dts -O dtb -o 1st-overlay.dtb 1st-overlay.dts

Cette fois, la compilation devrait se terminer avec succès. Il est intéressant de lister le contenu du fichier DTB pour voir ce que le compilateur a généré :

$ fdtdump 1st-overlay.dtb

/dts-v1/;
// magic:           0xd00dfeed
// totalsize:       0x106 (262)
// off_dt_struct:   0x38
// off_dt_strings:  0xe8
// off_mem_rsvmap:  0x28
// version:         17
// last_comp_version:    16
// boot_cpuid_phys: 0x0
// size_dt_strings: 0x1e
// size_dt_struct:  0xb0

/ {
    compatible = "brcm,bcm2708";
    fragment@0 {
        target = <0xdeadbeef>;
        __overlay__ {
            status = "okay";
        };
    };
    __fixups__ {
        i2s = "/fragment@0:target:0";
    };
};

 

Après la description détaillée de la structure du fichier, voici notre fragment. Mais regardez attentivement – où nous avions écrit &I2S, il est maintenant écrit 0xdeadbeef, un indice que quelque chose d’étrange s’est passé… Après le fragment il y a un nouveau nœud, __fixups__. Il contient une liste de propriétés reliant les noms des symboles non résolus aux listes de chemins vers des cellules dans les fragments qui doivent être patchée avec le pHandle du nœud cible, une fois que la cible a été localisé. Dans ce cas, le chemin a la valeur 0xdeadbeef de target, mais des fragments peuvent contenir d’autres références non résolues qui nécessiteraient des modifications supplémentaires.

Si vous écrivez des fragments plus compliqués le compilateur peut générer deux noeuds supplémentaires __local_fixups__ et __symbols__. Le premier est nécessaire si un nœud dans les fragments a un pHandle, parce que le programme qui effectue la fusion devra veiller à ce que les numéros de pHandle soient séquentiels et uniques, mais le dernier est la clé qui indique comment les symboles non résolus seront traitées.

Retour à la section 2.3 où il est dit que « les étiquettes originales ne figurent pas dans la sortie compilée« , mais cela n’est pas vrai lorsque vous utilisez le commutateur -@. Au lieu de cela, toutes les étiquettes résultent d’une propriété du nœud __symbols__, reliant une étiquette à un chemin, exactement comme le nœud aliases. En fait, le mécanisme est si semblable que lors de la résolution de symboles, le chargeur du Raspberry Pi va rechercher le nœud « aliases » en l’absence d’un nœud __symbols__. Cela est utile car en fournissant alias suffisamment d’alias, nous pouvons utiliser un ancien DTC pour construire les fichiers DTB de base.

Paramètres de l’Arborescence Matérielle

 Pour éviter d’avoir une superposition d’overlays dans l’Arborescence Matérielle, et (nous l’espérons) pour limiter l’écriture des fichiers DTS aux seuls fabricants de périphériques, le chargeur du Raspberry Pi prend en charge une nouvelle fonctionnalité – les paramètres d’Arborescence Matérielle. Cela permet de petits changements au DT en utilisant des paramètres nommés, un peu comme les modules du noyau peuvent recevoir des paramètres de modprobe et de la ligne de commande du noyau. Les paramètres peuvent être fournis par les DTB de base et par les overlays, y compris les overlays HAT.

Les paramètres sont définis dans les DTS par addition d’un noeud __overrides__ à la racine. Il contient des propriétés dont les noms sont les noms des paramètres choisis, et dont les valeurs sont une séquence comprenant un pHandle (référence à un label) pour le nœud cible, ainsi qu’ une chaîne indiquant la propriété cible. string, integer (cellule) et les propriétés booléennes sont pris en charge.

Paramètres string

Les paramètres string se déclarent ainsi :

name = <&label>,"property";

label et property sont remplacés par les valeurs appropriées. Les paramètres string peuvent faire croître, décroître ou créer leurs propriétés cibles.

Notez que les propriétés appelées status sont traités spécialement – les valeurs  non-zero/true/yes/on  sont converties en la chaîne « okay », alors que zero/false/no/off   deviennent « disabled ».

Paramètres entiers

Les paramètres entiers sont déclarées comme ceci :

name = <&label>,"property.offset"; // 8-bit
name = <&label>,"property;offset"; // 16-bit
name = <&label>,"property:offset"; // 32-bit
name = <&label>,"property#offset"; // 64-bit

label, property et offset sont remplacés par les valeurs appropriées; l’offset (décalage) est spécifié en octets par rapport au début de la propriété (en décimal par défaut), et le séparateur précédent impose la taille du paramètre. Les paramètres entiers doivent se référer à une partie existante d’une propriété – ils ne peuvent pas faire croître leurs propriétés cibles.

Paramètres BOOLÉENS

L’Arborescence Matérielle code les valeurs booléennes comme des propriétés de longueur nulle – si elle existe alors la propriété est vraie, sinon elle est fausse. Ils sont définis comme suit :

boolean_property; // Set 'boolean_property' to true

Notez que la propriété se voit attribuer la valeur false simplement en ne la définissant pas. Les paramètres booléens sont déclarées comme ceci :

name = <&label>,"property?";

label et property sont remplacés par les valeurs appropriées. Les paramètres booléens peuvent faire qu’une propriété soit crée ou supprimée.

 Exemples

Voici quelques exemples des différents types de propriétés, avec les paramètres pour les modifier :

/ {
    fragment@0 {
        target-path = "/";
        __overlay__ {

            test: test_node {
                string = "hello";
                status = "disabled";
                bytes = /bits/ 8 <0x67 0x89>;
                u16s = /bits/ 16 <0xabcd 0xef01>;
                u32s = /bits/ 32 <0xfedcba98 0x76543210>;
                u64s = /bits/ 64 < 0xaaaaa5a55a5a5555 0x0000111122223333>;
                bool1; // Defaults to true
                       // bool2 defaults to false
            };
        };
    };

    __overrides__ {
        string =      <&test>,"string";
        enable =      <&test>,"status";
        byte_0 =      <&test>,"bytes.0";
        byte_1 =      <&test>,"bytes.1";
        u16_0 =       <&test>,"u16s;0";
        u16_1 =       <&test>,"u16s;2";
        u32_0 =       <&test>,"u32s:0";
        u32_1 =       <&test>,"u32s:4";
        u64_0 =       <&test>,"u64s#0";
        u64_1 =       <&test>,"u64s#8";
        bool1 =       <&test>,"bool1?";
        bool2 =       <&test>,"bool2?";
    };
};

 Paramètres avec des cibles multiples

Il est parfois pratique de pouvoir régler la même valeur dans plusieurs endroits de l’Arborescence Matérielle. Plutôt que de créer maladroitement plusieurs paramètres, il est possible d’ajouter des cibles multiples à un seul paramètre en les enchaînant, comme ceci:

    __overrides__ {
        gpiopin = <&w1>,"gpios:4",
                  <&w1_pins>,"brcm,pins:0";
        ...
    };

Notez qu’il est même possible de cibler des propriétés de types différents avec un seul paramètre. Vous pourriez raisonnablement connecter un paramètre « enable » pour une string status , les cellules contenant zéro ou un, et une propriété booléenne correcte.

D’autres exemples d’overlays

Il y a une quantité croissante de fichiers sources d’overlays hébergés dans le dépot raspberrypi/linux github.

Partie 3 : Utilisation de l’Arborescence Matérielle sur le Raspberry Pi

Overlays et config.txt

Sur le Raspberry Pi c’est au chargeur (une des images start*.elf) de combiner les overlays avec une arborescence matérielle de base appropriée, puis de transmettre une arborescence matérielle entièrement résolue au noyau. Les arborescences matérielles de base sont situés près de start.elf dans la partition FAT (/boot de Linux), elles sont appelés bcm2708-RPI-b.dtb, bcm2708-RPI-b-plus.dtb, bcm2708-RPI-cm.dtb et bcm2709- RPI-2-b.dtb. Notez que les Modèles A et A+ utiliseront les variantes « b » et « b-plus », respectivement. Cette sélection est automatique et permet à une même image de carte SD d’être utilisée sur les différents Raspberry Pi.

ATTENTION : DT et ATAGs sont mutuellement exclusifs. En conséquence, le passage d’un blob DT à un noyau qui ne le comprend pas bloque le démarrage (boot failure). Pour éviter cela, le chargeur vérifie la compatibilité des images du noyau avec les DT. Elle est indiquée par un marqueur ajouté par l’utilitaire mkknlimg (que vous trouverez ici, ou dans le répertoire des scripts d’une source récente du noyau). Tout noyau sans marqueur est supposé être incapable d’utiliser le DT.

Le chargeur prend désormais également en charge les builds utilisant bcm2835_defconfig, qui supportent le BCM2835. Cette configuration va provoquer la construction de  bcm2835-RPI-b.dtb et bcm2835-RPI-b-plus.dtb. Si ces fichiers sont copiés avec le noyau, et si le noyau a été marqué par un mkknlimg récent, le chargeur va essayer de charger un de ces DTB par défaut.

Afin de gérer l’Arborescence Matérielle et les overlays, le chargeur prend en charge un certain nombre de nouvelles directives de config.txt :

dtoverlay=acme-board
dtparam=foo=bar,level=42

Ceci amène le chargeur à rechercher  overlays/acme-board-overlay.dtb dans la partition du firmware, que Raspbian monte sur /boot. Il recherche alors les paramètres foo et level, et leur attribue les valeurs indiquées.

Le chargeur recherchera également une carte HAT avec une mémoire EEPROM programmée, et chargera l’overlay correspondant : cela se produit sans aucune intervention de l’utilisateur.

Il ya plusieurs façons de dire que le noyau utilise l’Arborescence Matérielle :

  1. Le « Machine Model : » un message du noyau pendant le démarrage affiche une valeur spécifique à la carte comme « Raspberry Pi 2 Model B », plutôt que « BCM2709 ».
  2. Quelque temps plus tard, un autre message du noyau indique « No ATAGs? » – C’est ce qui est attendu.
  3. /proc/device-tree existe, et contient des sous-répertoires et des fichiers qui reflètent exactement les nœuds et les propriétés du DT.

Avec une Arborescence Matérielle, le noyau va rechercher automatiquement et charger les modules qui prennent en charge les périphériques activés indiqués. Par conséquent, si vous créez l’overlay de DT approprié pour un périphérique, vous évitez aux utilisateurs du périphérique d’avoir à modifier /etc/modules – toute la configuration se passe dans config.txt (dans le cas d’une carte HAT, même cette étape devient inutile). Notez, cependant, que les modules comme i2c-dev doivent toujours être chargée explicitement.

L’autre conséquence c’est que puisque les périphériques de la plate-forme ne sont pas créés, sauf sur demande du DTB, il ne devrait plus être nécessaire de blacklister les modules habituellement chargés du fait de leur définition dans le code de la carte. En fait, les images actuelles de Raspbian sont fournies sans fichier blacklist (liste noire).

Paramètres du DT

Comme décrit ci-dessus, les paramètres du DT sont un moyen pratique d’appliquer de petits changements à la configuration d’un périphérique. Les DTB de base actuels acceptent des paramètres pour activer et contrôler les interfaces I2C, I2S et SPI sans utiliser d’overlays dédiés. Les paramètres ressemblent à ceci :

dtparam=i2c_arm=on,i2c_arm_baudrate=400000,spi=on

Notez que plusieurs affectations peuvent être placés sur la même ligne (mais il ne faut pas dépasser 80 caractères (à moins que ce soit 79 ?). Parce que ce serait mal).

Un futur config.txt par défaut pourrait contenir une section comme celle-ci :

# Uncomment some or all of these to enable the optional hardware interfaces
#dtparam=i2c_arm=on
#dtparam=i2s=on
#dtparam=spi=on

 

Si vous avez un overlay qui définit certains paramètres, ils peuvent être spécifié soit sur des lignes consécutives comme ceci :

dtoverlay=lirc-rpi
dtparam=gpio_out_pin=16
dtparam=gpio_in_pin=17
dtparam=gpio_in_pull=down

soit ajoutés à la ligne de l’overlay comme ceci :

dtoverlay=lirc-rpi:gpio_out_pin=16,gpio_in_pin=17,gpio_in_pull=down

Notez ici l’utilisation de deux points ( : ) pour séparer le nom de l’overlay de ses paramètres, ce qui est une variante valable de la syntaxe.

Les paramètres d’overlay ne sont accessible que jusqu’au chargement de l’overlay suivant. Dans le cas où un paramètre portant le même nom est exporté à la fois par l’overlay et par la base (ne pas le faire – c’est source de confusion), le paramètre dans l’overlay est prioritaire. Pour utiliser le paramètre exporté par la base DTB , il faut terminer l’overlay par :

dtoverlay=

Labels et paramètres spécifiques à la carte

Les cartes Raspberry Pi ont deux interfaces I2C. Ce sont séparées d’origine – une pour l’ARM (CPU) et une pour le VideoCore (le «GPU»). Sur presque tous les modèles, i2c1 appartient à l’ARM et i2c0 au GPU, où elle est utilisé pour contrôler la caméra et lire l’EEPROM de la carte HAT. Cependant, dans les deux premières révisions du modèle B (les plus anciennes) ces rôles étaient inversés.

Pour rendre possible l’utilisation d’un jeu d’overlays et de paramètres avec tous les Raspberry Pi, le firmware crée certains paramètres de DT spécifiques à la carte. Ce sont :

<pre>i2c/i2c_arm
i2c_vc
i2c_baudrate/i2c_arm_baudrate
i2c_vc_baudrate</pre>

Ce sont des alias pour i2c0, i2c1, i2c0_baudrate et i2c1_baudrate. Il est recommandé d’utiliser uniquement i2c_vc et i2c_vc_baudrate si vous en avez vraiment besoin – par exemple, si vous programmez une mémoire EEPROM de carte HAT.  L’activation de I2c_v c peut par exemple empêcher la détection de la caméra Pi.

Pour les gens qui écrivent des overlays, le même aliasing a été appliqué aux labels sur les nœuds I2C du DT. Ainsi, vous devriez écrire :

fragment@0 {
    target = <&i2c_arm>;
    __overlay__ {
        status = "okay";
    };
};

Tous les overlays utilisant les variantes numériques seront modifiés pour utiliser les nouveaux alias.

Carte HAT et Arborescence Matérielle

Une carte HAT pour Raspberry Pi est une carte d’extension (add-on) pour une carte de format « Plus » (A+, B+ ou Pi 2 B) avec une mémoire EEPROM intégrée. L’EEPROM comprend l’overlay au DT nécessaire pour activer la carte, et cet overlay peut aussi fournir des paramètres.

L’overlay de la carte HAT est automatiquement chargé par le firmware après la DTB de base, de sorte que ses paramètres sont accessibles jusqu’au chargement d’un autre overlay (ou jusqu’à ce que la portée de l’overlay soit terminée en utilisant dtoverlay =. Si pour une raison quelconque vous souhaitez supprimer le chargement de l’overlay de la carte HAT, indiquez dtoverlay = avant tout autre directive dtoverlay ou dtparam.

Overlays et paramètres pris en charge

Plutôt que de documenter les overlays individuels ici, nous vous invitons à lire le fichier README qui se trouve dans le répertoire des fichiers overlay  /boot/overlays. Il est régulièrement mis à jour et inclut des ajouts et modifications.

PARTIE 4 : Dépannage et astuces

Debugging

Le chargeur va ignorer les overlays manquants et les mauvais paramètres, mais s’ il y a des erreurs graves comme un dtb de base manquant ou endommagé ou une fusion d’overlay échouée, le chargeur va revenir à un boot sans DT. Si cela se produit, ou si vos paramètres ne se comportent pas comme prévu, il vaut mieux vérifier les avertissements et erreurs du chargeur :

sudo vcdbg log msg

Le débogage supplémentaire peut être activée en ajoutant dtdebug = 1 à config.txt.

Si le noyau ne parvient pas à démarrer en mode DT, c’est probablement parce que l’image du noyau n’a pas un marqueur valide. Utilisez knlinfo pour vérifier si ce marqueur existe, et l’utilitaire mkknlimg pour en ajouter un. Notez que les deux utilitaires sont également inclus dans le répertoire de scripts des souces actuelles du noyau du raspberrypi.

Vous pouvez créer une représentation (semi-)lisible par un humain de l’état actuel du DT comme ceci :

dtc -I fs /proc/device-tree

ce qui peut être utile pour voir l’effet de la fusion d’overlays sur l’arborescence d’origine.

Si les modules du noyau ne se chargent pas comme prévu, vérifier qu’ils ne sont pas dans la liste noire (dans /etc/modprobe.d/raspi-blacklist.conf); il ne devrait pas être nécessaire de les mettre en liste noire lorsque vous utilisez l’Arborescence Matérielle. Si cela ne donne rien, vous pouvez également vérifier que le module exporte les bons alias corrects en vérifiant que la valeur compatible existe dans  /lib/modules/<version>/modules.alias. Si ce n’est pas le cas, votre driver n’avait pas soit :

.of_match_table = xxx_of_match,

ou :

MODULE_DEVICE_TABLE(of, xxx_of_match);

A défaut, depmod a échoué ou les modules mis à jour n’ont pas été installés sur le système de fichiers cible.

Force une Arborescence Matérielle spécifique

Si vous avez des besoins très spécifiques qui ne sont pas pris en charge par les DTB par défaut (en particulier, les gens qui expérimentent avec l’approche tout-DT utilisée par le projet de ARCH_BCM2835), ou si vous voulez juste vous lancer dans l’écriture de vos propres DT, vous pouvez forcer le chargeur à charger un fichier DTB alternatif de la façon suivante :

device_tree=my-pi.dtb

Désactiver l’utilisation de l’Arborescence Matérielle

Si vous décidez que le DT n’est pas pour vous (ou à des fins de diagnostic), vous pouvez désactiver le charfment du DT et forcer le noyau à revenir à l’ancien comportement en ajoutant :

device_tree=

à config.txt. Notez, cependant, que les futures versions du noyau pourraient à un moment donné ne plus soutenir cette option.

Raccourcis et variantes de syntaxe

Le chargeur accepte quelques raccourcis :

dtparam=i2c_arm=on
dtparam=i2s=on

peut être réduit en :

dtparam=i2c,i2s

(i2c est un alias de i2c_arm, et  =on est par défaut). Il accepte également toujours les versions longues – device_tree_overlay et device_tree_param.

Vous pouvez également utiliser des séparateurs alternatifs si vous pensez que = est galvaudé. Tout ce qui suit est accepté :

dtoverlay thing:name=value,othername=othervalue
dtparam setme andsetme='long string with spaces and "a quote"'
dtparam quote="'"

Ces exemples utilisent des espaces pour séparer la directive du reste de la ligne au lieu de =. Ils utilisent également une virgule pour séparer l’overlay de ses paramètres, et setme prend la valeur par défaut 1/true /on/okay.

Conclusion

Qu’on le veuille ou non, l’utilisation de l’Arborescence Matérielle devient la norme sur le Raspberry Pi. Les utilisateurs « lamba » n’ont pas à s’en inquiéter, et cela devrait même leur faciliter la vie.

Ceux qui sont concernés sont ceux qui interviennent dans la création de cartes d’extension, en particulier les cartes HAT, ou dans le développement de périphériques destinés au Raspberry Pi.

Avec cette traduction, j’espère avoir fourni un point de départ pour tous ceux qui sont intéressés mais rechignent à se lancer dans la doc. en anglais.

Cette traduction n’est certainement pas parfaite et si vous pensez que certaines parties (mots, phrases, tournures…) méritent d’être revues, n’hésitez pas à me contacter via les commentaires ci-dessous !

Sources

Share Button

À propos François MOCQ

Électronicien d'origine, devenu informaticien, et passionné de nouvelles technologies, formateur en maintenance informatique puis en Réseau et Télécommunications. Dès son arrivée sur le marché, le potentiel offert par Raspberry Pi m’a enthousiasmé j'ai rapidement créé un blog dédié à ce nano-ordinateur (www.framboise314.fr) pour partager cette passion. .

12 réflexions au sujet de « Un point sur le Device Tree »

  1. fabrice

    Existe t’il un BON bouquin COMPLET sur LINUX, (mis a part ceux de christophe Blaes) ?
    la question est ouverte a Tous,
    [ avec l’aimable autorisation de François !!]

    Répondre
    1. François MOCQ Auteur de l’article

      sans problème !
      bonne recherche
      cordialement
      François
      ps : dans mon livre il y a un chapitre pour démarrer sous Linux mais il n’est pas « complet » dans le sens où il ne concerne pas TOUT Linux (mais je ne sais pas si ça existe un livre complet ?)

      Répondre
      1. fabrice

        j’ai lu votre livre, j’ai meme racheté la V2 !!!!
        tout est partis du PI, j’ai profité de cette machine pour apprendre linux!!!!!!
        mon pc perso est desormais un DUAL boot abec ubuntu—-> je change de camps

        mais je vois sans arret des manip ‘exotiques’ que je en comprend pas toujours
        par exemple pou rester tres simple quelques exemples qui me viennent:

        parfois on fait apt get et d’autre fois un aptitude, pourquoi ?

        en C c’est quoi cette histoire de projeter dans un espace user les gpio ?

        bref, disons que les manip pour se deplacer, copier des ficheirs et des reopertoires ça va
        mais alors le reste, a commencer par l’arborescence du systeme
        qui est ou pourquoi.

        en resumé LA QUESTION que j’aimerai poser a ceux qui sont dessus depuis longtemps c’est comment avez vous fait, comment aprendre et progresser, car, je suis vraiment interessé mais disons que je ne sais faire que ce que j’ai trouvé dans des livres sur le PI.

        recement j’ai fait pour un ami une carte electronique, j’ai fais le pcb la carte et un code en python.
        mais linux, c’est du C avant tout et il me faut des infos mais des infos utiles et bien ecrites.

        Répondre
        1. François MOCQ Auteur de l’article

          Bonjour Fabrice
          Bravo pour cette « migration » et bon amusement
          effectivement ce n’est pas toujours simple… Au boulot les deux environnements coexistent et parfois on voit qu’une manip qui se fait sous Windows server en qques clics demande plus (parfois beaucoup plus 😉 ) de temps sous Linux… Mais au moins on sait si on le souhaite on sait ce qui se passe et pour quoi ça se passe. Sou Windows… beaucoup moins 🙁
          Concernant apt-get et aptitude ce sont deux interfaces pour dpkg. apt-get est juste un niveau au dessus. aptitude plus récent amène un peu plus d’abstraction. Ce qui est recommandé c’st d’en choisir un et de s’y tenir pour éviter les soucis avec le système. Pour ma part je suis resté fidèle à apt-get… (http://askubuntu.com/questions/347898/whats-difference-of-apt-get-and-aptitude)
          Après pour le reste il faut interroger Internet (Google est ton ami, enfin… juste pour ça 😉 )
          A une époque il y avait d’ailleurs un moteur de recherche dédié à Linux chez Google mais… il a disparu
          Donc vous pouvez chercher pour ce qui est dans le message : what is user space
          kernel space est la zone mémoire où le code du noyau réside, et est exécuté
          user space est la zone ou les processus des utilisateurs sont exécutés. Ils disposent de zones mémoires distinctes et le noyau veille à cette séparation et au fait que les users ne puissent pas accéder à la machine. D’où l’obligation du sudo pour accéder au GPIO… http://unix.stackexchange.com/questions/87625/what-is-difference-between-user-space-and-kernel-space
          En résumé, je dirai que ce sera difficile de trouver UN livre donnant toutes les infos.
          Il vaut mieux se tourner vers Internet en prêtant attention à la source (un site de marque ou de distribution est plus fiable qu’un blog), à la date de parution (parfois on trouve des tutoriels qui ont 6 ou 7 ans et ne sont plus du tout adaptés… en informatique un an c’est déjà vieux!), à la version (la aussi certains tutoriels sont publiés sans que soient précisés la distribution, la version utilisée, les prérequis… méfiance !).
          Bon courage mais vous êtes déjà sur la bonne voie ! 😀
          Cordialement
          François

          Répondre
  2. fabrice

    merci beaucoup françois.
    oui j’ai bien conscience que l’on ne devient pas linuxien du jour au lendemain !!!
    mais bon j’ai deja mon ubuntu qui tourne avec les equivalents logiciels que j’avais sous windows.

    je me souvient avoir un peut galeré pour partitionner mon disque dur et faire excatement celle attendu par linux, j’ai preferé le faire avant l’installation car pendant j’avais eu des doutes sur le nom de la partitition !!!

    et puis, apres installation j’ai vui cette consolle grub2 je crois !!!! que c’est son nom qui m’a proposé des choix y compris la partition de sauvegarde de windows qui etatit jusqu’alaors completement invisible.

    la premiere chose que j’ai faite c’est chercher [ le lx terminal d’ubuntu !!!] bah oui moi je sais pas trop
    ensuite je me suis dit je fais faiire les mises a jour comme françois nous a dit
    sudo apt-get update etc…..
    et la ça a marché !!!
    j’etait content!!!

    il y a aussi une chose, linux et gcc sont intimement lié, et je sais ça j’en suis sur que tout linux est livré ! avec ce celebre compilateur.
    ainsi, un ide comme un truc nommé GEANY je crois, les bons includes (la il faut deja les connaitres) et on peut demarrer quelques chosse en compilation directe sur la cible, le pi ou ubuntu.

    tu vois françois c’est toute ces demarches.
    j’ai fait les choses dans l’ordre, j’ai passé un an sur
    le pi
    linux
    python

    maintenant c’est linux que je veux !!!
    (oui je sais je suis pas sortis!!!!)

    j’aime beaucoup lire de bons bouquins bien gros et bien complet (heu comme un certain raspberry pi2 chez eni !!!!)
    je me disais alors que sur linux il y avavit cretainement de bons pavés bien didactiques et bien ecrit.

    ok pour les sites internet, je vais aller voir de ce coté.
    merci encore

    Répondre
  3. Ping : Hackable Magazine N° 9 : Framboise d’honneur ! | Framboise 314, le Raspberry Pi à la sauce française….

  4. blegoff

    Bonjour messieurs je profites de ce très bon article pour poser une question toutes bete mais qui me bloque dans mon travail.

    en gros pour résumer avec un ami nous avons construit un Cube LED qui se dirige grâce au port SPI, nous avons developpé notre petit logiciel qui va bien, et qui s’appui sur la librairies bcm2835.h mais depuis l’implémentation du device tree impossible de le faire fonctionner alors que tout fonctionnait bien avant. (on a mis du temps a comprendre que c’etait cela le probleme et que ce soit sur un nouveau RPI ou un RPI un sur lequel nous avons fait un rpi-update des que le device tree est present plus rien ne fonctionne.

    j’ai bien activé le device tree ainsi que les port spi
    je vois bien les device spidev0.0 spidev0.1
    mais que ce soit en python en C ou meme en envoyant des trames grace a echo, nous ne voyons rien passer sur les ports spi ( nous surveillons les trames grace a un oscilloscope ( bitscope qui marche tres bien )

    j’ai beau cherché des docs qui explique comment faire rien de bien transcendant

    et quand je fais un dtc -I fs /proc/device-tree j’ai bien mes ports SPI donc je n’y compredn rien

    l’un de vous aurais une idée peut etre ?

    Répondre
      1. blegoff

        merci pour votre réponse François.

        bon j’ai eue une idée et je pense avoir trouvé d’ou viens mon problème il faut que je teste.

        en gros après avoir vérifié la librairie que j’utilise dans mon dev, c’est la version 1.36 et apparemment la librairies ne supporte le RPI2 qu’a partir de la 1.40.

        je test je vous ferai un retour ça pourrait en aider certain !

        Répondre
          1. blegoff

            bon je viens de changer la librairies en 1.50 mais pas mieux.

            donc impossible de faire fonctionner le port spi depuis la mise a jour du kernel en 3.18 et l’apparition du device tree.

            quelqu’un aurait un test en python ou autre qui fonctionne sur et certain avec le device tree ?

  5. blegoff

    ok j’ai trouvé une solution en modifiant les sources du spidev_test.c qui n’utilise pas la librairies bcm2835 mais la librairies spi je vais modifier mon code dans ce sens .

    merci pour votre aide en tout cas bonne journée

    Répondre

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Complétez ce captcha SVP * Time limit is exhausted. Please reload CAPTCHA.