Introducción
Esta página trata sobre las salidas de datos síncronas de fuente (source-synchronous data outputs). Esta técnica consiste en que las salidas de datos son síncronas con un reloj que la FPGA genera en paralelo con dichas salidas.
Este enfoque es la opción obvia si la interfaz con un componente externo consiste únicamente en puertos de salida de la FPGA. También es adecuado si la E/S es bidireccional, como se comenta al final de esta página. Hay además una página aparte que trata las relaciones entre el reloj y los datos en general.
La aplicación básica
Considera este ejemplo en Verilog:
module top (
input clk,
output reg [7:0] data_out,
output reg sync_clk_out
);
always @(posedge clk)
begin
sync_clk_out <= !sync_clk_out;
if (sync_clk_out)
data_out <= data_out + 1;
end
endmodule
Nótese que @data_out cambia su valor simultáneamente con el flanco de bajada de @sync_clk_out: cuando @sync_clk_out está a nivel bajo, @data_out no cambia. Cuando @sync_clk_out está a nivel alto, @data_out cambia a un nuevo valor en respuesta al flanco de subida de @clk.
Si se usan registros IOB tanto para @data_out como para @sync_clk_out, todas estas señales de salida cambian de valor prácticamente al mismo tiempo. El componente externo que recibe estas señales puede por tanto usar @sync_clk_out como su reloj. Normalmente es fácil cumplir así los requisitos de temporización del componente externo, suponiendo que el muestreo de @data_out esté sincronizado con el flanco de subida de @sync_clk_out.
Uso de registros DDR
El ejemplo anterior tiene un inconveniente importante: la frecuencia de @sync_clk_out es la mitad que la de @clk. Por tanto, la velocidad de datos de @data_out está limitada también a la mitad de la frecuencia de @clk. La única excepción es si el componente externo es sensible a ambos flancos del reloj de @sync_clk_out, es decir, si tiene una entrada DDR. Normalmente no es el caso.
Es posible utilizar un registro DDR de salida para generar un reloj de salida alineado con las salidas de datos. La idea es emplear un biestable que cambie de valor en ambos flancos del reloj. De este modo, un biestable de este tipo se comporta de la siguiente manera:
always @(posedge clk or negedge clk)
if (clk)
sync_clk_out <= 0;
else
sync_clk_out <= 1;
@sync_clk_out pasa a nivel bajo en respuesta al flanco de subida de @clk. Lo contrario ocurre con el flanco de bajada. El resultado es un reloj de salida con la misma frecuencia.
Sin embargo, este código Verilog no funcionará con la mayoría de los sintetizadores, porque los biestables del entramado lógico normalmente no pueden ser sensibles a ambos flancos. Solo los registros IOB tienen esa capacidad, y rara vez el sintetizador es lo bastante inteligente como para comprender esta posibilidad.
Por tanto, se requiere la instanciación (instantiation) de una primitiva (primitive) lógica. La primitiva es específica de la FPGA utilizada. Este ejemplo muestra cómo puede hacerse para Kintex Ultrascale:
ODDRE1 ODDR_ins
(.C(clk), .Q(sync_clk_out),
.D1(1'b0), .D2(1'b1),
.SR(1'b0));
Pero ahora el reloj se genera con un registro DDR de salida, mientras que las señales de datos se generan con registros IOB normales. La alineación entre los datos y el reloj no es tan evidente como antes. Por tanto, es importante leer los informes de temporización de todas las salidas y comprobar que la diferencia entre los retardos es lo bastante pequeña.
También es posible usar registros DDR de salida para los datos: si se da el mismo valor a D1 y D2, la salida cambia solo una vez por cada ciclo de reloj:
module top (
input clk,
output [7:0] data_out,
output sync_clk_out
);
reg [7:0] data;
ODDRE1 ODDR_ins
(.C(clk), .Q(sync_clk_out),
.D1(1'b0), .D2(1'b1),
.SR(1'b0));
ODDRE1 ODDR_data_ins [7:0]
(.C(clk), .Q(data_out),
.D1(data), .D2(data),
.SR(1'b0));
always @(posedge clk)
data <= data + 1;
endmodule
Este método garantiza que los datos y el reloj estén alineados. La instanciación explícita de los registros DDR de salida garantiza también que se usen registros IOB. El único inconveniente de este método es que es específico de cada FPGA.
Nótese que el comportamiento de los registros DDR de salida puede ser inesperado y confuso. Por ejemplo, nótese que @data está sincronizado con el flanco positivo de @clk. Pero el ODDRE1 es activo en ambos flancos del reloj. Asegúrate de leer la documentación de tu FPGA acerca de cómo y cuándo se muestrean las dos entradas de datos.
Nótese también que en este ejemplo no se usa el reset. Esto puede ser peligroso, en particular en FPGAs sofisticadas: el registro DDR de salida puede formar parte de un elemento lógico más complejo dentro del IOB. Un elemento lógico de este tipo puede no funcionar correctamente sin un reset.
Restricciones de temporización
Entonces, ¿son necesarias restricciones de temporización con este método? Si el reloj y las salidas de datos están alineados casi perfectamente gracias al uso de registros IOB, ¿no es suficiente?
La respuesta es que este método puede funcionar perfectamente incluso sin restricciones de temporización. Sin embargo, es buena idea aprovechar las restricciones de temporización de todas formas. Las razones son las mismas que se exponen para este tema en la página sobre registros IOB.
Otras consideraciones
Sin embargo, usar registros IOB no es lo único que hay que asegurar: presta también atención a la proximidad física entre los puertos de salida. Si los puertos de salida están lejos unos de otros, esta distancia física puede afectar negativamente a la desviación de reloj (clock skew) en la FPGA. Las diferencias entre las longitudes de las pistas (es decir, los conductores de la placa PCB) también pueden tener un efecto negativo. Además, los parámetros eléctricos (tensión, estándar de E/S, velocidad de variación (slew rate), corriente de salida, etc.) deben ser los mismos para todas las salidas.
Si la FPGA no tiene registros IOB, las restricciones de temporización se vuelven cruciales: la base para usar salidas síncronas de fuente es poder garantizar una desviación de reloj (clock skew) reducida entre todos los puertos de salida relevantes. Controlar el retardo entre el biestable y el pin de E/S suele ser suficiente: si este retardo está cerca del mínimo posible para la FPGA en cuestión, las diferencias entre las salidas serán lo bastante pequeñas para la mayoría de los propósitos.
E/S bidireccional
La principal característica de las salidas síncronas de fuente es que el reloj asociado lo genera la FPGA. Esto es obviamente adecuado cuando todas las señales de datos se dirigen hacia el componente externo. Pero incluso cuando hay señales de datos en ambas direcciones, puede tener sentido confiar en un reloj generado por la FPGA.
Sin embargo, nótese que las entradas de datos son síncronas con el reloj en la PCB, no con el reloj dentro del chip de la FPGA. Estos dos relojes tienen, por supuesto, exactamente la misma frecuencia. Pero hay una diferencia de temporización que depende del retardo de reloj a salida (clock-to-output) de los puertos de salida. Esto puede ser de varios nanosegundos.
Por tanto, se recomienda hacer un bucle de retorno (loopback) en la PCB: conecta la salida de reloj de la FPGA a un puerto de entrada. Este puerto de entrada puede tratarse como si fuera un reloj síncrono de sistema (system synchronous clock): el componente externo se comporta como si su reloj viniera de un oscilador dedicado, y no de la FPGA. Esto es similar a un reloj síncrono de sistema, porque tanto la FPGA como el componente externo dependen de la misma señal física en la PCB.
También puede ser posible tratar las entradas de datos como entradas síncronas de fuente (source-synchronous inputs). En particular, si se utiliza la técnica de desplazamiento de fase, no hace falta un bucle de retorno del reloj. De hecho, esta es la forma habitual de conectar con memorias DDR SDRAM.
La conclusión importante es planificar cuidadosamente la temporización de las señales de entrada antes de fabricar la PCB, y considerar la posibilidad de añadir un bucle de retorno del reloj si es necesario.

