Esta página pertenece a una serie de páginas sobre temporización (timing). Las páginas anteriores explicaron la teoría detrás de los cálculos de temporización, analizaron la restricción del periodo de reloj e introdujeron el cierre de temporización (timing closure). Pero, ¿y si has intentado hacerlo bien y aun así tienes un problema de temporización? Esta página intenta responder a esa pregunta.
Introducción
En la página anterior intenté convencerte de que no existe un método único para resolver un problema de cierre de temporización. A veces es correcto centrarse en el camino crítico (critical path), y a veces no lo es. A veces el problema puede resolverse fácilmente con un cambio sencillo en la configuración de la herramienta, y a veces es mucho más difícil que eso. No hay sustituto para emplear toda la experiencia y el criterio que tengas con el fin de llegar a la causa raíz del problema. No hay manera de resumir el cierre de temporización con una lista de comprobación.
Y sin embargo, a menudo ayuda tener una lista de estrategias posibles. Así que en esta página he reunido algunos temas que merece la pena considerar cuando te enfrentas a un problema de temporización. Si has llegado aquí porque tienes un problema concreto de temporización que resolver, es posible que una de estas ideas te conduzca a la solución. Pero no esperes que tu solución esté escrita aquí literalmente.
Ten también en cuenta que esta serie de páginas no termina aquí. He elegido hablar del cierre de temporización antes que de muchos otros temas para motivar al lector. Sin embargo, la información de las páginas posteriores también es relevante.
Por la misma razón, la discusión sobre las restricciones de temporización de E/S se pospone para más adelante. Por ahora me centro en los caminos (paths) que comienzan y terminan dentro de la FPGA.
Así que aquí van algunas ideas para tener en cuenta en relación con el cierre de temporización.
Idea n.º 1: Arreglar el diseño lógico
Esta es siempre la solución menos atractiva. Lo es en particular si ya se sabe que el diseño funciona correctamente. No quieres cambiar algo que funciona. Y sin embargo, a menudo la razón fundamental del problema es que el código Verilog no se escribió con la suficiente calidad para el rendimiento requerido. Un cambio en el diseño lógico resuelve el problema de una vez por todas, en lugar de enfrentarte a dificultades continuas.
En la página anterior hay algunas sugerencias para escribir lógica rápida. Y conviene repetirlo: ten siempre presente la temporización durante el proceso de desarrollo. Es mucho más difícil corregir problemas de temporización que escribir bien el código Verilog desde el principio.
Idea n.º 2: Reducir el fan-out
Cuando una red (net) tiene un fan-out alto, el retardo de propagación (propagation delay) aumenta por dos razones principales:
- La capacitancia del cable físico es mayor, así que se necesita más carga eléctrica para cambiar el estado lógico.
- Para las herramientas resulta más difícil encontrar un enrutado con retardo bajo para todos los destinos de la red: esos destinos son elementos lógicos dispersos por el tejido lógico. Así que cuantos más destinos haya, más difícil resulta optimizar la temporización para que todas las redes tengan un retardo bajo.
Si se usa un reset síncrono (synchronous reset) en el diseño, es probable que esa señal tenga un fan-out alto. Este tema se trata en una página aparte.
Sin embargo, cualquier señal que llegue a muchos elementos lógicos puede causar problemas de temporización por un fan-out alto. A veces ese fan-out alto es evidente (p. ej., señales de habilitación de reloj (clock enable)), y a veces no es tan fácil preverlo. Las herramientas de FPGA suelen ayudar enumerando las redes con mayor fan-out.
Hay dos métodos para mantener el fan-out bajo:
- Las herramientas de FPGA tienen un límite para el fan-out. Cuando se alcanza ese límite, las herramientas duplican el registro que origina la red. Es posible cambiar el valor de ese límite para cada registro mediante restricciones de síntesis. También es posible cambiar el límite global modificando los parámetros del sintetizador (synthesizer).
- Editar el código Verilog: replicar explícitamente el registro de alto fan-out en varios registros.
Claramente, ambos métodos llegan al mismo resultado: el registro con fan-out alto se replica en varios registros. Entonces, si las herramientas pueden hacerlo automáticamente (con el primer método), ¿por qué molestarse en hacerlo manualmente (como en el segundo)?
El segundo método requiere más esfuerzo, pero tiene una ventaja significativa: es posible replicar el registro de manera razonada. Ten en cuenta que el objetivo no es solo reducir el fan-out. También es importante que la salida de cada registro se distribuya a elementos lógicos situados en una región pequeña del tejido lógico. De lo contrario, los retardos de enrutado serán grandes debido a la distancia física. Así que si el código Verilog se escribe pensando en el fan-out, es posible asegurar conexiones cortas entre los elementos lógicos. Esto se demuestra con una señal de reset síncrono en otra página.
Por el contrario, si las herramientas de FPGA se encargan de replicar los registros, el resultado puede no ser tan eficiente. La mejora del retardo de enrutado depende del algoritmo que decide cómo se utiliza cada registro replicado. La calidad del resultado depende de la herramienta de FPGA utilizada.
Observa que, por defecto, cuando el sintetizador detecta dos registros que se comportan exactamente igual, los fusiona automáticamente en un solo registro. Por tanto, si un registro se replica en el código Verilog, el sintetizador sustituirá todas las réplicas por un único registro. Esto suele ocurrir incluso si los registros equivalentes están en módulos distintos. Para evitar esa fusión, hay que desactivar explícitamente esta funcionalidad. Una forma habitual de conseguirlo es con atributos de síntesis, como "dont_touch", "dont_merge" o "keep".
Idea n.º 3: Revisar el floorplanning
Por defecto, la colocación de los elementos lógicos en el tejido lógico la determinan automáticamente las herramientas de FPGA (más concretamente, el colocador (placer)). Sin embargo, es posible pedir que ciertos elementos lógicos se sitúen en áreas concretas de la FPGA. También es posible pedir que un elemento lógico concreto se coloque en una posición determinada. Este tipo de peticiones se denomina floorplanning. Estas peticiones se hacen mediante restricciones de colocación, que a menudo son comandos Tcl con una sintaxis parecida a la de las restricciones de temporización (timing constraints).
En la mayoría de los casos, las restricciones de colocación dificultan el cumplimiento de las restricciones de temporización. La primera razón es obvia: cuando las opciones del colocador están limitadas, el resultado solo puede ser peor que sin limitación. Sin embargo, hay razones más concretas:
- El floorplanning puede obligar a meter mucha lógica en una zona pequeña de la FPGA. Eso puede provocar congestión de enrutado: los elementos lógicos de esa zona necesitan más recursos de enrutado de lo habitual. Como resultado, el enrutador se ve obligado a usar recursos subóptimos. Eso produce retardos de enrutado subóptimos y, posiblemente, el incumplimiento de los requisitos.
- Las restricciones de colocación pueden obligar a que los elementos lógicos queden lejos entre sí, aunque sería mejor colocarlos cerca. Al colocador se le impide, por tanto, mover los elementos lógicos para reducir el retardo de enrutado.
- El floorplanning puede crear regiones que supongan obstáculos para el enrutado. Por ejemplo, supongamos que el floorplanning ha creado una zona densamente poblada de elementos lógicos. El enrutado de otra lógica puede verse obligado a evitar esa zona congestionada. Como resultado, ese enrutado tiene que recorrer una distancia mayor y, por tanto, el retardo de enrutado aumenta.
En la mayoría de los diseños, lo mejor es evitar el floorplanning y dar libertad al colocador para optimizar la ubicación de los elementos lógicos. Sin embargo, hay casos en los que las restricciones de colocación se usan con frecuencia, por ejemplo:
- Un bloque de IP (IP core) puede incluir restricciones de colocación para los elementos lógicos que genera. Por ejemplo, una IP que implementa una interfaz PCIe suele crear restricciones de colocación que determinan la posición de sus componentes más importantes: los transceptores, las PLL y la IP dura (hard IP) dedicada de PCIe. Este tipo de restricciones de colocación suele ser necesario y correcto.
- La FPGA puede dividirse en regiones, de modo que cada región contenga solo determinados módulos. Ese es el significado original del floorplanning. La motivación para dividir así la FPGA puede ser permitir que distintos equipos de un proyecto trabajen de forma independiente.
- La reconfiguración parcial (partial reconfiguration) es una funcionalidad que permite cargar un bitstream en la FPGA mientras está funcionando, de modo que solo se vea afectada una parte de la FPGA. Para que funcione, se necesita floorplanning: la FPGA se divide en zonas que no se tocan cuando llega el nuevo bitstream y zonas que ese bitstream actualiza.
Para el cierre de temporización, es importante ser consciente de que las restricciones de colocación pueden ser la causa de problemas. En particular, si los retardos de enrutado son mayores de lo esperado, la causa raíz puede ser la incapacidad del colocador para optimizar las posiciones de los elementos lógicos. Recuerda que el floorplanning puede tener un efecto negativo sobre caminos (paths) que no tienen relación con los elementos lógicos cuya colocación está restringida.
Idea n.º 4: Revisar las restricciones de temporización
Las restricciones de temporización son cruciales para el funcionamiento fiable de la FPGA. Por tanto, deberían verificarse antes de la implementación del proyecto. Sin embargo, a veces las restricciones acaban siendo incorrectas. Los errores pueden hacerse evidentes durante el cierre de temporización. No debería ocurrir, pero cuando ocurre, por supuesto es mejor corregir el problema.
Hay una página completa sobre cómo revisar las restricciones de temporización. Aquí solo comentaré dos errores comunes que pueden llevar a problemas con el cierre de temporización:
- Imponer innecesariamente el cumplimiento de las restricciones de temporización en caminos entre relojes no relacionados (unrelated clocks).
- Imponer innecesariamente el cumplimiento de las restricciones de temporización sobre los resets asíncronos (asynchronous reset).
Comenzando por los relojes no relacionados: el tema de los cruces de dominios de reloj (clock domain crossing) ya se ha tratado antes: hay varias razones por las que es importante que las restricciones de temporización reflejen cuáles son relojes relacionados (related clocks) y cuáles no. La razón más importante es garantizar el funcionamiento correcto de la lógica, pero el cierre de temporización también se ve afectado: si una pareja de relojes es tratada innecesariamente como relojes relacionados por las herramientas, se imponen requisitos de temporización innecesarios en los caminos entre esos dos relojes. Como resultado, las herramientas desperdician esfuerzos en esos caminos a costa de otros que sí los necesitan.
La restricción de temporización que corrige este problema se explica más adelante en esta serie de páginas.
En cuanto a los resets asíncronos: en la mayoría de los casos es necesario imponer restricciones de temporización sobre un camino que termina en un reset asíncrono. Pero a veces no hace falta. Por ejemplo, si hay garantía de que el reloj no estará activo cuando el reset pase a inactivo. Otra posibilidad es que el flip-flop que recibe el reset asíncrono tenga un mecanismo de protección frente a violaciones de temporización, igual que ocurre con un cruce de dominios de reloj. En esas situaciones, los esfuerzos de las herramientas por cumplir los requisitos de temporización no sirven para nada.
Puede ser difícil darse cuenta de que problemas de este tipo dificultan el cierre de temporización: a veces el camino crítico (critical path) no tiene nada que ver con los dos relojes tratados innecesariamente como relacionados. Si un reset asíncrono desvía los esfuerzos de las herramientas, reconocerlo es aún más difícil. En tales situaciones, intentar centrarse en el camino crítico para mejorar su temporización puede ser inútil.
Las restricciones de temporización pueden, por supuesto, estar mal de muchas otras formas. La situación descrita aquí es solo una posibilidad. Así que un problema de temporización puede ser una buena oportunidad para revisar las restricciones de temporización en general.
Idea n.º 5: Sencillamente, inténtalo otra vez
Recuerda que el proceso de colocación y enrutado (place and route) comienza repartiendo los elementos lógicos de manera bastante arbitraria sobre el tejido lógico. Luego las herramientas intentan mejorar la temporización mediante intentos repetidos. Por tanto, el éxito del proceso depende en cierta medida de la suerte. También es posible que un comportamiento ligeramente distinto del algoritmo de colocación y enrutado obtenga mejores resultados, aunque no haya una explicación lógica de por qué.
Así que si las restricciones de temporización fallan y el margen (slack) negativo es relativamente pequeño (entre el 10 y el 20% del retardo total), puede bastar con intentarlo de nuevo. Pero volver a ejecutar la implementación probablemente no servirá de nada: la mayoría del software de FPGA está diseñado para repetir con exactitud el resultado cuando se ejecuta con la misma entrada. Así que es necesario cambiar algo antes de volver a ejecutar. Ese cambio no tiene por qué estar relacionado con el camino crítico. La cuestión es evitar una repetición exacta de la implementación anterior.
Por ejemplo, en Vivado cada ejecución tiene un atributo llamado "strategy". Como su nombre indica, ese atributo controla la estrategia que aplican las herramientas durante la implementación. Cambiar ese atributo garantiza que la siguiente implementación no sea idéntica a la anterior. También es posible que otra estrategia tenga más sentido para el diseño lógico concreto.
Todas las herramientas de FPGA ofrecen posibilidades parecidas para modificar los parámetros del proceso de implementación. A menudo se puede pedir un mayor nivel de esfuerzo para alcanzar los objetivos del diseño. A veces ese mayor esfuerzo es realmente necesario, pero con frecuencia pedir más esfuerzo ayuda solo porque las herramientas hacen algo distinto.
Otra forma de evitar la repetición es hacer cambios en el código Verilog. Una vez más, el cambio no tiene por qué estar relacionado con el camino crítico. A veces basta con cambiar el nombre de un registro para obtener una implementación lo bastante distinta de la anterior.
Esta idea puede llevarse al extremo: es posible ejecutar la implementación en varios ordenadores en paralelo, cada uno con parámetros ligeramente distintos. Eso puede tener sentido cuando el precio de la FPGA es importante. En ese escenario, merece la pena dejar que los ordenadores trabajen a fondo para cumplir las restricciones de temporización en una FPGA más barata.
En resumen, este método se basa sobre todo en la suerte. Las expectativas deben ser coherentes: volver a intentarlo solo ayuda cuando las herramientas fallan de forma ocasional. Pero siempre es mejor mejorar la temporización por otras vías, si es posible.
Idea n.º 6: ¿Está la FPGA llena?
Es bastante habitual que los problemas con las restricciones de temporización comiencen cuando el nivel de ocupación de la FPGA ronda el 70%. Hay tres razones principales:
- Los elementos lógicos se empaquetan de forma más densa en el tejido lógico de la FPGA. La libertad del colocador para mejorar la temporización queda por tanto más limitada, porque es más difícil mover elementos lógicos de un sitio a otro. Eso produce mayores retardos de enrutado.
- La cantidad de cableado (recursos de enrutado) en una FPGA es limitada. Mientras la FPGA está relativamente vacía, el enrutador puede elegir el trazado más adecuado entre los elementos lógicos. Cuando se añade más lógica, las elecciones subóptimas producen retardos de enrutado mayores.
- Los elementos lógicos especializados de la FPGA pueden agotarse. Por ejemplo, la mayoría de las FPGA tienen bloques de RAM y componentes dedicados para la multiplicación aritmética. Cuando esos recursos se agotan, las herramientas implementan la funcionalidad necesaria con elementos lógicos sencillos (slices). Eso suele producir un número elevado de niveles lógicos y, en consecuencia, un mayor retardo lógico. El nivel de ocupación de la FPGA también puede empezar a crecer más rápido de lo esperado, porque se usan slices en lugar de los elementos lógicos especializados.
De las tres razones mencionadas, solo la tercera tiene algún tipo de solución: por ejemplo, puede ayudar decidir manualmente qué lógica usa los bloques de RAM de la FPGA y otros recursos similares. Aparte de eso, la única solución para una FPGA llena es elegir una más grande, aunque no siempre es posible. Por tanto, es importante prever que el cierre de temporización será más difícil a medida que se añada más lógica.
Idea n.º 7: ¿Quizá necesitas otra FPGA?
A veces no queda más remedio que admitir que la FPGA no está a la altura. Si la misma FPGA está disponible con un grado de velocidad superior, la solución puede ser decidir subir a una FPGA más rápida. Esta decisión aumenta el coste de compra, por supuesto, pero también puede tener otra consecuencia menos esperada: puede haber escasez de FPGA con el grado de velocidad superior. Aunque esas FPGA más rápidas abunden en un momento dado, las FPGA rápidas suelen ser las primeras en desaparecer del mercado cuando la demanda supera a la oferta.
Es bastante natural: las FPGA más rápidas son las que superaron mejor los tests, y siempre pueden usarse en lugar de las más lentas. A veces el fabricante no consigue producir las FPGA rápidas, y a veces hay un gran consumidor que compra todo lo que puede usar.
Así que, si trabajas en un producto destinado a fabricarse durante mucho tiempo, prefiere siempre el grado de velocidad más bajo con el que tu diseño pueda funcionar. Esto es cierto incluso aunque el dinero no sea un problema.
Un tipo de actualización completamente distinto es elegir una FPGA de una familia más reciente. O quizá elegir una FPGA de otro fabricante. Ese cambio es más drástico, pero debería considerarse si el proyecto está en sus primeras fases. Todos tendemos a enamorarnos de las herramientas y los componentes que nos resultan familiares. Pero cuando todo resulta demasiado familiar, es buen momento para mirar a nuestro alrededor en busca de alternativas.
Dicho esto, los ingenieros de FPGA con experiencia saben que elegir la FPGA más reciente y sus herramientas es una apuesta arriesgada. Pero a menudo hay una alternativa bastante consolidada que es mucho mejor que la FPGA elegida actualmente. En tal situación, es mejor salir de la zona de confort y probar algo nuevo.
Idea n.º 8: Reducir el rango de temperatura
Hay una razón por la que he puesto esta posibilidad casi al final: es la solución más fea de todas. Pero a veces no hay otra opción.
Por defecto, la aplicación de las restricciones de temporización por parte de las herramientas garantiza que la FPGA funciona de forma fiable en todo el rango de temperatura definido en la hoja de datos. Algunas herramientas de FPGA permiten elegir otro rango de temperatura para un proyecto (por ejemplo, Quartus tiene un atributo llamado "MAX_CORE_JUNCTION_TEMP" para ese fin). Eso puede usarse para indicar a las herramientas que no hace falta soportar el rango completo de temperatura.
En general, los retardos de los elementos lógicos de la FPGA aumentan a medida que sube la temperatura. Si se reduce la temperatura máxima, los valores de los retardos en los cálculos de temporización son menores. Eso hace más fácil cumplir los requisitos de tsetup. A veces es la única manera de que las herramientas cumplan las restricciones de temporización.
Es importante entender los riesgos de usar este método. En particular, hay que tener en cuenta que hablamos de la temperatura de unión. Es decir, la temperatura sobre el silicio de la FPGA, no la temperatura ambiente.
Así que cuando la temperatura máxima sea de 85 °C, no significa que la FPGA funcione dentro de un horno a esa temperatura. Esa temperatura también puede alcanzarse a temperatura ambiente (25 °C), en particular si la FPGA no tiene disipador. Siempre hay una diferencia entre la temperatura de unión y la ambiente. La magnitud de esa diferencia depende del consumo de potencia de la FPGA y de la solución de refrigeración.
Si trabajas en un producto comercial, ten en cuenta que la temperatura ambiente de la FPGA puede ser bastante más alta que la temperatura de la sala. En particular, si la FPGA está dentro de una caja con mala circulación de aire, la temperatura interior puede superar con creces la temperatura exterior. Para empeorar las cosas, se espera que la mayoría de los productos electrónicos funcionen entre 0 °C y 40 °C (aproximadamente; estas cifras cambian de un producto a otro). Así que cuando se prueba el producto final a la temperatura más alta, con la FPGA dentro de la carcasa, ¿cuál es la temperatura de unión? Esa es la pregunta que hay que hacerse. Los cálculos de temporización deben basarse en esa temperatura (o en una superior).
En otras palabras, si reduces la temperatura máxima para cumplir las restricciones de temporización y todo funciona bien en el laboratorio, eso no significa nada. Recuerda que, en lo que respecta a las restricciones de temporización, el hecho de que el diseño de la FPGA funcione en el laboratorio nunca significa nada. Pero reducir la temperatura máxima es aún peor en ese sentido. Reducir el rango de temperatura sin pensar puede ser una invitación a un problema serio: la prueba final del producto antes de la fabricación, que se hace a la temperatura más alta, puede fallar, y será imposible cumplir las restricciones de temporización si se corrige el rango. La única manera de resolver ese problema será reescribir el diseño de la FPGA desde el principio.
Así que, antes de cambiar el rango de temperatura para el cierre de temporización, asegúrate de que es seguro hacerlo: haz una evaluación rigurosa del rango de temperatura de unión en todas las condiciones de funcionamiento posibles.
Observa que es imposible ampliar el rango de temperatura más allá del valor por defecto cambiando los parámetros de implementación. El rango de temperatura por defecto de las herramientas es siempre el mismo que indica la hoja de datos. Por tanto, no es posible garantizar un funcionamiento fiable de la FPGA fuera de ese rango por defecto.
Idea n.º 9: Relojes relacionados no alineados
Esta es una situación bastante esotérica y también un poco difícil de entender. Por eso la he dejado para el final.
Supongamos que hay un cruce de dominios de reloj (clock domain crossing) entre dos relojes relacionados (related clocks) que no están alineados. En otras palabras, los dos relojes derivan del mismo reloj de referencia, pero no hay ningún mecanismo que controle la desviación de reloj (clock skew) entre ellos.
Como resultado, a las herramientas les resulta más difícil cumplir los requisitos de temporización en los caminos entre esos dos relojes. Hay dos tipos posibles de dificultad (recuerda que tsetup y thold se explicaron antes):
- Cuando el reloj llega más tarde al primer flip-flop debido a la desviación de reloj, queda menos tiempo hasta que el siguiente flanco llegue al segundo flip-flop. Por tanto, es más difícil cumplir el requisito de tsetup.
- Cuando el reloj llega antes al primer flip-flop debido a la desviación de reloj, el primer flip-flop actualiza su salida antes de que el mismo flanco haya llegado al segundo. Como resultado, puede incumplirse el requisito de thold en el segundo flip-flop. Las herramientas lo evitan haciendo el enrutado del camino artificialmente largo. Eso puede provocar el incumplimiento del requisito de tsetup. También es un desperdicio de recursos de enrutado.
Es importante señalar que, cuando las herramientas necesitan trabajar más de lo habitual para superar estas dificultades, puede ser a costa de optimizar otros caminos.
Sin embargo, los relojes relacionados no alineados no son un error, y a veces son inevitables. Esa situación solo significa que las herramientas tienen que trabajar más. Si se cumplen las restricciones de temporización, no hay ningún problema con el diseño. No obstante, conviene evitarlo siempre que sea posible con un esfuerzo razonable.
Observa que el error mencionado en la «Idea n.º 4» es distinto, aunque ambos errores lo ponen innecesariamente difícil a las herramientas y ambos están relacionados con el cruce de dominios de reloj.
La mejor solución para una situación con relojes relacionados no alineados es alinear esos relojes. Normalmente se hace añadiendo una PLL o añadiendo una salida de reloj a una PLL existente. El objetivo es que los dos relojes en cuestión sean salidas de la misma PLL.
Otra solución posible es tratar los relojes como no relacionados (unrelated clocks). Eso exige un cambio tanto en el diseño lógico como en las restricciones de temporización. Puede merecer la pena si no resulta demasiado difícil. Más sobre este tema más adelante.
Veremos ahora un ejemplo de un cruce de dominios de reloj entre relojes relacionados no alineados.
wire pll_clk;
reg [24:0] result;
reg [11:0] x, y, x1, y1;
clk_wiz_0 pll_i
(.clk_in1(clk),
.clk_out1(pll_clk));
always @(posedge clk)
begin
x1 <= x;
y1 <= y;
end
always @(posedge pll_clk)
result <= x1 * y1;
Observa que hay una PLL (clk_wiz_0). Esta PLL usa @clk como reloj de referencia, y @clk tiene una frecuencia de 250 MHz. Es la misma PLL que se mostró en el ejemplo de la parte superior de una página anterior. La frecuencia de @pll_clk es de 125 MHz.
La parte importante de este ejemplo es el cruce de dominios de reloj entre dos relojes relacionados (@clk y @pll_clk). Como solo @pll_clk lo genera la PLL, estos dos relojes no están alineados. Por tanto, hay una desviación de reloj (clock skew) en los caminos hacia @result (desde @x1 y @y1). A pesar de esa desviación, los relojes siguen siendo relojes relacionados, y las herramientas intentarán cumplir los requisitos de temporización.
Si la única razón para usar @clk es la necesidad de un reloj a 250 MHz, la solución correcta es generar otro reloj con la PLL. No es un desperdicio de recursos producir un reloj con la misma frecuencia que el de referencia. Al contrario, hacerlo ahorra mucho esfuerzo a las herramientas. Solo hay una buena razón para usar @clk directamente como se muestra en el ejemplo: cuando la lógica que usa @clk debe funcionar antes de que la PLL genere relojes utilizables.
El informe de temporización generado por Vivado es el siguiente. En este caso concreto, solo hubo un problema con el requisito de tsetup.
Slack (VIOLATED) : -1.456ns (required time - arrival time) Source: x1_reg[7]/C (rising edge-triggered cell FDRE clocked by clk {rise@0.000ns fall@2.000ns period=4.000ns}) Destination: result_reg/DSP_OUTPUT_INST/ALU_OUT[10] (rising edge-triggered cell DSP_OUTPUT 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: 4.000ns (clk_out1_clk_wiz_0 rise@8.000ns - clk rise@4.000ns) Data Path Delay: 3.012ns (logic 2.677ns (88.878%) route 0.335ns (11.122%)) Logic Levels: 5 (DSP_A_B_DATA=1 DSP_ALU=1 DSP_M_DATA=1 DSP_MULTIPLIER=1 DSP_PREADD_DATA=1) Clock Path Skew: -2.192ns (DCD - SCD + CPR) Destination Clock Delay (DCD): 0.998ns = ( 8.998 - 8.000 ) Source Clock Delay (SCD): 3.202ns = ( 7.202 - 4.000 ) Clock Pessimism Removal (CPR): 0.012ns Clock Uncertainty: 0.148ns ((TSJ^2 + DJ^2)^1/2) / 2 + PE Total System Jitter (TSJ): 0.071ns Discrete Jitter (DJ): 0.103ns Phase Error (PE): 0.086ns Clock Net Delay (Source): 1.414ns (routing 0.002ns, distribution 1.412ns) Clock Net Delay (Destination): 1.184ns (routing 0.002ns, distribution 1.182ns) Clock Domain Crossing: Inter clock paths are considered valid unless explicitly excluded by timing constraints such as set_clock_groups or set_false_path. Location Delay type Incr(ns) Path(ns) Netlist Resource(s) ------------------------------------------------------------------- ------------------- (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.738 4.738 r clk_IBUF_inst/INBUF_INST/O net (fo=1, routed) 0.105 4.843 clk_IBUF_inst/OUT AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O) 0.049 4.892 r clk_IBUF_inst/IBUFCTRL_INST/O net (fo=1, routed) 0.795 5.687 clk_IBUF BUFGCE_X1Y2 BUFGCE (Prop_BUFCE_BUFGCE_I_O) 0.101 5.788 r clk_IBUF_BUFG_inst/O X2Y0 (CLOCK_ROOT) net (fo=62, routed) 1.414 7.202 clk_IBUF_BUFGCE SLICE_X52Y45 FDRE r x1_reg[7]/C ------------------------------------------------------------------- ------------------- SLICE_X52Y45 FDRE (Prop_HFF_SLICEM_C_Q) 0.138 7.340 f x1_reg[7]/Q net (fo=1, routed) 0.335 7.675 result_reg/A[7] DSP48E2_X8Y18 DSP_A_B_DATA (Prop_DSP_A_B_DATA_DSP48E2_A[7]_A2_DATA[7]) 0.396 8.071 r result_reg/DSP_A_B_DATA_INST/A2_DATA[7] net (fo=1, routed) 0.000 8.071 result_reg/DSP_A_B_DATA.A2_DATA<7> DSP48E2_X8Y18 DSP_PREADD_DATA (Prop_DSP_PREADD_DATA_DSP48E2_A2_DATA[7]_A2A1[7]) 0.182 8.253 r result_reg/DSP_PREADD_DATA_INST/A2A1[7] net (fo=1, routed) 0.000 8.253 result_reg/DSP_PREADD_DATA.A2A1<7> DSP48E2_X8Y18 DSP_MULTIPLIER (Prop_DSP_MULTIPLIER_DSP48E2_A2A1[7]_U[10]) 0.994 9.247 f result_reg/DSP_MULTIPLIER_INST/U[10] net (fo=1, routed) 0.000 9.247 result_reg/DSP_MULTIPLIER.U<10> DSP48E2_X8Y18 DSP_M_DATA (Prop_DSP_M_DATA_DSP48E2_U[10]_U_DATA[10]) 0.164 9.411 r result_reg/DSP_M_DATA_INST/U_DATA[10] net (fo=1, routed) 0.000 9.411 result_reg/DSP_M_DATA.U_DATA<10> DSP48E2_X8Y18 DSP_ALU (Prop_DSP_ALU_DSP48E2_U_DATA[10]_ALU_OUT[10]) 0.803 10.214 r result_reg/DSP_ALU_INST/ALU_OUT[10] net (fo=1, routed) 0.000 10.214 result_reg/DSP_ALU.ALU_OUT<10> DSP48E2_X8Y18 DSP_OUTPUT r result_reg/DSP_OUTPUT_INST/ALU_OUT[10] ------------------------------------------------------------------- ------------------- (clock clk_out1_clk_wiz_0 rise edge) 8.000 8.000 r BUFGCE_X1Y2 BUFGCE 0.000 8.000 r clk_IBUF_BUFG_inst/O net (fo=62, routed) 1.078 9.078 pll_i/inst/clk_in1 MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0) -1.777 7.301 r pll_i/inst/mmcme3_adv_inst/CLKOUT0 net (fo=1, routed) 0.422 7.723 pll_i/inst/clk_out1_clk_wiz_0 BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O) 0.091 7.814 r pll_i/inst/clkout1_buf/O X2Y0 (CLOCK_ROOT) net (fo=6, routed) 1.184 8.998 result_reg/CLK DSP48E2_X8Y18 DSP_OUTPUT r result_reg/DSP_OUTPUT_INST/CLK clock pessimism 0.012 9.010 clock uncertainty -0.148 8.862 DSP48E2_X8Y18 DSP_OUTPUT (Setup_DSP_OUTPUT_DSP48E2_CLK_ALU_OUT[10]) -0.104 8.758 result_reg/DSP_OUTPUT_INST ------------------------------------------------------------------- required time 8.758 arrival time -10.214 ------------------------------------------------------------------- slack -1.456
Este informe muestra que las herramientas no consiguieron cumplir las restricciones de temporización. El camino mostrado comienza en el flanco de subida de @clk en 4 ns y termina en el flanco de subida de @pll_clk en 8 ns. El problema es el tiempo que tarda el reloj en el pin de entrada en llegar al pin de reloj del primer flip-flop: 3,2 ns. El tiempo de llegada de ese flanco es, por tanto, de 7,2 ns.
Pero @pll_clk lo genera la PLL, así que ese reloj está alineado con el pin de entrada de @clk. El retardo es, por tanto, de solo 1,0 ns. El tiempo de llegada de @pll_clk al segundo flip-flop es, pues, de 9,0 ns. Así que el tiempo que queda para el camino de datos es de 9,0 – 7,2 = 1,8 ns (aproximadamente, por la incertidumbre del reloj y otros factores). No es suficiente para una multiplicación aritmética, ni siquiera usando la unidad aritmética específica. Por tanto, no se pudieron cumplir los requisitos de temporización.
Así que en este ejemplo el reloj llega tarde al primer flip-flop debido a la desviación de reloj. Eso produce el incumplimiento del requisito de tsetup.
Observa que esto puede resolverse manipulando la alineación de @pll_clk. Por ejemplo, el reloj de referencia de la PLL puede ser la salida del buffer global de reloj que distribuye @clk. También es posible definir el desfase de la PLL para conseguir una mejor alineación. Sin embargo, son soluciones que solo deberían usarse como último recurso.
Resumen
Una vez más, estas eran solo algunas ideas que podrían ayudar a resolver un problema de cierre de temporización. Desgraciadamente, resolver un problema de este tipo puede exigir mucho más. De hecho, no hay ningún tema en el campo de las FPGA que no esté relacionado de algún modo con el cierre de temporización.
Como ya se ha dicho, la mejor estrategia es escribir el diseño lógico con cuidado desde el principio. La mejor manera de abordar el cierre de temporización es evitarlo.
Hasta este punto, esta serie de páginas ha tratado la temporización, pero no ha dicho mucho sobre las restricciones de temporización. Eso está a punto de cambiar: a partir de la página siguiente, la discusión se vuelve más técnica.