01signal.com

Domaines d’horloge, horloges liées et horloges non liées

Cette page est la première d’une série de trois consacrée aux domaines d’horloge.

Introduction

À l’exception des conceptions FPGA les plus triviales, plusieurs horloges sont utilisées avec leurs éléments synchrones (bascules, blocs RAM, registres à décalage, etc.). Cependant, la plupart des unités fonctionnelles d’une conception logique reposent sur une seule horloge ; le plus souvent, la question des multiples horloges ne demande donc aucune attention particulière. Les choses se compliquent lorsque de la logique synchrone d’une horloge est connectée à de la logique qui dépend d’une autre horloge. Cette série de pages explique comment travailler avec plusieurs horloges dans une conception.

Quand on connecte de la logique qui dépend d’horloges différentes, la première question, et la plus importante, est de savoir si une logique de resynchronisation est nécessaire. Cela revient à demander si les deux horloges sont des horloges liées (related clocks) ou non. Cette page explique cette question et comment y répondre, ainsi que d’autres sujets connexes. Une grande partie de la discussion ci-dessous porte sur les contraintes temporelles (timing constraints) et sur la possibilité de les appliquer à certains éléments logiques. C’est pourquoi je commence par un bref rappel sur le timing.

La discussion dans cette série de pages se limite aux bascules (flip-flops) déclenchées par front positif, c’est-à-dire qui réagissent aux fronts montants de leur entrée d’horloge. Il existe bien sûr d’autres éléments synchrones, c’est-à-dire des éléments logiques qui échantillonnent et produisent des signaux selon une horloge : registres à décalage, blocs RAM et bien d’autres. Certains peuvent réagir sur front descendant, voire sur les deux. Pour simplifier, je fais abstraction de tout cela ici.

Par ailleurs, j’emploierai l’expression « X est synchrone avec Y » pour dire que l’entrée d’horloge de l’élément synchrone X est connectée à l’horloge Y. Cet élément échantillonne donc son entrée (ou ses entrées) avec cette horloge, et met aussi à jour ses sorties avec elle.

Bref rappel sur les bases du timing

Voici un court résumé de la théorie du timing. Une explication plus détaillée se trouve sur une page dédiée.

Considérons le schéma suivant, qui montre le chemin de signal (path) d’une bascule à une autre, à travers une LUT :

Crossing clock domains: Two flip-flops and a LUT between them

Dans ce schéma, la LUT (Look-Up Table, table de correspondance) représente un groupe quelconque de logique combinatoire (combinatorial logic) : toute variation de I1, I2, I3 ou I4 entraîne une variation immédiate de la sortie O, avec un certain délai de propagation (propagation delay).

Le chemin du signal est donc le suivant : la sortie Q de la bascule de gauche, étiquetée @foo, change après un front montant sur son entrée d’horloge @clk1. Comme cette sortie est reliée à I1, la sortie O de la LUT peut changer après un court délai. Cette sortie entre dans l’entrée de données D de la bascule de droite. Après un front montant sur @clk2, la bascule copie D dans Q (étiqueté @bar).

Mais ce n’est pas si simple. Toutes les bascules ont des exigences temporelles : l’entrée de données D doit être stable tsu (le temps de préparation, setup time) avant le front montant de @clk2, et rester stable thold (le temps de maintien, hold time) après ce front.

La grande majorité des chemins de signaux dans un FPGA sont relatifs à une seule horloge ; @clk1 et @clk2 sont donc exactement le même signal d’horloge (nous négligerons le décalage d’horloge, clock skew). Pour de tels chemins, il est possible de calculer si ces deux exigences temporelles peuvent être garanties.

Par exemple, regardons le schéma ci-dessus et l’exigence sur tsu : il s’agit de déterminer le temps écoulé entre le front montant de @clk1 et le moment où l’entrée D de la bascule de droite est mise à jour et stable. C’est la somme de tous les délais sur ce chemin : on part du délai entre le front montant de @clk1 et la mise à jour de @foo (le délai « clock to output »), puis on ajoute tous les délais jusqu’à l’entrée D de la bascule de droite. Cela comprend le délai de la LUT ainsi que les délais de câblage entre les éléments logiques (délais de routage).

