Motivación
En algún lugar al final del informe que genera el sintetizador (synthesizer) de ISE (xst), aparece la frecuencia máxima junto con un esquema de la ruta (path) más lenta. Es una función bastante útil, sobre todo cuando se intenta optimizar un módulo concreto. Con Vivado no se ofrece esa cifra después de una síntesis, posiblemente porque en Xilinx pensaron que esa «frecuencia máxima» podría inducir a error. Si fue así, tenían dos buenas razones:
- No existe algo así como una «frecuencia máxima»: las herramientas prestan atención a las restricciones de temporización (timing constraints) y hacen todo lo posible en consecuencia. En resumen: puede que no obtengas una determinada frecuencia a menos que la pidas.
- En un diseño típico hay muchos relojes con frecuencias diferentes. La ruta más lenta puede pertenecer a un reloj que, de todos modos, es lento.
Aun así, a veces resulta útil hacerse una idea de cómo están las cosas antes de la vía dolorosa de una implementación completa.
Cómo hacerlo en Vivado
Ante todo: establece las restricciones de temporización (timing constraints) según tus expectativas. O al menos, de forma que quede claro qué reloj es importante y cuál puede ser lento. Después realiza una síntesis del diseño.
Cuando la síntesis haya terminado correctamente, abre el diseño sintetizado (haciendo clic en «Open Synthesized Design» en la barra izquierda o con el comando Tcl «open_run synth_1»).
En la ventana de Tcl, escribe el comando
report_timing_summary -file mytiming.rpt
que escribe un informe de temporización completo posterior a la síntesis en mytiming.rpt. Si escribes simplemente «report_timing_summary», lo imprime en la consola.
También existe la opción «Report Timing Summary» dentro de «Synthesized Design», en la barra izquierda, pero a mí me resulta difícil extraer información del informe usando la interfaz gráfica.
Cómo leer el informe
REGLA #1: El informe de síntesis no es más que una estimación aproximada. Los retardos de encaminado son conjeturas. Puede indicar fallos de temporización que luego la implementación resuelva igualmente, y puede decir que todo está bien cuando la implementación falle estrepitosamente (en especial si el uso de lógica de la FPGA se acerca al 100%).
Manos a la obra: lo primero que hay que mirar es el resumen de relojes (Clock Summary) y la tabla Intra Clock, y averiguar con qué nombre ha llamado Vivado a cada reloj. Por ejemplo,
------------------------------------------------------------------------------------------------
| Clock Summary
| -------------
------------------------------------------------------------------------------------------------
Clock Waveform(ns) Period(ns) Frequency(MHz)
----- ------------ ---------- --------------
clk_fpga_1 {0.000 5.000} 10.000 100.000
gclk {0.000 4.000} 8.000 125.000
audio_mclk_OBUF {0.000 41.667} 83.333 12.000
clk_fb {0.000 20.000} 40.000 25.000
vga_clk_ins/clk_fb {0.000 20.000} 40.000 25.000
vga_clk_ins/clkout0 {0.000 1.538} 3.077 325.000
vga_clk_ins/clkout1 {0.000 7.692} 15.385 65.000
vga_clk_ins/clkout2 {0.000 7.692} 15.385 65.000
------------------------------------------------------------------------------------------------
| Intra Clock Table
| -----------------
------------------------------------------------------------------------------------------------
Clock WNS(ns) TNS(ns) TNS Failing Endpoints TNS Total Endpoints WHS(ns) THS(ns) THS Failing Endpoints THS Total Endpoints WPWS(ns) TPWS(ns) TPWS Failing Endpoints TPWS Total Endpoints
----- ------- ------- --------------------- ------------------- ------- ------- --------------------- ------------------- -------- -------- ---------------------- --------------------
clk_fpga_1 3.791 0.000 0 12474 0.135 0.000 0 12474 3.750 0.000 0 5021
gclk 6.751 0.000 0 2
audio_mclk_OBUF 76.667 0.000 0 1
clk_fb 12.633 0.000 0 2
vga_clk_ins/clk_fb 38.751 0.000 0 2
vga_clk_ins/clkout0 1.410 0.000 0 10
vga_clk_ins/clkout1 10.747 0.000 0 215 -0.029 -0.229 8 215 6.712 0.000 0 195
vga_clk_ins/clkout2 3.990 0.000 0 415 0.135 0.000 0 415 7.192 0.000 0 211
Si las frecuencias de reloj que aparecen en el Clock Summary (y que se derivan de las restricciones de temporización) no te ayudan a saber a qué reloj corresponde cada nombre, el valor TNS Total Endpoints de cada reloj en la tabla Intra Clock te permite distinguirlos. Así que, cuando encuentres el nombre del reloj que te interesa, busca ese nombre en el archivo y encontrarás algo como esto:
Max Delay Paths -------------------------------------------------------------------------------------- Slack (MET) : 3.791ns (required time - arrival time) Source: xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_offset_limit_1/C (rising edge-triggered cell FDRE clocked by clk_fpga_1 {rise@0.000ns fall@5.000ns period=10.000ns}) Destination: xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_end_offset_0/D (rising edge-triggered cell FDRE clocked by clk_fpga_1 {rise@0.000ns fall@5.000ns period=10.000ns}) Path Group: clk_fpga_1 Path Type: Setup (Max at Slow Process Corner) Requirement: 10.000ns (clk_fpga_1 rise@10.000ns - clk_fpga_1 rise@0.000ns) Data Path Delay: 6.077ns (logic 2.346ns (38.605%) route 3.731ns (61.395%)) Logic Levels: 8 (CARRY4=3 LUT3=1 LUT4=1 LUT6=3) Clock Path Skew: -0.040ns (DCD - SCD + CPR) Destination Clock Delay (DCD): 0.851ns = ( 10.851 - 10.000 ) Source Clock Delay (SCD): 0.901ns Clock Pessimism Removal (CPR): 0.010ns Clock Uncertainty: 0.154ns ((TSJ^2 + TIJ^2)^1/2 + DJ) / 2 + PE Total System Jitter (TSJ): 0.071ns Total Input Jitter (TIJ): 0.300ns Discrete Jitter (DJ): 0.000ns Phase Error (PE): 0.000ns Location Delay type Incr(ns) Path(ns) Netlist Resource(s) ------------------------------------------------------------------- ------------------- (clock clk_fpga_1 rise edge) 0.000 0.000 r PS7 0.000 0.000 r xillybus_ins/system_i/vivado_system_i/processing_system7_0/inst/PS7_i/FCLKCLK[1] net (fo=1, unplaced) 0.000 0.000 xillybus_ins/system_i/vivado_system_i/processing_system7_0/inst/n_707_PS7_i BUFG (Prop_bufg_I_O) 0.101 0.101 r xillybus_ins/system_i/vivado_system_i/processing_system7_0/inst/buffer_fclk_clk_1.FCLK_CLK_1_BUFG/O net (fo=5023, unplaced) 0.800 0.901 xillybus_ins/xillybus_core_ins/bus_clk_w r xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_offset_limit_1/C ------------------------------------------------------------------- ------------------- FDRE (Prop_fdre_C_Q) 0.496 1.397 f xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_offset_limit_1/Q net (fo=5, unplaced) 0.834 2.231 xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_offset_limit[1] LUT4 (Prop_lut4_I0_O) 0.289 2.520 r xillybus_ins/xillybus_core_ins/unitw_1_ins/Mcompar_n0037_lutdi/O net (fo=1, unplaced) 0.000 2.520 xillybus_ins/xillybus_core_ins/unitw_1_ins/Mcompar_n0037_lutdi CARRY4 (Prop_carry4_DI[0]_CO[3]) 0.553 3.073 r xillybus_ins/xillybus_core_ins/unitw_1_ins/Mcompar_n0037_cy[0]_CARRY4/CO[3] net (fo=1, unplaced) 0.000 3.073 xillybus_ins/xillybus_core_ins/unitw_1_ins/Mcompar_n0037_cy[3] CARRY4 (Prop_carry4_CI_CO[3]) 0.114 3.187 r xillybus_ins/xillybus_core_ins/unitw_1_ins/Mcompar_n0037_cy[4]_CARRY4/CO[3] net (fo=3, unplaced) 0.936 4.123 xillybus_ins/xillybus_core_ins/unitw_1_ins/Mcompar_n0037_cy[7] LUT6 (Prop_lut6_I4_O) 0.124 4.247 f xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_wr_request_condition/O net (fo=7, unplaced) 0.480 4.727 xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_wr_request_condition LUT3 (Prop_lut3_I2_O) 0.124 4.851 r xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_flush_condition_unitw_1_wr_request_condition_AND_179_o3_lut/O net (fo=1, unplaced) 0.000 4.851 xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_flush_condition_unitw_1_wr_request_condition_AND_179_o3_lut CARRY4 (Prop_carry4_S[2]_CO[3]) 0.398 5.249 f xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_flush_condition_unitw_1_wr_request_condition_AND_179_o2_cy_CARRY4/CO[3] net (fo=21, unplaced) 0.979 6.228 xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_flush_condition_unitw_1_wr_request_condition_AND_179_o LUT6 (Prop_lut6_I5_O) 0.124 6.352 r xillybus_ins/xillybus_core_ins/unitw_1_ins/_n03401/O net (fo=15, unplaced) 0.502 6.854 xillybus_ins/xillybus_core_ins/unitw_1_ins/_n0340 LUT6 (Prop_lut6_I5_O) 0.124 6.978 r xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_end_offset_0_rstpot/O net (fo=1, unplaced) 0.000 6.978 xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_end_offset_0_rstpot FDRE r xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_end_offset_0/D ------------------------------------------------------------------- ------------------- (clock clk_fpga_1 rise edge) 10.000 10.000 r PS7 0.000 10.000 r xillybus_ins/system_i/vivado_system_i/processing_system7_0/inst/PS7_i/FCLKCLK[1] net (fo=1, unplaced) 0.000 10.000 xillybus_ins/system_i/vivado_system_i/processing_system7_0/inst/n_707_PS7_i BUFG (Prop_bufg_I_O) 0.091 10.091 r xillybus_ins/system_i/vivado_system_i/processing_system7_0/inst/buffer_fclk_clk_1.FCLK_CLK_1_BUFG/O net (fo=5023, unplaced) 0.760 10.851 xillybus_ins/xillybus_core_ins/bus_clk_w r xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_end_offset_0/C clock pessimism 0.010 10.861 clock uncertainty -0.154 10.707 FDRE (Setup_fdre_C_D) 0.062 10.769 xillybus_ins/xillybus_core_ins/unitw_1_ins/unitw_1_end_offset_0 ------------------------------------------------------------------- required time 10.769 arrival time -6.978 ------------------------------------------------------------------- slack 3.791
Esto es un texto bastante enrevesado, pero los elementos clave están marcados en rojo.
Antes de sacar conclusiones, asegúrate de que estás mirando la sección adecuada:
- Se trata de la sección de rutas de retardo máximo (Max Delay Paths). La sección de rutas mínimas es útil para detectar violaciones del tiempo de hold, pero no tiene efecto en la frecuencia máxima.
- Es el reloj correcto. En el ejemplo anterior es clk_fpga_1. La fila Requirement indica no solo la restricción que se le ha dado a este reloj (10 ns = 100 MHz), sino también que el análisis va de un flanco de subida de clk_fpga_1 al siguiente flanco de subida.
Una vez hecho esto, veamos qué tenemos: el valor requerido era 10 ns y el margen (slack) era 3,791 ns (observa que es positivo). Esto significa que podríamos haber pedido un período de reloj 3,791 ns más corto y aun así habría sido correcto. Así que el período de reloj solicitado podría haber sido 10 – 3,791 = 6,209 ns, que son unos 161 MHz.
Así pues, la respuesta corta a la pregunta por el «reloj máximo» para clk_fpga_1 es 161 MHz. Pero recuerda que esta cifra puede cambiar si cambian las restricciones.
Y una nota final: el retardo de la ruta de datos (Data Path Delay) nos dice algo sobre qué hace que esta ruta en el peor de los casos sea lenta o rápida: cuánto retardo corresponde a lógica y cuánto a los retardos de encaminado (estimados). También lo hace el informe detallado de retardos que viene después. Para un informe más detallado, plantéate usar la opción «-nworst» al pedir el informe de temporización, de modo que se listen unas cuantas rutas en el peor de los casos. Esto puede ayudar a resolver problemas de temporización.