Así es como entiendo que esto debería funcionar. Hice esto y lo probé en Xilinx ISim.
Esto sincroniza el indicador válido en los dominios, manteniéndolo en el dominio A hasta que se vea que llega al dominio B, y utilizando la detección de bordes en el dominio B para regenerar un solo ciclo estroboscópico.
El bus de datos se registra desde el dominio A en el dominio B cuando sabemos que se ha mantenido estable durante varios ciclos de reloj (en ambos dominios), por lo que es seguro usarlo así.
Este diseño podría tener cosas como una bandera lista, etc.
bus_sync.vhd:
-- CDC bus synchroniser
-- Depends on bus A being at least ~1.5x faster than bus B
library ieee;
use ieee.std_logic_1164.all;
entity bus_sync is
port (
data_a : in std_logic_vector(7 downto 0);
valid_a : in std_logic;
clk_a : in std_logic;
data_b : out std_logic_vector(7 downto 0);
valid_b : out std_logic;
clk_b : in std_logic
);
end bus_sync;
architecture behavioral of bus_sync is
signal data_a_reg, data_b_reg : std_logic_vector(7 downto 0);
signal valid_a_hold: std_logic := '0';
signal valid_ack_a_sync, valid_ack_a: std_logic;
signal valid_b_sync, valid_b_in, valid_b_last: std_logic;
signal valid_b_reg: std_logic;
begin
bus_in_sync_proc: process (clk_a)
begin
if rising_edge(clk_a) then
if valid_a = '1' then
data_a_reg <= data_a;
valid_a_hold <= '1';
elsif valid_ack_a = '1' then
-- b domain has seen the valid strobe. We can deassert.
valid_a_hold <= '0';
end if;
end if;
end process;
valid_sync_ack: process (clk_a)
begin
-- synchronise valid_b back into A domain as an 'ack'
-- (could just keep valid_a_hold asserted for a number of clocks)
-- domain A clocks 3x faster than domain B so we will not miss it.
if rising_edge(clk_a) then
valid_ack_a_sync <= valid_b_in; -- avoid metastability
valid_ack_a <= valid_ack_a_sync;
end if;
end process;
b_sync_proc: process (clk_b)
begin
if rising_edge(clk_b) then
-- synchronise valid flag into b domain through 2 DFFs
valid_b_sync <= valid_a_hold;
valid_b_in <= valid_b_sync;
-- and one more for edge detection
valid_b_last <= valid_b_in;
if valid_b_last = '0' and valid_b_in = '1' then
-- valid strobe arriving in b domain
-- registered data is stable in data_a_reg
data_b_reg <= data_a_reg;
valid_b_reg <= '1';
else
valid_b_reg <= '0';
end if;
end if;
end process;
data_b <= data_b_reg;
valid_b <= valid_b_reg;
end behavioral;
un banco de pruebas de ejemplo:
library ieee;
use ieee.std_logic_1164.all;
entity bus_sync_tb is
end bus_sync_tb;
architecture behavior of bus_sync_tb is
-- component declaration for the unit under test (uut)
component bus_sync
port(
data_a : in std_logic_vector(7 downto 0);
valid_a : in std_logic;
clk_a : in std_logic;
data_b : out std_logic_vector(7 downto 0);
valid_b : out std_logic;
clk_b : in std_logic
);
end component;
--inputs
signal data_a : std_logic_vector(7 downto 0) := (others => '0');
signal valid_a : std_logic := '0';
signal clk_a : std_logic := '0';
signal clk_b : std_logic := '0';
--outputs
signal data_b : std_logic_vector(7 downto 0);
signal valid_b : std_logic;
-- clock period definitions
constant clk_a_period : time := 10 ns;
constant clk_b_period : time := 33.333 ns;
begin
-- instantiate the unit under test (uut)
uut: bus_sync port map (
data_a => data_a,
valid_a => valid_a,
clk_a => clk_a,
data_b => data_b,
valid_b => valid_b,
clk_b => clk_b
);
-- clock process definitions
clk_a_process :process
begin
clk_a <= '0';
wait for clk_a_period/2;
clk_a <= '1';
wait for clk_a_period/2;
end process;
clk_b_process :process
begin
clk_b <= '0';
wait for clk_b_period/2;
clk_b <= '1';
wait for clk_b_period/2;
end process;
-- stimulus process
stim_proc: process
begin
wait for clk_a_period*10;
-- change testbench inputs on falling edge of clk.
-- makes the ordering easier to see in wave view.
wait until clk_a = '0';
data_a <= x"5a";
valid_a <= '1';
wait until clk_a = '0';
data_a <= (others => '0');
valid_a <= '0';
-- must not send more data through too soon!
wait for clk_b_period*10;
-- halt the simulation the only way I know how in VHDL:
assert false report "TEST COMPLETE OK" severity failure;
end process;
end;