Comme @clk1 et @clk2 sont la même horloge, nous savons quand se produira le prochain front montant sur les deux bascules : un cycle d’horloge plus tard (par exemple 10 ns pour une horloge à 100 MHz). Le délai total du chemin doit donc être inférieur à la période de l’horloge, avec une marge de tsu. Si cette condition peut être garantie, l’exigence sur tsu est satisfaite : l’entrée D de la bascule de droite est stable avec une marge de temps (tsu) avant le front montant de @clk2. Reportez-vous à la page sur la théorie du timing pour un exemple chiffré.

Un calcul similaire peut être fait pour thold. Contrairement à tsu, l’exigence est que l’entrée D de la bascule de droite reste stable pendant un certain temps (thold) après un front montant de @clk2. La période de l’horloge est donc sans importance et n’entre pas en compte (lorsque @clk1 et @clk2 sont identiques). Ce qui compte, c’est ce qui se passe dans le même cycle d’horloge, pas dans le suivant.

Remarquez que de tels calculs ne sont possibles que parce que l’écart temporel entre le front montant de @clk1 et celui de @clk2 est connu. En particulier, si @clk1 et @clk2 sont la même horloge, cet écart utilisé pour calculer tsu est la période de cette horloge. Mais si l’écart entre ces deux horloges est inconnu, il est impossible de garantir ni tsu ni thold.

Les exigences tsu et thold s’appliquent à chaque bascule. Cela vaut bien sûr pour toutes les bascules du FPGA, mais aussi pour les bascules de dispositifs externes (par exemple depuis une broche de sortie du FPGA vers un composant externe qui échantillonne le signal avec une horloge). Pour chaque bascule du système, il faut donc se demander si ces deux exigences sont garanties. Pour celles où ce n’est pas le cas, il faut prévoir un mécanisme assurant un fonctionnement fiable malgré tout (c’est-à-dire une logique de resynchronisation). C’est en résumé tout l’enjeu du franchissement de domaines d’horloge.

Alors, quand le timing est-il garanti ?

Au cas où vous vous seriez perdus dans la section précédente, voici l’essentiel :

Rappelez-vous qu’une conception FPGA inclut toujours des contraintes temporelles (timing constraints), qui informent les outils de conception des fréquences des horloges. Sur la base de ces informations, les outils s’assurent que les deux exigences temporelles (tsu et thold) sont satisfaites. Et si les outils n’y parviennent pas, ils signalent cet échec.

Comme indiqué plus haut, il n’est possible de garantir tsu et thold que si l’on connaît l’écart temporel entre les fronts montants de l’horloge au début du chemin et de l’horloge à la fin du chemin. C’est le cas la plupart du temps, car la plus grande partie d’une conception logique est constituée de registres qui dépendent d’autres registres synchrones de la même horloge.

Mais qu’en est-il si deux horloges différentes sont utilisées ? Si une horloge est connectée à la bascule au début du chemin, et une autre à la bascule à la fin ? Autrement dit, si @clk1 n’est pas identique à @clk2 ? Est-il encore possible de faire un calcul de timing et de garantir les exigences temporelles ? Eh bien, cela dépend. C’est l’objet du reste de cette page.

Domaines d’horloge et franchissement de domaine d’horloge

Un domaine d’horloge (clock domain) est constitué de tous les éléments synchrones (bascules et autres) qui sont synchronisés sur un certain signal d’horloge.

Considérons ce petit extrait de code Verilog :

reg foo, bar;

always @(posedge clk1)
  foo <= !foo;

always @(posedge clk2)
  bar <= foo;

Dans cet exemple, @foo est synchrone avec @clk1, et @bar est synchrone avec @clk2. Ils appartiennent donc clairement à des domaines d’horloge différents (respectivement @clk1 et @clk2).

@foo ne pose aucun problème : il ne dépend que de lui-même. En revanche, @bar est synchrone avec @clk2 et dépend de @foo, qui est synchrone avec @clk1. @bar peut-il donc être utilisé comme n’importe quel registre ? Peut-on supposer que @bar obtient toujours la valeur de @foo dans des conditions de timing légales, de sorte que son comportement soit connu et reproductible ?

