01signal.com

El arte del cierre de temporización

Esta página pertenece a una serie de páginas sobre temporización (timing). Las páginas anteriores explicaron algunos temas básicos: la teoría detrás de los cálculos de temporización y la restricción de temporización del periodo del reloj. También se mostraron y explicaron varios informes de temporización. Ahora toca hablar de uno de los propósitos importantes de este conocimiento: resolver los problemas de temporización.

Introducción

La mayor dificultad de las herramientas de FPGA es cumplir los requisitos de las restricciones de temporización. Con suerte se consigue, pero a veces no. Y cuando no se consigue, somos los humanos quienes tenemos el deber de averiguar por qué ha fallado y de arreglarlo. Esta tarea tiene un nombre: la llamamos cierre de temporización (timing closure). Y no es una tarea fácil.

¿Por qué es difícil el cierre de temporización? La cuestión es que las herramientas tienen un algoritmo de colocación y enrutado (place and route) que intenta usar los recursos de la FPGA de forma óptima. Normalmente, ese algoritmo comienza colocando los elementos lógicos en la FPGA sin esforzarse demasiado. Luego comienza un proceso iterativo: las herramientas recorren todos los caminos (paths) y encuentran los que no cumplen las restricciones de temporización. Para corregir esos fallos, se toman medidas correctivas en esos caminos. Sobre todo, se mueven los elementos lógicos a otras posiciones de la FPGA y se ajusta el enrutado. En cuanto a medidas correctivas más avanzadas, cada herramienta de FPGA tiene sus propios métodos.

Cuando todos los caminos cumplen las restricciones de temporización, la implementación se considera terminada. Pero la implementación también puede terminar porque las herramientas no consiguieron ese objetivo y, en consecuencia, abandonaron el intento. En esa situación, nos quedamos con lo que las herramientas han conseguido cuando cesaron los esfuerzos. Ese resultado no es necesariamente óptimo: puede haber caminos en la implementación que podrían haberse mejorado, pero las herramientas estaban ocupadas arreglando otra cosa. Y cuando esa otra cosa falló, las herramientas se rindieron sin intentar arreglar lo demás. Es como si las herramientas dijeran: «No merece la pena perder el tiempo arreglando una implementación si de todos modos va a fracasar».

Nuestra tarea como diseñadores de FPGA es examinar ese resultado subóptimo y encontrar la razón por la que no se alcanzaron los objetivos de las restricciones de temporización.

Los algoritmos mejoran con el tiempo. Cuando hay una razón habitual para no cumplir las restricciones de temporización, la siguiente versión del software incluirá una solución específica para esa situación. Así que cuando las herramientas fallan, normalmente hay una buena razón.

Miramos lo que han conseguido las herramientas y nos preguntamos: ¿por qué han fallado? ¿Hemos pedido algo imposible? Aún más importante: ¿hemos pedido algo innecesario? Quizá el obstáculo que hizo fallar a las herramientas sea algo que ni siquiera necesitamos. ¿O quizá el algoritmo de optimización no funcionó demasiado bien? A veces es solo cuestión de mala suerte: la colocación inicial de los elementos lógicos puede ser tan mala que los intentos posteriores de mejorar el rendimiento están condenados al fracaso.

Sea cual sea el problema, encontrar la causa del fallo se parece a un detective que investiga la escena de un crimen: los hechos están delante de nosotros, pero la razón suele estar oculta. La mayoría de esos hechos se encuentran en los informes de temporización, pero las pistas no se revelan fácilmente. La pregunta que uno debe hacerse siempre es qué hay de malo, inusual o anómalo en el informe de temporización. Igual que el detective intenta encontrar al culpable, la meta es hallar el detalle que conduce al problema.

Pero para encontrar lo anómalo hay que saber lo que es normal: por ejemplo, ¿cuál es un retardo normal para una red con cierto fan-out? ¿Cuántos niveles lógicos son normales para implementar determinada función lógica? Las respuestas a preguntas de este tipo cambian de una FPGA a otra. Por tanto, es necesario adquirir experiencia leyendo y entendiendo los informes de temporización, incluso cuando todo va bien. Hay que saber qué aspecto tiene un informe de temporización cuando todo está correcto para poder localizar los puntos en los que el informe indica un problema. Por si te preguntabas por qué entré en tanto detalle en las páginas anteriores, esa es una de las razones.

El camino crítico

Cuando las herramientas no consiguen cumplir las restricciones de temporización, significa que hay al menos un camino con margen (slack) negativo. El camino con el margen más negativo se llama camino crítico (critical path). Ese nombre refleja la estrategia habitual para el cierre de temporización: centrarse en el camino crítico suele ser la manera de resolver un problema de temporización. Pero demostraré más abajo que esta estrategia también puede ser una pérdida de tiempo.

