01signal.com

Valider que les contraintes temporelles sont correctes

Cette page est la dernière d’une série de pages consacrée au timing. Les pages précédentes ont expliqué la théorie derrière les calculs de timing, montré comment écrire plusieurs contraintes temporelles et abordé les principes de la convergence temporelle (timing closure).

Introduction

Bien souvent, la tactique de développement sur FPGA consiste à ajouter une fonctionnalité, corriger ce qui ne fonctionne pas, puis recommencer. Il est facile de négliger les parties de la conception qui sont peut-être erronées, mais qui ne posent aucun problème visible.

Il est essentiel de comprendre qu’un design FPGA peut fonctionner parfaitement même si les contraintes temporelles sont appliquées incorrectement et même si elles ne sont pas respectées : l’utilisation correcte des contraintes temporelles est l’un des éléments clés qui garantissent un fonctionnement stable et correct du FPGA. Pourtant, négliger ce sujet ne conduit pas nécessairement à un échec immédiat. Plus subtilement, un timing inapproprié peut provoquer des dysfonctionnements occasionnels, qui peuvent être extrêmement déroutants.

Cette page résume et réaffirme un grand nombre des suggestions de cette série de pages consacrée aux contraintes temporelles. Le but est de mettre en lumière quelques sujets qu’il faut garder à l’esprit lorsqu’on recherche des problèmes causés par un timing inadapté. On aimerait avoir la sagesse de vérifier ces points de temps en temps pour le plaisir ; mais dans la vraie vie, la plupart d’entre nous le font pour résoudre un problème qui semble relever un peu de la sorcellerie.

Lire votre rapport de timing

Tous les outils de conception FPGA produisent des rapports de timing. La raison la plus courante pour laquelle nous les ouvrons est l’échec des outils à respecter les contraintes temporelles : c’est là qu’on trouve les chemins en échec et, avec un peu de chance, ce qu’on peut y faire.

Cependant, il existe une raison tout aussi importante d’examiner les rapports de timing : vérifier que les contraintes temporelles sont interprétées par les outils comme prévu, et donc correctement appliquées. Il est bon de faire cette vérification de temps en temps, en particulier après avoir ajouté de nouvelles contraintes temporelles.

Comme chaque outil de conception FPGA produit ces rapports avec des structures et des formats différents, il est impossible d’expliquer exactement ce que signifie chaque partie de chaque rapport. Cette page décrit donc les principes, communs à tous les outils. Quant aux formats et aux possibilités de vos propres rapports, il est de toute façon bon de prendre le temps de les apprendre.

Un autre bénéfice possible d’une vérification du rapport de timing est qu’elle peut révéler une utilisation incorrecte de la logique. Par exemple, si la conception comprend de la logique asynchrone mal utilisée, ou dépend d’horloges auxquelles on ne peut pas appliquer de contraintes, cela peut devenir évident dans le rapport : comme il est impossible d’appliquer des contraintes à cette logique, elle peut apparaître dans le rapport comme chemins non contraints.

Par ailleurs, il vaut la peine de mentionner que les IP — cœurs IP (IP cores) — incluses dans le design apportent souvent leurs propres contraintes temporelles, que les outils ajoutent à celles que vous fournissez. Il n’y a en général aucun intérêt à vérifier les chemins associés à ces contraintes, mais elles peuvent alourdir considérablement le rapport de timing.

Chemins internes non contraints

Pour cette discussion, un élément synchrone est une bascule, une block RAM, un registre à décalage, ou tout autre élément logique qui échantillonne son entrée de données et/ou modifie sa sortie de données sur le front montant ou descendant d’une horloge.

Tout chemin qui va de la sortie de données d’un élément synchrone vers l’entrée de données d’un autre élément synchrone doit être soumis à des contraintes temporelles. La seule exception est lorsqu’une éventuelle violation de timing est correctement gérée à l’élément synchrone destinataire, comme avec des domaines d’horloge non liés.

Autrement dit, le chemin vers tout élément synchrone doit être soumis à des contraintes temporelles, sauf s’il existe un mécanisme de resynchronisation explicite pour compenser le fait que les outils de conception ne prennent aucune responsabilité quant à l’échantillonnage prévisible du signal d’entrée.

Il suffit parfois d’une contrainte de timing d’une seule ligne qui définit l’horloge de référence, et les outils FPGA s’occupent du reste. Dans d’autres cas, cela demande davantage. Dans les cas vraiment mauvais, certains chemins internes restent non contraints par erreur. Plusieurs raisons possibles, notamment :

