01signal.com

Les fondamentaux du timing en conception logique

Cette page fait partie d’une série de pages consacrée au timing.

À propos de cette page

Il est impossible de traiter correctement le timing sans connaître quelques notions de base de la conception logique. Cette page explique quelques concepts fondamentaux, qui serviront de base au reste de cette série.

Tout ce qui est écrit ci-dessous est enseigné dans les cours universitaires de conception logique. Mais tous ceux qui travaillent avec des FPGA n’ont pas suivi ce genre de cours, et même ceux qui l’ont suivi ne se souviennent pas nécessairement de tout. Cette page vise à combler les lacunes, s’il y en a.

N’hésitez pas à passer les sections que vous connaissez déjà, mais il est tout de même recommandé de tout lire, même en diagonale. Les dernières sections de cette page sont particulièrement importantes pour comprendre les pages suivantes de cette série.

Un bref rappel

Avant d’aborder les concepts liés au timing, je fais un bref rappel de quelques termes utiles en conception logique. Si l’un de ces termes vous est inconnu, je vous suggère de vous familiariser avec avant de poursuivre votre lecture.

D’abord, la logique combinatoire (combinatorial logic). Ce terme désigne des éléments comme les portes logiques : portes ET, OU, NON, etc. Ces éléments logiques sont considérés comme de la logique combinatoire parce qu’ils n’ont pas de mémoire. Autrement dit, leur comportement ne dépend pas de ce qui s’est passé dans le passé. La valeur présente sur la sortie de ces éléments ne dépend que des valeurs présentes sur leurs entrées.

Un type important de logique combinatoire est la table de correspondance (look-up table, LUT). C’est un élément logique polyvalent qui peut implémenter toute fonction logique dépendant d’un certain nombre d’entrées. La LUT est importante car elle sert pour presque toute la logique combinatoire d’un FPGA. Les LUT d’un FPGA sont réalisées sous forme de ROM asynchrones (c’est-à-dire sans horloge) : les entrées sont utilisées comme adresse désignant les données contenues dans la LUT. La sortie contient généralement un ou deux bits.

Le nombre d’entrées de ces LUT est de 4 ou 6 dans presque tous les FPGA du marché. Chaque LUT contient donc 16 ou 64 cellules mémoire.

Le terme important suivant est la logique séquentielle. Elle comprend les bascules (flip-flops), les RAM synchrones et beaucoup d’autres blocs de base qui nécessitent une horloge pour fonctionner. Tous ces éléments logiques réagissent à un changement spécifique de l’horloge. Entre ces instants d’activité, les éléments séquentiels ignorent leurs entrées, conservent leur état interne et ne modifient pas leurs sorties.

Par exemple, les éléments logiques déclenchés par front montant sont actifs lorsque l’horloge passe de l’état bas à l’état haut, c’est-à-dire sur un front montant. De même, les éléments logiques déclenchés par front descendant sont actifs lorsque l’horloge passe de l’état haut à l’état bas, c’est-à-dire sur un front descendant. Il existe aussi des éléments actifs sur les deux fronts, mais ils ne servent presque qu’à l’échantillonnage (sampling) de signaux d’E/S ou à leur génération.

Tous les éléments séquentiels possèdent une certaine forme de mémoire. C’est une conclusion immédiate du fait qu’ils ne changent leur sortie que lorsqu’un front d’horloge arrive. Certains éléments séquentiels possèdent le minimum de mémoire, à savoir un bit par sortie. D’autres en possèdent davantage, par exemple les registres à décalage et les RAM.

Pour ce qui concerne le timing, seuls deux faits importent : l’entrée n’est échantillonnée qu’au moment d’un front d’horloge, et la sortie ne change presque qu’en conséquence d’un front d’horloge. Il n’y a qu’une exception : certains éléments séquentiels ont une entrée de reset asynchrone (asynchronous reset). Lorsque cette entrée est active (au niveau haut ou bas, selon l’élément), l’état interne de l’élément séquentiel passe immédiatement à une valeur prédéfinie, indépendamment de l’horloge. Par conséquent, certaines ou toutes les sorties de l’élément passent aussi immédiatement à une valeur connue.

