01signal.com

Más sobre la restricción del periodo de reloj

Esta página pertenece a una serie de páginas sobre temporización (timing). Tras una breve introducción a la teoría de las restricciones de temporización (timing constraints) y la primera página sobre la restricción del periodo de reloj, es el momento de ver algunos escenarios realistas con esta restricción.

El siguiente paso: usar una PLL

En el ejemplo mostrado en la página anterior, el pin de reloj externo se conectaba directamente a la lógica. En la mayoría de los diseños reales se utiliza algún tipo de PLL para generar el reloj que emplea la lógica. La razón más evidente es que la lógica necesita una frecuencia distinta de la del reloj externo. Pero usar una PLL también puede ayudar a limpiar el reloj externo de imperfecciones, en particular de jitter.

Se puede añadir una PLL al diseño con código Verilog como este:

module top(
    input clk,
    input foo,
    output reg bar_reg
);
    reg foo_reg;
    reg bar;
    wire pll_clk;

   clk_wiz_0 pll_i
   (.clk_in1(clk),
    .clk_out1(pll_clk));

always @(posedge pll_clk)
  begin
    foo_reg <= foo;
    bar <= !foo_reg;
    bar_reg <= bar;
  end
endmodule

Este ejemplo es como el anterior, pero esta vez el reloj de los flip-flops es @pll_clk en lugar de @clk. La PLL la genera el IP Clocking Wizard de Vivado, que se usa en el código Verilog como un módulo llamado clk_wiz_0.

Para este ejemplo, el Clocking Wizard se ha configurado para aceptar un reloj de referencia de 250 MHz y generar un reloj de 125 MHz en el puerto de salida (es decir, @clk_out1). Para mantener el ejemplo sencillo, clk_wiz_0 no tiene entrada de reset ni salida "locked". En la mayoría de los diseños reales se recomienda habilitar estos puertos y utilizarlos.

Otra cosa sobre clk_wiz_0 es que tiene activada la opción de alineación de fase, de modo que alinea los flancos de @pll_clk con los flancos de @clk. Esta opción resulta útil cuando el diseño tiene puertos de E/S síncronos con el reloj externo: gracias a la alineación de fase, la relación temporal entre el reloj externo y el reloj interno se vuelve predecible. Esto es útil con puertos de E/S que deben cumplir requisitos de temporización relativos al reloj externo.

Para ser precisos con la terminología de Xilinx, las FPGA de Xilinx tienen dos tipos de PLL: un tipo se llama PLL y el segundo se llama MMCM. La diferencia es irrelevante para este ejemplo. clk_wiz_0 es un MMCM, pero por claridad me referiré a él con el término PLL.

Conviene repetir que todo lo que se dice aquí sobre la PLL es aplicable a todas las FPGA del mercado. Este ejemplo se muestra con Vivado, pero se puede generar una PLL que haga exactamente lo mismo que clk_wiz_0 para cualquier FPGA.

La restricción de temporización con una PLL

Lo más importante que hay que saber sobre las restricciones de temporización con una PLL es que no hay que hacer nada especial. La restricción se escribe con relación al pin externo (@clk en este ejemplo), y si la PLL produce un reloj con otra frecuencia, es trabajo de las herramientas tenerlo en cuenta.

Vale la pena repetirlo: nunca debería ser necesario escribir una restricción de temporización adicional por el hecho de usar una PLL. Si alguna vez sientes la necesidad de hacerlo, hay muchas probabilidades de que algo vaya mal en el diseño, y «arreglar» el problema con una restricción adicional no resolverá el problema real.

Así pues, como antes, la restricción de temporización es simplemente esta:

create_clock -period 4.000 -name clk [get_ports clk]

Quienes usen Quartus deben tener en cuenta la trampa que se describe en esta página.

El informe de temporización con una PLL

Veremos ahora el informe de temporización del mismo camino que en el ejemplo de la página anterior. La única diferencia es que se ha añadido la PLL, como se ha mostrado arriba. Aquí se presenta el análisis del camino relevante en el mismo orden en que aparece en el informe de temporización. Así que, primero, este es el resumen del análisis:

Slack (MET) :             7.288ns  (required time - arrival time)
  Source:                 foo_reg_reg/C
                            (rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_0  {rise@0.000ns fall@4.000ns period=8.000ns})
  Destination:            bar_reg__0/D
                            (rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_0  {rise@0.000ns fall@4.000ns period=8.000ns})
  Path Group:             clk_out1_clk_wiz_0
  Path Type:              Setup (Max at Slow Process Corner)
  Requirement:            8.000ns  (clk_out1_clk_wiz_0 rise@8.000ns - clk_out1_clk_wiz_0 rise@0.000ns)
  Data Path Delay:        0.669ns  (logic 0.382ns (57.100%)  route 0.287ns (42.900%))
  Logic Levels:           1  (LUT1=1)
  Clock Path Skew:        -0.048ns (DCD - SCD + CPR)
    Destination Clock Delay (DCD):    -0.715ns = ( 7.285 - 8.000 )
    Source Clock Delay      (SCD):    -0.616ns
    Clock Pessimism Removal (CPR):    0.051ns
  Clock Uncertainty:      0.062ns  ((TSJ^2 + DJ^2)^1/2) / 2 + PE
    Total System Jitter     (TSJ):    0.071ns
    Discrete Jitter          (DJ):    0.103ns
    Phase Error              (PE):    0.000ns
  Clock Net Delay (Source):      1.389ns (routing 0.002ns, distribution 1.387ns)
  Clock Net Delay (Destination): 1.218ns (routing 0.002ns, distribution 1.216ns)

Hay varias diferencias notables. Primero, el Requirement es de 8 ns, en lugar de los 4 ns anteriores. Esto es de esperar, ya que la salida de la PLL es de 125 MHz. Las herramientas crean automáticamente una restricción de temporización adicional para esa salida, con un periodo de reloj de 8 ns. Como el periodo del reloj ha aumentado en 4 ns y el camino de datos sigue siendo el mismo, el margen (slack) ha aumentado aproximadamente en 4 ns.

