检测两个时钟域之间的脉冲的最佳方法是什么?
What is the best way to detect pulses between two clock domains?
我想将一个脉冲从一个时钟域clk1传输到另一个时钟域clk2,但我们不知道哪个比另一个快!
最好的方法是什么?
谢谢,
你需要一个频闪同步器。
频闪同步器将输入的上升沿转换为电平变化 (T-FF)。该电平变化通过 2-FF 同步器传输到第二个时钟域。然后通过 XOR 门恢复信息。 (注意:T-FF 是一个 D-FF,带有 XOR 以反转每个高输入的状态。)
另外,还可以计算出busy状态,通知发送时钟域整个电路的状态。此忙信号可用于锁定输入的上升沿检测。
电路是这样的:
一次多位的源代码:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;
library PoC;
use PoC.sync.all;
entity sync_Strobe is
generic (
BITS : positive := 1; -- number of bit to be synchronized
GATED_INPUT_BY_BUSY : boolean := TRUE; -- use gated input (by busy signal)
SYNC_DEPTH : T_MISC_SYNC_DEPTH := T_MISC_SYNC_DEPTH'low -- generate SYNC_DEPTH many stages, at least 2
);
port (
Clock1 : in std_logic; -- <Clock> input clock domain
Clock2 : in std_logic; -- <Clock> output clock domain
Input : in std_logic_vector(BITS - 1 downto 0); -- @Clock1: input bits
Output : out std_logic_vector(BITS - 1 downto 0); -- @Clock2: output bits
Busy : out std_logic_vector(BITS - 1 downto 0) -- @Clock1: busy bits
);
end entity;
architecture rtl of sync_Strobe is
attribute SHREG_EXTRACT : string;
signal syncClk1_In : std_logic_vector(BITS - 1 downto 0);
signal syncClk1_Out : std_logic_vector(BITS - 1 downto 0);
signal syncClk2_In : std_logic_vector(BITS - 1 downto 0);
signal syncClk2_Out : std_logic_vector(BITS - 1 downto 0);
begin
gen : for i in 0 to BITS - 1 generate
signal D0 : std_logic := '0';
signal T1 : std_logic := '0';
signal D2 : std_logic := '0';
signal Changed_Clk1 : std_logic;
signal Changed_Clk2 : std_logic;
signal Busy_i : std_logic;
-- Prevent XST from translating two FFs into SRL plus FF
attribute SHREG_EXTRACT of D0 : signal is "NO";
attribute SHREG_EXTRACT of T1 : signal is "NO";
attribute SHREG_EXTRACT of D2 : signal is "NO";
begin
process(Clock1)
begin
if rising_edge(Clock1) then
-- input delay for rising edge detection
D0 <= Input(i);
-- T-FF to converts a strobe to a flag signal
if GATED_INPUT_BY_BUSY then
T1 <= (Changed_Clk1 and not Busy_i) xor T1;
else
T1 <= Changed_Clk1 xor T1;
end if;
end if;
end process;
-- D-FF for level change detection (both edges)
D2 <= syncClk2_Out(i) when rising_edge(Clock2);
-- assign syncClk*_In signals
syncClk2_In(i) <= T1;
syncClk1_In(i) <= syncClk2_Out(i); -- D2
Changed_Clk1 <= not D0 and Input(i); -- rising edge detection
Changed_Clk2 <= syncClk2_Out(i) xor D2; -- level change detection; restore strobe signal from flag
Busy_i <= T1 xor syncClk1_Out(i); -- calculate busy signal
-- output signals
Output(i) <= Changed_Clk2;
Busy(i) <= Busy_i;
end generate;
syncClk2 : entity PoC.sync_Bits
generic map (
BITS => BITS, -- number of bit to be synchronized
SYNC_DEPTH => SYNC_DEPTH
)
port map (
Clock => Clock2, -- <Clock> output clock domain
Input => syncClk2_In, -- @async: input bits
Output => syncClk2_Out -- @Clock: output bits
);
syncClk1 : entity PoC.sync_Bits
generic map (
BITS => BITS, -- number of bit to be synchronized
SYNC_DEPTH => SYNC_DEPTH
)
port map (
Clock => Clock1, -- <Clock> output clock domain
Input => syncClk1_In, -- @async: input bits
Output => syncClk1_Out -- @Clock: output bits
);
end architecture;
源代码可以在这里找到:PoC.misc.sync.Strobe
您的问题的另一个解决方案是 Flancter,Doulos 对此做出了最好的解释:
嘿,我有一个与 xilinx 工具兼容的此代码的 verilog 版本,
src_pulse -> src_clk level_convertor-> 2 级目标同步器 -> dest_pulse 检测器
这是在 2 个相关未知时钟域之间传输脉冲时要遵循的序列步骤。
module pulse_cdc(
input src_clk,
input src_pulse,
input dest_clk,
output logic dest_pulse
);
(* ASYNC_REG = "TRUE" *)logic [2:0] dest_sync;
logic src_clk_level = '0;
//------------------------------//
//-- Pulse to level convertor --//
//------------------------------//
always_ff @(posedge src_clk)
if(src_pulse) src_clk_level <= #10 ~src_clk_level;
else src_clk_level <= #10 src_clk_level;
//----------------------------//
//------- Synchronizer -------//
//----------------------------//
always_ff @(dest_clk)begin
dest_sync[0] <= #10 src_clk_level;
dest_sync[1] <= #10 dest_sync[0];
dest_sync[2] <= #10 dest_sync[1];
end
//-------------------------//
//--- Pulse Generator -----//
//-------------------------//
always_comb dest_pulse = dest_sync[1] ^ dest_sync[2];
endmodule
我想将一个脉冲从一个时钟域clk1传输到另一个时钟域clk2,但我们不知道哪个比另一个快! 最好的方法是什么?
谢谢,
你需要一个频闪同步器。
频闪同步器将输入的上升沿转换为电平变化 (T-FF)。该电平变化通过 2-FF 同步器传输到第二个时钟域。然后通过 XOR 门恢复信息。 (注意:T-FF 是一个 D-FF,带有 XOR 以反转每个高输入的状态。)
另外,还可以计算出busy状态,通知发送时钟域整个电路的状态。此忙信号可用于锁定输入的上升沿检测。
电路是这样的:
一次多位的源代码:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;
library PoC;
use PoC.sync.all;
entity sync_Strobe is
generic (
BITS : positive := 1; -- number of bit to be synchronized
GATED_INPUT_BY_BUSY : boolean := TRUE; -- use gated input (by busy signal)
SYNC_DEPTH : T_MISC_SYNC_DEPTH := T_MISC_SYNC_DEPTH'low -- generate SYNC_DEPTH many stages, at least 2
);
port (
Clock1 : in std_logic; -- <Clock> input clock domain
Clock2 : in std_logic; -- <Clock> output clock domain
Input : in std_logic_vector(BITS - 1 downto 0); -- @Clock1: input bits
Output : out std_logic_vector(BITS - 1 downto 0); -- @Clock2: output bits
Busy : out std_logic_vector(BITS - 1 downto 0) -- @Clock1: busy bits
);
end entity;
architecture rtl of sync_Strobe is
attribute SHREG_EXTRACT : string;
signal syncClk1_In : std_logic_vector(BITS - 1 downto 0);
signal syncClk1_Out : std_logic_vector(BITS - 1 downto 0);
signal syncClk2_In : std_logic_vector(BITS - 1 downto 0);
signal syncClk2_Out : std_logic_vector(BITS - 1 downto 0);
begin
gen : for i in 0 to BITS - 1 generate
signal D0 : std_logic := '0';
signal T1 : std_logic := '0';
signal D2 : std_logic := '0';
signal Changed_Clk1 : std_logic;
signal Changed_Clk2 : std_logic;
signal Busy_i : std_logic;
-- Prevent XST from translating two FFs into SRL plus FF
attribute SHREG_EXTRACT of D0 : signal is "NO";
attribute SHREG_EXTRACT of T1 : signal is "NO";
attribute SHREG_EXTRACT of D2 : signal is "NO";
begin
process(Clock1)
begin
if rising_edge(Clock1) then
-- input delay for rising edge detection
D0 <= Input(i);
-- T-FF to converts a strobe to a flag signal
if GATED_INPUT_BY_BUSY then
T1 <= (Changed_Clk1 and not Busy_i) xor T1;
else
T1 <= Changed_Clk1 xor T1;
end if;
end if;
end process;
-- D-FF for level change detection (both edges)
D2 <= syncClk2_Out(i) when rising_edge(Clock2);
-- assign syncClk*_In signals
syncClk2_In(i) <= T1;
syncClk1_In(i) <= syncClk2_Out(i); -- D2
Changed_Clk1 <= not D0 and Input(i); -- rising edge detection
Changed_Clk2 <= syncClk2_Out(i) xor D2; -- level change detection; restore strobe signal from flag
Busy_i <= T1 xor syncClk1_Out(i); -- calculate busy signal
-- output signals
Output(i) <= Changed_Clk2;
Busy(i) <= Busy_i;
end generate;
syncClk2 : entity PoC.sync_Bits
generic map (
BITS => BITS, -- number of bit to be synchronized
SYNC_DEPTH => SYNC_DEPTH
)
port map (
Clock => Clock2, -- <Clock> output clock domain
Input => syncClk2_In, -- @async: input bits
Output => syncClk2_Out -- @Clock: output bits
);
syncClk1 : entity PoC.sync_Bits
generic map (
BITS => BITS, -- number of bit to be synchronized
SYNC_DEPTH => SYNC_DEPTH
)
port map (
Clock => Clock1, -- <Clock> output clock domain
Input => syncClk1_In, -- @async: input bits
Output => syncClk1_Out -- @Clock: output bits
);
end architecture;
源代码可以在这里找到:PoC.misc.sync.Strobe
您的问题的另一个解决方案是 Flancter,Doulos 对此做出了最好的解释:
嘿,我有一个与 xilinx 工具兼容的此代码的 verilog 版本,
src_pulse -> src_clk level_convertor-> 2 级目标同步器 -> dest_pulse 检测器
这是在 2 个相关未知时钟域之间传输脉冲时要遵循的序列步骤。
module pulse_cdc(
input src_clk,
input src_pulse,
input dest_clk,
output logic dest_pulse
);
(* ASYNC_REG = "TRUE" *)logic [2:0] dest_sync;
logic src_clk_level = '0;
//------------------------------//
//-- Pulse to level convertor --//
//------------------------------//
always_ff @(posedge src_clk)
if(src_pulse) src_clk_level <= #10 ~src_clk_level;
else src_clk_level <= #10 src_clk_level;
//----------------------------//
//------- Synchronizer -------//
//----------------------------//
always_ff @(dest_clk)begin
dest_sync[0] <= #10 src_clk_level;
dest_sync[1] <= #10 dest_sync[0];
dest_sync[2] <= #10 dest_sync[1];
end
//-------------------------//
//--- Pulse Generator -----//
//-------------------------//
always_comb dest_pulse = dest_sync[1] ^ dest_sync[2];
endmodule