(我会在EE中发布这个,但似乎这里有更多的VHDL问题......)
背景:我正在使用Xilinx Spartan-6LX9 FPGA和Xilinx ISE 14.4(webpack)。
我今天偶然发现了可怕的“PhysDesignRules:372 - 门控时钟”警告,我看到有很多关于这一点的讨论。一致意见似乎是使用FPGA上的其中一个DCM进行时钟分频但是...我的DCM似乎无法从32 MHz到4.096 KHz(根据向导它在5MHz时达到最低点) 32MHz ......尝试为这种低频目的链接多个DCM似乎很荒谬。)
我的当前设计使用clk_in计数到指定值(15265),将该值重置为零并切换clk_out位(因此我的最终占空比为50%,FWIW)。它完成了这项工作,我可以轻松地使用clk_out的上升沿来驱动我设计的下一个阶段。它似乎工作得很好,但是...... 门控时钟(即使它不在时钟偏差的范围内,恕我直言也非常相关)。 (注意:所有时钟测试都是在对给定时钟敏感的过程中使用rising_edge()函数完成的。)
所以,我的问题:
如果我们谈论的是从更快的 clk_in中获取相对较慢的clk_out,那么门控仍被视为不好吗? 或者是这种“计数到x并发送脉冲”的东西非常典型的用于FPGA 以生成KHz范围内的“时钟”,而是一些其他不必要的副作用可能会触发这个警告反而是?
是否有更好的方法可以从MHz范围的主时钟创建低KHz范围的时钟,请记住,在这里使用多个DCM似乎有点过分(如果输出非常低则可能频率)?我意识到50%的占空比可能是多余的,但是假设有一个时钟并且不使用板载DCM,那么用FPGA如何执行主时钟分频呢?
编辑:给定以下内容(其中CLK_MASTER是32 MHz输入时钟,CLK_SLOW是所需的慢速时钟,LOCAL_CLK_SLOW是一种存储整个占空比的时钟状态的方法),我学到了此配置导致警告:
architecture arch of clock is
constant CLK_MASTER_FREQ: natural := 32000000; -- time := 31.25 ns
constant CLK_SLOW_FREQ: natural := 2048;
constant MAX_COUNT: natural := CLK_MASTER_FREQ/CLK_SLOW_FREQ;
shared variable counter: natural := 0;
signal LOCAL_CLK_SLOW: STD_LOGIC := '0';
begin
clock_proc: process(CLK_MASTER)
begin
if rising_edge(CLK_MASTER) then
counter := counter + 1;
if (counter >= MAX_COUNT) then
counter := 0;
LOCAL_CLK_SLOW <= not LOCAL_CLK_SLOW;
CLK_SLOW <= LOCAL_CLK_SLOW;
end if;
end if;
end process;
end arch;
虽然此配置不会导致警告:
architecture arch of clock is
constant CLK_MASTER_FREQ: natural := 32000000; -- time := 31.25 ns
constant CLK_SLOW_FREQ: natural := 2048;
constant MAX_COUNT: natural := CLK_MASTER_FREQ/CLK_SLOW_FREQ;
shared variable counter: natural := 0;
begin
clock_proc: process(CLK_MASTER)
begin
if rising_edge(CLK_MASTER) then
counter := counter + 1;
if (counter >= MAX_COUNT) then
counter := 0;
CLK_SLOW <= '1';
else
CLK_SLOW <= '0';
end if;
end if;
end process;
end arch;
所以,在这种情况下,完全是缺少其他的(就像我说的那样,50%的占空比最初很有意思,但最终并不是一个要求,而且“本地”时钟位的切换似乎当时非常聪明......)我看起来大部分都在正确的轨道上。
此时我不清楚为什么使用计数器(存储大量位)不会引起警告,但存储和切换的输出位会导致警告。想法?
答案 0 :(得分:7)
如果您只需要一个时钟来驱动FPGA中的另一部分逻辑,那么简单的答案就是使用时钟启用。
也就是说,在与其他所有相同的(快速)时钟上运行慢速逻辑,但是我们对它的启用速度很慢。例如:
signal clk_enable_200kHz : std_logic;
signal clk_enable_counter : std_logic_vector(9 downto 0);
--Create the clock enable:
process(clk_200MHz)
begin
if(rising_edge(clk_200MHz)) then
clk_enable_counter <= clk_enable_counter + 1;
if(clk_enable_counter = 0) then
clk_enable_200kHz <= '1';
else
clk_enable_200kHz <= '0';
end if;
end if;
end process;
--Slow process:
process(clk_200MHz)
begin
if(rising_edge(clk_200MHz)) then
if(reset = '1') then
--Do reset
elsif(clk_enable_200kHz = '1') then
--Do stuff
end if;
end if;
end process;
虽然200kHz是近似的,但上述内容可以扩展到您需要的任何时钟使能频率。此外,它应该由大多数FPGA中的FPGA硬件直接支持(至少在Xilinx部分)。
门控时钟几乎总是一个坏主意,因为人们经常忘记他们正在创建新的时钟域,因此在这些时钟之间接口信号时不采取必要的预防措施。它还在FPGA内部使用了更多的时钟线,因此如果你有很多门控时钟,你可能会快速耗尽所有可用线路。
时钟启用没有这些缺点。一切都在同一时钟域运行(尽管速度不同),因此您可以轻松使用相同的信号而无需任何同步器或类似信号。
答案 1 :(得分:3)
请注意此示例使用此行,
信号clk_enable_counter:std_logic_vector(9 downto 0);
必须更改为
信号clk_enable_counter:unsigned(9 downto 0);
你需要包含这个库,
图书馆; 使用ieee.numeric_std.all;
答案 2 :(得分:0)
两个样本都会产生一个信号,其中一个信号以慢速切换,另一个信号以“慢速”脉冲输出一个窄脉冲。如果这些信号都转到其他触发器的时钟输入端,我预计时钟路由的警告不是最佳的。
我不确定为什么你会得到一个门控时钟警告,这通常是在你做的时候出现的:
gated_clock <= clock when en = '1' else '0';
答案 3 :(得分:-2)
以下是完整的示例代码:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
ENTITY Test123 IS
GENERIC (
clk_1_freq_generic : unsigned(31 DOWNTO 0) := to_unsigned(0, 32); -- Presented in Hz
clk_in1_freq_generic : unsigned(31 DOWNTO 0) := to_unsigned(0, 32) -- Presented in Hz, Also
);
PORT (
clk_in1 : IN std_logic := '0';
rst1 : IN std_logic := '0';
en1 : IN std_logic := '0';
clk_1 : OUT std_logic := '0'
);
END ENTITY Test123;
ARCHITECTURE Test123_Arch OF Test123 IS
--
SIGNAL clk_en_en : std_logic := '0';
SIGNAL clk_en_cntr1 : unsigned(31 DOWNTO 0) := (OTHERS => '0');
--
SIGNAL clk_1_buffer : std_logic := '0';
SIGNAL clk_1_freq : unsigned(31 DOWNTO 0) := (OTHERS => '0'); -- Presented in Hz, Also
SIGNAL clk_in1_freq : unsigned(31 DOWNTO 0) := (OTHERS => '0'); -- Presented in Hz
--
SIGNAL clk_prescaler1 : unsigned(31 DOWNTO 0) := (OTHERS => '0'); -- Presented in Cycles (Relative To The Input Clk.)
SIGNAL clk_prescaler1_halved : unsigned(31 DOWNTO 0) := (OTHERS => '0');
--
BEGIN
clk_en_gen : PROCESS (clk_in1)
BEGIN
IF (clk_en_en = '1') THEN
IF (rising_edge(clk_in1)) THEN
clk_en_cntr1 <= clk_en_cntr1 + 1;
IF ((clk_en_cntr1 + 1) = clk_prescaler1_halved) THEN -- a Register's (F/F) Output Only Updates Upon a Clock-Edge : That's Why This Comparison Is Done This Way !
clk_1_buffer <= NOT clk_1_buffer;
clk_1 <= clk_1_buffer;
clk_en_cntr1 <= (OTHERS => '0');
END IF;
END IF;
ELSIF (clk_en_en = '0') THEN
clk_1_buffer <= '0';
clk_1 <= clk_1_buffer;
clk_en_cntr1 <= (OTHERS => '0'); -- Clear Counter 'clk_en_cntr1'
END IF;
END PROCESS;
update_clk_prescalers : PROCESS (clk_in1_freq, clk_1_freq)
BEGIN
clk_prescaler1 <= (OTHERS => '0');
clk_prescaler1_halved <= (OTHERS => '0');
clk_en_en <= '0';
IF ((clk_in1_freq > 0) AND (clk_1_freq > 0)) THEN
clk_prescaler1 <= (clk_in1_freq / clk_1_freq); -- a Register's (F/F) Output Only Updates Upon a Clock-Edge : That's Why This Assignment Is Done This Way !
clk_prescaler1_halved <= ((clk_in1_freq / clk_1_freq) / 2); -- (Same Thing Here)
IF (((clk_in1_freq / clk_1_freq) / 2) > 0) THEN -- (Same Thing Here, Too)
clk_en_en <= '1';
END IF;
ELSE
NULL;
END IF;
END PROCESS;
clk_1_freq <= clk_1_freq_generic;
clk_in1_freq <= clk_in1_freq_generic;
END ARCHITECTURE Test123_Arch;