Ceci est le troisième article d’une série de quatre consacrée à la reconfiguration partielle (Partial Reconfiguration), ou Dynamic Function eXchange (DFX), avec Vivado de Xilinx. Alors que les deux premiers montrent comment configurer le design FPGA, celui-ci explique comment la logique statique peut faire face à la disparition puis à la réapparition de la logique reconfigurable.
Vue d’ensemble
Le chargement d’un flux binaire partiel (bitstream) s’apparente au remplacement à chaud d’un matériel physique : une partie est retirée brutalement, remplacée par une autre, puis remise sous tension. Cet article décrit les dispositions nécessaires pour que cette transition se fasse de manière fiable et sans à-coups.
Cet article suppose que la reconfiguration partielle est déclenchée par la logique statique du FPGA lui-même. Pour garantir un fonctionnement sans accroc, elle doit se dérouler selon les étapes suivantes (les explications viennent ci-dessous) :
- Mettre la logique reconfigurable dans un état sûr, afin que sa remise à zéro ne perturbe rien de ce qui est sensible à ses sorties.
- Activer les signaux de remise à zéro de la logique reconfigurable.
- Lancer le découplage.
- Charger le flux binaire partiel dans l’ICAP.
- Attendre la fin de la séquence STARTUP de la configuration.
- Désactiver le découplage.
- Désactiver les signaux de remise à zéro de la logique reconfigurable.
Pourquoi le découplage est nécessaire
Pendant le chargement du flux binaire partiel, les connexions entre la logique statique et la logique reconfigurable se trouvent dans un état imprévisible. En conséquence, les ports de sortie du module reconfigurable peuvent produire des motifs aléatoires ou des valeurs invalides. Cela n’arrive pas nécessairement sur chaque port de sortie à tout instant, mais il y a de fortes chances que l’on observe un comportement pour le moins étrange.
Comme la logique statique continue de fonctionner pendant le chargement du flux binaire partiel, il faut ignorer les signaux imprévisibles qui peuvent arriver du module reconfigurable, afin d’éviter tout effet néfaste. Le guide utilisateur de Xilinx, UG909, appelle cela le découplage (decoupling).
La manière de mettre en œuvre le découplage dépend de la nature des ports de sortie du module reconfigurable : il convient d’analyser l’influence de chacun de ces ports sur la logique statique et de prendre les mesures préventives nécessaires. Il peut par exemple s’agir de multiplexer les ports de sortie avec des valeurs neutres pendant la reconfiguration, d’ajouter une validation d’horloge (clock enable) à la logique qui ne doit pas réagir, ou de maintenir certaines parties de la logique statique en remise à zéro.
Si la logique reconfigurable est connectée directement à des broches d’E/S, il peut être nécessaire de les mettre en haute impédance (high-Z) ou de désactiver la validation d’horloge de la logique d’E/S (si cette logique d’E/S comporte un registre de sortie). De même, si la logique reconfigurable est connectée à des composants externes, il peut être nécessaire de mettre ces composants dans un état sûr avant la reconfiguration.
Une IP appelée Partial Reconfiguration Decoupler est disponible dans le catalogue d’IP de Vivado pour le découplage des connexions AXI.
Les ports d’entrée du module reconfigurable n’ont pas besoin d’un tel traitement : la logique statique ne se soucie pas que les signaux qu’elle émet ne soient pas consommés.
En ce qui concerne les circuits Ultrascale, le découplage est nécessaire avant de charger les flux d’effacement (clearing bitstreams), car ceux-ci arrêtent la logique reconfigurable.
Quelques mots sur la séquence STARTUP
Une partie importante du flux binaire (complet comme partiel) est la commande de configuration START, qui déclenche la séquence STARTUP. Cette séquence met en jeu deux mécanismes destinés à assurer une mise en route cohérente de la logique.
Le premier est que les éléments synchrones reçoivent leurs valeurs par défaut à la fin de la configuration. C’est toujours le cas pour les FPGA Ultrascale et plus récents. Sur les FPGA de la série 7, c’est vrai pour une configuration complète (c’est-à-dire avec un flux binaire initial), ainsi que pour la reconfiguration partielle si RESET_AFTER_RECONFIG est activé.
Le second est GWE (Global Write Enable, à ne pas confondre avec les entrées d’activation des éléments synchrones ni avec les entrées de validation d’écriture) : ce signal permet aux bascules et aux RAM de changer de valeur. Pendant une configuration complète, GWE est maintenu à l’état bas sur tout le FPGA, puis passe à l’état haut à un certain moment de la séquence de démarrage de la configuration. Lorsqu’un flux binaire partiel est chargé, seule la logique reconfigurée est concernée.
Naturellement, le passage de GWE est asynchrone par rapport à toute horloge fournie par la logique applicative ; la relation temporelle entre ce passage et le premier front d’horloge effectif est donc imprévisible pour un élément synchrone quelconque.
Dans un scénario de reconfiguration partielle, comme lors d’une configuration complète, cela signifie que tous les éléments synchrones se retrouvent avec leur valeur initiale immédiatement après la fin du processus (avec Ultrascale et plus récents, ou si RESET_AFTER_RECONFIG est défini). Il existe toutefois une part aléatoire : certains éléments synchrones peuvent réagir au premier cycle d’horloge de la logique applicative, tandis que d’autres n’y réagiront pas. Ce comportement aléatoire dépend du moment où ce premier front d’horloge arrive par rapport au passage de GWE à l’état haut. Il est donc important d’appliquer correctement une remise à zéro (reset) à toute la logique sensible à une telle incertitude.
La nécessité de remettre à zéro la logique reconfigurable après le chargement du flux binaire partiel est la même qu’après une configuration complète du FPGA. Il est toutefois plus intuitif de comprendre qu’une remise à zéro est nécessaire dans le cas d’une configuration complète, en particulier parce que la remise à zéro est souvent maintenue active jusqu’à ce que certains éléments aient atteint un état stable (par exemple lorsque les MMCM ou les PLL sont verrouillés, lorsque le matériel externe est prêt, etc.).
En résumé, il n’y a pas de réponse unique à la question de savoir s’il faut remettre à zéro la logique reconfigurable, ni quelles parties de celle-ci. Comme pour une configuration complète, les éléments synchrones reçoivent leurs valeurs par défaut et commencent à répondre aux horloges. Cela suffit dans certaines situations ; dans d’autres scénarios, une remise à zéro est nécessaire.
Détection de la fin de la séquence STARTUP
Parmi les étapes énumérées plus haut, la seule partie qui échappe au contrôle de la logique applicative est la séquence STARTUP. Il est néanmoins important de savoir quand elle est terminée.
La séquence STARTUP est décrite dans le guide de configuration de chaque famille de FPGA, mais pour faire court, sa durée dépend fortement des options du flux binaire. Par exemple, la séquence peut être configurée pour attendre que les MMCM se verrouillent ou que les DCI terminent leur adaptation d’impédance.
Le FPGA fournit un signal appelé End Of Startup (EOS), qui passe à l’état haut à la dernière étape de la séquence STARTUP, c’est-à-dire lorsque celle-ci est terminée. Se fier à EOS est la façon formellement correcte de déterminer à quel moment ramener la logique reconfigurable en service, en déclenchant une remise à zéro puis le recouplage des ports de sortie.
Le signal EOS n’est accessible que depuis le tissu logique du FPGA, par l’instanciation (instantiation) d’une primitive STARTUPE2, par exemple de la manière suivante :
wire eos;
STARTUPE2 #(.PROG_USR("FALSE")) startup_ins
(
.CLK(1'b0),
.GSR(1'b0),
.GTS(1'b0),
.KEYCLEARB(1'b1),
.PACK(1'b0),
.USRCCLKO(1'b0),
.USRCCLKTS(1'b0),
.USRDONEO(1'b1),
.USRDONETS(1'b1),
.CFGCLK(),
.CFGMCLK(),
.EOS(eos),
.PREQ());
Ainsi, lorsque le flux binaire a fini d’être chargé dans l’ICAP, attendez que EOS passe à l’état haut, puis lancez la remise à zéro et le recouplage.
Les FPGA Ultrascale ont quant à eux une primitive STARTUPE3, mais Vivado accepte aussi les primitives STARTUPE2 pour ces FPGA et les convertit correctement en STARTUPE3. L’exemple de code ci-dessus couvre donc toutes les familles de FPGA.
J’ai effectué quelques tests anecdotiques pour mesurer le temps que met EOS à passer à l’état haut après l’arrivée de la commande START du flux binaire dans l’ICAP.
Avec un Kintex-7 et les réglages par défaut du flux binaire, cela a pris 26 cycles d’horloge (à 100 MHz, soit environ 260 ns). Comme le flux binaire contenait des données supplémentaires, notamment des NOP, il est tout à fait possible que EOS soit passé à l’état haut très peu après que le dernier mot du flux a été envoyé à l’ICAP.
En revanche, le même test sur un Kintex Ultrascale a donné des résultats complètement différents : EOS a mis, de manière aléatoire, entre 0,8 ms et 4,5 ms pour passer à l’état haut après la commande START.
Même s’il est très facile d’utiliser la primitive STARTUPE2 comme indiqué ci-dessus, il est également possible de déclencher le recouplage après un délai fixe à partir de la fin du chargement du flux binaire. Par exemple, il est difficilement concevable que la séquence STARTUP dure jusqu’à 100 ms, et c’est pourtant un délai pratiquement imperceptible pour un humain. Compte tenu des deux résultats de test ci-dessus, utiliser la primitive STARTUPE2 semble toutefois être la solution la plus sûre.
En ce qui concerne les FPGA Ultrascale, le comportement de EOS après le chargement d’un flux d’effacement (clearing bitstream) n’est apparemment pas documenté. Toutefois, lors d’un test anecdotique, EOS est resté à l’état bas après le chargement du flux d’effacement, puis est passé à l’état haut après le flux binaire partiel chargé ensuite.
Le troisième article se termine ici. Le dernier article se penche sur ce qui se passe dans les coulisses de Vivado : comment l’outil gère la relation entre la logique statique et la logique reconfigurable au moyen des OOC et des DCP, et comment la compréhension de ce mécanisme ouvre la voie à une méthode fiable de production de flux binaires partiels pour le scénario Remote Update.