Esta página es la primera de tres en una serie sobre dominios de reloj.
Introducción
Salvo en diseños FPGA muy triviales, lo normal es que los elementos síncronos (flip-flops, RAMs de bloque, registros de desplazamiento, etc.) trabajen con más de un reloj. Aun así, la mayoría de las unidades funcionales dentro de un diseño lógico se basa en un solo reloj, así que, casi siempre, el tema de los múltiples relojes no exige atención especial. Las cosas se complican cuando se conecta lógica basada en un reloj con lógica que depende de otro. Esta serie de páginas explica cómo trabajar con varios relojes en un diseño.
Al conectar lógica que depende de relojes distintos, la primera y más importante pregunta es si hace falta lógica de resincronización. Eso equivale a preguntarse si los dos relojes son relojes relacionados (related clocks) o no. Esta página explica esta cuestión y cómo resolverla, así como otros temas relacionados. Buena parte de la discusión tiene que ver con las restricciones de temporización (timing constraints) y con la posibilidad de aplicarlas a ciertos elementos lógicos. Por esa razón, empezaré con un breve repaso de la temporización.
El análisis de esta serie de páginas se limita a flip-flops disparados por flanco positivo, es decir, flip-flops que reaccionan a los flancos de subida de su entrada de reloj. Naturalmente, existen también otros elementos síncronos, o sea, elementos lógicos que muestrean y generan señales a partir de un reloj: registros de desplazamiento, RAMs de bloque y muchos otros. Algunos de esos elementos pueden reaccionar al flanco de bajada, o incluso a ambos. Para simplificar, aquí haré caso omiso de todo eso.
Además, usaré la expresión «X es síncrono con Y» para expresar que la entrada de reloj del elemento síncrono X está conectada al reloj Y. Por tanto, ese elemento síncrono muestrea su entrada o sus entradas con ese reloj, y también actualiza sus salidas con él.
Brevemente sobre los fundamentos de la temporización
Este es un breve resumen de la teoría de la temporización. Hay una explicación más detallada en una página aparte.
Consideremos el siguiente dibujo, que muestra el camino de la señal (path) desde un flip-flop hasta otro a través de una LUT:
La LUT (Look-Up Table) de este dibujo representa cualquier grupo de lógica combinacional (combinatorial logic): cualquier cambio en I1, I2, I3 o I4 produce un cambio inmediato en la salida O, con una cierta cantidad de retardo de propagación (propagation delay).
El camino de la señal es, por tanto, el siguiente: la salida Q del flip-flop de la izquierda, etiquetada como @foo, cambia después de un flanco de subida en su reloj de entrada, @clk1. Como esa salida está conectada a I1, la salida O de la LUT puede cambiar tras un pequeño retardo. Esa señal llega a la entrada de datos D del flip-flop de la derecha. Tras un flanco de subida en @clk2, el flip-flop copia D en Q (que está etiquetada como @bar).
Pero no es tan sencillo. Todos los flip-flops tienen requisitos de temporización: la entrada de datos (D) debe permanecer estable durante un tiempo tsu (el tiempo de setup) antes del flanco de subida de @clk2, y debe seguir estable durante un tiempo thold (el tiempo de hold) después de ese flanco.
La gran mayoría de los caminos de señal en una FPGA están asociados a un solo reloj, de modo que @clk1 y @clk2 son exactamente la misma señal de reloj (vamos a ignorar la desviación de reloj, clock skew). Para esos caminos es posible calcular si esos dos requisitos de temporización pueden garantizarse.
Por ejemplo, fijémonos en el dibujo anterior y en el requisito de tsu: la idea es averiguar cuánto tiempo transcurre desde el flanco de subida de @clk1 hasta que la entrada D del flip-flop de la derecha queda actualizada y estable. Ese tiempo es la suma de todos los retardos del camino: empieza con el retardo desde el flanco de subida de @clk1 hasta que @foo se actualiza (lo que se llama «clock to output»), y continúa con todos los retardos hasta la entrada D del flip-flop de la derecha. Eso incluye el retardo de la LUT, además de los retardos del cableado entre los elementos lógicos (retardos de enrutado).
Como @clk1 y @clk2 son el mismo reloj, sabemos cuándo llega el siguiente flanco de subida a ambos flip-flops: exactamente un periodo de reloj después (por ejemplo, 10 ns para un reloj de 100 MHz). Por tanto, el retardo total del camino debe ser menor que el periodo del reloj, dejando un margen de tsu. Si eso puede garantizarse, se cumple el requisito de tsu: que la entrada D del flip-flop de la derecha esté estable con un margen de tiempo (tsu) antes del flanco de subida de @clk2. Consulta la página sobre teoría de la temporización para ver un ejemplo con números.
Se puede hacer un cálculo parecido para thold. A diferencia de tsu, el requisito es que la entrada D del flip-flop de la derecha permanezca estable durante un cierto tiempo (thold) después de un flanco de subida de @clk2. Por tanto, el periodo del reloj es irrelevante y no se tiene en cuenta (cuando @clk1 y @clk2 son el mismo). Lo que importa es lo que ocurre en el mismo ciclo de reloj, no en el siguiente.
Nótese que este tipo de cálculos solo es posible porque conocemos la diferencia de tiempo entre el flanco de subida de @clk1 y el flanco de subida de @clk2. En particular, si @clk1 y @clk2 son el mismo reloj, esa diferencia de tiempo que se usa para calcular tsu es el periodo de ese reloj. Pero si la diferencia de tiempo entre esos dos relojes es desconocida, es imposible garantizar tanto tsu como thold.
Tanto tsu como thold se aplican a todos los flip-flops. Eso incluye, por supuesto, a todos los flip-flops de la FPGA, pero también a los flip-flops de dispositivos externos (por ejemplo, desde un pin de salida de la FPGA hasta un dispositivo externo que muestrea la señal con un reloj). Así pues, por cada flip-flop del sistema hay que preguntarse si tenemos garantizado que esos dos requisitos se cumplen. Para los flip-flops en los que no se pueda garantizar, hay que disponer de un mecanismo que asegure un funcionamiento fiable a pesar de ello (es decir, lógica de resincronización). En pocas palabras, de eso trata el cruce de dominios de reloj.
Entonces, otra vez: ¿cuándo se garantiza la temporización?
Por si te has perdido en la sección anterior, estos son los puntos principales:
Recuerda que un diseño FPGA siempre incluye restricciones de temporización (timing constraints), que informan a las herramientas de diseño sobre las frecuencias de los relojes. A partir de esa información, las herramientas se aseguran de que se cumplan los dos requisitos de temporización (tsu y thold). O bien, si las herramientas no consiguen cumplirlos, informan de ese fallo.
Como se ha mencionado, solo es posible garantizar tsu y thold cuando se conoce la diferencia de tiempo entre los flancos de subida del reloj al principio del camino y el reloj al final del camino. Eso ocurre la mayoría de las veces, porque una gran parte del diseño lógico consiste en registros que dependen de otros registros síncronos con el mismo reloj.
¿Y si se usan dos relojes distintos? ¿Y si al flip-flop del principio del camino le llega un reloj, y al flip-flop del final le llega otro? Dicho de otro modo, ¿qué ocurre si @clk1 no es lo mismo que @clk2? ¿Sigue siendo posible hacer el cálculo de temporización y garantizar los requisitos? Bueno, depende. De eso trata el resto de esta página.
Dominios de reloj y cruce entre dominios de reloj
Un dominio de reloj (clock domain) está formado por todos los elementos síncronos (flip-flops y demás) que son síncronos con una determinada señal de reloj.
Considera este sencillo fragmento de código Verilog:
reg foo, bar;
always @(posedge clk1)
foo <= !foo;
always @(posedge clk2)
bar <= foo;
En este ejemplo, @foo es síncrono con @clk1, y @bar es síncrono con @clk2. Así que está claro que @foo y @bar pertenecen a dominios de reloj distintos (los de @clk1 y @clk2, respectivamente).
Con @foo no hay nada de qué preocuparse: solo depende de sí mismo. Sin embargo, @bar es síncrono con @clk2 y depende de @foo, que es síncrono con @clk1. Entonces, ¿se puede usar @bar como cualquier otro registro? ¿Podemos dar por hecho que @bar toma siempre el valor de @foo con una temporización válida, de modo que su comportamiento sea conocido y repetible?
Antes de intentar responder a esta pregunta, pongamos nombre a lo que acaba de ocurrir. Esto es un cruce de dominios de reloj (clock domain crossing): @foo y @bar se implementan como dos flip-flops síncronos con relojes distintos. Por tanto, el camino de @foo a @bar va de un dominio de reloj a otro.
En términos más generales, un cruce de dominios de reloj se produce cuando la salida de un elemento síncrono que pertenece a un dominio de reloj acaba en la entrada de un elemento síncrono que pertenece a otro dominio de reloj. A menudo hay lógica combinacional entre esos dos elementos síncronos.
Así que aunque @bar se hubiera definido como
always @(posedge clk2)
bar <= !foo || !bar;
seguiría habiendo un cruce de dominios de reloj. En ese caso, el cruce empezaría en @foo, pasaría por la LUT que implementa la función lógica y terminaría en @bar, igual que en el dibujo anterior.
Relojes relacionados frente a relojes no relacionados
El término relojes relacionados (related clocks) se refiere a relojes que se obtienen a partir de la misma señal de reloj de referencia de una manera que asegura diferencias de tiempo predecibles entre sus flancos de subida y de bajada (salvo imprecisiones conocidas y jitter).
A menudo se usa el término «relojes síncronos» en lugar de «relojes relacionados». Del mismo modo, «relojes asíncronos» se usa con frecuencia en lugar de «relojes no relacionados» (unrelated clocks).
Un ejemplo bastante común de relojes relacionados se da cuando se utiliza un único PLL de la FPGA para generar varios relojes, con relaciones de frecuencia conocidas entre ellos. En ese escenario, las herramientas de FPGA suelen colocar los búferes de reloj de modo que los flancos queden alineados en la medida de lo posible.
Por ejemplo, cuando un reloj de referencia se multiplica por 2 y por 3:
En este ejemplo, x1 clk, x2 clk y x3 clk son relojes relacionados, ya que la temporización entre cada par de estos relojes es predecible.
En el caso general, el reloj de referencia es un reloj no relacionado respecto a esos otros relojes: la relación temporal entre los flancos del reloj de referencia y los de los demás puede variar con la temperatura y otros factores. Sin embargo, si el PLL está configurado para asegurar una alineación predecible entre el reloj de referencia y las salidas del PLL, incluso el reloj de referencia es un reloj relacionado.
Conocer las relaciones temporales entre relojes permite hacer cálculos de temporización para los caminos que van entre sus dominios de reloj. Por ejemplo, la temporización de un camino entre los dominios de x1 clk y x2 clk se calcula con los mismos requisitos que un camino entre x2 clk y sí mismo. Esto se debe a que la diferencia de tiempo más corta entre estos dos relojes es la misma que el tiempo entre dos flancos de subida de x2 clk.
Del mismo modo, puede calcularse un camino entre x2 clk y x3 clk, pero la diferencia de tiempo más corta es solo una sexta parte del periodo de x1 clk. Por tanto, el requisito de temporización en el peor caso corresponde al de un imaginario x6 clk. La razón es la diferencia de tiempo entre el segundo flanco de subida de x2 clk y el tercer flanco de subida de x3 clk.
Consulta esta página para una discusión más detallada sobre cómo se calcula la temporización.
La intención de este ejemplo es decir algo que también es cierto en general: con relojes relacionados, es posible garantizar la temporización aplicando restricciones de temporización a los caminos entre esos relojes, igual que se hace con los caminos dentro de un mismo dominio de reloj. Esto es posible cuando las herramientas de FPGA se aseguran deliberadamente de que los flancos de reloj estén alineados. Como demuestra el ejemplo, los requisitos de temporización suelen ser más estrictos que los de cada reloj por separado, a veces mucho más estrictos.
Nótese, sin embargo, que el hecho de que dos relojes se deriven del mismo reloj de referencia no los convierte necesariamente en relojes relacionados. En particular, si la desviación entre esos relojes no está controlada o es desconocida (lo cual ocurre a menos que las herramientas de diseño hayan utilizado explícitamente recursos de distribución de reloj con retardos iguales), deben tratarse como relojes no relacionados.
Y ni siquiera x1 clk está necesariamente relacionado con su reloj de referencia, aunque tengan exactamente la misma frecuencia. A menos que esos relojes estén deliberadamente alineados entre sí, su relación de fase es desconocida.
Por este tema de la fase, en la definición de dominio de reloj he escrito «una determinada señal de reloj» y no simplemente «un determinado reloj». «Un determinado reloj» podría significar, por ejemplo, el mismo reloj de la placa, conectado a dos pines de entrada distintos de la FPGA. En cambio, «una determinada señal de reloj» se refiere a un cable en Verilog o en la lista de conexiones (netlist) del diseño que representa una señal de reloj, como @clk1 y @clk2 en el ejemplo anterior. En el diseño Verilog, esa señal se distribuye mediante las conexiones de los puertos en las instanciaciones (instantiations), o con simples sentencias «assign».
El hecho de que el reloj sea exactamente la misma señal en Verilog implica que las herramientas se encargarán de usar recursos que aseguren una baja desviación de reloj (clock skew) en el hardware. También tiene implicaciones sobre si las herramientas consideran que los relojes son relojes relacionados (más sobre esto abajo).
Relojes relacionados, restricciones de temporización y lógica de resincronización
Volvamos a la pregunta original: ¿se puede usar @bar, en el ejemplo anterior, como cualquier otro registro? O dicho en términos más generales, si un flip-flop es el destino de un camino que va de un dominio de reloj a otro, ¿podemos usar su salida con la misma fiabilidad que la de cualquier flip-flop? Eso es lo mismo que preguntarse si es posible garantizar el tiempo de setup y el tiempo de hold de ese flip-flop cuando al menos un camino que llega a su entrada de datos es síncrono con otro reloj.
La respuesta es simple y corta: si los relojes de ambos lados (@clk1 y @clk2 en el ejemplo) son relojes relacionados, la salida del flip-flop de destino es perfectamente válida y puede usarse como la de cualquier registro, siempre que se cumpla la restricción de temporización adecuada para ese camino. En caso contrario, la temporización en el destino no puede garantizarse, y hay que añadir lógica de resincronización para hacer frente a ese hecho.
Esta página muestra un análisis de temporización completo de un camino entre dos relojes relacionados.
Después de la larga discusión anterior, es hora de resumirla en unas pocas reglas:
- Es imposible aplicar restricciones de temporización a los caminos entre dominios de reloj que pertenecen a relojes no relacionados.
- Si hay lógica de resincronización en todos los caminos entre dos dominios de reloj, no hace falta aplicar restricciones de temporización a esos caminos.
- Si no hay lógica de resincronización en un camino entre dos dominios de reloj, deben aplicarse restricciones de temporización a ese camino.
La primera regla es la más sencilla: si no puedes estar seguro de que dos relojes son relojes relacionados, asegúrate de tener lógica de resincronización en todos los caminos entre los dominios de reloj (la próxima página explica cómo). Asegúrate también de que no se aplican restricciones de temporización a esos caminos, porque según la segunda regla eso no es necesario. Las restricciones de temporización innecesarias solo complican el trabajo de las herramientas de FPGA.
Otra consecuencia de estas reglas es que, aunque los relojes sean relacionados, es perfectamente válido tratarlos como si no lo fueran. Como acabo de mencionar, para ello hay que asegurarse de tener lógica de resincronización en todos los caminos y desactivar la aplicación de restricciones de temporización (más sobre esto abajo).
Y por último, si estás seguro de que los relojes son relacionados, no hace falta lógica de resincronización, pero deben existir restricciones de temporización aplicadas correctamente a todos los caminos entre los dominios de reloj.
La siguiente tabla resume esta sección. Las filas de la tabla indican si hay lógica de resincronización al final del camino, y las columnas, si los relojes son relacionados o no. El centro de la tabla indica si el camino necesita restricciones de temporización o no.
| Los relojes son | |||
| relojes relacionados | relojes no relacionados | ||
|
Resincroni- |
No aplicada | Se requieren restricciones de temporización en el camino | Esto es un error |
| aplicada | No se requieren restricciones de temporización en el camino | ||
Errores comunes
En un diseño complejo, donde las señales se cablean entre distintos módulos, es fácil pasar por alto qué señal es síncrona con qué reloj y, por tanto, pasar de un dominio de reloj a otro sin darse cuenta.
Hay dos tipos de errores perjudiciales. El primero es crear un camino entre dominios de reloj de relojes no relacionados sin ninguna lógica de resincronización. Eso significa que la temporización puede incumplirse de vez en cuando en el destino de esos caminos. Como ocurre con todos los errores de temporización, el problema visible puede ser muy engañoso. Las herramientas pueden añadir confusión si deducen de las restricciones de temporización que los dos relojes son relacionados y, por tanto, aplican innecesariamente restricciones a los caminos. Estas restricciones no tienen ningún significado entre relojes no relacionados, pero esos caminos aparecerán en los informes como si se hubieran tratado correctamente. Eso puede llevar a quien lea los informes de temporización a pensar que todo está bien, o incluso que los relojes son realmente relacionados.
El segundo error perjudicial se da cuando dos relojes son relacionados y la lógica los trata como tales, pero las herramientas no los consideran así. En consecuencia, no se aplican restricciones de temporización a los caminos entre los dominios de reloj y, por tanto, no hay garantía de que los requisitos de temporización se cumplan en el destino. Una vez más, esto puede dar lugar a un comportamiento poco fiable. No obstante, es posible detectar este tipo de error en una comprobación de validez de la temporización del diseño.
La única situación en la que no darse cuenta de un cruce de dominios de reloj es realmente inocuo es entre relojes relacionados, siempre que las herramientas de FPGA también los consideren como tales (y, por tanto, apliquen restricciones de temporización a los caminos relacionados). Aun así pueden existir fallos funcionales si los relojes tienen frecuencias distintas y la lógica no lo tiene en cuenta, pero eso es como cualquier otro fallo de lógica.
Evita restricciones innecesarias
Con bastante frecuencia se usa un único PLL para crear relojes de distintas frecuencias destinados a diferentes unidades funcionales. Desde el punto de vista del diseñador son relojes no relacionados, pero en realidad son relojes relacionados, y las herramientas normalmente los consideran como tales.
Supongamos, por ejemplo, que un PLL genera dos relojes a partir de un reloj de referencia de 10 MHz. Uno de los relojes es de 90 MHz y el segundo es de 100 MHz. La intención es usar estos relojes en distintas partes del proyecto, así que conceptualmente son relojes no relacionados. Pero ¿qué ocurre si hay una conexión entre dos flip-flops, de modo que uno es síncrono con un reloj y el segundo es síncrono con el otro?
Como el periodo del primer reloj es 11,11 ns y el periodo del segundo es 10 ns, la diferencia de tiempo en el peor caso entre los flancos de subida es 1,11 ns (teniendo en cuenta todas las combinaciones de fase posibles). Eso equivale al periodo de un reloj de 900 MHz. Por tanto, la temporización del camino entre esos dos flip-flops queda muy justa, e incluso si es posible cumplir ese requisito, supone una dificultad para las herramientas, en particular si hay muchos caminos de este tipo.
No es un caso teórico: una trampa muy común es usar una FIFO de doble reloj (dual-clock FIFO) para interconectar dominios de reloj sin prestar atención a si los relojes son relacionados o no. Este error no supone un problema funcional, pero como las FIFOs de doble reloj siempre tienen lógica de resincronización, no tiene sentido obligar a las herramientas a que apliquen restricciones de temporización a los caminos entre los dos dominios de reloj. Las herramientas pueden esforzarse en cumplir esas restricciones sin ninguna finalidad.
Por tanto, es importante prestar atención a los relojes relacionados que no se usan como tales, en particular a los que salen del mismo PLL: lo más probable es que se estén aplicando restricciones de temporización a los caminos de un dominio de reloj a otro. Esa aplicación no aporta ningún beneficio, porque la lógica de resincronización protege frente a las violaciones de temporización. Por otro lado, el esfuerzo de las herramientas por cumplir esas restricciones en esos caminos puede dificultar el cumplimiento de las restricciones de temporización de todo el diseño.
Para resolverlo, añade restricciones de temporización que declaren los relojes como no relacionados. Otra opción es definir caminos falsos (false paths) o retardos máximos para esos caminos. Sin embargo, en algunos casos puede que no sea necesario hacer nada, así que merece la pena revisar el informe de temporización de esos caminos antes de actuar. Por ejemplo, cuando se usa una FIFO de doble reloj proporcionada por las herramientas de FPGA, a menudo se añaden automáticamente restricciones de temporización adecuadas para los caminos que conectan los dominios de reloj.
Restricciones de temporización que inducen a error
Es importante subrayar que las herramientas de FPGA normalmente aceptan y aplican restricciones de temporización entre relojes no relacionados. Estas restricciones carecen de sentido y resultan confusas, sobre todo porque los caminos afectados aparecen en el informe de temporización como si sus requisitos estuvieran garantizados. Como ya se ha comentado, es imposible garantizar la temporización de un camino entre dominios de reloj que pertenecen a relojes no relacionados.
Conviene recordar que las restricciones de temporización no son más que una manera de dar información a las herramientas de FPGA sobre el diseño. Si un diseño cumple las restricciones de temporización, eso solo significa algo si las restricciones son correctas. Así que no intentes resolver un cruce de dominios de reloj añadiendo simplemente una restricción de temporización si no estás seguro de que los relojes son relacionados.
Y una vez más, cuando tenga sentido, es una buena idea añadir restricciones que definan los relojes como no relacionados, o definir caminos falsos (false paths). Eso no solo facilita el trabajo de las herramientas de FPGA, sino que también evita confusiones.
Asegúrate de que las herramientas reciben la información correcta
Por todas las razones mencionadas, es crucial que las herramientas de diseño FPGA tengan la información correcta sobre qué relojes son relacionados y cuáles no. O, más exactamente, que las herramientas de FPGA apliquen restricciones de temporización a todos los caminos que no estén protegidos por lógica de resincronización (eso incluye los caminos dentro de un mismo dominio de reloj, aunque aquí no viene al caso).
Sin embargo, asegurarse de que las herramientas tienen la misma percepción que el diseño lógico puede ser difícil: cada programa de diseño FPGA tiene su propia manera de hacer suposiciones automáticas sobre las relaciones entre relojes. Lo único que todas las herramientas parecen tener en común es que, si se usa el IP de reloj dedicado de la herramienta para generar dos o más relojes con el mismo PLL, la herramienta considerará que esos relojes son relacionados. En consecuencia, los caminos entre los dominios de reloj pertinentes se temporizarán y se aplicarán restricciones de temporización a esos caminos. Esa aplicación suele basarse en la restricción de temporización definida para el reloj de referencia. Pero ni siquiera eso debes darlo por sentado.
Aparte de eso, cada herramienta tiene su propia manera de deducir la relación entre relojes, e incluso distintas herramientas del mismo fabricante de FPGA pueden decidir de forma diferente en determinadas situaciones. En particular, Vivado de Xilinx tiende más a asumir que los relojes son relacionados que la anterior herramienta insignia de Xilinx, ISE.
Así pues, no hay un principio general para todas las herramientas de diseño FPGA, e incluso una herramienta concreta puede tomar decisiones sorprendentes. La única manera de abordar este tema de verdad es revisar los informes de temporización y generar informes para grupos de caminos específicos, para asegurarse de que los requisitos de temporización son correctos donde son necesarios, y no existen donde son innecesarios. Es una tarea ardua, pero merece la pena.
Con esto concluye la primera página de esta serie. La próxima página repasa los fundamentos del cruce de dominios de reloj.