Avant de tenter de répondre, donnons un nom à ce qui vient de se produire. C’est un franchissement de domaine d’horloge (clock domain crossing) : @foo et @bar sont implémentés comme deux bascules synchronisées sur des horloges différentes. Le chemin de @foo vers @bar passe donc d’un domaine d’horloge à un autre.

Plus généralement, on parle de franchissement de domaine d’horloge lorsque la sortie d’un élément synchrone appartenant à un domaine d’horloge aboutit à l’entrée d’un élément synchrone appartenant à un autre domaine d’horloge. Il y a souvent de la logique combinatoire entre ces deux éléments synchrones.

Ainsi, même si @bar avait été défini comme

always @(posedge clk2)
  bar <= !foo || !bar;

il y aurait quand même un franchissement de domaine d’horloge. Dans ce cas, le chemin partirait de @foo, traverserait la LUT qui implémente la fonction logique, et aboutirait à @bar, comme dans le schéma ci-dessus.

Horloges liées et horloges non liées

Le terme horloges liées (related clocks) désigne des horloges dérivées d’une même horloge de référence d’une manière qui garantit des écarts temporels prévisibles entre leurs fronts montants et descendants (aux imprécisions connues et à la gigue (jitter) près).

Le terme « horloges synchrones » est souvent utilisé à la place d’« horloges liées ». De même, « horloges asynchrones » est souvent employé à la place d’« horloges non liées ».

Un exemple courant d’horloges liées est l’utilisation d’une seule PLL du FPGA pour générer plusieurs horloges, avec des relations de fréquence connues entre elles. Dans ce scénario, les outils FPGA arrangent généralement les buffers d’horloge pour que les fronts d’horloge soient alignés dans la mesure du possible.

Par exemple, lorsqu’une horloge de référence est multipliée par 2 et par 3 :

Simple clock diagram with clock multiplication

Dans cet exemple, x1 clk, x2 clk et x3 clk sont des horloges liées, car les relations temporelles entre chaque paire de ces horloges sont prévisibles.

En règle générale, l’horloge de référence est une horloge non liée par rapport à n’importe laquelle de ces autres horloges : la relation entre les fronts de l’horloge de référence et ceux des autres horloges peut varier avec la température et d’autres facteurs. Toutefois, si la PLL est configurée pour garantir un alignement prévisible entre l’horloge de référence et les sorties de la PLL, l’horloge de référence devient elle aussi une horloge liée.

La connaissance des relations temporelles entre horloges permet de calculer le timing pour les chemins qui passent entre leurs domaines d’horloge. Par exemple, le timing d’un chemin entre les domaines de x1 clk et x2 clk se calcule avec les mêmes exigences qu’un chemin entre x2 clk et lui-même. En effet, l’écart temporel le plus court entre ces deux horloges est le même que le temps entre deux fronts montants de x2 clk.

De même, un chemin entre x2 clk et x3 clk peut se calculer, mais l’écart temporel le plus court n’est que d’un sixième de la période de x1 clk. Le cas de timing le plus défavorable correspond donc à celui d’une horloge x6 imaginaire. La raison est l’écart entre le deuxième front montant de x2 clk et le troisième front montant de x3 clk.

Voir cette page pour une discussion plus détaillée sur la façon dont le timing est calculé.

Le but de cet exemple est d’énoncer une vérité générale : avec des horloges liées, il est possible de garantir le timing en imposant des contraintes temporelles sur les chemins entre ces horloges, tout comme pour les chemins d’un même domaine d’horloge. C’est possible lorsque les outils FPGA veillent délibérément à aligner les fronts d’horloge. Comme le montre l’exemple, les exigences temporelles sont souvent plus sévères que pour chacune des horloges individuellement, parfois beaucoup plus sévères.

Remarquez toutefois que le fait que deux horloges soient dérivées d’une même horloge de référence n’en fait pas nécessairement des horloges liées. En particulier, si le décalage d’horloge entre ces horloges n’est pas maîtrisé ou est inconnu (ce qui est le cas à moins que les outils de conception n’aient explicitement utilisé des ressources de distribution d’horloge présentant des délais égaux), il faut les traiter comme des horloges non liées.

