Ouvrir le menu Fermer le menu

Taille du code : Combler le fossé entre RISC-V et ARM pour les applications embarquées

trait de séparation
Temps de lecture : 5 minutes
L'un des problèmes rencontrés par les développeurs RISC-V est que la densité de code du jeu d'instructions RISC-V est beaucoup plus élevée que celle des Cortex-M avec les outils existants.

Ce nouveau linker SEGGER pour RISC-V, reprend toutes les fonctionnalités du linker pour les cœurs ARM mais ajoute de nouvelles capacités et des optimisations spécifiques pour le jeu d’instructions 32-bits des RISC-V et des RV32I et RV32E.

Comment le Linker SEGGER pour RISC-V peut produire un code si réduit ?

Le tableau ci-dessous montre l'efficacité du Linker SEGGER pour RISC-V, pour une application de taille réduite :
Linker SEGGER pour RISC-V-tableau
L'éditeur de liens de GNU et de la bibliothèque GNU provient de la plateforme pour RISC-V de SiFive, et sont comparés à la librairie et à l’éditeur de liens développés par SEGGER.

Les résultats montrent que le linker SEGGER fait beaucoup mieux que le linker GNU. De même la librairie Runtime SEGGER pour RISC-V est beaucoup plus performante que la version GNU. La combinaison des deux (qui sont inclus dans l’IDE SEGGER Embedded Studio) permet une incroyable réduction de 76,4% de la taille en flash, en passant de 47784 à 11270 octets, pour la même application. Même le simple fait de re-linker l’application avec le linker SEGGER fait gagner 13%.

Relocalisation : Maitriser où le code et les données vont s'exécuter

L’objectif premier du Linker SEGGER est la réduction de la taille du code afin de réduire l’empreinte mémoire des applications RISC-V. Le compilateur a pour charge de générer le code exécutable et de créer les modules objets relogeable. Comme le compilateur ne sait pas où les fonctions et les données seront finalement implémentés (rôle dévolu au linker), il prend par défaut les hypothèses les plus défavorables pour tous les appels de fonction et les accès aux données globales (à la fois en lecture-écriture et en lecture seule), ce qui induit des séquences de code relativement longues.

L'architecture RISC-V offre des instructions plus petites et plus compactes qui peuvent être utilisées, mais le fait qu'une adresse soit inconnue au moment de la compilation ne permet pas au compilateur de les utiliser car il ne peut pas faire d’assomption, laissant au linker la charge d’optimiser le code

Relaxer : Rendre les choses plus petites et plus rapides

C’est à l’éditeur de liens de résoudre ces références inter-modules définies par le compilateur. Un simple éditeur de liens se contenterait de réaliser cette allocation du code et des données dans la mémoire physique du composant, alors qu’un linker plus évolué peut « relaxer » de manière opportuniste certaines séquences d'instructions en séquences plus petites, lorsque la résolution de l’adresse physique est connue. Un éditeur de liens évolué organiserait même le code et les données en tenant soigneusement compte de la disposition des différentes sections de mémoire.

C’est exactement ce que fait le Linker SEGGER. Il utilise des heuristiques pour paginer le code et les données, de concert avec le script de l'éditeur de liens, afin de maximiser le nombre de « relaxations » qui s'appliquent.

Par exemple, les groupes de fonctions étroitement liés (appels) sont placés à proximité les uns des autres, même s'ils proviennent de fichiers objets différents. Cette implémentation rapprochée permet de remplacer les deux instructions 32 bits définies par le compilateur pour un appel de fonction par une seule instruction 32 bits ou, mieux encore, à une instruction compacte 16 bits.

La relaxation est également bonne pour les données !

Le même principe s’applique aux données. L'accès aux données globales s'effectue généralement en formant une adresse de base de 32 bits dans un registre et en utilisant un principe de décalage pour lire l'élément. L'éditeur de liens GNU peut optimiser cela en employant un pointeur global afin qu'une instruction puisse être éliminée, si les données « courtes » et regroupées. Mais il n'y a aucune intelligence sur la façon dont le pointeur global est placé, ce qui oblige l'utilisateur à regrouper les données et à attribuer un pointeur global manuellement. A l’heure de l’intelligence artificielle pourquoi ne pas laisser la puissance de calcul des ordinateurs actuels pour calculer la meilleure implémentation des données et du pointeur global ? Eh bien, c’est ce que fait pour vous le linker SEGGER : Le choix vous appartient, soit laisser le linker décider soit manuellement vous pouvez regrouper les sections et définir le pointeur global.

Deux valent généralement mieux qu'un

Qu'y a-t-il de mieux qu'un pointeur global ? deux ? Ou trois ?

Le fichier de registre RISC-V a un registre de pointeur de thread, tp, qui est conçu pour les données locales au thread. Si votre application n'utilise pas de données locales de ce type, alors ce registre tp n'est pas utilisé (pour éviter toute confusion, vous pouvez avoir une application multitâche qui n'utilise pas de copies de données par thread et ne nécessite donc pas de pointeur de thread). Le Linker SEGGER peut déverrouiller ce registre et l’utiliser comme une deuxième base d’adresse globale : Dès lors tous les principes de transformations appliqués pour le pointeur global sont également appliqués pour le pointeur de thread. Bien sûr, vous pouvez spécifier le modèle à utiliser par le pointeur de thread : réservé, utilisé comme base globale, utilisé pour les données locales de thread ou attribuer automatiquement son modèle en fonction des fichiers d'entrée.

De plus si vus implémentez votre application à partir de l’adresse zéro, le linker SEGGER transformera automatiquement le code pour utiliser le registre «zéro» x0 pour appliquer les mêmes optimisations. Ainsi le linker SEGGER peut jongler avec trois pointeurs basés sur des registres pour optimiser le la pagination du code et des données.

Où est le piège ?

Les optimisations et transformations décrites ici ont un double avantage : elles produisent du code non seulement plus petit, mais aussi plus rapide !

Une optimisation qui réduit légèrement les performances (une pénalité de branche) est appelé Springboarding ou « tremplin ». Le linker SEGGER peut transformer des groupes d'appels et de sauts via des « tremplins communs », permettant ainsi une réduction importante de la taille du code avec comme pénalité l’exécution d'une seule branche. C'est un outil de plus à votre disposition, accepter quelques cycles supplémentaires afin d’optimiser votre empreinte mémoire en flash.

Mais l'utilisateur garde un contrôle total : toutes les transformations peuvent être activées ou désactivées individuellement, bien qu'elles s'appliquent à l'ensemble du programme. Dans un proche avenir, il sera également de contrôler l'optimisation non plus globalement mais par section, parfait pour isoler les fonctions critiques en temps ou en espace !

Conclusion

Le nouvel éditeur de liens SEGGER pour RISC-V amène beaucoup d’avantages pour les applications déjà existantes. Adoptant la philosophie de SEGGER « it simply works ! » en mode automatique il tirera le meilleur parti de votre application. Mais si vous êtes un fervent adepte du contrôle manuel, il vous permettra de définir par vous-même l’implémentation du code et des données ainsi que le rôle des pointeurs de base.

Alors une seule question se pose à vous si vous développez des applications RISC-V : Ne devriez-vous pas utiliser le linker SEGGER Linker?
Écrit par Paul Curtis
0