01signal.com

Utiliser l'échantillonnage du signal 01 pour les entrées synchrones à la source

Introduction

Cette page explique l'échantillonnage du signal 01 (01-signal sampling) comme méthode d'interface avec une entrée synchrone à la source (source-synchronous input). D'autres stratégies pour interfacer des sources de données de ce type sont présentées sur une page consacrée aux entrées synchrones à la source en général. Cette page explique également ce qu'est une entrée synchrone à la source.

L'idée derrière l'échantillonnage du signal 01 est que l'horloge externe est traitée comme un signal de données : l'horloge est donc échantillonnée par un registre. Ce registre utilise une horloge interne stable et indépendante de l'horloge externe.

Les signaux de données sont échantillonnés par des registres supplémentaires. La même horloge interne est utilisée à cette fin. Cette horloge interne sert également à toute la logique qui met en œuvre l'échantillonnage du signal 01.

Cette logique détecte les changements sur le registre qui échantillonne l'horloge externe. Lorsque ce registre passe de « 0 » à « 1 », cela signifie qu'il y a eu un front montant sur l'horloge externe. La logique réagit à cet événement en écrivant les valeurs des autres registres dans une FIFO. Ces registres contiennent les valeurs des signaux de données qui étaient présents lors d'un front montant de l'horloge externe.

Cette logique aboutit au même résultat que si l'horloge de la FIFO était l'horloge externe et que les entrées de données de la FIFO étaient connectées directement aux signaux de données. La différence réside dans l'horloge utilisée par la logique : l'horloge externe ou l'horloge interne. L'avantage de l'échantillonnage du signal 01 est que toute la logique ne dépend que de l'horloge interne. Cette horloge est stable et fiable. Même si l'horloge externe se comporte mal, la logique continue de se comporter de manière raisonnable.

L'image suivante illustre l'échantillonnage du signal 01 :

Example of 01-signal sampling

Sur cette image, @stable_clk est l'horloge interne du FPGA. @data_clk et @data sont des signaux qui arrivent au FPGA. @data_clk_samp et @data_samp sont des registres à l'intérieur du FPGA. Le signal externe @data_clk est représenté par @data_clk_samp. Il en va de même pour @data_samp par rapport à @data.

L'image illustre que lorsqu'un motif « 0 1 » apparaît dans @data_clk_samp, la valeur de @data_samp est écrite dans une FIFO. C'est la raison pour laquelle cette méthode est appelée « échantillonnage du signal 01 ».

La FIFO est donnée ici comme exemple de ce que l'on peut faire des données arrivantes. C'est souvent une solution appropriée, car la fréquence de l'horloge interne est nettement supérieure au débit de données. Il est donc souvent commode de continuer à traiter les données avec une logique basée sur une fréquence d'horloge plus basse. Une FIFO est une méthode pratique pour confier les données à une logique située dans un autre domaine d'horloge (clock domain).

Il est néanmoins possible d'implémenter le reste de la logique avec la même horloge interne que celle utilisée pour l'échantillonnage du signal 01. L'utilisation d'une FIFO n'est qu'une possibilité.

Un exemple en Verilog

Ce code Verilog illustre l'idée. L'horloge externe est @data_clk.

module top (
   input stable_clk,

   input data_clk,
   input [7:0] data
);

   reg [7:0] data_guard, data_samp;
   reg       data_clk_guard, data_clk_samp, data_clk_samp_d;
   wire      fifo_wr_en;

   always @(posedge stable_clk)
     begin
        data_guard <= data;
        data_clk_guard <= data_clk;

        data_samp <= data_guard;
        data_clk_samp <= data_clk_guard;

        data_clk_samp_d <= data_clk_samp;
     end

   assign fifo_wr_en = data_clk_samp && !data_clk_samp_d;

   data_fifo fifo_i
     (
      .wr_clk(stable_clk),
      .din(data_samp),
      .wr_en(fifo_wr_en),

       [ ... other ports connected here ... ]
      );
endmodule

La partie importante est le wr_en de la FIFO : @fifo_wr_en. Ce signal vaut « data_clk_samp && !data_clk_samp_d ». Ce signal est donc à l'état haut suite à la détection d'un motif « 0 1 » sur @data_clk_samp. Cela provoque l'écriture de la valeur de @data_samp dans la FIFO.

