Esta página pertenece a una serie de páginas sobre temporización (timing). Las páginas anteriores explicaron la teoría de los cálculos de temporización, mostraron cómo escribir varias restricciones de temporización (timing constraints) y analizaron los principios del cierre de temporización (timing closure). La página anterior explicó algunos principios fundamentales sobre las restricciones de temporización de E/S. Esta página continúa con los aspectos prácticos de este tema.
Introducción
El propósito de las restricciones de temporización de E/S es garantizar una interfaz fiable con el exterior: aseguran que cada señal procedente del exterior llegue de forma fiable al flip-flop correspondiente de la FPGA. Del mismo modo, también garantizan que cada señal que sale de la FPGA hacia el exterior llegue de forma fiable al flip-flop del componente externo.
Las restricciones de temporización de E/S son el tipo más difícil de restricciones de temporización: algunos de los parámetros de temporización dependen de los componentes electrónicos externos de la placa. Normalmente es necesario leer las hojas de datos (datasheets) de esos componentes externos para determinar los requisitos de temporización correctos. A menudo se necesita un cálculo con lápiz y papel para obtener la restricción correcta.
Resulta tentador saltarse esta tarea complicada y preferir una alternativa sencilla: la prueba y el error. Este atajo consiste en dejar que las herramientas hagan lo que quieran y ver si funciona. Si hay un problema con un puerto de entrada, usa el flanco de reloj opuesto en el flip-flop que recibe la señal. Del mismo modo, si una salida no funciona bien, usa el flanco opuesto en el flip-flop de salida. Esta vía suele hacer que la electrónica funcione rápida y sencillamente.
El problema de este enfoque es que el comportamiento temporal cambia con la temperatura. También hay incertidumbres debidas al proceso de fabricación de los componentes semiconductores. Esto es cierto tanto para la FPGA como para la electrónica externa. Así que unas restricciones de temporización inadecuadas pueden llevar a un «modo magia negra» (Black Magic Mode). Esto es cierto para todas las restricciones de temporización, pero ocurre más con las de E/S.
Lo peor de saltarse los cálculos con lápiz y papel es que a veces resulta imposible garantizar los requisitos de temporización por culpa del diseño de la placa. Si esta situación se descubre durante el proceso de diseño de la placa, suele haber una solución sencilla (cambiar el cableado hacia la FPGA o replantear la distribución de los relojes). Pero si un defecto de este tipo se descubre después de fabricar la PCB, puede que no haya forma de arreglarlo. En otras palabras, se vuelve imposible garantizar que la electrónica funcione de forma fiable.
Qué contiene esta página
Esta página esboza las restricciones de temporización básicas para los puertos de E/S. La sintaxis que se muestra aquí es la SDC, utilizada por Vivado y Quartus, así como por otras herramientas de FPGA.
La página comienza con las restricciones de temporización dedicadas a la E/S: set_input_delay y set_output_delay. Se explica el significado de estas restricciones. A continuación se hace referencia a dos páginas aparte que muestran ejemplos de informes de temporización de Vivado y Quartus.
También es posible definir restricciones de temporización con set_max_delay y set_min_delay. Estos comandos son más adecuados en algunos escenarios. Ya los hemos visto en relación con los caminos internos de la FPGA. Su significado como restricciones de temporización de E/S también se explica a continuación.
Esta página trata únicamente los aspectos técnicos de estas restricciones de temporización de E/S. Para la parte teórica, consulta la página anterior, que también muestra cómo definir caminos falsos (false paths) para puertos de E/S.
Significado de set_input_delay y set_output_delay
Estos dos comandos son adecuados cuando la interfaz con el componente externo es síncrona del sistema (system synchronous). En resumen,
- set_input_delay -clock … -max … : el retardo de reloj a salida (clock-to-output) máximo del componente externo conectado al puerto de entrada, más el retardo de la pista de la placa.
- set_input_delay -clock … -min … : el retardo de reloj a salida mínimo del componente externo conectado al puerto de entrada. Si la hoja de datos no ofrece esta información, elige cero (por si acaso una futura revisión del componente se fabrica con un proceso realmente rápido).
- set_output_delay -clock … -max … : el tsu del componente externo que recibe la señal de salida, más el retardo de la pista de la placa.
- set_output_delay -clock … -min … : –thold del componente externo que recibe la señal de salida. Observa el signo menos. Por ejemplo, define esta restricción como -1 si el tiempo de hold es de 1 ns.
Es importante señalar que estas definiciones solo son correctas si se cumplen estas dos condiciones:
- La interfaz es síncrona del sistema.
- El comando create_clock que define el reloj se refiere a la señal de reloj con un comando get_ports (en lugar de depender de otra señal interna de la FPGA con, por ejemplo, get_pins).
Estas dos condiciones son necesarias para asegurar que los retardos del reloj se calculan correctamente.
Observa también que si no se usa ni -min ni -max, el comando se interpreta como si hubiera dos comandos: uno con el atributo -min y otro con el atributo -max. Probablemente eso no es lo que quieres.
La definición de estos comandos es un poco confusa: set_input_delay define cuándo se permite que la señal de datos cambie de valor después de un flanco del reloj. Pero set_output_delay define cuándo se permite un flanco del reloj después de que la señal de datos haya cambiado de valor. Presumiblemente, la razón de estas definiciones es que los números de la hoja de datos pueden usarse directamente en las restricciones.
Los comandos set_input_delay y set_output_delay tienen varias opciones que no se tratan aquí. En particular, se puede elegir el flanco de bajada del reloj como referencia temporal. Consulta la documentación de las herramientas para más información.
Usa siempre min y max
Puede parecer inútil insistir en usar tanto -min como -max en cada restricción. Por ejemplo, si el tsetup del componente externo es de 8 ns, ¿qué tiene de malo esto?
set_output_delay -clock theclk 8 [get_ports test_out]
Esto define correctamente el tiempo de setup (setup time). En cuanto al tiempo de hold (hold time), queda definido sin querer como –8 ns. Eso permite que el puerto de salida cambie su valor 8 ns antes del reloj. Pero, ¿a quién le importa? Eso no podría ocurrir, ¿verdad?
Bueno, en realidad sí puede. Ya he comentado antes el uso de una PLL para generar el reloj interno, a partir de un reloj de un pin de entrada (es decir, el reloj visible en la placa). Esto permite que la PLL alinee el reloj interno de la FPGA con el reloj de entrada. La PLL lo hace moviendo (desplazando) ligeramente el reloj para compensar el retardo de la red de distribución del reloj.
De hecho, las herramientas de FPGA pueden sentirse libres de adelantar ligeramente el reloj respecto al reloj de la placa para cumplir una restricción: si el reloj interno de la FPGA se adelanta respecto al reloj externo, el retardo de reloj a salida que percibe el componente externo se hace menor. Esto se debe a que el flip-flop de la FPGA es síncrono con el reloj interno, pero la temporización visible es relativa al reloj externo.
Pero cuando el reloj interno de la FPGA es más temprano que el reloj de la placa, la salida de la FPGA puede cambiar antes del flanco del reloj externo. Eso puede provocar una violación del tiempo de hold en el componente que recibe esas salidas.
Si el comando set_output_delay define el tiempo de hold como –8 ns, no significa que la salida vaya a cambiar su valor 8 ns antes del reloj. Pero eso permite a las herramientas mover el reloj interno de una manera que viola el requisito de thold. Usar set_output_delay con -min evita correctamente que esto ocurra.
Ajustes debidos al retardo de las pistas
Es importante recordar que las herramientas no tienen en cuenta el retardo de las pistas de la PCB. Las herramientas no disponen de esa información. Por tanto, suponen que ese retardo es cero cuando hacen los cálculos de temporización para set_input_delay y set_output_delay. La corrección consiste en añadir el retardo de la pista a los valores de retardo de reloj a salida y de tsu que indica la hoja de datos.
También puede ser necesario tener en cuenta la desviación del reloj (clock skew): en una PCB perfecta, el reloj llega a todos los componentes con el mismo retardo. En la vida real, puede haber una desviación del reloj entre la FPGA y el componente externo. Esa desviación no se tiene en cuenta en los cálculos de temporización de las herramientas.
Por tanto, si el reloj llega antes a la FPGA (en relación con el componente externo), son necesarias las siguientes correcciones:
- Para set_input_delay -clock … -max … : suma la desviación del reloj al valor de retardo del comando (es similar a un mayor retardo de reloj a salida del componente externo).
- Para set_output_delay -clock … -min … : resta la desviación del reloj del valor de retardo del comando, es decir, hazlo más negativo (es similar a un mayor thold del componente externo).
Del mismo modo, si el reloj llega más tarde a la FPGA, son necesarias las siguientes correcciones:
- Para set_input_delay -clock … -min … : resta la desviación del reloj del valor de retardo del comando (es similar a un menor retardo de reloj a salida del componente externo). Si la hoja de datos no indica ningún valor mínimo de retardo de reloj a salida, usa el negativo de la desviación del reloj como valor de este comando.
- Para set_output_delay -clock … -max … : suma la desviación del reloj al valor de retardo del comando (es similar a un mayor retardo de pista de la PCB).
Observa que el informe de temporización puede mostrar una desviación del reloj distinta de cero, independientemente de los ajustes descritos aquí. Sin embargo, la desviación que aparece en el informe se refiere a los retardos del reloj dentro de la FPGA, y no a los de la PCB.
Ejemplos de informes de temporización
Los ejemplos se basan en el siguiente código Verilog:
module top(
input test_clk,
input test_in,
output reg test_out
);
reg test_samp;
always @(posedge test_clk)
begin
test_samp <= test_in;
test_out <= test_samp;
end
endmodule
@test_clk es el reloj de entrada, @test_in es un pin de entrada y @test_out es un pin de salida. Observa que no se usa ninguna PLL para alinear el reloj interno con el reloj de la placa, así que hay un retardo de reloj significativo.
Las restricciones de temporización son las siguientes:
create_clock -name theclk -period 20 [get_ports test_clk] set_output_delay -clock theclk -max 8 [get_ports test_out] set_output_delay -clock theclk -min -3 [get_ports test_out] set_input_delay -clock theclk -max 4 [get_ports test_in] set_input_delay -clock theclk -min 2 [get_ports test_in]
Como los informes de temporización son bastante largos, se muestran en páginas aparte:
- Haz clic aquí para ver el análisis de temporización de Quartus
- Haz clic aquí para ver el análisis de temporización de Vivado
Uso de set_max_delay y set_min_delay
Cuando la interfaz con el componente externo es síncrona de fuente (source synchronous), el uso de set_input_delay y set_output_delay resulta menos natural. set_max_delay y set_min_delay son más adecuados para esta situación. En una página anterior, estos dos comandos se mencionaron solo como complementos o ajustes (excepciones de temporización) a las restricciones de periodo de reloj. Todos los caminos (paths) eran internos: comenzaban y terminaban en un elemento secuencial. Cuando estos comandos se usan como restricciones de temporización de E/S, o bien el inicio o bien el final del camino es un puerto de E/S. ¿Cómo se hace el análisis de tiempos en esta situación?
La verdad es que a menudo no merece la pena profundizar en el análisis de tiempos de estos comandos: su propósito es normalmente limitar el comportamiento de la herramienta escribiendo restricciones que las herramientas apenas pueden cumplir. Por tanto, los números de esas restricciones se encuentran por tanteo, intentando hacerlas cada vez más estrictas. Con esta metodología, el propio análisis de tiempos no tiene importancia.
Dicho esto, sigue siendo buena idea entender los cálculos que hay detrás de set_max_delay y set_min_delay:
Recuerda de antes que un análisis de tiempos tiene dos partes: la primera es el camino de origen (source path): calcula el tiempo desde un flanco del reloj (en el pin de reloj externo) hasta que hay un valor actualizado y válido en la entrada de datos del segundo flip-flop. Esta parte es la suma de tres elementos:
- El tiempo que tarda el flanco en llegar al primer flip-flop (el camino del reloj)
- El tiempo que tarda ese flip-flop en actualizar su valor
- El tiempo que tarda ese nuevo valor en llegar al segundo flip-flop
La segunda parte es el camino de destino (destination path), que consiste únicamente en el tiempo que tarda el flanco del reloj en llegar al segundo flip-flop. Ya sabemos cuándo se actualiza la entrada de ese flip-flop (gracias al camino de origen), así que la diferencia de tiempos puede compararse con el tsu o el thold exigidos, según corresponda.
Pero eso era con dos elementos secuenciales. ¿Qué ocurre cuando uno de los lados es un puerto de E/S? Para el análisis de tiempos, el puerto se trata como si fuera un flip-flop imaginario. El retardo del camino del reloj hasta ese flip-flop es cero.
Consideremos la situación habitual, en la que el reloj se define con un comando create_clock que se apoya en get_ports (como en casi todos mis ejemplos). Un retardo de camino del reloj igual a cero significa que la entrada de reloj de ese flip-flop imaginario está conectada directamente al pin de reloj. Por tanto, no hay retardo entre el pin de reloj y ese flip-flop imaginario.
Todos los parámetros de temporización de este flip-flop son cero: el tsu, el thold y el retardo de reloj a salida. Eso no refleja ningún componente electrónico realista, pero da significado a set_max_delay y set_min_delay cuando se usan con un puerto de salida: el retardo de reloj a salida del puerto. Por ejemplo:
set_max_delay -to [get_ports test_out] 7 set_min_delay -to [get_ports test_out] 0
Estas dos restricciones exigen que el retardo de reloj a salida de @test_out esté entre 0 ns y 7 ns.
Expliquemos por qué: Recuerda que normalmente un comando set_max_delay es similar a una restricción de periodo para caminos entre flip-flops concretos. Entonces, ¿qué ocurre con el camino del reloj de destino? El cálculo comienza en el instante del segundo flanco, es decir, en 7 ns. Pero el retardo del camino del reloj hasta el segundo flip-flop es cero, y el tsu de ese flip-flop también es cero. Así que el resultado del cálculo del camino del reloj de destino es simplemente 7 ns. Ese es el máximo permitido para el camino de origen, que se calcula como siempre: el camino del reloj de origen más el camino de datos. En resumen, el requisito es que la salida de datos sea válida 7 ns después del primer flanco. Esto es exactamente la definición del retardo de reloj a salida del puerto de salida. Si el comando create_clock del reloj correspondiente se basó en get_ports, este retardo de reloj a salida es relativo al reloj de la PCB.
Ver el ejemplo de informes de temporización con Vivado.
Observa que set_output_delay se relaciona con el tsu o el thold del componente externo. set_max_delay define el retardo de reloj a salida del puerto de salida de la FPGA. Así que la principal diferencia entre estas dos opciones es dónde está el foco.
En cuanto a un puerto de entrada, no hay una explicación intuitiva de lo que significan set_max_delay y set_min_delay: el camino de origen consiste en el retardo entre el pin de entrada y la entrada de datos del flip-flop que recibe la señal. El camino del reloj de destino comienza en el instante especificado en el comando de la restricción. A ese instante se le suma el retardo del camino del reloj. Son cálculos sin mucho significado (ver los informes de temporización). Es más natural usar set_input_delay, que se relaciona con el retardo de reloj a salida del componente externo.
Observa que el análisis de tiempos que se hace en nombre de set_max_delay y set_min_delay no depende del periodo del reloj. Por tanto, si cambia la frecuencia del reloj, se usan los mismos números mientras las herramientas aplican estas restricciones. En cambio, los cálculos para set_input_delay y set_output_delay dependen de la frecuencia del reloj.
Que las restricciones de temporización de E/S dependan de la frecuencia del reloj puede ser una ventaja o un inconveniente, según las circunstancias. Si las restricciones están escritas basándose en los parámetros de temporización del componente externo (y la interfaz es síncrona del sistema), probablemente sea mejor apoyarse en set_input_delay y set_output_delay: estas restricciones seguirán siendo correctas aunque cambie la frecuencia del reloj. Sin embargo, cuando la intención de las restricciones es obligar a las herramientas a tomar ciertas decisiones (por ejemplo, usar registros IOB), es más probable que set_max_delay y set_min_delay sean adecuados.
Uso de -datapath_only
Una posible motivación para una restricción de temporización es asegurarse de que las herramientas hagan lo que sea necesario para lograr el menor retardo posible hacia o desde el puerto de E/S. Normalmente esto significa usar el registro IOB. También puede significar evitar la inserción de un retardo adicional entre un puerto de entrada y el flip-flop (las herramientas pueden hacerlo para cumplir el requisito de thold con más margen).
Cuando se usa una restricción con este fin, no hay un retardo concreto que sea el objetivo. La idea es impedir que las herramientas hagan algo que no sea lograr el mejor resultado posible. Si las herramientas de FPGA soportan -datapath_only, es mejor usar set_max_delay con esa opción. Así se elimina por completo de los cálculos el camino del reloj, de modo que solo se tiene en cuenta el retardo entre el puerto de E/S y el flip-flop. De esta manera, el requisito de la restricción se corresponde exactamente con su propósito: controlar el retardo entre el flip-flop y el pin de E/S.
Este es un ejemplo sencillo para Vivado:
set_max_delay -datapath_only -from [get_ports test_in] 2 set_max_delay -datapath_only -from [all_registers] \ -to [get_ports test_out] 3
Pero ¿cuál es el propósito de la parte que dice «-from [all_registers]»? ¿Por qué hace falta un «-from»? La respuesta corta es que Vivado se negaba a aceptar este comando sin una parte «-from». No había un requisito similar en el comando relativo al puerto de entrada.
Los informes de temporización con datapath_only están al final de la página con los ejemplos.
Resumen
set_input_delay y set_output_delay suelen considerarse los comandos preferidos para las restricciones de temporización de E/S. En efecto, normalmente es la elección correcta cuando la interfaz es síncrona del sistema. En otros escenarios puede merecer la pena plantearse usar set_max_delay y set_min_delay en su lugar, ya que pueden reflejar mejor las limitaciones exigidas a la temporización del puerto de E/S.
Esta página concluye esta serie de páginas sobre temporización. Pero hay una página final que resume muchos de los temas de una forma cómoda para inspeccionar un diseño existente.