Et même l’horloge x1 n’est pas nécessairement liée à son horloge de référence, même si elle a exactement la même fréquence. À moins que ces horloges ne soient volontairement alignées entre elles, leur relation de phase est inconnue.

À cause de cette histoire de phase, j’ai écrit « un certain signal d’horloge » dans la définition d’un domaine d’horloge plus haut, et pas simplement « une certaine horloge ». « Une certaine horloge » pourrait désigner, par exemple, la même horloge sur la carte, connectée à deux broches d’entrée différentes du FPGA. « Un certain signal d’horloge », en revanche, désigne un fil dans le Verilog ou dans la netlist (liste de connexions) de la conception, qui représente un signal d’horloge, comme @clk1 et @clk2 dans l’exemple ci-dessus. Ce signal est distribué dans la conception Verilog par l’intermédiaire de connexions de ports dans des instanciations (instantiation) ou avec de simples instructions « assign ».

Le fait que l’horloge soit exactement le même signal dans le Verilog implique que les outils utiliseront des ressources garantissant un faible décalage d’horloge sur le matériel. Cela a aussi des conséquences sur la façon dont les outils considèrent les horloges comme liées ou non (nous y reviendrons plus bas).

Horloges liées, contraintes temporelles et logique de resynchronisation

Revenons à la question d’origine : @bar, dans l’exemple ci-dessus, peut-il être utilisé comme n’importe quel registre ? Plus généralement, si une bascule est la destination d’un chemin qui part d’un autre domaine d’horloge, peut-on utiliser sa sortie de façon fiable comme celle de n’importe quelle bascule ? Cela revient à se demander s’il est possible d’assurer le temps de préparation (setup time) et le temps de maintien (hold time) de cette bascule lorsqu’au moins un chemin qui atteint son entrée de données est synchrone avec une autre horloge.

La réponse est simple et brève : si les horloges des deux côtés (@clk1 et @clk2 dans l’exemple) sont des horloges liées, la sortie de la bascule de destination est parfaitement fiable, et peut être utilisée comme n’importe quel registre, à condition que la contrainte temporelle appropriée soit satisfaite sur ce chemin. Sinon, le timing à destination ne peut pas être garanti, et il faut ajouter de la logique de resynchronisation pour pallier ce problème.

Cette page présente une analyse de timing complète d’un chemin entre deux horloges liées.

Après cette longue discussion, résumons tout cela en quelques règles simples :

  1. Il est impossible d’imposer des contraintes temporelles sur des chemins entre domaines d’horloge appartenant à des horloges non liées.
  2. Si une logique de resynchronisation est présente sur tous les chemins entre deux domaines d’horloge, il n’est pas nécessaire d’imposer de contraintes temporelles sur ces chemins.
  3. S’il n’y a pas de logique de resynchronisation sur un chemin entre deux domaines d’horloge, des contraintes temporelles doivent être imposées sur ce chemin.

La première règle est la plus simple : si vous ne pouvez pas être certain que deux horloges sont liées, assurez-vous d’avoir une logique de resynchronisation sur tous les chemins entre les domaines d’horloge (la page suivante explique comment faire). Veillez aussi à ce qu’aucune contrainte temporelle ne soit imposée sur ces chemins, car c’est acceptable selon la deuxième règle. Des contraintes temporelles inutiles ne font que compliquer la tâche des outils FPGA.

Autre conséquence de ces règles : même si les horloges sont liées, on a le droit de les traiter comme non liées. Pour ce faire, comme on vient de le dire, mettez de la logique de resynchronisation sur tous les chemins et désactivez l’application des contraintes temporelles (nous y reviendrons plus bas).

Enfin, si vous êtes certain que les horloges sont liées, aucune logique de resynchronisation n’est nécessaire, mais il faut que des contraintes temporelles correctement appliquées existent sur tous les chemins entre les domaines d’horloge.

Le tableau ci-dessous résume cette section. Les lignes correspondent à la présence ou non d’une logique de resynchronisation à l’extrémité du chemin, et les colonnes au fait que les horloges soient liées ou non. Le centre du tableau indique si le chemin doit être soumis ou non à des contraintes temporelles.