Otra indicación de la restricción automática es que el Path Group ahora dice "clk_out1_clk_wiz_0" en el informe. Antes decía "clk". De hecho, en este informe aparece "clk_out1_clk_wiz_0" en todos los sitios donde antes aparecía "clk". La representación de las restricciones automáticas en el informe de temporización se comenta más abajo, en el contexto de varios relojes.

Una consecuencia menor de la PLL es que la Clock Uncertainty ha subido a 0,062 ns: en el ejemplo anterior era de 0,035 ns. Esto se debe a que el Discrete Jitter ahora es de 0,103 ns, y no cero.

Pasemos ahora al análisis de tiempos en sí. Esta vez, el camino del reloj de origen, el camino de datos y el camino del reloj de destino se muestran juntos, exactamente como aparecen en el informe real:

    Location             Delay type                Incr(ns)  Path(ns)    Netlist Resource(s)
  -------------------------------------------------------------------    -------------------
                         (clock clk_out1_clk_wiz_0 rise edge)
                                                      0.000     0.000 r
    AG12                                              0.000     0.000 r  clk (IN)
                         net (fo=0)                   0.000     0.000    pll_i/inst/clkin1_ibuf/I
    AG12                 INBUF (Prop_INBUF_HRIO_PAD_O)
                                                      0.738     0.738 r  pll_i/inst/clkin1_ibuf/INBUF_INST/O
                         net (fo=1, routed)           0.105     0.843    pll_i/inst/clkin1_ibuf/OUT
    AG12                 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
                                                      0.049     0.892 r  pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
                         net (fo=1, routed)           0.975     1.867    pll_i/inst/clk_in1_clk_wiz_0
    MMCME3_ADV_X1Y0      MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0)
                                                     -4.474    -2.607 r  pll_i/inst/mmcme3_adv_inst/CLKOUT0
                         net (fo=1, routed)           0.501    -2.106    pll_i/inst/clk_out1_clk_wiz_0
    BUFGCE_X1Y0          BUFGCE (Prop_BUFCE_BUFGCE_I_O)
                                                      0.101    -2.005 r  pll_i/inst/clkout1_buf/O
    X2Y0 (CLOCK_ROOT)    net (fo=3, routed)           1.389    -0.616    pll_clk
    SLICE_X49Y58         FDRE                                         r  foo_reg_reg/C
  -------------------------------------------------------------------    -------------------
    SLICE_X49Y58         FDRE (Prop_EFF2_SLICEL_C_Q)
                                                      0.138    -0.478 f  foo_reg_reg/Q
                         net (fo=1, routed)           0.241    -0.237    foo_reg
    SLICE_X49Y58         LUT1 (Prop_D5LUT_SLICEL_I0_O)
                                                      0.244     0.007 r  bar__0_i_1/O
                         net (fo=1, routed)           0.046     0.053    p_0_in
    SLICE_X49Y58         FDRE                                         r  bar_reg__0/D
  -------------------------------------------------------------------    -------------------

                         (clock clk_out1_clk_wiz_0 rise edge)
                                                      8.000     8.000 r
    AG12                                              0.000     8.000 r  clk (IN)
                         net (fo=0)                   0.000     8.000    pll_i/inst/clkin1_ibuf/I
    AG12                 INBUF (Prop_INBUF_HRIO_PAD_O)
                                                      0.515     8.515 r  pll_i/inst/clkin1_ibuf/INBUF_INST/O
                         net (fo=1, routed)           0.066     8.581    pll_i/inst/clkin1_ibuf/OUT
    AG12                 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
                                                      0.034     8.615 r  pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
                         net (fo=1, routed)           0.873     9.488    pll_i/inst/clk_in1_clk_wiz_0
    MMCME3_ADV_X1Y0      MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0)
                                                     -3.934     5.554 r  pll_i/inst/mmcme3_adv_inst/CLKOUT0
                         net (fo=1, routed)           0.422     5.976    pll_i/inst/clk_out1_clk_wiz_0
    BUFGCE_X1Y0          BUFGCE (Prop_BUFCE_BUFGCE_I_O)
                                                      0.091     6.067 r  pll_i/inst/clkout1_buf/O
    X2Y0 (CLOCK_ROOT)    net (fo=3, routed)           1.218     7.285    pll_clk
    SLICE_X49Y58         FDRE                                         r  bar_reg__0/C
                         clock pessimism              0.051     7.336
                         clock uncertainty           -0.062     7.274
    SLICE_X49Y58         FDRE (Setup_DFF2_SLICEL_C_D)
                                                      0.067     7.341    bar_reg__0
  -------------------------------------------------------------------
                         required time                          7.341
                         arrival time                          -0.053
  -------------------------------------------------------------------
                         slack                                  7.288

Al comparar con el ejemplo anterior, solo hay una diferencia en los caminos: la PLL (que aparece como MMCME3_ADV_X1Y0 en el informe) se ha insertado entre el pin de reloj externo y el buffer global de reloj.

Esta PLL tiene un efecto drástico: en el camino del reloj de origen, el retardo de la PLL es de −4,474 ns, y en el camino del reloj de destino, ese mismo retardo es de −3,934 ns. Este retardo negativo refleja el hecho de que la PLL ajusta el flanco del reloj de modo que el reloj global llega ligeramente antes que el reloj en el pin de entrada. Observa que el retardo total del reloj a la salida del buffer global de reloj es de −0,616 ns en el camino del reloj de origen. Ese mismo retardo es de 7,285 ns en el camino del reloj de destino, es decir, 0,715 ns antes que el segundo flanco del reloj (en 8 ns).

