时钟过程中的 1 周期使能信号

1-cycle enable signal in a clocked process

我正在参加 vhdl 在线课程。 其中一项实验室工作是:“基于分频器和8位循环移位寄存器实现一个移位周期为1s的环形计数器。”

任务表示计数器的最高有效位不能用作移位寄存器的时钟信号(即在 if rising_edge(移位器 (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

如果enable信号的持续时间是1个周期clk,那么有概率在rising_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 和 de-asserted 上升沿 N+1 之后置为高电平.因此,您的移位寄存器在上升沿移位 N+1.

所以在 en 的断言和寄存器移位之间有大约一个时钟周期的延迟。你不在乎,因为你的规范说你想要 1 秒的轮班周期。只要 en 是周期性的,周期为一秒,即使 en 和您的移位寄存器之间有一个小的恒定延迟,您也符合规格。

但最重要的是,正如您的移位寄存器所见,en 被断言足够高 上升沿 N 之后为了避免过早的转变和 de-asserted 在 上升沿 N+1 之后充分 以允许良好的转变。如果您也对此感兴趣,请继续阅读。

详细解释

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

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

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

在数字设计中,您总是有所谓的关键路径。它是一组源寄存器和目标寄存器之间的特定逻辑门链,电信号的传播延迟是整个设计中最大的。

在所有可能的路径中选择哪条路径以及这条路径上的总延迟取决于您设计的复杂性(例如计数器的位数)、目标硬件技术(例如原型板的 FPGA)和操作条件(温度、电源电压、speed-grade 你的 FPGA)。

(是的,这也取决于温度,hard-core 游戏玩家使用高性能冷却系统冷却计算机的原因。这避免了硅的破坏并允许以更高的时钟运行计算机每秒帧数更多的频率和更好的用户体验。)

信号从源clock-edge到达目的地所需的最长时间,增加了一个小的安全余量,称为目标寄存器的设置时间,是您可以 运行 设计的最小时钟周期(最高时钟频率)。只要您不超过此限制,您的系统就会按预期工作。

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

但是,如果您需要 650 MHz,是时候卷起袖子,看看关键路径(STA 也会显示路径),了解它并重新设计您的设计以加快速度(例如流水线长计算,使用进位先行加法器而不是进位纹波...)请注意,通常,当您遇到时序收敛问题时,您不会只考虑一个关键路径,而是考虑超出您的时间预算的所有路径的集合,因为您想要消除它们全部,不仅仅是最坏的。这就是为什么 STA 不仅会为您提供最差的关键路径,还会按严重性降序提供关键路径列表。