Notez que seule @stable_clk est utilisée comme horloge par la logique. @data_clk est traitée comme une entrée d'E/S ordinaire.

Les exigences temporelles de @data_guard et @data_clk_guard ne sont pas garanties : les ports d'entrée sont asynchrones par rapport à @stable_clk. @data_guard et @data_clk_guard servent donc de garde-fous contre la métastabilité (metastability guards). La logique ne dépend pas directement des valeurs de ces deux registres. En revanche, @data_samp et @data_clk_samp sont utilisés directement par la logique, car ces registres constituent la deuxième étape du garde-fou contre la métastabilité.

Mais est-ce que cela fonctionne vraiment ? La réponse se trouve dans l'analyse temporelle.

Analyse temporelle

L'analyse temporelle de l'échantillonnage du signal 01 diffère de la méthode habituelle : Normalement, il existe une différence temporelle constante entre le front d'horloge et l'instant où les signaux de données sont échantillonnés. Cela vient du fait que l'horloge utilisée pour l'échantillonnage est synchrone avec les signaux de données. Mais avec l'échantillonnage du signal 01, l'échantillonnage de @data est effectué avec @stable_clk. L'instant d'échantillonnage n'a donc rien à voir avec le timing propre des signaux de données. La logique sélectionne plutôt uniquement les valeurs de @data_clk_samp qui sont proches d'un front montant d'horloge.

Il y a donc une différence temporelle aléatoire entre le moment du front montant de l'horloge de données et le moment où l'échantillonnage réel a lieu. Comment cette méthode peut-elle être fiable ?

Il existe une page séparée sur les fondements du timing dans une conception logique. Cette page explique la signification de tsu et thold. En résumé, l'entrée d'une bascule doit être stable avant et après le front montant de l'horloge (en supposant que la bascule est activée sur front montant). tsu définit le temps pendant lequel l'entrée doit être stable avant le front montant. thold définit le temps pendant lequel l'entrée doit être stable après le front montant. Si l'une de ces conditions n'est pas respectée, la réponse de la bascule au front montant est imprévisible.

On peut voir les choses autrement : l'entrée doit être stable pendant une période précise autour du front montant. Appelons cette période Δt = tsu + thold. À partir de Δt et d'autres paramètres, nous allons maintenant déterminer les exigences temporelles qui assurent un fonctionnement fiable.

Toute l'analyse temporelle repose sur la situation où @data_clk_guard est à l'état haut et @data_clk_samp à l'état bas. Lorsque cela se produit, le motif « 0 1 » est détecté un cycle d'horloge plus tard. En d'autres termes, @data_clk_samp_d sera à « 0 » et @data_clk_samp à « 1 » au cycle d'horloge suivant. @fifo_wr_en sera donc à l'état haut, et @data_samp doit alors contenir la valeur que la source de données avait l'intention d'envoyer.

L'analyse se concentrera donc sur la question suivante : lorsque @data_clk_guard est à l'état haut et @data_clk_samp à l'état bas, quelles sont les exigences sur @data pour garantir que l'information arrive correctement ?

L'analyse se fera en deux parties. Dans les deux parties, je supposerai que @data_clk_guard est à l'état haut et @data_clk_samp à l'état bas. Dans la première partie, la question sera : quel est le moment le plus tardif auquel @data_clk peut passer de l'état bas à l'état haut sans remettre en cause cette hypothèse ? Je déterminerai ensuite l'exigence temporelle sur @data qui garantit que @data_samp contient la valeur correcte.

Dans la deuxième partie de l'analyse, je poserai la question inverse : quel est le moment le plus tôt auquel @data_clk peut passer de l'état bas à l'état haut sans remettre en cause l'hypothèse sur @data_clk_guard et @data_clk_samp ? Je ferai ensuite une analyse similaire concernant les exigences temporelles.

Mais commençons par définir quelques symboles :

Première partie de l'analyse

Ce chronogramme montre deux cycles d'horloge de @stable_clk. Dans la discussion ci-dessous, @data_clk_guard copie sa nouvelle valeur depuis @data_clk sur le front montant situé à droite. De même, @data_guard copie sa nouvelle valeur depuis @data sur le même front d'horloge.