Les horloges sont :
horloges liées horloges non liées

Logique de resynchroni-
sation :

non appliquée Des contraintes temporelles sont requises sur le chemin C’est une erreur
appliquée Des contraintes temporelles ne sont pas requises sur le chemin

Erreurs courantes

Dans une conception complexe, où les signaux sont câblés à travers différents modules, il est facile de ne plus savoir quel signal est synchrone avec quelle horloge, et donc de passer d’un domaine d’horloge à un autre sans s’en rendre compte.

Deux types d’erreurs peuvent être nuisibles. La première consiste à créer un chemin entre des domaines d’horloge d’horloges non liées sans logique de resynchronisation. Le timing de ces chemins peut donc être violé de temps en temps à destination. Comme pour toute erreur de timing, le problème visible peut être très trompeur. Les outils peuvent ajouter à la confusion s’ils déduisent des contraintes temporelles que les deux horloges sont liées et appliquent donc inutilement des contraintes aux chemins concernés. Ces contraintes n’ont pas de signification pour des chemins entre horloges non liées, mais ces chemins apparaîtront dans les rapports comme s’ils avaient été correctement traités. Cela peut induire en erreur un lecteur humain des rapports de timing, qui pourrait croire que tout va bien, ou même que les horloges sont effectivement liées.

La deuxième erreur nuisible est le cas où deux horloges sont liées et traitées comme telles par la logique, alors que les outils ne les considèrent pas comme liées. Aucune contrainte temporelle n’est donc appliquée aux chemins entre les domaines d’horloge. En conséquence, rien ne garantit que les exigences temporelles soient satisfaites à destination. Là encore, cela peut conduire à un comportement non fiable. Il est cependant possible de repérer ce type d’erreur lors d’un contrôle de validation du timing de la conception.

Le seul cas où le fait de ne pas remarquer un franchissement de domaine d’horloge est vraiment sans danger est celui d’horloges liées que les outils FPGA considèrent également comme telles (et donc pour lesquelles des contraintes temporelles sont appliquées aux chemins concernés). Il peut toujours y avoir des bugs fonctionnels si les horloges ont des fréquences différentes et que la logique n’en tient pas compte, mais c’est comme n’importe quel bug logique.

Éviter de contraindre inutilement

Il arrive souvent qu’une seule PLL soit utilisée pour créer des horloges de fréquences différentes destinées à différentes unités fonctionnelles. Du point de vue du concepteur, ce sont des horloges non liées, mais en réalité ce sont des horloges liées, et les outils les considèrent généralement comme telles.

Supposons par exemple qu’une PLL génère deux horloges à partir d’une horloge de référence de 10 MHz. L’une est à 90 MHz et l’autre à 100 MHz. L’intention est d’utiliser ces horloges pour différentes parties du projet, donc sur le plan conceptuel, ce sont des horloges non liées. Mais que se passe-t-il s’il existe une connexion entre deux bascules, l’une synchrone d’une horloge et l’autre de l’autre ?

La période de la première horloge est de 11,11 ns et celle de la seconde de 10 ns ; l’écart temporel le plus défavorable entre les fronts montants est donc de 1,11 ns (en tenant compte de toutes les combinaisons de phase possibles). Cela équivaut à la période d’une horloge à 900 MHz. Le timing d’un chemin entre ces deux bascules est donc serré, et même s’il est possible de satisfaire cette exigence, elle représente une difficulté pour les outils, surtout s’il y a beaucoup de chemins de ce type.

Ce n’est pas un cas théorique : un piège courant consiste à utiliser une FIFO à double horloge (dual-clock FIFO) pour faire l’interface entre domaines d’horloge sans prêter attention au fait que les horloges sont liées ou non. Cette erreur ne pose pas de problème fonctionnel, mais comme les FIFO à double horloge ont toujours une logique de resynchronisation, il est inutile de forcer les outils à appliquer des contraintes temporelles sur les chemins entre les deux domaines d’horloge. Les outils peuvent s’échiner à satisfaire ces contraintes sans aucun bénéfice.

