如何将时钟分频器集成到现有的 VHDL 代码和约束文件中
How do I integrate a Clock divider into existing VHDL code and constraint File
所以我有一个简单的 2 位计数器,它在按下按钮时从一个状态移动到下一个状态。然而,我可以访问的唯一时钟以 125MHz 运行,这对于按下按钮来说太快了,所以我需要将时钟分频到更合理的速度。我在这个网站上看到了几个时钟分频器的例子,但是我想不通的是:
- 如何将时钟分频器添加到现有的 VHDL 代码中,我只是添加
分频时钟作为新的输出或输入端口?我该如何设置
所以状态变化只会发生在分频时钟之后?
在约束文件中,如何包含分频时钟?我认为
我使用生成的时钟作为语句的一部分,但它必须
分配给它自己的单独引脚?现在我有主时钟
指定为:
set_propertyPACKAGE_PINL16[get_portsclk]
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 编程
感谢任何和所有帮助,谢谢!
首先(稍后我会回答你关于时钟分频的问题),你不需要时钟分频器来设计你的按键计数系统。您需要一个边缘检测器,它完全不同,运行频率为 125 MHz。它是一种同步设备,可检测输入信号的上升(或下降)沿,并仅在检测到该沿时才在一个时钟周期内断言其输出。您可以使用此输出来触发 125 MHz 运行 计数器的增量。
但这还不是全部:因为您的按钮与您的主时钟不同步,所以您不能按原样使用它。如果这样做,将存在其值仅在时钟的上升沿(或非常接近时钟上升沿)发生变化并且路径上的第一个寄存器采样既不是 1 也不是 0 的风险。然后寄存器可以进入我们所说的亚稳态,这是非常不受欢迎的,特别是如果它传播。
在使用按钮输入之前,您必须将其与 2 级或 3 级移位寄存器同步。它起作用的原因以及为什么 2 或 3 个阶段超出了这个答案的范围,并且可以在许多教科书中找到。按钮还有另一个问题:它们有时会在稳定之前在两个状态之间跳动。您的 Zybo 可能配备了某种程度上有帮助的电阻器,但如果您的计数器没有按预期运行并且在您按下按钮时并不总是只增加一个,您将需要一个 "debouncer" 来过滤掉额外的边缘。但是这个答案太长了。
无论如何,下面的代码完成了两者(同步和边缘检测):
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 位计数器,它在按下按钮时从一个状态移动到下一个状态。然而,我可以访问的唯一时钟以 125MHz 运行,这对于按下按钮来说太快了,所以我需要将时钟分频到更合理的速度。我在这个网站上看到了几个时钟分频器的例子,但是我想不通的是:
- 如何将时钟分频器添加到现有的 VHDL 代码中,我只是添加 分频时钟作为新的输出或输入端口?我该如何设置 所以状态变化只会发生在分频时钟之后?
在约束文件中,如何包含分频时钟?我认为 我使用生成的时钟作为语句的一部分,但它必须 分配给它自己的单独引脚?现在我有主时钟 指定为:
set_propertyPACKAGE_PINL16[get_portsclk]
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 编程 感谢任何和所有帮助,谢谢!
首先(稍后我会回答你关于时钟分频的问题),你不需要时钟分频器来设计你的按键计数系统。您需要一个边缘检测器,它完全不同,运行频率为 125 MHz。它是一种同步设备,可检测输入信号的上升(或下降)沿,并仅在检测到该沿时才在一个时钟周期内断言其输出。您可以使用此输出来触发 125 MHz 运行 计数器的增量。
但这还不是全部:因为您的按钮与您的主时钟不同步,所以您不能按原样使用它。如果这样做,将存在其值仅在时钟的上升沿(或非常接近时钟上升沿)发生变化并且路径上的第一个寄存器采样既不是 1 也不是 0 的风险。然后寄存器可以进入我们所说的亚稳态,这是非常不受欢迎的,特别是如果它传播。
在使用按钮输入之前,您必须将其与 2 级或 3 级移位寄存器同步。它起作用的原因以及为什么 2 或 3 个阶段超出了这个答案的范围,并且可以在许多教科书中找到。按钮还有另一个问题:它们有时会在稳定之前在两个状态之间跳动。您的 Zybo 可能配备了某种程度上有帮助的电阻器,但如果您的计数器没有按预期运行并且在您按下按钮时并不总是只增加一个,您将需要一个 "debouncer" 来过滤掉额外的边缘。但是这个答案太长了。
无论如何,下面的代码完成了两者(同步和边缘检测):
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 文档是您的朋友。