En otras palabras, el pin de reloj externo y el reloj global (el que se entrega a la lógica) tienen casi la misma diferencia temporal en ambos caminos del reloj. Aunque un camino del reloj se calcula con los retardos máximos y el segundo con los mínimos, el resultado total es casi el mismo.

No es una coincidencia: la PLL utiliza la salida del reloj global como referencia, por lo que la fase de la entrada del buffer global de reloj se ajusta para garantizar la relación con el pin de entrada de reloj. Las diferencias entre los retardos mínimos y los máximos son compensadas por la PLL. Los cálculos de temporización reflejan esto en que la diferencia entre el caso más rápido y el más lento es de solo 0,1 ns. Esa diferencia era mucho mayor en el ejemplo anterior, donde no había PLL (0,575 ns; ver "Clock pessimism removal" en la página anterior).

Por qué el reloj global se ajusta a unos 0,6 ns antes de la entrada externa, y no a otro valor, es otra historia. Ese ajuste facilita en muchos casos el cumplimiento de las restricciones de temporización relacionadas con pines de E/S, así que las herramientas toman esa decisión automáticamente. No obstante, todas las FPGA ofrecen la opción de manipular este retardo.

Dos relojes relacionados

Con bastante frecuencia, un diseño lógico necesita más de un reloj. La existencia de varios relojes en una FPGA es un tema en sí mismo, que se trata en la introducción a los dominios de reloj. Los dos temas, dominios de reloj y temporización, son inseparables, así que se recomienda leer esa introducción (aunque sea por encima) antes de seguir aquí. Debido a la estrecha relación entre ambos temas, hay cierto solapamiento entre esa introducción y esta serie de páginas.

En la discusión siguiente usaré a menudo expresiones como «la señal X es síncrona con @clk». Esto significa que X es la salida de un flip-flop que solo cambia de valor en respuesta a un flanco de subida de un reloj llamado "clk" (salvo resets asíncronos, pero eso es irrelevante). Naturalmente, si dos señales son salidas de flip-flops que responden al mismo reloj, esas dos señales son «síncronas con el mismo reloj».

Veremos ahora dos relojes generados por la misma PLL. Esto es interesante porque, en la mayoría de los casos, estos dos relojes se consideran relojes relacionados (related clocks). Para entender por qué esto es interesante, supongamos que hay un flip-flop síncrono con uno de estos relojes y otro flip-flop síncrono con el segundo. En ese caso, se pueden conectar señales entre esos dos flip-flops como si fueran síncronos con el mismo reloj. Las herramientas de FPGA se encargan de que se cumplan los requisitos de temporización en esta situación.

Para entender mejor cómo trabajar con varios relojes, hay una página aparte sobre relojes relacionados (related clocks) y relojes no relacionados (unrelated clocks). Esa página es la introducción a la cruce de dominios de reloj (clock domain crossing). Aquí nos centramos en los aspectos de temporización de los relojes relacionados y, en particular, en el informe de temporización relativo a esos relojes.

Para generar los informes de temporización siguientes se ha utilizado una PLL distinta (es decir, otro IP Clocking Wizard). El nombre de esta nueva PLL es clk_wiz_1. Se ha usado el siguiente código Verilog:

module top(
    input clk,
    input foo,
    output reg bar_reg
);
    reg foo_reg;
    reg bar;
    wire pll_clk_8, pll_clk_6;

   clk_wiz_1 pll_i
   (.clk_in1(clk),
    .clk_out1(pll_clk_8),
    .clk_out2(pll_clk_6));

always @(posedge pll_clk_8)
  foo_reg <= foo;

always @(posedge pll_clk_6)
  begin
    bar <= !foo_reg;
    bar_reg <= bar;
  end

Como antes, la entrada de esta PLL (es decir, @clk) es de 250 MHz, pero tiene dos salidas: una está conectada a @pll_clk_8, que funciona a 125 MHz (el periodo del reloj es de 8 ns, de ahí el nombre de la señal). Es exactamente igual que el @pll_clk del ejemplo anterior. La segunda salida está conectada a @pll_clk_6, cuyo periodo de reloj es de 6 ns, es decir, aproximadamente 166,67 MHz.

Como @pll_clk_8 y @pll_clk_6 las genera la misma PLL, son relojes relacionados.

clk_wiz_1 también tiene activada la opción de alineación de fase, sobre todo para mantener la coherencia con el ejemplo anterior. Y por si había alguna duda: sigue habiendo una única restricción de temporización, la misma de antes:

create_clock -period 4.000 -name clk [get_ports clk]

Entender el resumen del reloj

La información sobre todos los relojes se resume al principio del informe de temporización. Solo lo menciono ahora porque el resumen es más interesante cuando hay varios relojes. Todas las herramientas de FPGA generan un resumen de este tipo en el informe, y siempre es buena idea echarle un vistazo:

-------------------------------------------------------------------------
| Clock Summary
| -------------
-------------------------------------------------------------------------

Clock                 Waveform(ns)         Period(ns)      Frequency(MHz)
-----                 ------------         ----------      --------------
clk                   {0.000 2.000}        4.000           250.000
  clk_out1_clk_wiz_1  {0.000 4.000}        8.000           125.000
  clk_out2_clk_wiz_1  {0.000 3.000}        6.000           166.667
  clkfbout_clk_wiz_1  {0.000 2.000}        4.000           250.000

La principal ventaja de esta parte es que es fácil de entender, y también es fácil comprobar el error más habitual con las restricciones de temporización: si un reloj tiene la frecuencia correcta.

Este resumen muestra que la restricción se ha interpretado correctamente: hay un reloj externo definido con el nombre clk y cuyo periodo es de 4 ns. Además, hay tres relojes derivados: clk_out1_clk_wiz_1, clk_out2_clk_wiz_1 y clkfbout_clk_wiz_1. Como sus nombres indican, se han generado automáticamente a causa de la PLL llamada clk_wiz_1.

