Me gustaría entender diferentes enfoques para implementar un contador de dominio de reloj cruzado. En todas las siguientes posibilidades tengo:
clk_a : in std_logic;
clk_b : in std_logic;
reset : in std_logic;
-- cross-domain counter
signal frm_cnt : standard_logic_vector(7 downto 0);
-- control signal: increment counter (synced to clk_a)
signal frm_cnt_inc : std_logic;
-- control signal: decrement counter (synced to clk_b)
signal frm_cnt_dec : std_logic;
-- control signal, perform action on the counter
signal frm_cnt_modif : std_logic;
Los enfoques que considero son:
Contador no cronometrado similar a la respuesta en ¿Cómo se implementa este simple contador en un FPGA sin reloj?
frm_cnt_modif <= frm_cnt_inc or frm_cnt_dec; -- can also be xored
frame_counter : process(frm_cnt_modif, reset)
begin
if reset = RST_ACTIVE then
frm_cnt <= (others => '0');
elsif rising_edge(frm_cnt_modif) then
if frm_cnt_inc = '1' then
frm_cnt <= incr_vec(frm_cnt);
elsif frm_cnt_dec = '1' then
frm_cnt <= decr_vec(frm_cnt);
end if;
end if;
end process;
Esto simula que está bien, y Vivado puede sintetizarlo, sin embargo, se queja de utilizar frm_cnt_modif
como reloj , lo que causa problemas de tiempo. Además, las señales de control pueden superponerse.
Otro enfoque que he intentado es tener contadores separados en sus respectivos dominios de reloj y realizar una lógica basada en su diferencia. En este caso, me preocupa el desbordamiento del contador, por lo que restablecerlos cuando no se realiza ninguna acción en ambos lados y son iguales parece una buena solución, sin embargo, creo que la señal que controla el restablecimiento estará sujeta a problemas de sincronización similares.
Lo que estoy experimentando ahora es la 'sincronización con el reloj más rápido'. Tengo un:
control_synchronizer : process(clk_b, reset)
begin
if reset = RST_ACTIVE then
frm_cnt_inc_sync <= '0';
elsif rising_edge(clk_b) then
if frm_cnt_inc_sync = '1' then
frm_cnt_inc_sync <= '0';
elsif frm_cnt_inc = '1' then
frm_cnt_inc_sync <= '1';
else
frm_cnt_inc_sync <= '0';
end if;
end if;
end process;
Y el incremento y decremento reales del contador se realizan en un proceso sincronizado con clk_b
. Esto parece funcionar bien siempre y cuando clk_b > clk_a and clk_b < 2 * clk_a
.
¿Alguna otra posible solución y / o mejores prácticas? (Soy consciente de que probablemente debería usar códigos grises para el propio contador).
EDIT1:
Después de la respuesta de Dave y de leer enlace , terminé con lo siguiente:
control_a : process(clk_a, reset)
begin
if reset = RST_ACTIVE then
frm_cnt_inc_d <= '0';
elsif rising_edge(clk_a) then
if frm_cnt_inc = '1' then
frm_cnt_inc_d <= '1';
else
frm_cnt_inc_d <= '0';
end if;
end if;
end process;
control_b : process(clk_b, reset)
begin
if reset = RST_ACTIVE then
frm_cnt_inc_sync <= '0';
frm_cnt_inc_sync_d <= '0';
elsif rising_edge(clk_b) then
frm_cnt_inc_sync_d <= frm_cnt_inc_sync;
-- this check was ensuring 1 to 1 pulse translation before rising edge detection
--if frm_cnt_inc_sync = '1' then
--frm_cnt_inc_sync <= '0';
if frm_cnt_inc = '1' then
frm_cnt_inc_sync <= '1';
else
frm_cnt_inc_sync <= '0';
end if;
end if;
end process;
frm_cnt_inc_result <= not(frm_cnt_inc_sync_d) and frm_cnt_inc_sync;
Esto parece seguro para cualquier valor de reloj. Lo que no entiendo, sin embargo, es el propósito del frm_cnt_inc_d
FF (el código simula perfectamente bien sin él). ¿Alguien puede explicar si es realmente necesario?