时钟过程中的1周期启用信号

问题描述

我正在上vhdl在线课程。 实验室的一项工作是:“基于分频器和8位循环移位寄存器,实现了具有1 s移位周期的环形计数器。”

任务表明,计数器的最高有效位不能用作移位寄存器的时钟信号(即,在if上升边缘(移位器(MSB))结构中)。 必须将使能信号形成为选通脉冲。

我做了工作。结果被接受。

我有一个与使能移位寄存器有关的问题。

    shift_reg_proc : process(clk)
    begin
        if (rising_edge(clk)) then
            if    (srst = '1') then
                shift_reg <= "10000000";
            elsif (en = '1') then
                shift_reg <= shift_reg(0) & shift_reg(7 downto 1);
            end if;
        end if;
    end process shift_reg_proc

如果使能信号的持续时间为1个周期clk,则很有可能在rise_edge(clk)时刻en信号电平将没有时间变为= 1。 如果是这种情况,则不能保证寄存器移位将在下一秒发生。 有什么“正确”的方法可以完成此任务吗? 是这样吗?我的决定正确吗?实验室线索会误导人吗?

我将附加实现代码,测试台和波形图。

ring_counter.vhd

--------------------------------------------------------------------------------
-- Based on frequency divider and 8-bit cyclic shift register implement a ring 
-- counter with a shift period of 1 s.
--------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity ring_counter is
    
    port(clk  : in  std_logic;
         srst : in  std_logic;
         dout : out std_logic_vector(7 downto 0);
         en_o : out std_logic
    );

end entity ring_counter;

architecture behave of ring_counter is

    signal   cntr             : std_logic_vector(26 downto 0) := (others => '0');
    signal   cntr_msb_delayed : std_logic;
    signal   shift_reg        : std_logic_vector(7 downto 0);
    signal   en               : std_logic;
    constant cntr_msb_num     : integer := 4; -- 26 for DE board,4 for test bench 

begin
    
    -- signal for test bench
    en_o <= en;

    --------------------------------------------------------------------------------
    -- Counter implementation
    --------------------------------------------------------------------------------

    cntr_proc : process(clk)
    begin
        if (rising_edge(clk)) then
            if (srst = '1') then
                cntr <= (others => '0');
            else
                cntr <= unsigned(cntr) + 1;
            end if;
        end if;
    end process cntr_proc;

    ----------------------------------------------------------------------------
    -- Shift register implementation
    ----------------------------------------------------------------------------

    shift_reg_proc : process(clk)
    begin
        if (rising_edge(clk)) then
            if    (srst = '1') then
                shift_reg <= "10000000";
            elsif (en = '1') then
                shift_reg <= shift_reg(0) & shift_reg(7 downto 1);
            end if;
        end if;
    end process shift_reg_proc;
    
    dout <= shift_reg;

    ----------------------------------------------------------------------------
    -- Enable signal generation
    ----------------------------------------------------------------------------

    -- Counter MSB delay for 1 period of clk
    delay_proc : process(clk)
    begin
        if (rising_edge(clk)) then
            cntr_msb_delayed <= cntr(cntr_msb_num);
        end if;
    end process delay_proc;

    en <= cntr(cntr_msb_num) and not cntr_msb_delayed;


end architecture behave;

ring_counter_tb.vhd

library ieee;
use ieee.std_logic_1164.all;

entity ring_counter_tb is
    
end entity ring_counter_tb;

architecture behave of ring_counter_tb is

    component ring_counter is
        port(clk  : in  std_logic;
             srst : in  std_logic;
             dout : out std_logic_vector(7 downto 0);
             en_o : out std_logic
        );
    end component ring_counter;

    signal clk  : std_logic;
    signal srst : std_logic;
    signal dout : std_logic_vector(7 downto 0);
    signal en_o : std_logic;

    constant clk_period : time := 4 ns;