Cuando se cumplen las restricciones de temporización, el camino crítico es el camino con el menor margen. Ese camino a menudo no es interesante, porque las herramientas no intentan mejorar los caminos con margen positivo. Así que si el peor camino tiene margen positivo, puede ser una coincidencia que ese camino haya resultado ser el peor.

Pero si el margen es positivo y casi cero (digamos, menos de 0,2 ns), puede indicar que fue difícil conseguir que ese camino cumpliera las restricciones de temporización. Este tipo de caminos críticos pueden considerarse avisos de que esos caminos pueden dar problemas en el futuro (en particular cuando la FPGA se llene con más lógica y los esfuerzos de las herramientas se desvíen hacia otros caminos).

El informe de temporización suele contener un número limitado de caminos críticos para cada reloj. La opción por defecto de la mayoría de las herramientas de FPGA es mostrar unos pocos caminos críticos aunque su margen sea positivo (es decir, cuando se han cumplido las restricciones de temporización). Esta es la configuración recomendada.

Un ejemplo de camino crítico

Empezaré con un ejemplo del análisis de un camino crítico. Para este ejemplo, el código Verilog es el siguiente:

   reg [24:0] calc, result;
   reg [11:0] x, y, z;

   always @(posedge clk)
     begin
	calc <= x * y + z;
	result <= calc;
     end

En este ejemplo, la frecuencia de @clk es de 250 MHz y no se usa ninguna PLL para generar ese reloj. Supongamos también que @x, @y y @z son registros síncronos con @clk. El código Verilog que asigna valores a esos registros no se muestra, porque es irrelevante.

Las restricciones de temporización no se cumplieron al intentar este código con Vivado. En el informe de temporización, el camino crítico era este:

Slack (VIOLATED) :        -0.239ns  (required time - arrival time)
  Source:                 x_reg[1]__0_replica_2/C
                            (rising edge-triggered cell FDRE clocked by clk  {rise@0.000ns fall@2.000ns period=4.000ns})
  Destination:            calc_reg[23]/D
                            (rising edge-triggered cell FDRE clocked by clk  {rise@0.000ns fall@2.000ns period=4.000ns})
  Path Group:             clk
  Path Type:              Setup (Max at Slow Process Corner)
  Requirement:            4.000ns  (clk rise@4.000ns - clk rise@0.000ns)
  Data Path Delay:        4.180ns  (logic 1.642ns (39.282%)  route 2.538ns (60.718%))
  Logic Levels:           7  (CARRY8=4 LUT3=1 LUT4=1 LUT6=1)
  Clock Path Skew:        -0.087ns (DCD - SCD + CPR)
    Destination Clock Delay (DCD):    3.176ns = ( 7.176 - 4.000 )
    Source Clock Delay      (SCD):    3.864ns
    Clock Pessimism Removal (CPR):    0.601ns
  Clock Uncertainty:      0.035ns  ((TSJ^2 + TIJ^2)^1/2 + DJ) / 2 + PE
    Total System Jitter     (TSJ):    0.071ns
    Total Input Jitter      (TIJ):    0.000ns
    Discrete Jitter          (DJ):    0.000ns
    Phase Error              (PE):    0.000ns
  Clock Net Delay (Source):      2.032ns (routing 0.396ns, distribution 1.636ns)
  Clock Net Delay (Destination): 1.748ns (routing 0.365ns, distribution 1.383ns)

    Location             Delay type                Incr(ns)  Path(ns)    Netlist Resource(s)
  -------------------------------------------------------------------    -------------------
                         (clock clk rise edge)        0.000     0.000 r
    AG12                                              0.000     0.000 r  clk (IN)
                         net (fo=0)                   0.000     0.000    clk_IBUF_inst/I
    AG12                 INBUF (Prop_INBUF_HRIO_PAD_O)
                                                      0.738     0.738 r  clk_IBUF_inst/INBUF_INST/O
                         net (fo=1, routed)           0.105     0.843    clk_IBUF_inst/OUT
    AG12                 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
                                                      0.049     0.892 r  clk_IBUF_inst/IBUFCTRL_INST/O
                         net (fo=1, routed)           0.839     1.731    clk_IBUF
    BUFGCE_X1Y0          BUFGCE (Prop_BUFCE_BUFGCE_I_O)
                                                      0.101     1.832 r  clk_IBUF_BUFG_inst/O
    X2Y0 (CLOCK_ROOT)    net (fo=106, routed)         2.032     3.864    clk_IBUF_BUFG
    SLICE_X54Y54         FDRE                                         r  x_reg[1]__0_replica_2/C
  -------------------------------------------------------------------    -------------------
    SLICE_X54Y54         FDRE (Prop_HFF2_SLICEL_C_Q)
                                                      0.137     4.001 r  x_reg[1]__0_replica_2/Q
                         net (fo=21, routed)          0.371     4.372    x[1]_repN_2
    SLICE_X56Y53         LUT6 (Prop_E6LUT_SLICEL_I1_O)
                                                      0.219     4.591 r  calc[23]_i_101/O
                         net (fo=2, routed)           0.550     5.141    calc[23]_i_101_n_0
    SLICE_X54Y57         CARRY8 (Prop_CARRY8_SLICEL_DI[5]_CO[7])
                                                      0.228     5.369 r  calc_reg[23]_i_30/CO[7]
                         net (fo=1, routed)           0.030     5.399    calc_reg[23]_i_30_n_0
    SLICE_X54Y58         CARRY8 (Prop_CARRY8_SLICEL_CI_O[1])
                                                      0.163     5.562 r  calc_reg[23]_i_22/O[1]
                         net (fo=3, routed)           0.351     5.913    calc_reg[23]_i_22_n_14
    SLICE_X56Y57         LUT3 (Prop_C6LUT_SLICEL_I1_O)
                                                      0.146     6.059 r  calc[23]_i_26/O
                         net (fo=3, routed)           0.240     6.299    calc[23]_i_26_n_0
    SLICE_X55Y58         LUT4 (Prop_A6LUT_SLICEM_I0_O)
                                                      0.089     6.388 r  calc[23]_i_7/O
                         net (fo=1, routed)           0.407     6.795    calc[23]_i_7_n_0
    SLICE_X53Y57         CARRY8 (Prop_CARRY8_SLICEM_DI[2]_O[4])
                                                      0.308     7.103 r  calc_reg[23]_i_2/O[4]
                         net (fo=1, routed)           0.538     7.641    P[20]
    SLICE_X54Y56         CARRY8 (Prop_CARRY8_SLICEL_S[4]_O[7])
                                                      0.352     7.993 r  calc_reg[23]_i_1/O[7]
                         net (fo=1, routed)           0.051     8.044    P0_out[23]
    SLICE_X54Y56         FDRE                                         r  calc_reg[23]/D
  -------------------------------------------------------------------    -------------------

                         (clock clk rise edge)        4.000     4.000 r
    AG12                                              0.000     4.000 r  clk (IN)
                         net (fo=0)                   0.000     4.000    clk_IBUF_inst/I
    AG12                 INBUF (Prop_INBUF_HRIO_PAD_O)
                                                      0.515     4.515 r  clk_IBUF_inst/INBUF_INST/O
                         net (fo=1, routed)           0.066     4.581    clk_IBUF_inst/OUT
    AG12                 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
                                                      0.034     4.615 r  clk_IBUF_inst/IBUFCTRL_INST/O
                         net (fo=1, routed)           0.722     5.337    clk_IBUF
    BUFGCE_X1Y0          BUFGCE (Prop_BUFCE_BUFGCE_I_O)
                                                      0.091     5.428 r  clk_IBUF_BUFG_inst/O
    X2Y0 (CLOCK_ROOT)    net (fo=106, routed)         1.748     7.176    clk_IBUF_BUFG
    SLICE_X54Y56         FDRE                                         r  calc_reg[23]/C
                         clock pessimism              0.601     7.777
                         clock uncertainty           -0.035     7.741
    SLICE_X54Y56         FDRE (Setup_HFF_SLICEL_C_D)
                                                      0.063     7.804    calc_reg[23]
  -------------------------------------------------------------------
                         required time                          7.804
                         arrival time                          -8.044
  -------------------------------------------------------------------
                         slack                                 -0.239

El margen (slack) de este camino era de –0,239 ns, así que es un incumplimiento leve de las restricciones. Lo primero que hay que examinar es el principio y el final del camino: miramos el "Source" y el "Destination" en la cabecera del informe y encontramos x_reg y calc_reg. Así que el origen del problema es claramente esta parte:

calc <= x * y + z;

Esto no debería sorprender, porque es la única operación relevante del código Verilog. En un escenario real, no resulta tan obvio qué parte de la lógica causó el problema.

También es evidente en el informe que hay un número elevado de niveles lógicos: 7. El camino combinacional es demasiado largo. En otras palabras, hay demasiado que hacer entre dos flancos del reloj @clk.

