1

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

  1. 如何将时钟分频器添加到现有的 VHDL 代码中,我是否只需将分频时钟添加为新的输出或输入端口?以及如何设置它以使状态更改仅在分频时钟之后发生?
  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 编程,感谢任何和所有帮助,谢谢!

4

1 回答 1

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 文档是您的朋友。

于 2015-08-27T06:38:36.473 回答