Intento averiguar cómo especificar correctamente las restricciones de tiempo en los diseños de FPGA (en archivos .sdc/ .xdc). Sé lo que significan los tiempos de configuración y espera. Sin embargo: ¿Cómo averiguo qué restricciones de tiempo tiene mi circuito externo?
Lo que espero haber entendido hasta ahora es:
Necesito especificar mis relojes externos (especialmente su velocidad) alimentados en el FPGA mediante el uso de una create_clock
declaración. Esto asegura que los tiempos internos (entre pines de celdas, por ejemplo, entre FlipFlops o memoria) estén dentro de los límites.
Para todos los pines de entrada, puedo especificar la configuración ( -min
) y los tiempos de retención ( -max
) para todos los pines de entrada de la FPGA (que se denominan ports
en este contexto). Estos son relativos a un reloj y se especifican medianteset_input_delay -clock {clockname} -min/-max <time> [ get_ports {portname} ]
Para todos los pines de salida, puedo especificar -min
tiempos de configuración ( ) y retención ( -max
) para todos los pines de entrada de la FPGA (que se denominan ports
en este contexto). Estos son nuevamente relativos a un reloj y se especifican a través deset_input_delay -clock {clockname} -min/-max <time> [ get_ports {portname} ]
Digamos que la FPGA tiene conectado un reloj externo 'CLK' de 50 MHz. Especificar este es fácil:
create_clock -name CLK -period 20ns [ get_ports {CLK} ]
Ahora se le conecta un microcontrolador (ESP8266, hoja de datos ), que tiene su propio reloj externo de 80 MHz ("CLK").
El microcontrolador envía y recibe sus datos utilizando un sistema de bus sincronizado simple:
Mi microcontrolador tiene el siguiente bucle de programa (pseudocódigo):
BEGIN:
(TX, CLK) <= (write_data, LOW) [1th cycle]
(TX, CLK) <= (write_data, HIGH) [2th cycle]
(read_data) <= (RX) [3th cycle]
JUMP BEGIN [4th cycle]
Supongamos que el cableado de tx, rx y la línea de reloj tienen una longitud de unos 20 cm y pueden desviarse 5 cm (cable más corto frente a cable más largo). Esto significa un tiempo promedio de 0,67 ns para cada ruta, pero cada señal puede ser +/- 0,17 ns más rápida o más lenta.
Temporización a µC en relación con el flanco ascendente:
(read_data) <= (RX) -37.5ns <-- last read
JUMP BEGIN -25.0ns
(TX, CLK) <= (write_data, LOW) -12.5ns <-- current data written
(TX, CLK) <= (write_data, HIGH) 0.0ns <-- current low->high
(read_data) <= (RX) 12.5ns <-- current read
JUMP BEGIN 25.0ns
(TX, CLK) <= (write_data, LOW) 37.5ns <-- next data written
(TX, CLK) <= (write_data, HIGH) 50.0ns <-- next low->high
Entradas:
Cuando el flanco ascendente del "reloj" llega a la FPGA, los datos en el pin "RX" de la FPGA ya son válidos para 1 ciclo de reloj del µC t=-12.5ns
reducido por el máx. Desviación del tiempo de viaje t=-12.33 ns
. Por lo tanto, el tiempo de configuración de la FPGA puede ser de hasta 12,33 ns.
Los datos de entrada en "RX" serán válidos para 3 ciclos de reloj del µC reducidos por la incertidumbre del tiempo de viaje: 37,5 ns - 0,17 ns = 37,33 ns.
Salidas:
La salida de datos en el pin "TX" de la FPGA debe ser válida/estable y viajar al pin "RX" de µCs del µC dentro de los 12,5 ns - t_setup del µC. Por lo tanto, debe ser estable en 12.5 ns - 0.67 ns - 0.17 ns - t_setup = 11,66 ns - t_setup
. Sin embargo, no sé cómo averiguar t_setup para µC.
Además, el µC no debería enviar datos a earl. Los últimos datos terminan de leerse hasta que t <= -37.5ns + t_hold
se miden en µC, lo que significa t <= -38 ns + t_hold
en el FPGA. Este es el que establece el límite para el tiempo de configuración de la FPGA.
Ahora hay algunos parámetros desconocidos (los µCs setup_time y hold_time). Y no sé si calculo todo bien...
Además, al escribir la restricción, ¿debería usar los valores negados para setup_time en las restricciones o la hora más temprana/última en relación con el evento del reloj?
Aquí está mi solución intermedia/incompleta:
set_input_delay -clock clock -min -12.330ns [ get_ports {RX} ]
set_input_delay -clock clock -max 37.330ns [ get_ports {RX} ]
set_output_delay -clock clock -min -38.000ns [ get_ports {TX} ]
set_output_delay -clock clock -max 12.330ns [ get_ports {TX} ]
Sin embargo, no incluí la configuración y el tiempo de espera del pin "RX" en el µC y de alguna manera necesito especificar la señal de reloj recibida por el µC a través de la línea de "reloj" del bus en el archivo de restricciones, ya que las restricciones se relacionan a este reloj y no al propio reloj externo de 50 MHz de la FPGA. ¿Bien?
Hay un diagrama de tiempo en el "TimeQuest Wizzard" en Quartus II. Pero no puedo leerlo porque me resultó confuso qué bloque ( <....>
) es mi bit de datos actual y qué <////>
significa el bloque. Solo pude identificar la señal del reloj, sin saber con seguridad si el estándar está subiendo o bajando. Esto, a su vez, hace que sea difícil concluir cuál de los bloques es cuál y con qué momento se relacionan esas flechas. Tal vez, hay convenciones, pero no las conozco. Así que por favor dame una idea aquí...
En el ejemplo anterior, el µC es un ESP8266 ( hoja de datos ).
Aquí hay un ejemplo de código fuente de FPGA (VHDL) que, con suerte, explica cómo pienso usar los dos relojes. Por supuesto, la velocidad del reloj asíncrono para los datos debe ser lo suficientemente lenta, de modo que la FPGA tenga suficientes ciclos de reloj para reconocer, que lleguen nuevos datos asíncronos (RX_flag) y para preparar la salida al TX_reg antes del siguiente flanco ascendente en el reloj del µC. Dado el tiempo anterior, el µC tiene 50 ns para reconocer y procesar los datos de entrada. Por lo tanto, debe haber al menos 1 ciclo completo de CLK que tenga medio período de CLK antes (un borde descendente que advierte si los datos ya están disponibles):
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
entity AsyncIO is
port(
-- own clock, reset
CLK : in std_logic;
reset : in std_logic;
-- IO-Pins to uC
clock, RX_pin : in std_logic;
TX_pin : out std_logic
);
end entity;
architecture RTL of AsyncIO is
signal RX_buffer : std_logic := '0'; -- holds received bit
signal RX_flag : std_logic := '0'; -- is set when new bit arrive / reset when processed
signal RX_ready : std_logic := '0'; -- is set when new bit was available at falling_edge / reset when processed
signal TX_reg : std_logic := '0'; -- output that is currently to be send
signal last_RX : std_logic;
signal counter : std_logic_vector(1 downto 0) := "00";
begin
-- async process
process (clock) is
begin
if rising_edge(clock) then
RX_buffer <= RX_pin;
RX_flag <= '1';
TX_pin <= TX_reg;
end if;
end process;
-- async reset
process (reset) is
begin
if reset = '1' then
counter <= "00";
RX_buffer <= '0';
RX_flag <= '0';
RX_ready <= '0';
TX_reg <= '0';
end if;
end process;
-- synced process
process (CLK) is
-- processes RX
-- count similar bit in a row
-- TX_reg <= '1' if 5 similar bit in a row
-- else TX_reg <= '0'
procedure processRX is
begin
if (RX_buffer = last_RX) then
-- count similar bits
counter <= counter + 1;
-- no overflow at value '11'
-- instead keep at '11' and set TX_reg to '1'
if counter = "11" then
counter <= "11";
TX_reg <= '1';
end if;
else
-- reset if not similar
counter <= "00";
TX_reg <= '0';
end if;
-- remind last_RX
last_RX <= RX_buffer;
end;
begin
if falling_edge(CLK) then
if RX_flag = '1' then
RX_ready <= '1';
end if;
end if;
if rising_edge(CLK) then
-- if RX_read is '1' then RX_flag was already '1' at falling_edge
-- hence RX_buffer is safe to read now
if RX_ready = '1' then
processRX; -- call procedure to process bit in RX_buffer
RX_flag <= '0';
RX_ready <= '0';
end if;
end if;
end process;
end architecture;
Como su FPGA y microcontrolador funcionan con diferentes relojes, NO existe una relación de tiempo entre ellos. Para transferir datos de manera segura entre ellos , debe usar sincronizadores o un circuito que tenga una lógica de cruce de dominio de reloj incorporada como un FIFO asíncrono con un reloj de lectura y escritura (Todos los proveedores de FPGA que conozco tienen IP para esos).
Esto también significa que no puede (y por lo tanto no tiene que hacerlo) establecer restricciones de tiempo entre ellos.
Esto es válido tanto para los datos de CLK como de TX de la CPU a la FPGA, y también para los datos de RX de la FPGA al microcontrolador.
Esto todavía lo deja con el problema de cómo configurar el tiempo FPGA para su interfaz de ejemplo con datos CLK y TX. Desafortunadamente, no hay una respuesta simple, ya que depende en gran medida de cómo las instrucciones generan las señales. por ejemplo, ¿el reloj y TX siempre se configuran en el mismo ciclo de reloj? Puede usar el flanco descendente del reloj para registrar los datos.
Por último, pero no menos importante: los sincronizadores y FIFO asíncronos requieren más de un ciclo de reloj para transferir los datos entre dominios de reloj. Por lo tanto, debe tener un CLK en ejecución continua o debe generar varios ciclos más después de haber enviado datos o antes de planear recibir datos.
eliot alderson
eliot alderson
Enanos SD