Los dos primeros relojes (clk_out1_clk_wiz_1 y clk_out2_clk_wiz_1) son las dos salidas de la PLL. El tercer reloj (clkfbout_clk_wiz_1) lo usa la PLL para ajustar la fase de los relojes globales con el reloj externo. clkfbout_clk_wiz_1 tiene la misma frecuencia que @clk.

Cuando la opción de alineación de fase está activada, el reloj de realimentación (feedback clock) de la PLL (clkfbout_clk_wiz_1) se conecta al buffer global de reloj. Como las PLL siempre se sincronizan con su reloj de entrada y su reloj de realimentación (alineando esos relojes o manteniendo un retardo fijo entre ellos), el hecho de que clkfbout_clk_wiz_1 sea un reloj global también garantiza un retardo conocido entre el reloj de entrada y los otros relojes de salida.

Es importante señalar que clk_out1_clk_wiz_1, clk_out2_clk_wiz_1 y clkfbout_clk_wiz_1 no definen relojes que existan realmente. Son solo símbolos de los relojes que el software utiliza para los cálculos de temporización. Una muestra de que estos relojes son teóricos son sus formas de onda, que aparecen en el resumen: esas formas de onda reflejan el ciclo de trabajo (duty cycle) de cada uno de estos relojes. Pero no significa nada que los cuatro relojes (clk y los tres relojes teóricos) tengan un flanco de subida exactamente en 0 ns. En particular, no significa que esos cuatro relojes estén perfectamente alineados. Aunque lo estuvieran, se vería a través de los cálculos de temporización, y la alineación real no es perfecta.

Cómo se usan estos relojes teóricos se comenta más abajo, en relación con el cálculo de temporización que involucra a dos de ellos.

Otra cosa importante sobre estos relojes teóricos es que no existe una restricción de temporización explícita para crearlos: la creación de clk_out1_clk_wiz_* no aparece en ningún sitio de las fuentes del diseño ni en los archivos generados por las herramientas. En la mayoría de los casos es incorrecto intentar escribir una restricción de este tipo, porque así no se tendrán en cuenta correctamente los retardos del camino del reloj. Si se escriben tales restricciones, las herramientas calcularán mal la temporización relativa entre relojes y los caminos entre dominios de reloj relacionados no se calcularán correctamente.

Así que, una vez más, una única restricción de temporización debe generar automáticamente las restricciones de todos los relojes de salida de la PLL.

El informe de temporización con dos relojes relacionados

Como se desprende del código Verilog anterior, @foo_reg es síncrono con @pll_clk_8 y @bar es síncrono con @pll_clk_6. Por tanto, la asignación que actualiza @bar implica una cruce de dominios de reloj:

bar <= !foo_reg;

Los dos relojes son relojes relacionados (related clocks), así que las herramientas garantizan que se cumplen los requisitos de temporización de @bar mediante este cálculo (para tsu):

Slack (MET) :             0.475ns  (required time - arrival time)
  Source:                 foo_reg_reg/C
                            (rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_1  {rise@0.000ns fall@4.000ns period=8.000ns})
  Destination:            bar_reg__0/D
                            (rising edge-triggered cell FDRE clocked by clk_out2_clk_wiz_1  {rise@0.000ns fall@3.000ns period=6.000ns})
  Path Group:             clk_out2_clk_wiz_1
  Path Type:              Setup (Max at Slow Process Corner)
  Requirement:            2.000ns  (clk_out2_clk_wiz_1 rise@18.000ns - clk_out1_clk_wiz_1 rise@16.000ns)
  Data Path Delay:        1.160ns  (logic 0.307ns (26.466%)  route 0.853ns (73.534%))
  Logic Levels:           1  (LUT1=1)
  Clock Path Skew:        -0.250ns (DCD - SCD + CPR)
    Destination Clock Delay (DCD):    -0.681ns = ( 17.319 - 18.000 )
    Source Clock Delay      (SCD):    -0.600ns = ( 15.400 - 16.000 )
    Clock Pessimism Removal (CPR):    -0.169ns
  Clock Uncertainty:      0.182ns  ((TSJ^2 + DJ^2)^1/2) / 2 + PE
    Total System Jitter     (TSJ):    0.071ns
    Discrete Jitter          (DJ):    0.103ns
    Phase Error              (PE):    0.120ns
  Clock Net Delay (Source):      1.369ns (routing 0.002ns, distribution 1.367ns)
  Clock Net Delay (Destination): 1.208ns (routing 0.002ns, distribution 1.206ns)

    Location             Delay type                Incr(ns)  Path(ns)    Netlist Resource(s)
  -------------------------------------------------------------------    -------------------
                         (clock clk_out1_clk_wiz_1 rise edge)
                                                     16.000    16.000 r
    AG12                                              0.000    16.000 r  clk (IN)
                         net (fo=0)                   0.000    16.000    pll_i/inst/clkin1_ibuf/I
    AG12                 INBUF (Prop_INBUF_HRIO_PAD_O)
                                                      0.738    16.738 r  pll_i/inst/clkin1_ibuf/INBUF_INST/O
                         net (fo=1, routed)           0.105    16.843    pll_i/inst/clkin1_ibuf/OUT
    AG12                 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
                                                      0.049    16.892 r  pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
                         net (fo=1, routed)           0.975    17.867    pll_i/inst/clk_in1_clk_wiz_1
    MMCME3_ADV_X1Y0      MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0)
                                                     -4.438    13.429 r  pll_i/inst/mmcme3_adv_inst/CLKOUT0
                         net (fo=1, routed)           0.501    13.930    pll_i/inst/clk_out1_clk_wiz_1
    BUFGCE_X1Y1          BUFGCE (Prop_BUFCE_BUFGCE_I_O)
                                                      0.101    14.031 r  pll_i/inst/clkout1_buf/O
    X2Y0 (CLOCK_ROOT)    net (fo=1, routed)           1.369    15.400    pll_clk_8
    SLICE_X49Y58         FDRE                                         r  foo_reg_reg/C
  -------------------------------------------------------------------    -------------------
    SLICE_X49Y58         FDRE (Prop_EFF_SLICEL_C_Q)
                                                      0.139    15.539 f  foo_reg_reg/Q
                         net (fo=1, routed)           0.807    16.346    foo_reg
    SLICE_X49Y58         LUT1 (Prop_D5LUT_SLICEL_I0_O)
                                                      0.168    16.514 r  bar__0_i_1/O
                         net (fo=1, routed)           0.046    16.560    p_0_in
    SLICE_X49Y58         FDRE                                         r  bar_reg__0/D
  -------------------------------------------------------------------    -------------------

                         (clock clk_out2_clk_wiz_1 rise edge)
                                                     18.000    18.000 r
    AG12                                              0.000    18.000 r  clk (IN)
                         net (fo=0)                   0.000    18.000    pll_i/inst/clkin1_ibuf/I
    AG12                 INBUF (Prop_INBUF_HRIO_PAD_O)
                                                      0.515    18.515 r  pll_i/inst/clkin1_ibuf/INBUF_INST/O
                         net (fo=1, routed)           0.066    18.581    pll_i/inst/clkin1_ibuf/OUT
    AG12                 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
                                                      0.034    18.615 r  pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
                         net (fo=1, routed)           0.873    19.488    pll_i/inst/clk_in1_clk_wiz_1
    MMCME3_ADV_X1Y0      MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT1)
                                                     -3.890    15.598 r  pll_i/inst/mmcme3_adv_inst/CLKOUT1
                         net (fo=1, routed)           0.422    16.020    pll_i/inst/clk_out2_clk_wiz_1
    BUFGCE_X1Y0          BUFGCE (Prop_BUFCE_BUFGCE_I_O)
                                                      0.091    16.111 r  pll_i/inst/clkout2_buf/O
    X2Y0 (CLOCK_ROOT)    net (fo=2, routed)           1.208    17.319    pll_clk_6
    SLICE_X49Y58         FDRE                                         r  bar_reg__0/C
                         clock pessimism             -0.169    17.150
                         clock uncertainty           -0.182    16.967
    SLICE_X49Y58         FDRE (Setup_DFF2_SLICEL_C_D)
                                                      0.067    17.034    bar_reg__0
  -------------------------------------------------------------------
                         required time                         17.034
                         arrival time                         -16.560
  -------------------------------------------------------------------
                         slack                                  0.475