begin

    dut : ring_counter
        port map (
            clk  => clk,srst => srst,dout => dout,en_o => en_o
        );

    clk_gen : process
    begin
        clk <= '0';
        wait for clk_period;
        loop
            clk <= '0';
            wait for clk_period/2;
            clk <= '1';
            wait for clk_period/2;
        end loop;
    end process clk_gen;

    srst <= '0','1' after 100 ns,'0' after 150 ns;

    
end architecture behave;

wave for test bench

解决方法

TL; DR

clk的上升沿之后的en上升与移位寄存器移位的clk的上升沿不同。 en在上升沿N之后被置为高电平,而在 上升沿N+1之后被置为无效。因此,您的移位寄存器在N+1的上升沿移位。

因此,在en的声明和寄存器移位之间大约有一个时钟周期延迟。您不在乎,因为您的规格说明您想要1秒的转换时间。只要en是周期为一秒的周期,即使en与移位寄存器之间的常数延迟很小,您就可以满足规范。

但是最重要的是,正如您的移位寄存器所看到的,在{strong>上升沿en之后,N被断言为足够高,以避免过早出现在N+1上升沿之后 撤消并充分断言以允许进行良好的平移。如果您对此也有兴趣,请继续阅读。

详细说明

您的en信号是根据与移位寄存器在同一时钟clk上同步的寄存器的输出计算得出的。您在那里没有任何保持时间问题:从时钟的上升沿到cntrcntr_msb_delayed寄存器的输出的传播延迟保证了en将在引起时钟的时钟上升沿之后足够地到达(假设您没有较大的时钟偏斜)。它不能太早到达。

到达时间是否太晚(设置时间问题)?是的,如果您的时钟频率太高。这样一来,时钟周期将太短,en将没有足够的时间来计算,稳定并传播到下一个时钟上升沿之前的移位寄存器,并且可能发生任何事情(根本没有移位,部分移位,亚稳定...)

这是数字设计中非常普遍的问题:您不能以任意高的时钟频率进行操作。如果可以的话,您可以将自己的计算机以yotta-Hz或更高的频率(而不是giga-Hz)提供时钟,一切都会变得瞬间。很好,但这不是现实世界的工作方式

在数字设计中,您始终拥有称为关键路径的东西。它是一组源寄存器和目标寄存器之间的特定逻辑门链,沿着该逻辑门,电信号的传播延迟是整个设计中最大的。

这是一条可行路径,该路径上的总延迟取决于设计的复杂性(例如,计数器的位数),目标硬件技术(例如,原型板的FPGA)和工作条件(温度,电源电压,FPGA的速度等级)。

(是的,这还取决于温度,以及为什么硬核游戏玩家使用高性能冷却系统为计算机冷却的原因。这避免了硅的破坏,并允许以更高的时钟频率和更多的帧来操作计算机每秒和更好的用户体验。)

信号从源时钟沿到到达目的地所花费的最长时间,并加上一个很小的安全裕量,称为“目标寄存器的建立时间”,这是最小的。时钟周期(最高时钟频率),您可以在其中运行设计。只要您不超过此限制,系统就可以按预期运行。

硬件设计工具链通常包含一个静态时序分析器(STA),它告诉您该最大时钟频率对于您的设计,目标和操作条件而言是什么。如果它告诉您500 MHz,而您只需要350 MHz,那么一切都很好(但是您可以进行调查,看看是否可以修改设计,节省一些硬件并仍在350 MHz上运行)。

但是,如果您需要650 MHz,则该轮到袖子了,查看关键路径(STA也将显示路径),了解它并重新设计以加快速度(例如,流水线长计算,使用提前进位加法器,而不是进位纹波...)请注意,通常,当您遇到时序收敛问题时,您不会只考虑一条关键路径,而是考虑超出时间预算的所有路径,因为您想消除所有这些,不只是最坏的。这就是为什么STA不仅按照严重性从高到低的顺序为您提供最坏的关键路径,而且还列出关键路径的原因。