Les outils de conception FPGA permettent de créer des rapports de timing qui listent les chemins non contraints, c’est-à-dire les chemins auxquels aucune contrainte temporelle n’a été appliquée. En principe, cette liste devrait être vide : les chemins qui n’ont pas besoin de contraintes devraient être explicitement définis comme chemins faux dans le fichier de contraintes. De telles contraintes ne changent rien fonctionnellement pour des chemins qui n’étaient de toute façon pas contraints, mais elles permettent de garder vide la liste des chemins non contraints dans le rapport de timing. Cela rend facile la détection des chemins laissés de côté involontairement. De plus, comme le nombre de chemins dans cette liste est limité, des chemins qui ne devraient absolument pas s’y trouver peuvent être masqués parce que des chemins sans danger y figurent à la place.

Mais malheureusement, dans certains rapports de timing, la section des chemins non contraints peut aussi inclure des chemins qui n’ont aucune raison d’être couverts par des contraintes temporelles. Par exemple, le chemin entre la sortie d’un buffer d’horloge et l’entrée d’une autre ressource d’horloge. Le rapport peut donc lister beaucoup de chemins comme non contraints, et c’est parfaitement normal. Cela rend un peu plus difficile de déduire la situation à partir du rapport, mais ce n’est pas vraiment un problème, car ces chemins sont généralement listés séparément des chemins qui se terminent sur l’entrée de données d’une bascule ou d’un autre élément synchrone. Tout revient donc à lire attentivement le rapport et à prêter attention à ce que signifie chaque groupe de chemins listés.

Le rapport de timing généré par défaut n’est parfois pas assez détaillé pour contenir ces informations. Tout outil raisonnable permet de générer à la demande un rapport de timing listant les chemins non contraints, et de choisir combien de chemins de ce type lister pour chaque groupe (10 est un nombre raisonnable, même si les listes concernées devraient être complètement vides).

Chemins entre domaines d’horloge

Des contraintes temporelles doivent être appliquées aux chemins internes qui sont synchrones avec des horloges différentes à leur source et à leur destination, si ces horloges sont des horloges liées (related clocks) (ou plus précisément si la logique les traite comme liées). Sinon, il ne faut pas le faire, car cette restriction inutile peut gaspiller des ressources de routage de grande qualité et éventuellement provoquer l’échec des contraintes temporelles. Voir cette page au sujet des horloges liées et non liées.

Chaque suite d’outils a sa propre manière de décider automatiquement si une paire d’horloges doit être considérée comme liée. Les outils qui utilisent la syntaxe SDC dans leurs fichiers de contraintes temporelles (par exemple Vivado et Quartus) disposent d’une commande set_clock_groups, qui permet de définir des groupes d’horloges liées et non liées. Il existe aussi d’autres méthodes pour guider les outils sur ce point, notamment par des contraintes de chemins faux.

La question est donc de savoir comment vérifier si les outils considèrent les horloges comme liées ou non. La réponse, malheureusement, est que chaque suite d’outils a une manière différente. Vivado, par exemple, propose un Clock Interaction Report, avec un diagramme en couleurs qui montre la situation pour chaque paire d’horloges :

Il est également possible d’utiliser des rapports de timing personnalisés, limités à certains groupes de chemins, pour enquêter sur la question. Les chemins peuvent être sélectionnés en fonction des horloges impliquées. Cela peut se faire en sélectionnant les chemins qui commencent à des éléments logiques synchrones d’une horloge et se terminent à une autre horloge. Il peut aussi être utile d’examiner spécifiquement les groupes de chemins connus pour être impliqués dans des changements de domaine d’horloge.

Même si cela peut être difficile, il est crucial de procéder à un examen complet des changements de domaine d’horloge auxquels les outils appliquent des contraintes, et de vérifier si c’est correct. C’est aussi l’occasion de vérifier si la conception logique dispose d’une logique de resynchronisation là où il en faut. Il est facile de se retrouver avec un changement de domaine d’horloge non sûr à cause d’une confusion sur l’horloge utilisée avec chaque signal.

Il est important de faire cet examen en gardant à l’esprit la nature de chaque horloge. Par exemple, si deux horloges issues d’oscillateurs différents ont la même fréquence voulue, et ont donc des définitions semblables dans le fichier de contraintes, les outils pourraient les considérer à tort comme liées et calculer le timing sur les chemins qui vont de l’une à l’autre. Appliquer des contraintes à de tels chemins n’a pas de sens, car aucune relation de phase n’est garantie entre ces deux horloges. En soi, des contraintes inutiles ne font que rendre plus difficile la convergence temporelle, ce qui est assez inoffensif. Le vrai problème est que le rapport de timing peut nous laisser croire que les horloges sont effectivement liées. Ainsi, en ne regardant que les contraintes et les rapports, les chemins entre elles peuvent sembler sûrs (c’est-à-dire sans besoin de protection contre les violations de timing) alors que ce n’est pas le cas : les deux horloges n’ont rien en commun, si ce n’est à peu près la même fréquence. La seule façon d’éviter ce type d’erreur est de comprendre comment chaque horloge est générée.

Chemins externes non contraints

