01signal.com

Quartus : Placer les registres dans les cellules d'E/S

Je préfère souvent traiter le timing (timing) des E/S en m'assurant que tous les registres sont placés dans les cellules d'E/S. Enfin, là où le timing compte.

Il semble que le placement des registres dans les cellules d'E/S ne soit pas le comportement par défaut de Quartus. Quoi qu'il en soit, voici la recette pour les paresseux dans ce scénario.

Dans une version antérieure de cet article, je suggérais de désactiver la vérification temporelle sur toutes les E/S. Cela fait taire l'avertissement concernant les chemins (paths) non contraints pendant l'implémentation et, surtout, évite que la section « TimeQuest Timing Analyzer » du panneau des rapports de Quartus passe au rouge :

set_false_path -from [get_ports]
set_false_path -to [get_ports]

Il s'avère que ce n'est pas une si bonne idée, en particulier en ce qui concerne les ports d'entrée. Nous y reviendrons plus en détail plus bas.

Il faut néanmoins convaincre le fitter de mettre les registres dans le bloc d'E/S. Dans le fichier QSF, ajoutez

set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to *
set_instance_assignment -name FAST_INPUT_REGISTER ON -to *
set_instance_assignment -name FAST_OUTPUT_ENABLE_REGISTER ON -to *

C'est un peu agressif d'appliquer ces affectations à absolument tous les registres, mais ça fait le travail. Le fitter émet des avertissements pour les éléments d'E/S sur lesquels il n'arrive pas à imposer ces contraintes, ce qui est en réalité une bonne chose.