Pero ¿cuál es la razón real del fallo? ¿Tal vez que el enrutado sea responsable del 61% del retardo? Recuerda la regla general de que el retardo de enrutado suele ser el 40% del retardo total. Entonces, ¿quizá convendría intentar que las herramientas de FPGA trabajaran mejor? Sin embargo, es poco probable que esa sea una solución exitosa, porque las herramientas suelen esforzarse al máximo antes de abandonar los intentos de cumplir las restricciones de temporización de un camino.

Intentar cambiar la función lógica entre @x y @calc es igualmente inútil: la multiplicación es imprescindible, así que no hay manera de poner algo más sencillo en su lugar.

En la página siguiente presentaré otros enfoques posibles para resolver un problema como este. Pero repasar una lista de técnicas no ayudará en este caso. Este ejemplo sencillo demuestra que a veces tenemos que pensar como detectives.

No hay sustituto para tu cerebro

La primera pregunta al leer un informe de temporización es qué tiene de anómalo. En este ejemplo, la respuesta es que el camino combinacional consta solo de slices: prácticamente todas las FPGA tienen unidades aritméticas específicas (DSP, ALU; los nombres varían) que se utilizan cuando se pide una multiplicación. De hecho, la multiplicación y la suma es la función más habitual en ese tipo de lógica específica. Así que la solución más sencilla en la mayoría de los casos es hacer que las herramientas usen una unidad aritmética específica. El informe de temporización de esta solución se muestra al final de esta página.

Pero la verdadera pregunta que deberíamos hacernos es por qué se usaron slices en lugar de una unidad aritmética específica. La razón más habitual es que todas las unidades aritméticas disponibles en la FPGA ya las está usando otra parte del diseño. En ese caso, el cambio necesario puede no tener nada que ver con el camino crítico: quizá haga falta liberar unas cuantas unidades aritméticas eliminando algo de lógica del diseño. Otra posibilidad sería indicar a las herramientas que repartan esas unidades aritméticas de otra manera entre las distintas partes del diseño.

En este ejemplo, se usaron slices en lugar de unidades aritméticas porque yo quise que ocurriera así: desactivé deliberadamente el uso de unidades aritméticas (con uno de los parámetros del sintetizador (synthesizer) de Vivado: puse max_dsp a cero). Pero eso no hace artificial este ejemplo. A veces se usan parámetros incorrectos con las herramientas de FPGA, y se llega exactamente a esta situación. De hecho, a veces es correcto no usar unidades aritméticas a propósito, porque se necesitan más en otra parte del diseño.

La solución fácil era, por tanto, usar unidades aritméticas específicas. Pero, ¿y si tenemos que usar slices? Una vez más, la solución es indirecta. Recuerda que la parte problemática era:

calc <= x * y + z;

Pero observa que justo después viene esto:

result <= calc;

Si @calc se usa solo en esa línea y no en ningún otro sitio, es posible dividir el cálculo en dos etapas. Esta técnica suele llamarse segmentación (pipelining). Así que el código Verilog cambia a esto:

   reg [24:0] calc, result;
   reg [11:0] x, y, z, z_d;

   always @(posedge clk)
     begin
	z_d <= z;

	calc <= x * y;
	result <= calc + z_d;
     end

En esta solución, a @calc se le asigna únicamente el resultado de la multiplicación. El valor de @z se suma a @calc solo en la etapa siguiente. Más concretamente, la operación de suma se hace entre @calc y @z_d, porque esa operación ocurre un ciclo de reloj más tarde. Por tanto, el valor de @result es exactamente el mismo que antes.

Fue fácil resolverlo así porque @result no era más que una copia retardada de @calc en el código Verilog original. En la vida real no solemos tener tanta suerte.

Observa que el camino crítico involucraba solo a @calc y a @x. @z ni siquiera aparecía en el camino. El propósito de esta manipulación es, por tanto, reducir la carga de la operación aritmética. O, más exactamente, reducir el número de niveles lógicos.

Recuerda que el camino crítico es el peor camino después de ejecutar un algoritmo de optimización. Ese algoritmo no pregunta por la causa del problema; más bien intenta mejorar los caminos con margen negativo. Y aunque la solución del problema requiera manipular @z, el camino relacionado con @z no era el camino crítico. Fue una coincidencia, pero ocurre con frecuencia.

El informe de temporización con el camino crítico de esta solución también se muestra al final de esta página. En él se ve que el número de niveles lógicos se redujo de 7 a 6. Como resultado, el retardo del camino de datos disminuyó en 0,715 ns, más que suficiente para cumplir las restricciones de temporización.

La lección de este ejemplo es que el camino crítico no siempre es la razón directa del problema. Sigue siendo correcto preguntarse por qué falló ese camino, pero la solución puede estar en otro sitio. Ten en cuenta que cada herramienta de FPGA tiene sus propias utilidades que ofrecen información capaz de ayudar a encontrar la causa raíz del problema. Merece la pena dedicar tiempo a explorar esas utilidades y a leer su documentación.