Dans un FPGA, certains éléments séquentiels ont un reset asynchrone, d’autres non. Le reset asynchrone est la seule entrée asynchrone possible dans presque tous les éléments séquentiels d’un FPGA. Et ce, même si la théorie de la conception logique connaît plusieurs éléments dotés de multiples entrées asynchrones, par exemple la bascule RS (S-R flip-flop).

À partir de maintenant, je parlerai de bascules

Par souci de simplicité, je vais supposer que tous les éléments séquentiels sont des bascules déclenchées par front montant, c’est-à-dire des bascules qui réagissent au front montant de l’horloge. En d’autres termes, tous les éléments séquentiels seront représentés ici par des bascules. Ces bascules ne répondent à leurs entrées et ne mettent à jour leurs sorties que lorsque l’horloge passe de l’état bas à l’état haut.

Le timing est plus facile à comprendre ainsi, et le raisonnement sur la bascule se généralise sans difficulté à tout autre élément séquentiel.

Temps de préparation et de maintien

Pour qu’une bascule fonctionne correctement et de manière fiable, ses entrées doivent être stables (c’est-à-dire ne pas changer de valeur) conformément à deux exigences de timing :

Timing diagram showing setup and hold times

Le schéma ci-dessus montre le sens de tsu et thold : l’entrée D ne doit pas changer de valeur pendant la période marquée en jaune. Dans cet exemple, D passe de l’état haut à l’état bas avec un timing valide, c’est-à-dire en dehors de cette période jaune.

Une façon de comprendre ces deux paramètres de timing est la suivante : il est assez évident que si une entrée d’une bascule change exactement en même temps que le front d’horloge, on ne sait pas si la bascule interprétera cette entrée comme étant haute ou basse. Que devrait-elle choisir ? L’état juste avant le front d’horloge, ou juste après ?

Ensuite, on peut se demander ce que signifie changer « exactement en même temps » que le front. À quel point est-ce exact ? Les deux paramètres tsu et thold définissent à quelle distance du front il est sans risque de changer la valeur de l’entrée. Ces paramètres sont définis pour tous les éléments séquentiels avec la même signification.

Notez qu’en cas de violation de ces exigences de timing, les conséquences peuvent être bien plus graves qu’une simple sortie aléatoire de la bascule après le front d’horloge. Voici deux raisons principales :

Clock-to-output

Le troisième paramètre de timing défini pour tous les éléments séquentiels est le temps clock-to-output, qui porte plusieurs symboles courants, par exemple tcko, tco, tC_Q, etc. Ce paramètre n’est pas une exigence de timing. Il indique plutôt à quel moment la sortie (Q) de l’élément séquentiel est garantie comme valide. Plus précisément, il indique au bout de combien de temps après le front d’horloge la sortie est valide (voir le chronogramme ci-dessus).

Il y a d’ailleurs deux paramètres à ce sujet :

La plupart du temps, seul le clock-to-output maximal présente un intérêt. Ainsi, quand ce paramètre figure dans une fiche technique, il s’agit presque certainement de la valeur maximale.

Notez que si les exigences de timing (tsu et thold) d’une bascule sont violées, il n’y a aucune garantie sur le moment où la sortie devient stable. Dans ce cas, le clock-to-output maximal n’a pas de sens, car la bascule peut rester dans un état indéfini (métastabilité) pendant un court instant.

tsu et thold peuvent être négatifs

Même si le schéma ci-dessus montre des tsu et thold positifs, il est possible que l’un de ces paramètres soit négatif. En fait, cela arrive souvent avec les bascules des FPGA.

Par exemple, un tsu négatif signifie que la donnée n’a pas besoin d’être stable au moment où le front d’horloge arrive. Elle doit simplement avoir atteint sa valeur stable un peu après le front. Mais tsu limite quand même ce « un peu après ».