Pour voir si cela a bien fonctionné, ouvrez la « Resource Section » du rapport du fitter (qu'on trouve peut-être dans le panneau des rapports de Quartus) et cherchez la rubrique « Input Registers », ou toute autre rubrique pertinente.

La différence saute aux yeux dans les rapports d'analyse temporelle des chemins qui font intervenir des cellules d'E/S. Par exemple, comparez ce chemin, qui utilise un registre d'E/S :

+----------------------------------------------------------------------------------+
; Data Arrival Path                                                                ;
+---------+---------+----+------+--------+-----------------------+-----------------+
; Total   ; Incr    ; RF ; Type ; Fanout ; Location              ; Element         ;
+---------+---------+----+------+--------+-----------------------+-----------------+
; 2.918   ; 2.918   ;    ;      ;        ;                       ; data path       ;
;   0.000 ;   0.000 ;    ;      ; 1      ; DDIOOUTCELL_X3_Y0_N32 ; rst             ;
;   0.465 ;   0.465 ; RR ; CELL ; 1      ; DDIOOUTCELL_X3_Y0_N32 ; rst|q           ;
;   0.465 ;   0.000 ; RR ; IC   ; 1      ; IOOBUF_X3_Y0_N30      ; RESETB~output|i ;
;   2.918 ;   2.453 ; RR ; CELL ; 1      ; IOOBUF_X3_Y0_N30      ; RESETB~output|o ;
;   2.918 ;   0.000 ; RR ; CELL ; 0      ; PIN_P3                ; RESETB          ;
+---------+---------+----+------+--------+-----------------------+-----------------+

Remarquez l'élément DDIOOUTCELL et l'incrément nul dans le routage entre le registre et l'IOBUF.

À titre de comparaison, voici un chemin pour lequel le registre d'E/S n'a pas été utilisé (parce que de la logique l'en empêchait) :

+--------------------------------------------------------------------------------+
; Data Arrival Path                                                              ;
+---------+---------+----+------+--------+-----------------+---------------------+
; Total   ; Incr    ; RF ; Type ; Fanout ; Location        ; Element             ;
+---------+---------+----+------+--------+-----------------+---------------------+
; 8.284   ; 8.284   ;    ;      ;        ;                 ; data path           ;
;   0.000 ;   0.000 ;    ;      ; 1      ; FF_X3_Y0_N17    ; Dir_flop_sig        ;
;   0.496 ;   0.496 ; RR ; CELL ; 8      ; FF_X3_Y0_N17    ; Dir_flop_sig|q      ;
;   2.153 ;   1.657 ; RR ; IC   ; 1      ; IOOBUF_X3_Y0_N9 ; DATA[7]~output|oe   ;
;   8.284 ;   6.131 ; RF ; CELL ; 1      ; IOOBUF_X3_Y0_N9 ; DATA[7]~output|o    ;
;   8.284 ;   0.000 ; FF ; CELL ; 1      ; PIN_T3          ; DATA[7]             ;
+---------+---------+----+------+--------+-----------------+---------------------+

On voit ici comment une bascule à usage général génère le signal, ce qui donne un délai de routage de 1,657 ns. Le principal problème est que ce délai de routage variera d'une implémentation à l'autre. Si la carte souffre d'un problème d'intégrité du signal, le FPGA pourrait en être tenu pour responsable, puisque différentes versions du design FPGA sembleront tour à tour corriger le problème ou le faire réapparaître.

Contraintes temporelles

Les ports d'entrée comme les ports de sortie doivent avoir des contraintes temporelles (timing constraints) serrées, afin qu'on ne puisse les satisfaire qu'en tirant pleinement parti des registres d'E/S. De cette façon, un problème dans le placement des registres provoquera une violation temporelle, et c'est également indispensable pour obtenir le délai minimal entre l'entrée et le registre, comme expliqué plus loin.

La discussion ci-dessous ne s'applique que lorsque l'horloge qui pilote les registres est directement liée à une horloge externe (par exemple avec une PLL qui multiplie la fréquence par un nombre entier). Si l'horloge des registres n'a pratiquement aucun rapport avec l'horloge externe, les choses deviennent nettement plus compliquées, comme expliqué dans cet article.

Pour illustrer ce problème, prenons le code Verilog suivant :

module top
  (
   input        clk,
   input        in,
   output reg   out
   );

   reg 		in_d, in_d2;
   wire  	pll_clk;

   always @(posedge pll_clk)
     begin
	in_d <= in;
	in_d2 <= in_d;
	out <= in_d2;
     end

  /* Here comes an instantiation of a phase-compensating PLL, which
     doesn't change the frequency */
endmodule

Considérons également la contrainte suivante dans le fichier SDC :

create_clock -name main_clk -period 10 -waveform { 0 5 } [get_ports {clk}]

derive_pll_clocks
derive_clock_uncertainty

set_input_delay -clock main_clk -max 8.5 [get_ports in*]
set_input_delay -clock main_clk -min 0 [get_ports in*]

Comme expliqué dans cet article, set_input_delay représente le délai maximal de la source qui génère le signal, entre l'horloge et un état logique stable. Comme la période de l'horloge est fixée à 10 ns, imposer une contrainte de délai de 8,5 ns laisse 1,5 ns avant l'arrivée du front suivant (à 10 ns). Autrement dit, le temps de préparation (setup time) sur la broche du FPGA est astreint à ne pas dépasser 1,5 ns.

Remarquez que set_max_delay peut aussi être utilisé à cette fin (dans certains cas, c'est la seule manière), comme expliqué dans cet article.

Une compilation de tout cela (avec l'affectation FAST_INPUT_REGISTER ON dans le QSF, comme indiqué plus haut) donne l'extrait suivant dans le rapport d'analyse temporelle :

+----------------------------------------------------------------------------------+
; Data Arrival Path                                                                ;
+---------+---------+----+------+--------+-------------------+---------------------+
; Total   ; Incr    ; RF ; Type ; Fanout ; Location          ; Element             ;
+---------+---------+----+------+--------+-------------------+---------------------+
; 0.000   ; 0.000   ;    ;      ;        ;                   ; launch edge time    ;
; 0.000   ; 0.000   ;    ;      ;        ;                   ; clock path          ;
;   0.000 ;   0.000 ; R  ;      ;        ;                   ; clock network delay ;
; 8.500   ; 8.500   ; F  ; iExt ; 1      ; PIN_F2            ; in                  ;
; 9.550   ; 1.050   ;    ;      ;        ;                   ; data path           ;
;   8.500 ;   0.000 ; FF ; IC   ; 1      ; IOIBUF_X0_Y22_N15 ; in~input|i          ;
;   9.308 ;   0.808 ; FF ; CELL ; 1      ; IOIBUF_X0_Y22_N15 ; in~input|o          ;
;   9.308 ;   0.000 ; FF ; IC   ; 1      ; FF_X0_Y22_N17     ; in_d|d              ;
;   9.550 ;   0.242 ; FF ; CELL ; 1      ; FF_X0_Y22_N17     ; in_d                ;
+---------+---------+----+------+--------+-------------------+---------------------+

Contrairement au cas du registre de sortie, on ne voit ici aucune bascule du type « DDIOINCELL », mais plutôt quelque chose qui ressemble à une bascule ordinaire. Remarquez toutefois que le câblage vers cette bascule présente un délai nul (marqué en rouge), ce qui indique clairement que la bascule et le buffer d'entrée sont fusionnés.

Le rapport de type « datasheet » pour cette entrée indique :

+---------------------------------------------------------------------------------------------------+
; Setup Times                                                                                       ;
+-----------+------------+-------+-------+------------+---------------------------------------------+
; Data Port ; Clock Port ; Rise  ; Fall  ; Clock Edge ; Clock Reference                             ;
+-----------+------------+-------+-------+------------+---------------------------------------------+
; in        ; main_clk   ; 1.282 ; 1.461 ; Rise       ; altpll_component|auto_generated|pll1|clk[0] ;
+-----------+------------+-------+-------+------------+---------------------------------------------+

+-----------------------------------------------------------------------------------------------------+
; Hold Times                                                                                          ;
+-----------+------------+--------+--------+------------+---------------------------------------------+
; Data Port ; Clock Port ; Rise   ; Fall   ; Clock Edge ; Clock Reference                             ;
+-----------+------------+--------+--------+------------+---------------------------------------------+
; in        ; main_clk   ; -0.683 ; -0.862 ; Rise       ; altpll_component|auto_generated|pll1|clk[0] ;
+-----------+------------+--------+--------+------------+---------------------------------------------+

Comme exigé, le temps de préparation (setup) imposé par le FPGA est inférieur à la limite de 1,5 ns fixée par la contrainte.

Relâchons maintenant la contrainte de délai d'entrée de 2 ns, laissons tout le reste inchangé, et relançons la compilation :

set_input_delay -clock main_clk -max 6.5 [get_ports in*]
set_input_delay -clock main_clk -min 0 [get_ports in*]

Le segment du rapport d'analyse temporelle devient alors :

+----------------------------------------------------------------------------------+
; Data Arrival Path                                                                ;
+---------+---------+----+------+--------+-------------------+---------------------+
; Total   ; Incr    ; RF ; Type ; Fanout ; Location          ; Element             ;
+---------+---------+----+------+--------+-------------------+---------------------+
; 0.000   ; 0.000   ;    ;      ;        ;                   ; launch edge time    ;
; 0.000   ; 0.000   ;    ;      ;        ;                   ; clock path          ;
;   0.000 ;   0.000 ; R  ;      ;        ;                   ; clock network delay ;
; 6.500   ; 6.500   ; F  ; iExt ; 1      ; PIN_F2            ; in                  ;
; 8.612   ; 2.112   ;    ;      ;        ;                   ; data path           ;
;   6.500 ;   0.000 ; FF ; IC   ; 1      ; IOIBUF_X0_Y22_N15 ; in~input|i          ;
;   7.308 ;   0.808 ; FF ; CELL ; 1      ; IOIBUF_X0_Y22_N15 ; in~input|o          ;
;   8.370 ;   1.062 ; FF ; IC   ; 1      ; FF_X0_Y22_N17     ; in_d|d              ;
;   8.612 ;   0.242 ; FF ; CELL ; 1      ; FF_X0_Y22_N17     ; in_d                ;
+---------+---------+----+------+--------+-------------------+---------------------+

Hein ? Le délai d'interconnexion est soudain passé à 1,062 ns ?! Notons que le placement du registre n'a pas changé : il ne fait donc aucun doute que in_d est bien un registre d'E/S. Alors, d'où vient ce délai ?

Pour répondre à cette question, il faut examiner le design de plus près. Après une compilation complète, si l'on choisit Tools > Netlist Viewers > Technology Map Viewer (Post-Fitting), on obtient le schéma suivant (montré partiellement ci-dessous, cliquer pour agrandir) :

Design diagram

Un clic droit sur in_d (le registre), puis Locate Node > Locate in Resource Property Editor, révèle ce qui suit (cliquer pour agrandir) :

Property editor view

À droite de ce dessin (non représenté ci-dessus), la propriété « Input Pin to Input Register Delay » est réglée à 2. C'est la cause de ce délai. Avant que la contrainte ne soit relâchée, elle était à 0. La leçon immédiate est :

Si la contrainte de préparation (setup) n'est pas fixée à la meilleure valeur possible pour la technologie, Quartus peut ajouter un délai qui se fera au détriment de cette contrainte.

Mais pourquoi, Quartus, pourquoi ?

On peut donc se demander pourquoi Quartus insère ce délai entre le pad d'entrée et le registre. Le but n'était-il pas d'échantillonner le plus tôt possible ? Pour répondre, examinons le rapport « datasheet » mis à jour :

---------------------+
; Data Port ; Clock Port ; Rise  ; Fall  ; Clock Edge ; Clock Reference                             ;
+-----------+------------+-------+-------+------------+---------------------------------------------+
; in        ; main_clk   ; 2.205 ; 2.523 ; Rise       ; altpll_component|auto_generated|pll1|clk[0] ;
+-----------+------------+-------+-------+------------+---------------------------------------------+

+-----------------------------------------------------------------------------------------------------+
; Hold Times                                                                                          ;
+-----------+------------+--------+--------+------------+---------------------------------------------+
; Data Port ; Clock Port ; Rise   ; Fall   ; Clock Edge ; Clock Reference                             ;
+-----------+------------+--------+--------+------------+---------------------------------------------+
; in        ; main_clk   ; -1.570 ; -1.882 ; Rise       ; altpll_component|auto_generated|pll1|clk[0] ;
+-----------+------------+--------+--------+------------+---------------------------------------------+

Rappelons que l'on a retiré 2 ns de la contrainte de délai. Le temps de préparation (setup) maximal autorisé est donc passé de 1,5 ns à 3,5 ns. On voit sans peine que cette exigence est satisfaite, avec une marge (slack) de presque 1 ns.

Quartus s'est donc dit quelque chose comme : « Je satisfais la contrainte de setup sans difficulté, avec 2 ns de surplus. Offrons 1 ns supplémentaire au temps de préparation, et 1 ns au temps de maintien (hold), qui est de 0 ns. » Et de fait, en ajoutant ces 1,062 ns de délai, le temps de maintien est passé de -0,683 ns à -1,570 ns (et ne m'en veuillez pas si la différence n'est pas tout à fait exacte).

En résumé : Quartus a élargi la marge à la fois pour le setup et pour le hold, ce qui rend l'entrée plus robuste vis-à-vis de la gigue (jitter). C'est une attitude plutôt sensée, mais ce n'est souvent ni souhaité ni prévu.

Conclusion : si vous voulez obtenir le délai absolument minimal entre l'entrée et le registre, lancez une compilation avec une contrainte de délai qui échoue, puis relâchez la contrainte juste assez pour que l'échec disparaisse. Cela garantit que Quartus n'essaiera pas d'« améliorer » le timing en ajoutant ce délai d'entrée pour obtenir un meilleur temps de maintien.

Utiliser des primitives DDR

Les FPGA d'Intel contiennent, dans les cellules d'E/S ou à proximité, une logique dédiée qui permet de produire des sorties aussi bien que d'échantillonner les entrées à une cadence double. Ce sujet est détaillé dans le guide utilisateur correspondant : ug_altddio.pdf. L'instanciation (instantiation) d'une primitive DDR (ou l'utilisation de la mégafonction ALTDDIO_BIDIR) est une façon séduisante de forcer les outils à placer les registres dans les cellules d'E/S. Ce n'est cependant pas forcément une bonne idée.

Par exemple, une instanciation comme celle-ci :

altddio_bidir ioddr
 (
 .padio(pin),
 .aclr (1'b0),
 .datain_h(datain_h),
 .datain_l(datain_l),
 .inclock(clk),
 .oe(oe),
 .outclock(clk),
 .dataout_h(dataout_h),
 .dataout_l(dataout_l),
 .oe_out (),
 .aset (1'b0),
 .combout(),
 .dqsundelayedout(),
 .inclocken(1'b1),
 .outclocken(1'b1),
 .sclr(1'b0),
 .sset(1'b0));
 defparam
   ioddr.extend_oe_disable = "OFF",
   ioddr.implement_input_in_lcell = "OFF",
   ioddr.intended_device_family = "Cyclone IV E",
   ioddr.invert_output = "OFF",
   ioddr.lpm_hint = "UNUSED",
   ioddr.lpm_type = "altddio_bidir",
   ioddr.oe_reg = "REGISTERED",
   ioddr.power_up_high = "OFF",
   ioddr.width = 1;

Cela donne effectivement une logique qui implémente une interface DDR bidirectionnelle, mais avec un succès partiel sur le plan du timing, du moins sur Cyclone IV. Le délai de propagation horloge-vers-sortie (clock-to-output) est exactement le même qu'avec un simple registre de sortie logé dans la cellule d'E/S, mais le délai sur le chemin (path) d'entrée est en réalité plus mauvais avec l'instanciation ci-dessus. Les résultats peuvent différer sur d'autres familles de FPGA d'Intel.

Notons que pour imiter de simples registres SDR avec une primitive DDR, les ports datain_h et datain_l doivent être reliés au même fil, pour que le front descendant de l'horloge ne change rien. De même, la valeur du port dataout_l doit être ignorée, puisqu'elle est échantillonnée sur le front descendant. Remarquez aussi que le port de validation de sortie (oe) est une entrée SDR : à ma connaissance, il n'est pas possible d'activer ou de désactiver l'état haute impédance (high-Z) au rythme de la DDR sur les FPGA d'Intel. Du moins pas avec les primitives logiques fournies.

Maintenant, pourquoi cela fonctionne bien sur les registres de sortie et pas sur ceux d'entrée ? L'indice est dans les rapports d'analyse temporelle ci-dessus : même pour un simple registre de cellule d'E/S, c'est un composant DDIOOUTCELL_Xn_Ym_Nk qui est utilisé comme registre. Autrement dit, le registre de sortie DDR est utilisé même pour des sorties à simple débit, mais avec un seul front d'horloge. Pour ce qui est du chemin (path) d'entrée, les rapports ci-dessus montrent qu'un registre de la matrice logique (FF_Xn_Ym_Nk) est utilisé. Et c'est là que le bât blesse : la logique d'entrée DDR est elle aussi implantée dans la matrice logique. Pour empirer les choses, des blocs de logique combinatoire (combinatorial logic) sont intercalés entre la cellule d'E/S et la bascule dans le cas de la DDR. Franchement, je ne comprends pas pourquoi, car chacun de ces blocs combinatoires n'est qu'une simple liaison entre une entrée unique et une sortie unique.

Ces observations sont confirmées par les rapports d'analyse temporelle et par les schémas affichés par le Technology Map Viewer post-placement de Quartus. En particulier, ces blocs combinatoires inutiles apparaissent clairement dans ces sources d'information.

Tout ce problème varie très probablement d'une famille de FPGA à l'autre. Pour ce qui est de Cyclone IV, l'utilisation de primitives DDR ne se justifie que pour les sorties.

Plus important encore, le fait qu'une primitive DDR de sortie soit utilisée lorsqu'un registre de sortie est requis permet de produire une horloge de sortie alignée avec les autres sorties. Pour y parvenir, il faut alimenter une primitive DDR de sortie avec les constantes '1' et '0' respectivement sur les ports datain_h et datain_l. Pour les autres sorties, il faut imposer le placement des registres dans les cellules d'E/S. Les commutations des autres sorties sont alors synchronisées sur le front montant de l'horloge issue de la sortie DDR.

Enfin, presque alignées. L'analyse temporelle d'une horloge de sortie est différente, car l'horloge fait commuter un multiplexeur qui choisit lequel des deux registres de sortie alimente la broche (faites défiler horizontalement pour voir le détail) :

+------------------------------------------------------------------------------------------------------------------------------------+
; Data Arrival Path                                                                                                                  ;
+---------+---------+----+------+--------+-------------------------+-----------------------------------------------------------------+
; Total   ; Incr    ; RF ; Type ; Fanout ; Location                ; Element                                                         ;
+---------+---------+----+------+--------+-------------------------+-----------------------------------------------------------------+
; 0.000   ; 0.000   ;    ;      ;        ;                         ; launch edge time                                                ;
; 0.000   ; 0.000   ;    ;      ;        ;                         ; clock path                                                      ;
;   0.000 ;   0.000 ; R  ;      ;        ;                         ; clock network delay                                             ;
; 0.000   ; 0.000   ; R  ;      ; 1      ; PIN_B12                 ; osc_clock                                                       ;
; 5.610   ; 5.610   ;    ;      ;        ;                         ; data path                                                       ;
;   0.000 ;   0.000 ; RR ; IC   ; 1      ; IOIBUF_X19_Y29_N8       ; osc_clock~input|i                                               ;
;   0.667 ;   0.667 ; RR ; CELL ; 2      ; IOIBUF_X19_Y29_N8       ; osc_clock~input|o                                               ;
;   0.853 ;   0.186 ; RR ; IC   ; 1      ; CLKCTRL_G12             ; osc_clock~inputclkctrl|inclk[0]                                 ;
;   0.853 ;   0.000 ; RR ; CELL ; 165    ; CLKCTRL_G12             ; osc_clock~inputclkctrl|outclk                                   ;
;   1.971 ;   1.118 ; RR ; IC   ; 1      ; DDIOOUTCELL_X16_Y29_N11 ; sram_controller_ins|ddr_clk|auto_generated|ddio_outa[0]|muxsel  ;
;   3.137 ;   1.166 ; RR ; CELL ; 1      ; DDIOOUTCELL_X16_Y29_N11 ; sram_controller_ins|ddr_clk|auto_generated|ddio_outa[0]|dataout ;
;   3.137 ;   0.000 ; RR ; IC   ; 1      ; IOOBUF_X16_Y29_N9       ; sram_clk~output|i                                               ;
;   5.610 ;   2.473 ; RR ; CELL ; 1      ; IOOBUF_X16_Y29_N9       ; sram_clk~output|o                                               ;
;   5.610 ;   0.000 ; RR ; CELL ; 0      ; PIN_E10                 ; sram_clk                                                        ;
+---------+---------+----+------+--------+-------------------------+-----------------------------------------------------------------;

Remarquez qu'il ne s'agit pas d'une analyse registre-vers-broche, mais d'une analyse horloge-vers-broche. Une contrainte set_output_delay inclura néanmoins ce chemin (path). En revanche, une contrainte set_max_delay allant des registres vers les ports, si elle est utilisée, n'inclura pas ce chemin, et il faut donc le traiter séparément. Autrement dit, si vous utilisez set_max_delay, elle devra être de la forme :

set_max_delay -from [get_clocks main_clk] -to [get_ports sram_clk] 3.8

Comparez ceci avec une autre broche ayant la même norme de tension, etc., mais pilotée simplement par un registre :

+----------------------------------------------------------------------------------------------------------------------+
; Data Arrival Path                                                                                                    ;
+---------+---------+----+------+--------+-------------------------+---------------------------------------------------+
; Total   ; Incr    ; RF ; Type ; Fanout ; Location                ; Element                                           ;
+---------+---------+----+------+--------+-------------------------+---------------------------------------------------+
; 0.000   ; 0.000   ;    ;      ;        ;                         ; launch edge time                                  ;
; 2.507   ; 2.507   ;    ;      ;        ;                         ; clock path                                        ;
;   0.000 ;   0.000 ;    ;      ;        ;                         ; source latency                                    ;
;   0.000 ;   0.000 ;    ;      ; 1      ; PIN_B12                 ; osc_clock                                         ;
;   0.000 ;   0.000 ; RR ; IC   ; 1      ; IOIBUF_X19_Y29_N8       ; osc_clock~input|i                                 ;
;   0.667 ;   0.667 ; RR ; CELL ; 2      ; IOIBUF_X19_Y29_N8       ; osc_clock~input|o                                 ;
;   0.853 ;   0.186 ; RR ; IC   ; 1      ; CLKCTRL_G12             ; osc_clock~inputclkctrl|inclk[0]                   ;
;   0.853 ;   0.000 ; RR ; CELL ; 165    ; CLKCTRL_G12             ; osc_clock~inputclkctrl|outclk                     ;
;   1.970 ;   1.117 ; RR ; IC   ; 1      ; DDIOOUTCELL_X37_Y29_N11 ; sram_controller_ins|dq_wr_data[6]|clk             ;
;   2.507 ;   0.537 ; RR ; CELL ; 1      ; DDIOOUTCELL_X37_Y29_N11 ; sram_controller:sram_controller_ins|dq_wr_data[6] ;
; 5.645   ; 3.138   ;    ;      ;        ;                         ; data path                                         ;
;   2.717 ;   0.210 ;    ; uTco ; 1      ; DDIOOUTCELL_X37_Y29_N11 ; sram_controller:sram_controller_ins|dq_wr_data[6] ;
;   3.182 ;   0.465 ; RR ; CELL ; 1      ; DDIOOUTCELL_X37_Y29_N11 ; sram_controller_ins|dq_wr_data[6]|q               ;
;   3.182 ;   0.000 ; RR ; IC   ; 1      ; IOOBUF_X37_Y29_N9       ; sram_dq[6]~output|i                               ;
;   5.645 ;   2.463 ; RR ; CELL ; 1      ; IOOBUF_X37_Y29_N9       ; sram_dq[6]~output|o                               ;
;   5.645 ;   0.000 ; RR ; CELL ; 1      ; PIN_G14                 ; sram_dq[6]                                        ;
+---------+---------+----+------+--------+-------------------------+---------------------------------------------------;

Le délai total de propagation horloge-vers-sortie (clock-to-output) ne diffère pas de plus de 35 ps, même si le second chemin (path) est en apparence complètement différent. Ce n'est pas un hasard. Le FPGA est manifestement conçu pour produire une telle similitude. Plus précisément, l'analyse temporelle ci-dessus est faite en modèle lent 1200 mV à une température de 100 °C, mais cette faible différence se retrouve dans les autres conditions analysé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)