Evita el problema al principio en lugar de resolverlo después

Gran parte del trabajo del cierre de temporización puede evitarse si el diseño lógico se hace correctamente desde el principio. Esto exige ser consciente en todo momento de que un diseño lógico no es software. El propósito del código Verilog no es producir el resultado correcto durante la simulación. Lo que realmente importa es la salida que genera el sintetizador (synthesizer) a partir del código Verilog.

Un buen diseño lógico comienza pensando a fondo cómo debería cumplir la lógica su propósito. Eso incluye identificar los obstáculos potenciales relacionados con la temporización.

Los diseñadores de FPGA con menos experiencia suelen desarrollar el código Verilog por ensayo y error. La simulación se usa para ver si la lógica funciona como se espera, y se hacen correcciones poco a poco hasta que la salida de la simulación es correcta. El resultado puede ser una lógica que no se puede usar en el hardware: hay que reescribir el código Verilog por completo para cumplir las restricciones de temporización.

Es importante prever los caminos combinacionales que genera el código Verilog. La idea es mirar cada registro y seguir el camino combinacional hasta su final. Recuerda que un camino combinacional siempre comienza en un registro y termina en un registro.

Veamos este ejemplo:

reg [15:0] a, b;
wire [16:0] x, y;
reg [33:0] z;

assign x = a + 2;
assign y = b + 3;

always @(posedge clk)
  z <= x * y;

Respecto a @a: cuando ese registro cambia, el camino combinacional llega a @x como primera etapa. Pero @x no es un registro: @x se actualiza mediante una asignación continua. Por tanto, el camino continúa hasta @z. Así pues, la lógica realiza dos operaciones importantes en este camino combinacional: una suma aritmética y una multiplicación aritmética. ¿Es demasiado? ¿Hace falta dividirlo en dos ciclos de reloj mediante segmentación (pipelining)? Eso depende de la frecuencia del reloj y de la FPGA utilizada.

Otro factor importante es lo difícil que resulta acortar los caminos combinacionales. A veces un camino combinacional largo es inevitable. Pero cuando es fácil mejorar la temporización de un camino, hazlo aunque haya caminos mucho peores en el diseño: si hay unos pocos caminos problemáticos, las herramientas a menudo consiguen cumplir las restricciones concentrando sus esfuerzos en esos caminos. Ayuda mucho que sea fácil cumplir los requisitos de temporización de los demás caminos.

Así que no hay reglas sobre lo que está permitido o prohibido para conseguir un diseño que cumpla las restricciones de temporización. Se necesita experiencia con el diseño de FPGA para tomar decisiones correctas en este terreno. La experiencia con las herramientas de FPGA concretas también es importante. La única regla que siempre es válida es: cuando sea posible mejorar la temporización con un cambio sencillo en el código Verilog, hazlo. No seas perezoso y haz ese cambio desde el principio. Ten siempre presente la temporización.

Escribir lógica rápida

Como ya se ha dicho, el objetivo es conseguir caminos combinacionales cortos entre registros: las funciones lógicas que calculan el siguiente valor de los registros deben ser sencillas. En otras palabras, hace falta un número reducido de niveles lógicos para implementar esas funciones lógicas.

Nuestra tarea como diseñadores de FPGA consiste en mirar el código Verilog y evaluar lo complicadas que serán las funciones lógicas. Para ello hace falta saber cómo traduce el sintetizador (synthesizer) el Verilog a elementos lógicos (LUT y otras primitivas (primitives) lógicas). Ese conocimiento se adquiere con la experiencia, que en parte se gana analizando los informes de temporización. Para complicarlo más, esa traducción cambia de una FPGA a otra. Así que no es tarea fácil escribir código Verilog que se traduzca en lógica rápida.

Si eres novato con FPGA, se recomienda dedicar tiempo a mirar los resultados de la implementación para aprender. El informe de temporización muestra ejemplos de cómo el diseño lógico se descompone en elementos lógicos sencillos. Las herramientas de FPGA también ofrecen otras utilidades para ver los elementos lógicos de bajo nivel.

Además, hay algunas reglas sencillas que pueden ayudar:

Dos informes de temporización adicionales

He prometido dos informes de temporización en la sección «No hay sustituto para tu cerebro», más arriba. Los he puesto aquí, y no donde se mencionan, porque son largos y no son del todo relevantes en ese punto.

Observa que cada uno de estos dos informes es el camino crítico del escenario correspondiente. Por tanto, el camino no empieza y termina en los mismos registros que el camino mostrado antes.