De même, un thold négatif autorise la donnée à changer avant le front d’horloge. Là encore, cette avance reste limitée, et c’est thold qui l’exprime.

Cependant, comme tsu et thold peuvent être négatifs, il est théoriquement possible de définir des exigences de timing absurdes.

Pour l’expliquer, notons tsamp l’instant réel auquel la bascule échantillonne son entrée. tsamp n’est pas connu à l’avance, mais la définition de tsu nous dit que –tsu < tsamp. Autrement dit, la bascule échantillonne son entrée après que la donnée s’est stabilisée à la suite d’un changement, ce que garantit tsu. Par le même principe, tsamp < thold : l’échantillonnage doit se produire avant que la donnée puisse changer en réponse à un front d’horloge. En réunissant les deux, on obtient –tsu < tsamp < thold, ou plus simplement –tsu < thold. Cela doit être vrai pour tout couple tsu, thold.

Donc tsu ou thold peut être négatif, à condition que cette condition soit respectée. Mais les deux ne peuvent pas être négatifs en même temps.

Le paramètre clock-to-output est toujours positif : une bascule ne peut pas réagir à un front d’horloge avant que celui-ci ne soit arrivé. En revanche, le fait que tsamp puisse être négatif n’implique pas que l’échantillonnage ait eu lieu avant le front d’horloge. Cela peut venir d’un délai sur le signal de données.

Le délai de propagation

Two flip-flops connected through a LUT

Le schéma ci-dessus montre une connexion simple entre deux bascules, avec une LUT au milieu. Par souci de simplicité, supposons que la sortie de la LUT ne dépende que de I1. Par exemple, cette logique pourrait être le résultat du code Verilog suivant ; la LUT implémente alors une porte NON :

always @(posedge clk)
  begin
    foo_reg <= foo;   // FF1 = foo_reg
    bar <= !foo_reg;  // FF2 = bar
  end

Notez que les deux bascules sont connectées à la même horloge. Quelle est la fréquence maximale de cette horloge ?

Pour répondre, il manque une information : combien de temps s’écoule entre le moment où la sortie de FF1 est stable et le moment où l’entrée de FF2 est stable ? Nous noterons ce temps tpd (temps de propagation, propagation delay).

Notez que le terme temps de propagation s’applique toujours à un segment précis de logique combinatoire. Il est important de définir explicitement le segment concerné. Par exemple, on peut aussi définir comme temps de propagation le temps entre l’entrée I1 de la LUT et sa sortie O (c’est-à-dire de son entrée à sa sortie). Ce délai est probablement différent de la définition précédente de tpd.

Sur un FPGA, il y a notamment un délai de routage entre la sortie de FF1 et l’entrée de la LUT. Dans la réalité, le point Q de FF1 n’est donc pas le même que le point I1 de la LUT, et il faut un certain temps pour que le signal se propage entre ces deux points.

C’est pour cette ambiguïté que l’on voit rarement le symbole tpd dans les fiches techniques de FPGA. Lorsqu’un temps de propagation y est indiqué, la signification exacte du paramètre est généralement précisée explicitement.

J’y reviendrai plus tard.

Le chemin

Il est difficile de donner une définition concise d’un chemin (path), mais nous en avons déjà vu un exemple. Plus haut, j’ai défini tpd comme le temps de propagation entre Q de FF1 et D de FF2. Ce tpd correspond à un scénario précis : FF1 change de valeur, puis la nouvelle valeur atteint la LUT, la LUT met à jour sa sortie, et enfin la nouvelle valeur atteint FF2. Cette séquence commence par le changement d’un seul signal (la sortie de FF1) et se termine quand le signal en un autre point (l’entrée de FF2) est stable.

tpd est donc le temps de propagation du chemin qui va de Q de FF1 à D de FF2. Ou, pour faire court, le chemin de FF1 à FF2.