Par le même principe, @data_clk_samp et @data_samp sont liés au front montant de @stable_clk situé à gauche.

Timing diagram for late @data_clk scenario

Dans ce scénario, @data_clk passe à l'état haut exactement à la fin de la région jaune. Les exigences temporelles de la bascule sont violées, le résultat est donc imprévisible. Mais il est possible que @data_clk_guard soit à l'état haut et @data_clk_samp à l'état bas.

Cependant, si @data_clk change de valeur un peu plus tard, @data_clk_guard sera certainement à l'état bas, car le comportement de la bascule est prévisible : dans ce scénario, l'entrée est à l'état bas pendant toute la période jaune. C'est pourquoi on peut affirmer ceci : si @data_clk_guard est à l'état haut et @data_clk_samp à l'état bas, alors @data_clk est passé de l'état bas à l'état haut plus tôt que ce qui est montré sur le chronogramme ci-dessus. Par conséquent, le chronogramme représente le scénario où @data_clk a changé au moment le plus tardif possible.

Pour garantir que @data_guard contienne une valeur fiable, @data doit être stable avant la zone jaune. Cela nous donne la première exigence temporelle : @data doit être stable pendant une durée de Δt avant le front montant de @data_clk.

Notez que si @data_clk passe de l'état bas à l'état haut plus tôt, cette exigence temporelle garantit tout de même le respect de tsu pour @data_guard. Ainsi, le tsu des bascules est garanti pour tous les scénarios où @data_clk_guard est à l'état haut et @data_clk_samp à l'état bas.

Le chronogramme ci-dessus ne montre rien concernant le décalage (skew). Pour prendre en compte le décalage, l'exigence temporelle devient : @data doit être stable pendant une durée de Δt + tskew avant le front montant de @data_clk.

Il n'est pas nécessaire de prendre la gigue (jitter) en compte dans ce scénario, car un seul front d'horloge est impliqué dans la discussion.

Deuxième partie de l'analyse

Voici le chronogramme pour ce scénario :

Timing diagram for early @data_clk scenario

Dans ce scénario, @data_clk passe à l'état haut exactement au début de la région jaune du front d'horloge précédent de @stable_clk.

Il ne fait aucun doute que @data_clk_guard sera à l'état haut dans cette situation. En revanche, la valeur de @data_clk_samp n'est pas prévisible, en raison de la violation des exigences temporelles au cycle d'horloge précédent. Comme précédemment, il est possible que @data_clk_samp soit à l'état bas. Mais si @data_clk change de valeur plus tôt, @data_clk_samp sera certainement à l'état haut.

Donc, si @data_clk_guard est à l'état haut et @data_clk_samp à l'état bas, @data_clk est passé de l'état bas à l'état haut plus tard que ce qui est montré sur le chronogramme ci-dessus. Par conséquent, le chronogramme représente le scénario où @data_clk a changé au moment le plus tôt possible.

Pour garantir que @data_guard contienne la valeur correcte, @data doit être stable après la région jaune située à droite.

Selon le chronogramme ci-dessus, la différence temporelle entre le front montant de @data_clk et la fin de la deuxième région jaune est Δt + tclk. Mais il faut aussi tenir compte du décalage et de la gigue. Par conséquent, @data doit être stable pendant une durée de Δt + tclk + tskew + tj après @data_clk.

Notez que si @data_clk passe de l'état bas à l'état haut plus tôt, cette exigence temporelle couvre tout de même l'exigence de temps de maintien (hold) de @data_guard. Ainsi, le thold des bascules est garanti pour tous les scénarios où @data_clk_guard est à l'état haut et @data_clk_samp à l'état bas.

Les exigences temporelles

En conclusion de l'analyse temporelle ci-dessus, voici les deux exigences temporelles pour garantir un fonctionnement fiable de l'échantillonnage du signal 01 :

Ces exigences peuvent sembler compliquées, mais il est souvent facile de les satisfaire. Par exemple, si @data change en même temps que le front descendant de @data_clk, il est généralement facile de garantir ces exigences. Si @stable_clk est trois fois plus rapide que @data_clk, cela suffit souvent.