El primer informe corresponde al primer ejemplo de código Verilog. A diferencia del informe anterior, a las herramientas se les permitió usar unidades aritméticas específicas. El resultado es que las restricciones de temporización se cumplieron sin dificultad.

Este informe se generó para una FPGA Kintex Ultrascale. En esta familia de FPGA, la unidad aritmética específica se llama DSP48E2. Observa que el camino comienza y termina en la misma unidad DSP48E2. Por tanto, el retardo lógico es del 100%.

Slack (MET) :             1.406ns  (required time - arrival time)
  Source:                 calc_reg/DSP_A_B_DATA_INST/CLK
                            (rising edge-triggered cell DSP_A_B_DATA clocked by clk  {rise@0.000ns fall@2.000ns period=4.000ns})
  Destination:            calc_reg/DSP_OUTPUT_INST/ALU_OUT[10]
                            (rising edge-triggered cell DSP_OUTPUT clocked by clk  {rise@0.000ns fall@2.000ns period=4.000ns})
  Path Group:             clk
  Path Type:              Setup (Max at Slow Process Corner)
  Requirement:            4.000ns  (clk rise@4.000ns - clk rise@0.000ns)
  Data Path Delay:        2.445ns  (logic 2.445ns (100.000%)  route 0.000ns (0.000%))
  Logic Levels:           4  (DSP_ALU=1 DSP_M_DATA=1 DSP_MULTIPLIER=1 DSP_PREADD_DATA=1)
  Clock Path Skew:        -0.010ns (DCD - SCD + CPR)
    Destination Clock Delay (DCD):    3.392ns = ( 7.392 - 4.000 )
    Source Clock Delay      (SCD):    4.096ns
    Clock Pessimism Removal (CPR):    0.694ns
  Clock Uncertainty:      0.035ns  ((TSJ^2 + TIJ^2)^1/2 + DJ) / 2 + PE
    Total System Jitter     (TSJ):    0.071ns
    Total Input Jitter      (TIJ):    0.000ns
    Discrete Jitter          (DJ):    0.000ns
    Phase Error              (PE):    0.000ns
  Clock Net Delay (Source):      2.264ns (routing 0.756ns, distribution 1.508ns)
  Clock Net Delay (Destination): 1.964ns (routing 0.696ns, distribution 1.268ns)

    Location             Delay type                Incr(ns)  Path(ns)    Netlist Resource(s)
  -------------------------------------------------------------------    -------------------
                         (clock clk rise edge)        0.000     0.000 r
    AG12                                              0.000     0.000 r  clk (IN)
                         net (fo=0)                   0.000     0.000    clk_IBUF_inst/I
    AG12                 INBUF (Prop_INBUF_HRIO_PAD_O)
                                                      0.738     0.738 r  clk_IBUF_inst/INBUF_INST/O
                         net (fo=1, routed)           0.105     0.843    clk_IBUF_inst/OUT
    AG12                 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
                                                      0.049     0.892 r  clk_IBUF_inst/IBUFCTRL_INST/O
                         net (fo=1, routed)           0.839     1.731    clk_IBUF
    BUFGCE_X1Y0          BUFGCE (Prop_BUFCE_BUFGCE_I_O)
                                                      0.101     1.832 r  clk_IBUF_BUFG_inst/O
    X2Y1 (CLOCK_ROOT)    net (fo=80, routed)          2.264     4.096    calc_reg/CLK
    DSP48E2_X11Y34       DSP_A_B_DATA                                 r  calc_reg/DSP_A_B_DATA_INST/CLK
  -------------------------------------------------------------------    -------------------
    DSP48E2_X11Y34       DSP_A_B_DATA (Prop_DSP_A_B_DATA_DSP48E2_CLK_A2_DATA[9])
                                                      0.302     4.398 r  calc_reg/DSP_A_B_DATA_INST/A2_DATA[9]
                         net (fo=1, routed)           0.000     4.398    calc_reg/DSP_A_B_DATA.A2_DATA<9>
    DSP48E2_X11Y34       DSP_PREADD_DATA (Prop_DSP_PREADD_DATA_DSP48E2_A2_DATA[9]_A2A1[9])
                                                      0.182     4.580 r  calc_reg/DSP_PREADD_DATA_INST/A2A1[9]
                         net (fo=1, routed)           0.000     4.580    calc_reg/DSP_PREADD_DATA.A2A1<9>
    DSP48E2_X11Y34       DSP_MULTIPLIER (Prop_DSP_MULTIPLIER_DSP48E2_A2A1[9]_U[10])
                                                      0.994     5.574 f  calc_reg/DSP_MULTIPLIER_INST/U[10]
                         net (fo=1, routed)           0.000     5.574    calc_reg/DSP_MULTIPLIER.U<10>
    DSP48E2_X11Y34       DSP_M_DATA (Prop_DSP_M_DATA_DSP48E2_U[10]_U_DATA[10])
                                                      0.164     5.738 r  calc_reg/DSP_M_DATA_INST/U_DATA[10]
                         net (fo=1, routed)           0.000     5.738    calc_reg/DSP_M_DATA.U_DATA<10>
    DSP48E2_X11Y34       DSP_ALU (Prop_DSP_ALU_DSP48E2_U_DATA[10]_ALU_OUT[10])
                                                      0.803     6.541 r  calc_reg/DSP_ALU_INST/ALU_OUT[10]
                         net (fo=1, routed)           0.000     6.541    calc_reg/DSP_ALU.ALU_OUT<10>
    DSP48E2_X11Y34       DSP_OUTPUT                                   r  calc_reg/DSP_OUTPUT_INST/ALU_OUT[10]
  -------------------------------------------------------------------    -------------------

                         (clock clk rise edge)        4.000     4.000 r
    AG12                                              0.000     4.000 r  clk (IN)
                         net (fo=0)                   0.000     4.000    clk_IBUF_inst/I
    AG12                 INBUF (Prop_INBUF_HRIO_PAD_O)
                                                      0.515     4.515 r  clk_IBUF_inst/INBUF_INST/O
                         net (fo=1, routed)           0.066     4.581    clk_IBUF_inst/OUT
    AG12                 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
                                                      0.034     4.615 r  clk_IBUF_inst/IBUFCTRL_INST/O
                         net (fo=1, routed)           0.722     5.337    clk_IBUF
    BUFGCE_X1Y0          BUFGCE (Prop_BUFCE_BUFGCE_I_O)
                                                      0.091     5.428 r  clk_IBUF_BUFG_inst/O
    X2Y1 (CLOCK_ROOT)    net (fo=80, routed)          1.964     7.392    calc_reg/CLK
    DSP48E2_X11Y34       DSP_OUTPUT                                   r  calc_reg/DSP_OUTPUT_INST/CLK
                         clock pessimism              0.694     8.086
                         clock uncertainty           -0.035     8.050
    DSP48E2_X11Y34       DSP_OUTPUT (Setup_DSP_OUTPUT_DSP48E2_CLK_ALU_OUT[10])
                                                     -0.104     7.946    calc_reg/DSP_OUTPUT_INST
  -------------------------------------------------------------------
                         required time                          7.946
                         arrival time                          -6.541
  -------------------------------------------------------------------
                         slack                                  1.406