Como se ha dicho antes, un cálculo de temporización es un experimento imaginario en el que un cronómetro imaginario se pone en marcha junto con los flancos del reloj. En el ejemplo anterior, ese cronómetro arrancaba en 0 ns. Pero en este cálculo arranca con el flanco en 16 ns. Esto se debe a que los periodos de los dos relojes no son iguales. El cálculo se hace sobre la combinación de peor caso entre el primer reloj y el segundo.

Los flancos de subida del primer reloj están en 0 ns, 8 ns, 16 ns, 24 ns, y así sucesivamente. Los flancos de subida del segundo reloj están en 0 ns, 6 ns, 12 ns, 18 ns, 24 ns, etc. Así que la menor separación temporal entre el primer reloj y el segundo es la del flanco de 16 ns del primero y el de 18 ns del segundo. Esa es la situación que examina este cálculo de temporización.

Esto significa que el camino de datos está limitado a unos 2 ns, lo que equivale a unos 500 MHz. Es un requisito muy estricto, pero como solo hay una LUT en el camino de datos y ambos flip-flops están en la misma slice, fue fácil cumplirlo. Sin embargo, este ejemplo muestra por qué es importante elegir frecuencias que funcionen bien juntas para relojes relacionados. A menudo se elige la frecuencia de un reloj como múltiplo de la del otro. Así se evitan escenarios como el intervalo de 2 ns de este ejemplo.

Por cierto, si resulta imposible elegir frecuencias que combinen bien, la solución es tratar los relojes como relojes no relacionados (unrelated clocks).

Los relojes derivados

He mencionado antes que clk_out1_clk_wiz_1 y clk_out2_clk_wiz_1 son relojes teóricos, y así se refleja en el informe de temporización: observa que ambos caminos se calculan tomando como punto de partida el pin externo (AG12). ¿Cómo puede tener sentido eso? En realidad, la señal en ese pin es el reloj de referencia, no ninguno de esos dos relojes.

Tomemos clk_out1_clk_wiz_1 como ejemplo: la idea del cálculo es fingir que en el pin externo hay un reloj de 125 MHz. En realidad, un reloj de 125 MHz solo existe a la salida de la PLL, es decir, en la salida de MMCME3_ADV_X1Y0. Pero en lugar de introducir la manipulación de frecuencia de la PLL en el cálculo de temporización, se usa un reloj teórico. Ese reloj tiene una forma de onda ideal que comienza en 0 ns. La PLL se trata como si no cambiara la frecuencia de ningún reloj, sino que solo añadiera retardos.

Así pues, el reloj teórico (por ejemplo, clk_out1_clk_wiz_1) define la forma de onda del reloj (frecuencia, ciclo de trabajo, jitter, etc.). Sin embargo, ese reloj teórico no define las relaciones temporales con otros relojes que existen como señales reales en la FPGA.

Entonces, ¿cómo podemos ver que @pll_clk_6 y @pll_clk_8 están realmente alineados? La respuesta está en comparar la temporización real con la ideal de estos relojes. Por ejemplo, el flanco teórico de clk_out1_clk_wiz_1 está en 16 ns en el cálculo anterior. Por otro lado, el flanco de clk_out2_clk_wiz_1 está en 18 ns, es decir, 2 ns más tarde. Comparemos ahora los flancos a la salida del árbol de reloj global: según el informe, el flanco del primer reloj llega en 15,400 ns. Para el segundo flanco, el informe dice 17,319 ns. Según el cálculo, la diferencia temporal es de 1,919 ns en lugar de 2 ns. Eso es solo 0,081 ns menos que la diferencia ideal. Así que los relojes están claramente alineados.