Dans la plupart des cas, il n'est pas nécessaire de connaître les valeurs exactes de Δt, tskew ou tj. Il suffit souvent de calculer la valeur maximale que peuvent atteindre Δt et Δt + tskew + tj, à partir des deux exigences ci-dessus. Si la fréquence de @stable_clk est suffisamment élevée, les valeurs de ces deux expressions sont souvent supérieures à ce qui est réalistement possible pour un FPGA.

Des registres IOB devraient être utilisés pour minimiser les différences temporelles entre les ports d'entrée du FPGA (tskew). De plus, des contraintes temporelles (timing constraints) devraient être écrites en relation avec ces ports d'entrée afin de garantir que des registres IOB soient bien utilisés.

Variantes de l'échantillonnage du signal 01

Dans la discussion jusqu'ici, j'ai supposé que @data change en même temps que le front descendant de @data_clk. Si @data change en même temps que le front montant de @data_clk, la logique doit être ajustée pour devenir active lorsque @data_clk_guard est à l'état bas et @data_clk_samp à l'état haut. Autrement dit, la logique doit détecter le front descendant de @data_clk en cherchant un motif « 1 0 ».

Il est également possible de choisir un autre critère pour décider quand capturer la valeur de @data. Par exemple, il peut être préférable de retarder @fifo_wr_en de quelques cycles d'horloge. Parfois, il vaut mieux que @fifo_wr_en soit actif plus tôt que ce qui a été suggéré ci-dessus. Cela dépend des relations temporelles entre @data_clk et @data. Consultez la fiche technique (datasheet) de la source des signaux et analysez le timing comme indiqué ci-dessus.

Si la fréquence de @data_clk est relativement élevée, il est possible d'utiliser des registres DDR pour échantillonner @data_clk et @data. La logique qui met en œuvre cette solution est un peu plus compliquée, mais les mêmes principes s'appliquent.

Le registre @data_guard est-il vraiment un garde-fou contre la métastabilité ?

La réponse courte à cette question est oui. @data est asynchrone avec @stable_clk, donc les exigences temporelles de @data_guard ne sont pas garanties.

Mais resserrons la discussion sur les cycles d'horloge où le contenu de @data_guard est réellement utilisé : notez que les deux exigences temporelles ci-dessus garantissent que les bascules qui implémentent @data_guard fonctionnent de manière fiable. Le tsu comme le thold de ces bascules sont garantis.

Par conséquent, @data_guard n'est pas réellement utilisé comme garde-fou contre la métastabilité. Dans la manière dont ce registre est utilisé, il ne fonctionne que comme un registre à retard. Mais il ne fait pas de mal d'avoir un registre supplémentaire qui protège contre les violations temporelles pouvant survenir si le signal physique (@data) se comporte mal. Cela est particulièrement pertinent lorsque le signal est connecté au FPGA par l'intermédiaire d'un connecteur.

Un exemple concret

Il existe un exemple de logique qui s'interface avec un capteur de caméra OV7670 sur une autre page. Cette logique obtient les données des pixels du capteur grâce à l'échantillonnage du signal 01. Les entrées provenant du capteur sont les suivantes :

   input       pclk_in;
   input [7:0] D_in;
   input       hsync_in, vsync_in;

La logique qui effectue l'échantillonnage du signal 01 est la suivante :

   (* IOB = "TRUE" *) reg [7:0] D_guard;
   (* IOB = "TRUE" *) reg       pclk_guard, hsync_guard, vsync_guard;

   reg [7:0]  D;
   reg 	      pclk, hsync, vsync;

   wire       sample_valid;
   reg 	      previous_pclk;

   always @(posedge stable_clk)
     begin
	// Metastability guards on asynchronous inputs
	D_guard <= D_in;
	pclk_guard <= pclk_in;
	hsync_guard <= hsync_in;
	vsync_guard <= vsync_in;

	D <= D_guard;
	pclk <= pclk_guard;
	hsync <= hsync_guard;
	vsync <= vsync_guard;

        previous_pclk <= pclk;
     end

   assign sample_valid = pclk && !previous_pclk;