Il est donc important de prêter attention aux horloges liées qui ne sont pas utilisées comme telles, en particulier aux horloges issues de la même PLL : il y a de fortes chances que des contraintes temporelles soient appliquées sur des chemins allant d’un domaine d’horloge à un autre. Cette application n’apporte rien, car la logique de resynchronisation protège contre les violations de timing. En revanche, les efforts des outils pour satisfaire ces contraintes sur ces chemins peuvent rendre plus difficile l’obtention des contraintes temporelles pour l’ensemble de la conception.

Pour résoudre ce problème, ajoutez des contraintes temporelles qui déclarent les horloges comme non liées. Autre possibilité : définissez des faux chemins (false paths) ou des délais maximaux (maximal delays) pour ces chemins. Dans certains cas, cela n’est peut-être pas nécessaire, aussi vaut-il la peine de consulter le rapport de timing avant d’agir. Par exemple, lorsqu’on utilise une FIFO à double horloge fournie par les outils FPGA, des contraintes temporelles adéquates sont souvent ajoutées automatiquement pour les chemins entre les domaines d’horloge.

Contraintes temporelles trompeuses

Il est important de souligner que les outils FPGA acceptent et appliquent généralement des contraintes temporelles entre des horloges non liées. Ces contraintes sont sans objet et prêtent à confusion, surtout parce que les chemins concernés apparaissent dans le rapport de timing comme si leurs exigences temporelles étaient garanties. Comme déjà dit, il est impossible de garantir le timing d’un chemin entre des domaines d’horloge appartenant à des horloges non liées.

Gardez à l’esprit que les contraintes temporelles ne sont qu’un moyen de donner des informations sur la conception aux outils FPGA. Si une conception respecte les contraintes, cela ne veut dire quelque chose que si les contraintes sont correctes. Ne cherchez donc pas à résoudre un franchissement de domaine d’horloge en ajoutant simplement une contrainte temporelle si vous n’êtes pas sûr que les horloges sont liées.

Et une fois encore, lorsque c’est pertinent, il est bon d’ajouter des contraintes temporelles qui définissent des horloges comme non liées, ou de définir des faux chemins (false paths). Cela ne facilite pas seulement le travail des outils FPGA, mais évite aussi toute confusion.

Veiller à ce que les outils soient correctement informés

Pour toutes les raisons évoquées ci-dessus, il est crucial que les outils de conception FPGA disposent des bonnes informations sur les horloges qui sont liées et celles qui ne le sont pas. Ou, plus précisément, que les outils appliquent des contraintes temporelles à tous les chemins qui ne sont pas protégés par une logique de resynchronisation (cela inclut les chemins à l’intérieur des domaines d’horloge, mais ce n’est pas le sujet ici).

Veiller à ce que les outils aient la même perception que la conception logique peut toutefois être difficile : chaque logiciel de conception FPGA a sa propre façon de faire des hypothèses automatiques sur les relations entre horloges. Le seul point commun à tous les outils semble être que, si l’IP d’horloge dédiée de l’outil est utilisée pour générer deux horloges ou plus avec la même PLL, l’outil considérera ces horloges comme liées. Les chemins entre les domaines concernés seront donc soumis à une vérification temporelle, et les contraintes seront appliquées sur ces chemins. Cette application se fonde en général sur la contrainte temporelle donnée pour l’horloge de référence. Mais ne tenez même pas cela pour acquis.

Pour le reste, chaque outil a sa propre façon de déduire la relation entre horloges, et même des outils différents du même fournisseur de FPGA peuvent prendre des décisions différentes dans certaines situations. En particulier, Vivado de Xilinx est plus enclin que l’ancien outil phare de Xilinx, ISE, à supposer que des horloges sont liées.

Il n’existe donc pas de principe général valable pour tous les outils de conception FPGA, et même un outil précis peut prendre des décisions surprenantes. La seule façon de vraiment traiter ce sujet est de vérifier les rapports de timing, et de produire des rapports pour des groupes de chemins spécifiques afin de s’assurer que les exigences temporelles sont correctes là où elles sont nécessaires, et inexistantes là où elles sont inutiles. C’est une tâche ardue, mais elle vaut la peine.

Ceci conclut la première page de cette série. La page suivante présente les bases du franchissement de domaine d’horloge.

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)