Comparemos con el ejemplo anterior, donde solo había un reloj: la diferencia temporal ideal entre dos flancos era el periodo del reloj, es decir, 8 ns. Pero según el informe correspondiente (ver arriba), el primer flanco llegaba en −0,616 ns y el segundo en 7,285 ns. La diferencia temporal ideal era de 8 ns, pero en realidad fue de 7,901 ns. Así que el segundo flanco llegó 0,099 ns antes de lo esperado.

Con dos relojes relacionados, la desviación respecto a la diferencia temporal ideal es de 0,081 ns. Con un solo reloj, esa desviación era parecida, 0,099 ns. En ambos casos, la razón de la desviación es que el cálculo para el primer flanco se hace con retardos máximos y para el segundo flanco se usan retardos mínimos.

La conclusión es, por tanto, que @pll_clk_6 y @pll_clk_8 están alineados con aproximadamente la misma precisión que si fueran un solo reloj.

Observa que estos relojes estarían alineados entre sí incluso sin la opción que activa la alineación de fase de la PLL: las salidas de una PLL suelen estar alineadas de todas formas. Esa alineación se garantiza usando buffers globales de reloj que tienen retardos casi idénticos, independientemente de su fan-out. En otras palabras, no importa con cuántos elementos lógicos esté conectado cada uno de esos buffers: el retardo desde la salida de la PLL hasta el destino es aproximadamente el mismo.

Lo que hemos aprendido sobre los relojes relacionados

El análisis de este informe de temporización demuestra que un camino entre dos dominios de reloj relacionados (related clock domains) es aproximadamente equivalente a un camino dentro de un mismo dominio de reloj. Y sin embargo, no es exactamente igual. En particular, la Clock Uncertainty ha subido de 0,062 ns a 0,182 ns, porque cada reloj tiene su propio jitter y la alineación tampoco es perfecta.

Este informe también ha mostrado cómo se refleja la alineación de los dos relojes en los cálculos de temporización.

Cuando hay cruces de dominios de reloj en un diseño para FPGA, es buena idea examinar las interacciones entre esos relojes en el informe de temporización. El objetivo es asegurarse de que las herramientas tratan los relojes como esperamos. Estas son las dos cosas más importantes que hay que comprobar:

Vivado puede crear un Clock Interaction Report, que muestra gráficamente los cruces de dominios de reloj del diseño y cómo trata las herramientas cada uno de esos cruces. Otras herramientas de FPGA tienen una función similar, por ejemplo el CDC Viewer de Quartus Pro. Cuando sea posible, se recomienda generar y examinar este informe.

Otra cosa que conviene mirar es si los relojes están alineados. Si el diseño usa por error un reloj no alineado, puede crear dificultades innecesarias para cumplir las restricciones de temporización. Por ejemplo, si hay un camino entre @clk y @pll_clk_8, las herramientas impondrán las restricciones y se asegurarán de que ese camino funcione de forma fiable. Observa, sin embargo, que @clk es el reloj de referencia que entra en la PLL y @pll_clk_8 es la salida de esa PLL. Por tanto, estos dos relojes no están alineados. Como resultado, las herramientas pueden esforzarse innecesariamente para cumplir las restricciones de temporización de ese camino. Otros caminos pueden dejar de cumplir sus restricciones debido a ese esfuerzo innecesario.

Y una vez más, el tema de los dominios de reloj se trata en esta serie de páginas.

thold también es importante

Todos los cálculos de temporización que he mostrado hasta ahora estaban relacionados con el requisito de tsu. Es natural centrarse en este requisito, porque cuando las herramientas no consiguen cumplir las restricciones de temporización, casi siempre es porque al menos un camino no ha cumplido el requisito de tsu.

No obstante, es importante tener presente thold: para cumplir este requisito, las herramientas a veces ralentizan artificialmente el camino de datos añadiendo retardo de enrutado. Por eso, aunque rara vez se menciona thold como motivo del incumplimiento de las restricciones de temporización, ese requisito puede ser la causa oculta del fallo.

De hecho, algo parecido ocurrió con el retardo de enrutado del cable que conecta los dos flip-flops: en todos los informes de temporización con un solo reloj, ese cable estaba dentro de la misma slice (SLICE_X49Y58). Por eso, el retardo de ese cable era de 0,241 ns en todos esos informes. Pero cuando en el camino intervenían dos relojes, ese retardo subió hasta 0,807 ns.

La explicación es que 0,241 ns es el retardo mínimo que puede conseguirse entre dos flip-flops colocados en la misma slice. El retardo mayor (0,807 ns) es el resultado de un enrutado distinto de ese cable. No ocurrió por casualidad: las herramientas alargaron deliberadamente ese enrutado para cumplir el requisito de thold. Esto también se refleja en la fila "Data Path Delay" del informe: el retardo de la lógica es solo del 26,5%, y el resto es enrutado. Eso por sí solo es señal de que algo ha ocurrido. Más sobre esto abajo.

Análisis del requisito de thold

Recuerda de la página teórica de esta serie que thold es la cantidad de tiempo que la entrada de datos del segundo flip-flop debe permanecer estable después del flanco del reloj. Describamos la situación en la que se incumple thold: un flanco de reloj llega al primer flip-flop y el segundo flip-flop también recibe un flanco aproximadamente al mismo tiempo (observa que estos flancos pueden provenir del mismo reloj o de relojes distintos). En respuesta a su flanco, el primer flip-flop actualiza su salida tras un cierto retardo (de reloj a salida). Pero el valor actualizado llega al segundo flip-flop demasiado pronto. Como resultado, ese flip-flop no captura de forma fiable el valor anterior: el valor nuevo llegó antes de que el segundo flip-flop tuviera tiempo de terminar de reaccionar a su flanco. En otras palabras, se incumple el requisito de thold.