Pour plus de clarté, il existe de légères différences entre le code Verilog présenté ici et le code Verilog sur la page consacrée à l'OV7670. Ces différences n'ont aucun impact sur le fonctionnement de la logique.

Comparons ce code Verilog avec celui que j'ai présenté en haut de cette page. Les noms des signaux sont différents, mais ils signifient la même chose : au lieu de @data, nous avons maintenant @D_in, @hsync_in et @vsync_in. Au lieu de @data_clk, nous avons @pclk_in. Et au lieu de @fifo_wr_en, nous avons @sample_valid. Le changement des noms peut prêter à confusion, mais il n'y a aucune différence par rapport au code Verilog ci-dessus.

Remarquez la mention « (* IOB = "TRUE" *) » avant les déclarations des registres. Avec Vivado, c'est un moyen possible de demander que les registres soient insérés dans les IOB.

Dans cet exemple, la FIFO n'est pas présentée. Cela est dû au fait que nous ne souhaitons pas écrire toutes les données dans la FIFO : lorsque @sample_valid est à l'état haut, cela signifie que @D, @hsync et @vsync contiennent des valeurs correctes provenant du capteur. Mais cela ne signifie pas que nous voulons écrire @D dans la FIFO : cela dépend de @hsync et @vsync. Il y a donc une logique supplémentaire dans l'exemple OV7670 qui garantit que seuls les pixels sont écrits dans la FIFO.

Mais venons-en à la partie intéressante : l'analyse temporelle.

La fréquence de @stable_clk dans cet exemple est de 100 MHz. La fréquence de @pclk_in est de 25 MHz. Selon la fiche technique de l'OV7670, les signaux @D_in, @hsync_in et @vsync_in sont garantis stables pendant les 5 ns qui suivent le passage de @pclk_in de l'état haut à l'état bas (front descendant).

Le cycle d'horloge de @pclk_in est de 40 ns. La distance entre le front descendant et le front montant est donc de 20 ns. Comparons maintenant avec les exigences temporelles.

La première exigence temporelle était que @D_in, @hsync_in et @vsync_in doivent être stables pendant une durée de Δt avant le front montant de @pclk_in. En réalité, ces signaux sont stables à partir de 5 ns après le front descendant. Ces signaux sont donc stables au moins 15 ns avant le prochain front montant. Rappelons que Δt = tsu + thold. L'exigence temporelle revient donc à dire que tsu + thold doit être inférieur à 15 ns. C'est le cas pour tous les FPGA.

La deuxième exigence est que ces signaux soient stables pendant une durée d'au moins Δt + tclk + tskew + tj après le front montant de @pclk_in. Mais ces signaux ne changent qu'en réponse à un front descendant. L'exigence est donc que Δt + tclk + tskew + tj soit inférieur à 20 ns. tclk vaut 10 ns car la fréquence de @stable_clk est de 100 MHz. L'exigence effective est donc que Δt + tskew + tj soit inférieur à 10 ns. Là encore, c'est évident pour tout FPGA.

Cet exemple montre comment les exigences temporelles peuvent être satisfaites facilement sans connaître les paramètres temporels exacts du FPGA.

Conclusion

L'échantillonnage du signal 01 est une excellente solution lorsque le débit de données est faible par rapport à la fréquence d'horloge que le FPGA peut supporter : l'horloge de données n'a pas besoin d'être stable. De plus, la fréquence exacte de l'horloge de données n'a pas besoin d'être connue à l'avance. Il suffit de garantir les deux exigences temporelles.

Cette méthode présente d'autres avantages : si cette horloge devient inactive pendant un bref moment, la seule conséquence est que les données ne sont pas collectées pendant cette période. Les dégâts causés par un mauvais fonctionnement de cette horloge se limitent à une perturbation du flux de données. Cela conduit à un dysfonctionnement visible du système, mais ce dysfonctionnement ressemble à un problème d'horloge (et non à un FPGA hanté par des fantômes).

Ainsi, même si l'échantillonnage des signaux de données présente un caractère aléatoire inhérent, l'échantillonnage du signal 01 est une solution fiable et robuste pour une entrée synchrone à la source. Le seul véritable inconvénient est la limitation du débit de données.

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)