El segundo informe corresponde al segundo ejemplo de código Verilog. En este ejemplo, la situación ha mejorado gracias a la segmentación (pipelining):

Slack (MET) :             0.433ns  (required time - arrival time)
  Source:                 y_reg[1]__0/C
                            (rising edge-triggered cell FDRE clocked by clk  {rise@0.000ns fall@2.000ns period=4.000ns})
  Destination:            calc_reg[23]/D
                            (rising edge-triggered cell FDRE clocked by clk  {rise@0.000ns fall@2.000ns period=4.000ns})
  Path Group:             clk
  Path Type:              Setup (Max at Slow Process Corner)
  Requirement:            4.000ns  (clk rise@4.000ns - clk rise@0.000ns)
  Data Path Delay:        3.465ns  (logic 1.653ns (47.706%)  route 1.812ns (52.294%))
  Logic Levels:           6  (CARRY8=4 LUT4=2)
  Clock Path Skew:        -0.129ns (DCD - SCD + CPR)
    Destination Clock Delay (DCD):    3.373ns = ( 7.373 - 4.000 )
    Source Clock Delay      (SCD):    4.040ns
    Clock Pessimism Removal (CPR):    0.538ns
  Clock Uncertainty:      0.035ns  ((TSJ^2 + TIJ^2)^1/2 + DJ) / 2 + PE
    Total System Jitter     (TSJ):    0.071ns
    Total Input Jitter      (TIJ):    0.000ns
    Discrete Jitter          (DJ):    0.000ns
    Phase Error              (PE):    0.000ns
  Clock Net Delay (Source):      2.208ns (routing 0.756ns, distribution 1.452ns)
  Clock Net Delay (Destination): 1.945ns (routing 0.696ns, distribution 1.249ns)

    Location             Delay type                Incr(ns)  Path(ns)    Netlist Resource(s)
  -------------------------------------------------------------------    -------------------
                         (clock clk rise edge)        0.000     0.000 r
    AG12                                              0.000     0.000 r  clk (IN)
                         net (fo=0)                   0.000     0.000    clk_IBUF_inst/I
    AG12                 INBUF (Prop_INBUF_HRIO_PAD_O)
                                                      0.738     0.738 r  clk_IBUF_inst/INBUF_INST/O
                         net (fo=1, routed)           0.105     0.843    clk_IBUF_inst/OUT
    AG12                 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
                                                      0.049     0.892 r  clk_IBUF_inst/IBUFCTRL_INST/O
                         net (fo=1, routed)           0.839     1.731    clk_IBUF
    BUFGCE_X1Y0          BUFGCE (Prop_BUFCE_BUFGCE_I_O)
                                                      0.101     1.832 r  clk_IBUF_BUFG_inst/O
    X2Y1 (CLOCK_ROOT)    net (fo=121, routed)         2.208     4.040    clk_IBUF_BUFG
    SLICE_X54Y88         FDRE                                         r  y_reg[1]__0/C
  -------------------------------------------------------------------    -------------------
    SLICE_X54Y88         FDRE (Prop_EFF2_SLICEL_C_Q)
                                                      0.138     4.178 r  y_reg[1]__0/Q
                         net (fo=25, routed)          0.505     4.683    y[1]
    SLICE_X53Y91         LUT4 (Prop_B6LUT_SLICEM_I0_O)
                                                      0.150     4.833 r  calc[7]_i_28/O
                         net (fo=1, routed)           0.344     5.177    calc[7]_i_28_n_0
    SLICE_X53Y89         CARRY8 (Prop_CARRY8_SLICEM_DI[2]_CO[7])
                                                      0.424     5.601 r  calc_reg[7]_i_9/CO[7]
                         net (fo=1, routed)           0.043     5.644    calc_reg[7]_i_9_n_0
    SLICE_X53Y90         CARRY8 (Prop_CARRY8_SLICEM_CI_O[0])
                                                      0.122     5.766 r  calc_reg[23]_i_30/O[0]
                         net (fo=3, routed)           0.402     6.168    calc_reg[23]_i_30_n_15
    SLICE_X51Y88         LUT4 (Prop_C5LUT_SLICEL_I0_O)
                                                      0.169     6.337 r  calc[15]_i_8/O
                         net (fo=1, routed)           0.437     6.774    calc[15]_i_8_n_0
    SLICE_X51Y92         CARRY8 (Prop_CARRY8_SLICEL_DI[1]_CO[7])
                                                      0.422     7.196 r  calc_reg[15]_i_1/CO[7]
                         net (fo=1, routed)           0.030     7.226    calc_reg[15]_i_1_n_0
    SLICE_X51Y93         CARRY8 (Prop_CARRY8_SLICEL_CI_O[7])
                                                      0.228     7.454 r  calc_reg[23]_i_1/O[7]
                         net (fo=1, routed)           0.051     7.505    calc_reg[23]_i_1_n_8
    SLICE_X51Y93         FDRE                                         r  calc_reg[23]/D
  -------------------------------------------------------------------    -------------------

                         (clock clk rise edge)        4.000     4.000 r
    AG12                                              0.000     4.000 r  clk (IN)
                         net (fo=0)                   0.000     4.000    clk_IBUF_inst/I
    AG12                 INBUF (Prop_INBUF_HRIO_PAD_O)
                                                      0.515     4.515 r  clk_IBUF_inst/INBUF_INST/O
                         net (fo=1, routed)           0.066     4.581    clk_IBUF_inst/OUT
    AG12                 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
                                                      0.034     4.615 r  clk_IBUF_inst/IBUFCTRL_INST/O
                         net (fo=1, routed)           0.722     5.337    clk_IBUF
    BUFGCE_X1Y0          BUFGCE (Prop_BUFCE_BUFGCE_I_O)
                                                      0.091     5.428 r  clk_IBUF_BUFG_inst/O
    X2Y1 (CLOCK_ROOT)    net (fo=121, routed)         1.945     7.373    clk_IBUF_BUFG
    SLICE_X51Y93         FDRE                                         r  calc_reg[23]/C
                         clock pessimism              0.538     7.910
                         clock uncertainty           -0.035     7.875
    SLICE_X51Y93         FDRE (Setup_HFF_SLICEL_C_D)
                                                      0.063     7.938    calc_reg[23]
  -------------------------------------------------------------------
                         required time                          7.938
                         arrival time                          -7.505
  -------------------------------------------------------------------
                         slack                                  0.433

La diferencia no es tan espectacular como cuando se usó una unidad aritmética específica. Pero aun así es suficiente para cumplir la restricción de temporización.


Con esto concluye la discusión general sobre el cierre de temporización. La página siguiente sugiere varias estrategias prácticas sobre este tema.

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)