【问题标题】:How do I integrate a Clock divider into existing VHDL code and constraint File如何将时钟分频器集成到现有的 VHDL 代码和约束文件中
【发布时间】:2015-08-26 18:09:33
【问题描述】:

所以我有一个简单的 2 位计数器,可以在按下按钮时从一个状态移动到下一个状态。但是,我唯一可以访问的时钟运行在 125MHz,这对于按下按钮来说太快了,所以我需要将时钟分频到一个更合理的速度。我在这个网站上看到了一些时钟分频器的例子,但我不知道的是:

  1. 如何将时钟分频器添加到现有的 VHDL 代码中,是否只需添加 分频时钟作为新的输出或输入端口?以及如何设置 up 所以状态变化只会发生在分频时钟之后?
  2. 在约束文件中,如何包含分频时钟?我想 我使用生成的时钟作为语句的一部分,但它必须 分配给自己的单独引脚?现在我有主时钟 分配为:

    set_property PACKAGE_PIN L16 [get_ports clk]

    set_property IOSTANDARD LVCMOS33 [get_ports clk]

    create_clock -period 10.000 -name clk -waveform {0.000 5.000} [get_ports clk]

VHDL 代码如下:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity des_src is
    Port ( clk : in STD_LOGIC;
           BTN_0 : in STD_LOGIC;
           LED_1 : out STD_LOGIC;
           LED_0 : out STD_LOGIC);
end des_src;

architecture Behavioral of des_src is
    TYPE        statetype IS (Start, One, Two, Three);
    SIGNAL      currentstate, nextstate : statetype;

begin
    fsm1:   PROCESS (currentstate, BTN_0)
    BEGIN
        CASE currentstate IS
            WHEN Start =>
                LED_1 <= '0';
                LED_0 <= '0';
                CASE BTN_0 IS
                    WHEN '1' =>
                        nextstate <= One;
                    WHEN OTHERS =>
                        nextstate <= Start;
                END CASE;
            WHEN One =>
                LED_1 <= '0';
                LED_0 <= '1';
                CASE BTN_0 IS
                    WHEN '1' =>
                        nextstate <= Two;
                    WHEN OTHERS =>
                        nextstate <= One;
                END CASE;
            WHEN Two =>
                LED_1 <= '1';
                LED_0 <= '0';
                CASE BTN_0 IS
                    WHEN '1' =>
                        nextstate <= Three;
                    WHEN OTHERS =>
                        nextstate <= Two;
                END CASE;
            WHEN Three =>
                LED_1 <= '1';
                LED_0 <= '1';
                CASE BTN_0 IS
                    WHEN '1' =>
                        nextstate <= Start;
                    WHEN OTHERS =>
                        nextstate <= Three;
                END CASE;
        END CASE;
    END PROCESS;

    fsm2:   PROCESS (clk)
    BEGIN
        IF (clk'EVENT) AND (clk = '1') THEN
            currentstate <= nextstate;
        END IF;
    END PROCESS;

end Behavioral;

我正在使用 Vivado 2015.2 为 ZYBO 编程 感谢您提供任何和所有帮助,谢谢!

【问题讨论】:

    标签: vhdl clock divider vivado


    【解决方案1】:

    首先(我稍后会回答您关于时钟分频的问题),您不需要时钟分频器来设计您的按键计数系统。您需要一个边缘检测器,它完全不同,运行频率为 125 MHz。它是一种同步设备,可检测输入信号的上升(或下降)沿,并仅在检测到该沿时才在一个时钟周期内断言其输出。您可以使用此输出触发 125 MHz 运行计数器的增量。

    但这还不是全部:由于您的按钮与主时钟不同步,因此您无法按原样使用它。如果你这样做了,那么它的值可能会在时钟的上升沿(或非常接近它)发生变化,并且路径上的第一个寄存器既不会采样 1 也不会采样 0。然后寄存器可以输入我们所说的亚稳态,这是非常不受欢迎的,尤其是在它传播的情况下。

    在使用按钮输入之前,您必须将其与 2 或 3 级移位寄存器同步。它起作用的原因以及为什么 2 或 3 个阶段超出了这个答案的范围,可以在许多教科书中找到。按钮还有另一个问题:它们有时会在稳定之前在两种状态之间反弹。您的 Zybo 可能配备了电阻器,以某种方式提供帮助,但如果您的计数器未按预期运行并且在您按下按钮时并不总是仅增加一个,您将需要一个“去抖动器”来过滤掉额外的边缘。但这对于这个答案来说太长了。

    不管怎样,下面的代码做了两个(同步和边缘检测):

    signal pipe: std_ulogic_vector(0 to 2);
    signal tick: std_ulogic;
    ...
    process(clk)
    begin
      if rising_edge(clk) = '1' then
        pipe <= press_button & pipe(0 to 1);
      end if;
    end process;
    
    tick <= pipe(1) and (not pipe(2));
    

    就是这样,每当您按下按钮时,tick 将在一个时钟周期内被断言,无论您何时按下它与 125 MHz 时钟边沿相比,无论您按下它多长时间......好吧,如果你按下它至少持续一个时钟周期,但除非您是超人本人,否则应该始终如此。

    现在对于您的计数系统,我认为它的级别太低(VHDL 是一种高级语言)。因此,如果您想计算,请使用整数或其他定义算术的类型(例如 ieee.numeric_std.unsigned):

    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    ...
    signal counter: unsigned(1 downto 0);
    ...
    process(clk)
    begin
      if rising_edge(clk) then
        counter <= counter + tick;
      end if;
    end process;
    LED_0 <= counter(0);
    LED_1 <= counter(1);
    

    注意:除非你确定counter会在上电时自动复位,否则你也应该使用一种复位来初始化它:

    process(clk)
    begin
      if rising_edge(clk) then
        if reset = '1' then
          counter <= "00";
        else
          counter <= counter + tick;
        end if;
      end if;
    end process;
    

    最后,如果你真的需要一个时钟分频器,你可以使用一个计数器(就像上面的那个,但总是计数)并将它的最高有效位用作时钟。但是您必须告诉 Vivado,以便它在正确的位置插入时钟缓冲器。在这里,Vivado 文档是您的朋友。或者,您可以实例化一个时钟管理器,它可以从主时钟生成许多不同频率的时钟,而不仅仅是主时钟频率的整数分频器。同样,Xilinx 文档是您的朋友。

    【讨论】:

    • 2 cmets 关于你的好答案:1) 你没有解决按钮输入反弹的问题。 2)您解释了一个完全同步的设计,但是您使用的是异步复位。对于 FPGA,最好使用同步。重置。
    • @Paebbels:你说得对,我没有提到弹跳问题。我猜 Zybo 的按钮上有一些电阻器有助于减少问题,但 sfagin 可能应该检查这一点(例如使用他的计数器)并设计一个去抖动电路以防万一。我在我的答案中添加了一个 not eon this。关于同步/异步重置,这是另一场无休止的战争。此外,即使异步使用,复位信号也可以在别处同步。无论如何,为了避免冗长的解释,我也让我们改变一下。
    • 只是想说声谢谢,这真的很有帮助,并回答了我在想问他们之前提出的一些问题。谢谢!
    猜你喜欢
    • 2020-09-04
    • 1970-01-01
    • 1970-01-01
    • 2020-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多