Cuando ambos flip-flops usan el mismo reloj, esto puede ocurrir sobre todo por una desviación del reloj (clock skew): si el flanco llega antes al primer flip-flop, es posible que este cambie su salida demasiado pronto. Eso abre la posibilidad de que la señal actualizada llegue al segundo flip-flop con la rapidez suficiente para incumplir el requisito de thold. Observa que es el mismo flanco el que llega a ambos flip-flops, así que ni la frecuencia del reloj ni la cantidad de jitter tienen importancia. Solo una diferencia de retardos (es decir, el clock skew) juega un papel.

Pero para el análisis de temporización que viene a continuación nos quedaremos con el último ejemplo, el de dos relojes: @pll_clk_8 y @pll_clk_6. Un análisis con un solo reloj sería parecido, pero no tan interesante, porque es demasiado fácil cumplir el requisito de thold con un único reloj.

Así pues, el informe de temporización que vamos a ver está hecho para dos relojes relacionados. Estos dos relojes tienen frecuencias distintas y, como antes, hay diferentes combinaciones entre el instante del flanco del primer reloj y el del segundo. Pero a diferencia del cálculo para tsu, el peor caso para thold se produce cuando ambos flancos están en 0 ns: los incumplimientos de thold ocurren cuando los dos flancos son casi simultáneos, así que ninguna otra combinación es peor que esta.

Con estas ideas en mente, veamos el informe de temporización:

Slack (MET) :             0.093ns  (arrival time - required time)
  Source:                 foo_reg_reg/C
                            (rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_1  {rise@0.000ns fall@4.000ns period=8.000ns})
  Destination:            bar_reg__0/D
                            (rising edge-triggered cell FDRE clocked by clk_out2_clk_wiz_1  {rise@0.000ns fall@3.000ns period=6.000ns})
  Path Group:             clk_out2_clk_wiz_1
  Path Type:              Hold (Min at Fast Process Corner)
  Requirement:            0.000ns  (clk_out2_clk_wiz_1 rise@0.000ns - clk_out1_clk_wiz_1 rise@0.000ns)
  Data Path Delay:        0.458ns  (logic 0.104ns (22.707%)  route 0.354ns (77.293%))
  Logic Levels:           1  (LUT1=1)
  Clock Path Skew:        0.127ns (DCD - SCD - CPR)
    Destination Clock Delay (DCD):    -0.542ns
    Source Clock Delay      (SCD):    -0.248ns
    Clock Pessimism Removal (CPR):    -0.421ns
  Clock Uncertainty:      0.182ns  ((TSJ^2 + DJ^2)^1/2) / 2 + PE
    Total System Jitter     (TSJ):    0.071ns
    Discrete Jitter          (DJ):    0.103ns
    Phase Error              (PE):    0.120ns
  Clock Net Delay (Source):      0.495ns (routing 0.002ns, distribution 0.493ns)
  Clock Net Delay (Destination): 0.576ns (routing 0.002ns, distribution 0.574ns)

    Location             Delay type                Incr(ns)  Path(ns)    Netlist Resource(s)
  -------------------------------------------------------------------    -------------------
                         (clock clk_out1_clk_wiz_1 rise edge)
                                                      0.000     0.000 r
    AG12                                              0.000     0.000 r  clk (IN)
                         net (fo=0)                   0.000     0.000    pll_i/inst/clkin1_ibuf/I
    AG12                 INBUF (Prop_INBUF_HRIO_PAD_O)
                                                      0.339     0.339 r  pll_i/inst/clkin1_ibuf/INBUF_INST/O
                         net (fo=1, routed)           0.025     0.364    pll_i/inst/clkin1_ibuf/OUT
    AG12                 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
                                                      0.015     0.379 r  pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
                         net (fo=1, routed)           0.405     0.784    pll_i/inst/clk_in1_clk_wiz_1
    MMCME3_ADV_X1Y0      MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0)
                                                     -1.721    -0.937 r  pll_i/inst/mmcme3_adv_inst/CLKOUT0
                         net (fo=1, routed)           0.167    -0.770    pll_i/inst/clk_out1_clk_wiz_1
    BUFGCE_X1Y1          BUFGCE (Prop_BUFCE_BUFGCE_I_O)
                                                      0.027    -0.743 r  pll_i/inst/clkout1_buf/O
    X2Y0 (CLOCK_ROOT)    net (fo=1, routed)           0.495    -0.248    pll_clk_8
    SLICE_X49Y58         FDRE                                         r  foo_reg_reg/C
  -------------------------------------------------------------------    -------------------
    SLICE_X49Y58         FDRE (Prop_EFF_SLICEL_C_Q)
                                                      0.049    -0.199 f  foo_reg_reg/Q
                         net (fo=1, routed)           0.343     0.144    foo_reg
    SLICE_X49Y58         LUT1 (Prop_D5LUT_SLICEL_I0_O)
                                                      0.055     0.199 r  bar__0_i_1/O
                         net (fo=1, routed)           0.011     0.210    p_0_in
    SLICE_X49Y58         FDRE                                         r  bar_reg__0/D
  -------------------------------------------------------------------    -------------------

                         (clock clk_out2_clk_wiz_1 rise edge)
                                                      0.000     0.000 r
    AG12                                              0.000     0.000 r  clk (IN)
                         net (fo=0)                   0.000     0.000    pll_i/inst/clkin1_ibuf/I
    AG12                 INBUF (Prop_INBUF_HRIO_PAD_O)
                                                      0.595     0.595 r  pll_i/inst/clkin1_ibuf/INBUF_INST/O
                         net (fo=1, routed)           0.042     0.637    pll_i/inst/clkin1_ibuf/OUT
    AG12                 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
                                                      0.022     0.659 r  pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
                         net (fo=1, routed)           0.457     1.116    pll_i/inst/clk_in1_clk_wiz_1
    MMCME3_ADV_X1Y0      MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT1)
                                                     -2.474    -1.358 r  pll_i/inst/mmcme3_adv_inst/CLKOUT1
                         net (fo=1, routed)           0.209    -1.149    pll_i/inst/clk_out2_clk_wiz_1
    BUFGCE_X1Y0          BUFGCE (Prop_BUFCE_BUFGCE_I_O)
                                                      0.031    -1.118 r  pll_i/inst/clkout2_buf/O
    X2Y0 (CLOCK_ROOT)    net (fo=2, routed)           0.576    -0.542    pll_clk_6
    SLICE_X49Y58         FDRE                                         r  bar_reg__0/C
                         clock pessimism              0.421    -0.121
                         clock uncertainty            0.182     0.061
    SLICE_X49Y58         FDRE (Hold_DFF2_SLICEL_C_D)
                                                      0.056     0.117    bar_reg__0
  -------------------------------------------------------------------
                         required time                         -0.117
                         arrival time                           0.210
  -------------------------------------------------------------------
                         slack                                  0.093

