在VHDL中创建低时钟频率的替代方法
在过去,我问了一个关于复位的问题,以及如何将高时钟频率分解为一系列低时钟方波频率,其中每个输出是彼此的谐波,例如,第一个输出是10 Hz,第二个是20 Hz等 我收到了一些非常有用的答案,建议使用时钟使能引脚来创建较低的频率 我想到了另一个选择;使用一个不断递增的n位数字,并将该数字的最后x位作为时钟输出,其中x是输出数 对我来说,它在合成中起作用——但我很好奇——因为我从来没有在网上或其他地方看到过它,所以,我是否遗漏了一些东西,这意味着它实际上是一个糟糕的想法,我只是在为以后制造问题 我知道这方面的限制是,我只能产生输入频率除以2的幂的频率,因此大多数情况下,它只能近似于所需的输出频率(但仍然是正确的顺序)。这是不建议使用的唯一原因吗 非常感谢 大卫在VHDL中创建低时钟频率的替代方法,vhdl,clock,conventions,xilinx-ise,spartan,Vhdl,Clock,Conventions,Xilinx Ise,Spartan,在过去,我问了一个关于复位的问题,以及如何将高时钟频率分解为一系列低时钟方波频率,其中每个输出是彼此的谐波,例如,第一个输出是10 Hz,第二个是20 Hz等 我收到了一些非常有用的答案,建议使用时钟使能引脚来创建较低的频率 我想到了另一个选择;使用一个不断递增的n位数字,并将该数字的最后x位作为时钟输出,其中x是输出数 对我来说,它在合成中起作用——但我很好奇——因为我从来没有在网上或其他地方看到过它,所以,我是否遗漏了一些东西,这意味着它实际上是一个糟糕的想法,我只是在为以后制造问题 我知道
IEEE库;
使用IEEE.STD_LOGIC_1164.ALL;
使用IEEE.NUMERIC_STD.ALL;
UNISIM图书馆;
使用UNISIM.VComponents.all;
使用IEEE.math_real.all;
实体CLK_分隔符为
通用(输入频率:整数;--只能将输入频率除以2的幂a
输出1_频率:整数
);
端口(SYSCLK:标准逻辑中;
复位N:在标准逻辑中;
OUT1:OUT STD_逻辑——实际除法器为2^(上限[log2(输入/频率)])
OUT2:OUT标准逻辑)--实际输出是输入超过上述值
端时钟分频器;
Clk_分配器的架构是
常数除法器:整数:=输入\频率/输出1 \频率;
常量计数器_位:整数:=整数(ceil(log2(实数(除法器)));
信号计数器:无符号(计数器位-1到0):=(其他=>“0”);
开始
进程:进程(SYSCLK)
开始
如果上升沿(SYSCLK),则
计数器从功能上讲,两个输出OUT1
和OUT2
可以用作时钟,但这种制作时钟的方法不可缩放,可能会在实现中造成问题,因此这是一个坏习惯。然而,理解为什么会这样当然很重要
它不可伸缩的原因是,FPGA中用作时钟的每个信号都将通过一个特殊的时钟网络进行分配,在这个时钟网络中,延迟和偏移都有明确的定义,因此每个时钟上的所有触发器和存储器都会同步更新。此类时钟网的数量非常有限,通常在FPGA设备中为10到40个,对使用和位置的一些限制使得规划时钟网的使用通常更加关键。因此,通常需要为真正的异步时钟预留时钟网络,在这种情况下,除了使用时钟网络之外别无选择
它可能导致问题的原因是,基于计数器中的位创建的时钟没有保证的定时关系。因此,如果需要在这些时钟域之间移动数据,则需要额外的同步约束,以确保正确处理时钟域交叉(CDC)。这是通过对合成和/或静态时序分析(STA)的约束来实现的,通常有点棘手,因此使用简化STA的设计方法是节省设计时间的习惯
因此,在可以使用公共时钟,然后生成同步时钟启用信号的设计中,这应该是首选方法。对于上述特定设计,只需检测相关计数器
位的'0'
到'1'
转换,然后在检测到转换的单个周期中断言时钟启用,即可生成时钟启用。然后可以使用单个时钟网络,以及2个时钟使能,如CE1
和CE2
,并且不需要特殊的STA约束。Morten已经在中指出了该理论。
借助两个示例,我将演示使用生成的时钟而不是时钟启用时遇到的问题
时钟分布
首先,必须注意所有目标触发器的时钟到达(几乎)同一时间。否则,即使是具有两级的简单移位寄存器,也会出现如下故障:
process(clk_gen)
begin
if rising_edge(clk_gen) then
tmp <= d;
q <= tmp;
end if;
end if;
以下测试台仅在输入d
处输入“1”,因此,q
在1个时钟边缘后应为“0”,在两个时钟边缘后应为“1”
library ieee;
use ieee.std_logic_1164.all;
entity shift_reg_tb is
end shift_reg_tb;
architecture sim of shift_reg_tb is
signal clk_gen : std_logic;
signal d : std_logic;
signal q : std_logic;
begin -- sim
DUT: entity work.shift_reg port map (clk_gen => clk_gen, d => d, q => q);
WaveGen_Proc: process
begin
-- Note: registers inside DUT are initialized to zero
d <= '1'; -- shift in '1'
clk_gen <= '0';
wait for 2 ns;
clk_gen <= '1'; -- just one rising edge
wait for 2 ns;
assert q = '0' report "Wrong output" severity error;
wait;
end process WaveGen_Proc;
end sim;
现在,只需通过该测试台的寄存器0输入新的数据值“11”:
library ieee;
use ieee.std_logic_1164.all;
entity handover_tb is
end handover_tb;
architecture sim of handover_tb is
signal clk_orig : std_logic := '0';
signal d : std_logic_vector(1 downto 0);
signal q : std_logic_vector(1 downto 0);
begin -- sim
DUT: entity work.handover port map (clk_orig => clk_orig, d => d, q => q);
WaveGen_Proc: process
begin
-- Note: registers inside DUT are initialized to zero
d <= "11";
clk_orig <= '0';
for i in 0 to 7 loop -- 4 clock periods
wait for 2 ns;
clk_orig <= not clk_orig;
end loop; -- i
wait;
end process WaveGen_Proc;
end sim;
ieee库;
使用ieee.std_logic_1164.all;
实体移交是
结束移交;;
切换的架构sim_tb为
信号时钟源:标准逻辑:='0';
信号d:标准逻辑向量(1到0);
信号q:std_逻辑_向量(1到0);
开始——模拟
DUT:entity work.handover端口映射(clk_orig=>clk_orig,d=>d,q=>q);
WaveGen_过程:过程
开始
--注:DUT内的寄存器初始化为零
d只是为了澄清-你说它不可缩放是什么意思?
library ieee;
use ieee.std_logic_1164.all;
entity shift_reg_tb is
end shift_reg_tb;
architecture sim of shift_reg_tb is
signal clk_gen : std_logic;
signal d : std_logic;
signal q : std_logic;
begin -- sim
DUT: entity work.shift_reg port map (clk_gen => clk_gen, d => d, q => q);
WaveGen_Proc: process
begin
-- Note: registers inside DUT are initialized to zero
d <= '1'; -- shift in '1'
clk_gen <= '0';
wait for 2 ns;
clk_gen <= '1'; -- just one rising edge
wait for 2 ns;
assert q = '0' report "Wrong output" severity error;
wait;
end process WaveGen_Proc;
end sim;
library ieee;
use ieee.std_logic_1164.all;
library unisim;
use unisim.vcomponents.all;
entity handover is
port (
clk_orig : in std_logic; -- original clock
d : in std_logic_vector(1 downto 0); -- data input
q : out std_logic_vector(1 downto 0)); -- data output
end handover;
architecture rtl of handover is
signal div_q : std_logic := '0'; -- output of clock divider
signal bufg_o : std_logic := '0'; -- output of clock buffer
signal clk_gen : std_logic; -- generated clock
signal reg_0_q : std_logic_vector(1 downto 0) := "00"; -- output of register 0
signal reg_1_d : std_logic_vector(1 downto 0); -- data input of register 1
signal reg_1_q : std_logic_vector(1 downto 0) := "00"; -- output of register 1
begin -- rtl
-- Generate a clock by dividing the original clock by 2.
-- The 100 ps delay is the clock-to-output time of the flip-flop.
div_q <= not div_q after 100 ps when rising_edge(clk_orig);
-- Add global clock-buffer as well as mimic some delay.
-- Clock arrives at (almost) same time on all destination flip-flops.
clk_gen_bufg : BUFG port map (I => div_q, O => bufg_o);
clk_gen <= transport bufg_o after 1000 ps;
-- Sample data input with original clock
reg_0_q <= d after 100 ps when rising_edge(clk_orig);
-- Different wire delays between register 0 and register 1 for each bit
reg_1_d(0) <= transport reg_0_q(0) after 500 ps;
reg_1_d(1) <= transport reg_0_q(1) after 1500 ps;
-- All flip-flops of register 1 are clocked at the same time due to clock buffer.
reg_1_q <= reg_1_d after 100 ps when rising_edge(clk_gen);
q <= reg_1_q;
end rtl;
library ieee;
use ieee.std_logic_1164.all;
entity handover_tb is
end handover_tb;
architecture sim of handover_tb is
signal clk_orig : std_logic := '0';
signal d : std_logic_vector(1 downto 0);
signal q : std_logic_vector(1 downto 0);
begin -- sim
DUT: entity work.handover port map (clk_orig => clk_orig, d => d, q => q);
WaveGen_Proc: process
begin
-- Note: registers inside DUT are initialized to zero
d <= "11";
clk_orig <= '0';
for i in 0 to 7 loop -- 4 clock periods
wait for 2 ns;
clk_orig <= not clk_orig;
end loop; -- i
wait;
end process WaveGen_Proc;
end sim;