Le chemin se compose de tout ce qui contribue au délai entre le début et la fin de cette séquence. Il y a deux types d’éléments dans un chemin :

L’intérêt d’un chemin est de pouvoir calculer son temps de propagation. La façon dont ce résultat est utilisé est expliquée ensuite.

Un chemin représente souvent une expérience théorique où la sortie d’une bascule change, et où l’on suit un trajet précis jusqu’à l’entrée d’une autre bascule. Dans cette expérience théorique, un chronomètre imaginaire est déclenché lorsque la sortie de la première bascule change. Il est arrêté lorsque l’entrée de la seconde bascule change.

Cette expérience théorique aide à répondre à la question de savoir si le temps mesuré par ce chronomètre est trop long, ce qui voudrait dire que l’exigence sur tsu n’est pas respectée. Une deuxième question est de savoir si ce temps est trop court, ce qui violerait thold.

Remarquez que le trajet d’un chemin ne comprend que des connexions et de la logique combinatoire. Le temps nécessaire pour que le signal soit stable à destination ne dépend donc que des éléments de logique combinatoire et du routage le long du chemin. Peu importe quand on réalise cette expérience théorique, le résultat est toujours le même.

Dans une conception FPGA réelle, chaque bascule possède généralement de nombreux chemins qui arrivent sur son entrée et de nombreux chemins qui partent de sa sortie. Il peut même y avoir plusieurs chemins entre deux bascules données. Pourtant, les calculs de timing se font toujours en supposant qu’une seule bascule a changé de sortie et que tout ce qui se passe dans la logique du FPGA est une conséquence directe de ce changement. Le nombre de chemins à calculer dans une conception FPGA peut être énorme, mais cela est bien sûr fait automatiquement par le logiciel.

Une analyse statique de timing simple

Pour la démonstration, je vais faire une analyse de timing simple de l’exemple ci-dessus avec deux bascules. Les contraintes de timing (timing constraints) seront abordées plus tard, mais pour le moment, supposons que @clk a une fréquence de 250 MHz (4 ns) et qu’elle est connectée directement à la logique (c’est-à-dire sans PLL, ce qui n’est pas recommandé dans une vraie conception, mais simplifie l’analyse de timing). La contrainte de timing, au format SDC, pourrait ressembler à ceci :

create_clock -period 4.000 -name clk [get_ports clk]

Un exemple d’analyse de timing réelle est présenté sur la page suivante de cette série, mais cette analyse étant fidèle à la réalité, elle contient beaucoup de détails qui la rendent difficile à comprendre. Voici donc une analyse simple, qui ne fait que montrer le principe.

L’analyse met en œuvre l’expérience théorique mentionnée plus haut : un chronomètre imaginaire démarre en même temps que le front montant de @clk. Voici la chaîne des événements, avec le délai (fictif) que chaque événement introduit.

Timing delay of path with a LUT between two flip-flops

Le temps de propagation (tpd) de ce chemin est la somme de tous ces délais : 0,2 + 0,3 + 0,3 + 0,4 = 1,2 ns. Pour les besoins de l’exemple, supposons que le tsu de FF2 est de 0,1 ns. Cela signifie que l’entrée (D) de FF2 doit être stable 0,1 ns avant le prochain front montant de @clk. Autrement dit, le tpd maximal autorisé est de 4 - 0,1 = 3,9 ns.

Mais tpd ne vaut que 1,2 ns. D’après ce calcul, le chemin respecte donc la contrainte de timing avec une large marge. Cette marge est appelée slack (le terme anglais est conservé tel quel en français) ; ici elle vaut 3,9 - 1,2 = 2,7 ns. Quand ce nombre apparaît dans les calculs de timing du logiciel, il indique si les outils ont eu du mal ou non à respecter les contraintes de timing : si le slack est proche de zéro, cela signifie souvent que le logiciel a dû travailler dur pour que le chemin satisfasse les exigences de timing.