En primer lugar, hay algunas diferencias evidentes: el Path Type es Hold, y no Setup como antes. Eso es de esperar. Después dice "Min at Fast Process Corner", que es lo contrario de lo que decía para el camino de setup: este cálculo usa los retardos mínimos, y no los máximos, para el camino de datos. Eso es adecuado para un cálculo en el peor caso respecto a la situación en la que la entrada de datos cambia demasiado pronto.

El Requirement es de 0 ns, lo que es típico en un camino de hold. Las frecuencias de los relojes no juegan ningún papel aquí: el escenario que se examina es aquel en el que ambos relojes tienen un flanco de subida al mismo tiempo.

En cuanto a los caminos del reloj, observa que los retardos de cada componente en el camino del reloj de origen son sistemáticamente menores que los mismos retardos en el camino del reloj de destino (y no mayores). Una vez más, esto es coherente con el propósito del cálculo, porque el peor caso se produce cuando los datos cambian demasiado pronto en relación con la llegada del flanco al segundo flip-flop. El análisis de tiempos multi-corner se explicó en la página anterior.

Pero lo más importante de este informe de temporización es que el margen (slack) es pequeño: solo 0,093 ns. A menudo esto indica que las herramientas tuvieron que hacer un esfuerzo para cumplir el requisito. No obstante, ten en cuenta que un margen pequeño no indica necesariamente que hubiera un problema que hubo que resolver.

Cuando el análisis de tiempos se hace para thold, un margen pequeño es bastante habitual. Entonces, ¿por qué resulta sospechoso este camino? Principalmente por el mayor retardo de enrutado entre dos flip-flops de la misma slice (SLICE_X49Y58), como se ha mencionado antes. Es probable que en las primeras fases de la colocación y el enrutado (place and route), el software detectara un incumplimiento del requisito de thold entre estos dos flip-flops. Entonces, ¿cómo se corrigió?

Un incumplimiento del requisito de thold significa que la señal de datos llega demasiado pronto en relación con el flanco de reloj del segundo flip-flop. Esto se corrige añadiendo artificialmente retardo al camino de datos. Como resultado, la entrada de datos cambia de valor un poco más tarde en el segundo flip-flop. Las herramientas probablemente alargaron el cableado entre los dos flip-flops. Eso aumenta el retardo de enrutado y resuelve el problema con thold. Ese aumento del retardo podría haber creado un problema para cumplir el requisito de tsu, pero en este caso no lo hubo: la restricción de temporización se cumplió para este camino (es decir, se cumplieron los requisitos tanto de tsu como de thold).

Sin embargo, este ejemplo muestra cómo la necesidad de resolver un problema con thold puede crear un problema aparentemente no relacionado con tsu. Esto es algo a tener en cuenta cuando la proporción entre el retardo lógico y el retardo de enrutado de un camino es muy baja. Por supuesto, hay otras posibles razones para un retardo de enrutado grande, en particular un fan-out alto. Pero cuando el fan-out es bajo (como en este caso, donde el fan-out es 1), vale la pena preguntarse si ese gran retardo de enrutado fue insertado deliberadamente por las herramientas para resolver un problema de thold.

Pero ¿por qué había un problema con thold solo en el caso de dos relojes? La respuesta es que, con dos relojes, hay una mayor incertidumbre sobre cuándo llegan los flancos a cada uno de los dos flip-flops. Varios factores contribuyen a esa incertidumbre, sobre todo el clock skew y el jitter. Como el cálculo para thold se hace de 0 ns a 0 ns, este cálculo es más sensible a las pequeñas incertidumbres sobre cuándo llegan los flancos.

Por tanto, la incertidumbre asociada a dos relojes relacionados obliga a menudo a tomar medidas correctoras para cumplir el requisito de thold. Estas correcciones las hacen las herramientas automáticamente, por supuesto. No obstante, es importante ser consciente de ellas cuando hay problemas para cumplir las restricciones de temporización.

Resumen

Estas dos últimas páginas de la serie han mostrado varios ejemplos de informes de temporización que eran todos el resultado de una única restricción de temporización concreta. Todos esos informes estaban además relacionados con un ejemplo de lógica sencillo y específico. Esperemos que estos ejemplos hayan ayudado a establecer una comprensión de los fundamentos de la temporización.

En este punto, se recomienda mirar los informes de temporización de tu propio diseño para ver cómo se aplican los mismos principios a caminos que contienen más de una LUT.

La página siguiente comienza la discusión sobre los problemas de temporización y cómo resolverlos.

Esta página se ha traducido del inglés mediante traducción automática. En caso de duda, consulta el texto original.
Copyright © 2021-2026. All rights reserved. (dcc38493)