Des contraintes temporelles doivent toujours être appliquées aux chemins qui commencent ou se terminent sur des broches d’entrées-sorties. Une page séparée explique comment le faire. La seule exception concerne les broches d’horloge en entrée et les broches à interface spéciale, par exemple les émetteurs-récepteurs gigabit, les broches connectées directement à un processeur matériel sur le silicium du FPGA, etc. Si l’interface est très lente, on peut aussi pardonner l’absence de contraintes temporelles, par exemple avec des LED, des boutons-poussoirs et même des liaisons I²C. Mais il est bien préférable de leur affecter des contraintes de chemin faux, car cela maintient vide la liste des broches d’entrées-sorties non contraintes dans le rapport de timing.

Il arrive souvent que l’application de contraintes temporelles sur des chemins externes semble dénuée de sens. Par exemple, s’il existe plusieurs broches de sortie synchrones avec la même horloge et que le timing correct est assuré par le fait qu’elles basculent toutes simultanément. Un moyen courant d’obtenir ce basculement simultané consiste à utiliser le registre IOB (IOB register). Cette bascule offre le meilleur délai de l’horloge vers la sortie (clock-to-output) possible et aussi un décalage remarquablement faible entre les broches de sortie.

C’est pourtant un bon exemple de situation où les contraintes temporelles sur les broches de sortie sont importantes : en maintenant des contraintes serrées, on garantit un faible décalage entre ces broches. Quand le registre IOB est utilisé, des contraintes serrées peuvent être un moyen de forcer les outils à utiliser cette bascule, ou de s’assurer que s’ils ne le font pas, cela ne passera pas inaperçu : si les outils ne placent pas la bascule comme voulu, le timing échoue.

Pour des raisons analogues, il convient aussi d’appliquer des contraintes serrées sur les broches d’entrée.

Lorsque des contraintes temporelles servent à obtenir un faible décalage entre plusieurs broches, il est important de ne pas se contenter de contraindre les broches jusqu’à ce que la marge (slack) indiquée soit presque nulle, mais de vérifier qu’en exigeant un timing plus serré, la contrainte échoue. Cela tient au fait que le chemin du signal d’entrée-sortie peut contenir des lignes à retard optionnelles que les outils peuvent utiliser d’une manière contre-intuitive. Par exemple, Quartus d’Intel FPGA peut ajouter un certain délai sur le chemin d’entrée s’il y a un surplus de budget temporel. Le choix des outils de faire cela n’est ni souhaité, ni prévisible.

Chemins faux incorrects et timing autrement relâché

Parfois, des contraintes temporelles sont appliquées, mais elles sont trop permissives. C’est beaucoup plus difficile à détecter, car les chemins concernés sont bien soumis à des contraintes, mais avec une mauvaise exigence.

Plusieurs raisons peuvent expliquer ce type d’incident, notamment :

Il n’existe pas de recette simple pour repérer ce type de problème. Lire attentivement le rapport de timing de haut en bas est certainement une bonne idée, mais même si le rapport affiche dix chemins par groupe, rien ne garantit que les chemins problématiques s’y trouveront. Pas plus que dans un nombre quelconque de chemins affichés, d’ailleurs.

Une autre façon d’aborder le problème consiste à revoir les contraintes temporelles telles qu’elles sont écrites. Comme les contraintes SDC sont écrites en Tcl, il est possible d’évaluer comme expressions Tcl les expressions qui sélectionnent les éléments logiques (avec « from » et « to ») dans le fichier de contraintes, puis de lire la liste des extrémités obtenues.

Ainsi, par exemple, si cette ligne apparaît dans un fichier .xdc de Vivado :

set_false_path -to [ get_pins -hier -filter {name =~ */pclk_i1_bufgctrl.pclk_i1/S*} ]

on peut ouvrir le design implémenté et lister les destinations auxquelles les chemins faux s’appliquent :

puts [join [ get_pins -hier -filter {name =~ */pclk_i1_bufgctrl.pclk_i1/S*} ] "\n" ]

Les commandes Tcl (« puts » et « join ») font en sorte que chaque élément soit listé sur une ligne séparée, ce qui rend la sortie facile à lire (même si elle peut être très longue).

Ce genre de revue des contraintes est important, mais il est tout aussi difficile, car il demande une vraie réflexion.

Résumé

S’assurer que les outils appliquent les bonnes limites temporelles aux différents chemins est une tâche délicate. C’est un mélange de connaissance exacte de la signification des contraintes temporelles et de savoir comment lire les rapports de timing, afin d’augmenter les chances de repérer les erreurs.

Mais plus que tout, il s’agit d’avoir la discipline de vérifier et de revérifier la conception, en particulier lorsqu’elle semble bien fonctionner.

Cette page a été traduite de l’anglais par une machine. En cas de doute, veuillez vous reporter au texte original
Copyright © 2021-2026. All rights reserved. (dcc38493)