【问题标题】:VHDL: creating a very slow clock pulse based on a very fast clockVHDL:基于非常快的时钟创建非常慢的时钟脉冲
【发布时间】:2013-03-06 10:37:39
【问题描述】:

(我会在 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;

所以,在这种情况下,这完全是因为缺少 else(就像我说的,50% 占空比最初很有趣,但最终不是必需的,“本地”时钟位的切换似乎当时很聪明...)我大部分时间都在正确的轨道上。

目前我还不清楚为什么使用计数器(存储大量位)不会导致警告,但存储和切换输出位导致警告。 想法?

【问题讨论】:

    标签: vhdl clock fpga


    【解决方案1】:

    如果您只需要一个时钟来驱动 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 内部使用了更多的时钟线,因此如果您有很多门控时钟,您可能会很快用完所有可用的线。

    时钟启用没有这些缺点。一切都在同一个时钟域中运行(尽管速度不同),因此您无需任何同步器或类似设备即可轻松使用相同的信号。

    【讨论】:

    • 有趣...这几乎就是我已经在做的事情了,尽管我使用逻辑来切换本地 std_logic 位,当它达到所需的值(没有“else”子句)然后将其分配给一个输出(无论如何都不再需要 50% 的占空比)。去掉那个切换,然后添加一个 else 子句(相当于你上面的“clk_enable_200kHz
    • 这听起来很奇怪。您是否在这两种情况下都以相同的方式使用门控时钟信号?警告应取决于用作时钟的逻辑信号,而不是取决于该逻辑信号的生成方式。
    • 我已经用“之前和之后”代码更新了我的问题(我测试了这两种形式,只有“之前”一个触发了这个错误......虽然很明显它有一个额外的存储位作为你会看到的)。在这一点上它有点学术但很高兴理解为什么我首先做的是“错误”(我所能想到的是它与没有“其他”或额外的一点有关切换或两者的组合,所有其他事情始终相同。)
    【解决方案2】:

    注意这个例子在这条线上工作,

    信号 clk_enable_counter : std_logic_vector(9 down to 0);

    必须改为

    信号 clk_enable_counter : unsigned(9 downto 0);

    你需要包含这个库,

    图书馆ieee; 使用 ieee.numeric_std.all;

    【讨论】:

      【解决方案3】:

      您的两个样本都会产生一个信号,其中一个以慢速切换,而其中一个以“慢速”发出窄脉冲。如果这两个信号都进入其他触发器的时钟输入,我会收到有关时钟路由非最优的警告。

      我不确定您为什么会收到门控时钟警告,这通常会在您这样做时出现:

      gated_clock <= clock when en = '1' else '0';
      

      【讨论】:

      • 我在想 LOCAL_CLK_SLOW 这里的门控时钟......虽然为什么计数器变量 没有有这种效果仍不清楚对我来说。
      • 但是 LOCAL_CLOCK_SLOW 只是一个同步写入的信号,就像所有其他正常的同步代码一样......发生了一些非常奇怪的事情!
      【解决方案4】:

      这是一个完整的示例代码:

      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;
      

      【讨论】:

      • 你挖掘了一个已有四年历史的问题,该问题已经有一个公认的答案,并通过发布大量不正确缩进的代码来回答它。它甚至不是一个完整的最小可验证示例。请不要这样做
      • @JHBonarius:嗯,那个“已接受的答案”中的代码对我不起作用,而且我的“大量代码”无论如何只有约 40 LOC 长。
      • 很抱歉,您的代码没有解释或 cmets。例如看看进程proc1_running_at_clk_1:它是空的......为什么clk_in1_freqclk_1_freq 信号看起来应该是常量......甚至不是64位unsigneds,但只是整数。进一步查看您的代码,它没有任何意义。
      • 我相信我的代码是有意义的,并且它非常实用。根据流程proc1_running_at_clk_1 - 这显然只是一个占位符。根据clk_in1_freqclk_1_freq - 希望将这些变为常量的人可以非常轻松地做到这一点,只是这样做 - 运行时调整的可能性将会丢失。
      • 如果值不是常量,你认为clk_prescaler1 &lt;= clk_in1_freq / clk_1_freq 将如何在 FPGA 中工作......为什么你要输入大写的每个单词?
      猜你喜欢
      • 2018-07-28
      • 2021-11-18
      • 1970-01-01
      • 1970-01-01
      • 2021-11-29
      • 1970-01-01
      • 2012-02-08
      • 1970-01-01
      相关资源
      最近更新 更多