Le temps de propagation permet aussi de calculer la fréquence maximale de @clk pour laquelle ce chemin peut respecter le timing. tpd vaut 1,2 ns, et l’exigence sur tsu implique que le front montant suivant peut arriver 0,1 ns plus tard. Autrement dit, il faut au moins 1,3 ns entre deux fronts montants. Cela donne une fréquence d’environ 769 MHz. C’est très élevé, mais le résultat est réaliste parce que le chemin ne contient qu’une seule LUT. La logique réelle est généralement plus compliquée que cela, ce qui explique pourquoi les fréquences réelles sont souvent beaucoup plus basses.

Une analyse statique de timing réelle effectue précisément ce calcul, mais ce n’est qu’une partie de l’histoire. Le chemin calculé ici est appelé chemin de données (data path) dans un calcul réel. Cependant, une analyse de timing statique réelle prend aussi en compte le fait que le front d’horloge n’arrive pas exactement au même moment sur les deux bascules. En effet, le délai du buffer d’horloge jusqu’à chacune des bascules est légèrement différent. La différence entre ces délais s’appelle le décalage d’horloge (clock skew). De plus, l’intervalle entre deux fronts d’horloge n’est pas tout à fait constant à cause de la gigue d’horloge (jitter). Ces irrégularités rendent le calcul précis plus compliqué, comme expliqué sur la page suivante.

Recovery et removal

Si une bascule possède une entrée de reset asynchrone (êtes-vous sûr de vouloir cela ?), il existe des exigences sur le moment où cette entrée devient inactive. Notez que le moment où le reset devient actif n’a pas d’importance : la bascule passera de toute façon dans un état connu.

Mais lorsque le reset devient inactif, la bascule redevient sensible à l’horloge. Si cette désactivation du reset se produit près d’un front d’horloge, on ne sait pas vraiment si la bascule doit y répondre. Tout comme tsetup et thold, le reset doit rester stable pendant une période autour du front d’horloge. Plus précisément :

Ces définitions ressemblent à celles de tsetup et thold. Ce n’est pas une coïncidence : le temps de récupération est un cas particulier de temps de préparation. L’analyse de timing se fait de la même façon. La seule différence est que le temps de préparation s’applique quelle que soit la valeur du signal de données. En revanche, le temps de récupération n’est pas imposé lorsque le signal de reset asynchrone passe à l’état actif. La relation entre le temps de retrait et le temps de maintien est identique.

En raison de cette similarité, recovery et removal ne seront pas abordés au-delà de cette mention. Notez également que ce qui vient d’être dit vaut pour toutes les entrées asynchrones, et pas seulement pour le reset asynchrone.

Le paradigme RTL et le timing

L’exemple des deux bascules présenté plus haut est simple, mais il représente toute la logique créée selon le paradigme RTL : chaque chemin commence à une bascule et se termine à une bascule cadencée par la même horloge (ou par des horloges liées, related clocks). Le chemin lui-même est constitué de logique combinatoire et de routage. Dans cet exemple, la logique combinatoire se limite à une LUT, mais il n’y a pas de différence de fond entre ce cas et un chemin contenant plusieurs éléments logiques. La structure est la même.

La raison pour laquelle le paradigme RTL est si important est que, lorsqu’on l’utilise, presque tous les chemins de données ont cette même structure simple. Et comme le nombre de chemins dans une conception logique est souvent énorme, la simplicité de l’analyse de timing aide à éviter les erreurs. Entre autres, le fait que l’analyse de timing suive un schéma bien précis permet de lire le rapport de timing et de se demander si les résultats sont cohérents.

Dans la mesure du possible, tout devrait donc commencer sur un élément séquentiel et se terminer sur un élément séquentiel. C’est une ligne directrice utile aussi bien pour écrire du code Verilog que pour planifier la structure logique en général.


Ceci conclut la brève introduction à la théorie qui sous-tend les contraintes de timing. Une contrainte de période d’horloge et les rapports de timing correspondants sont expliqués sur la page suivante.

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)