【问题标题】:How do I solve this delta cycle clock delay issue如何解决这个增量周期时钟延迟问题
【发布时间】:2016-07-09 06:12:03
【问题描述】:

我的代码有以下简化示例,其中可以模拟 DeltasTest 实体以显示问题。实际设计中的时钟是倒置的,或者不是基于泛型的,而是为低于该泛型的其他几个实体提供数据。

问题在于简单的边缘检测器在行为模拟中不起作用(data_out 只是一个小故障),这是由于反转阶段在时钟上引入了增量周期延迟。有没有标准或其他优雅的方法来解决这个问题?

到目前为止,我最好的解决方案是将data_in 信号分配给另一个信号,使其具有与clk 相同的增量周期延迟。我想过使用一个函数来根据需要根据泛型作为函数的第二个参数来反转时钟,但是时钟在很多地方都使用过,这看起来不是很优雅,我注意到它甚至可以解决问题。紧抓着稻草,我还尝试将时钟反转分配设为transport 分配,但正如预期的那样,这没有任何区别。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity Deltas is
    Generic (
        CLK_INVERT : boolean := false
    );
    Port (
        clk : in std_logic;
        data_in : in std_logic
    );
end Deltas;

architecture Behavioral of Deltas is

    -- Signals
    signal data_reg : std_logic := '0';
    signal clk_inverted : std_logic := '0';
    signal data_out : std_logic := '0';

begin

    ClkInvert : if (CLK_INVERT) generate
        clk_inverted <= not clk;
    else generate
        clk_inverted <= clk;
    end generate;

    process (clk_inverted)  
    begin
        if (rising_edge(clk_inverted)) then
            data_reg <= data_in;
        end if;
    end process;

    process (data_reg, data_in) 
    begin
        if (data_reg /= data_in) then
            data_out <= '1';
        else
            data_out <= '0';
        end if;
    end process;

    -- Other entities use `clk_inverted`. Commented out so that this example compiles
    --LowerEntity : entity work.Counter
    --port map (
    --  clk => clk_inverted
    --);

end Behavioral;

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity DeltasTest is
end DeltasTest;

architecture Behavioral of DeltasTest is

    signal clk : std_logic := '0';
    signal data_in : std_logic := '0';

begin

    clk <= not clk after 10 ns;

    process (clk)
        variable count : integer := 0;
    begin
        if (rising_edge(clk)) then
            count := count + 1;
            if (count = 4) then
                count := 0;
                data_in <= not data_in;
            end if;
        end if;
    end process;

    uut : entity work.Deltas
    Port map (
        clk => clk,
        data_in => data_in
    );

end Behavioral;

【问题讨论】:

  • 两个问题:1)为什么不在Deltas中使用falling_edge(),而不是生成一个带有增量延迟的clk_inverted,然后在此使用rising_edge()? 2) 为什么data_in 上的刺激不是基于clk 生成的,就像在实际设计中那样,而不仅仅是基于时间?改变这将使边缘检测器工作。如果需要反转时钟,则在 clk 之外的顶层进行此操作,并通过层次结构进行分配。
  • @MortenZilmer 感谢 cmets。 1)我没有使用falling_edge(),因为时钟极性取决于通用。 2)数据信号实际上是由clk生成的,但这最终没有任何区别,因为我的示例与我的真实代码并不完全相同。我将更新示例以包含您的更改。
  • 您可能会在electronics.stackexchange.com找到更多的受众

标签: vhdl simulation


【解决方案1】:

我认为您在data_in 上的额外增量循环的“解决方案”可能是最干净简单的解决方案。

从语义上讲,对clk_inverted 的分配可以转换为硬件作为时钟信号中的反相器,因此模拟中的增量周期延迟表示在真实硬件中引入的(可能是(*)真实的)竞争条件。

因此,仿真行为不仅令人烦恼,而且是一种有用的属性,它警告您可能会偏离必须注意的良好同步设计实践。

(*) 仅“可能是真实的”,因为根据技术的不同,合成可能会通过检测相反边缘将反转“推”到下游 FF - 根据 Morten 的评论自动转换您的设计。

当额外的增量周期隐藏在其他人的 IP 中时,问题就变得更大了,也许是外部组件的模型,例如内存。然后 IP 不再受您的控制,板级仿真可能会在错误的时钟周期内读取或写入数据。

在这里,我的答案是添加近似 I/O 延迟,或者作为 FPGA 的 I/O 分配中的惯性延迟(来自 FPGA 数据表或综合报告),或者作为 PCB 上的传输延迟。这确保了我的模拟近似于电路板的行为 - 不是完美的,但足够接近让我能够按照正常的同步实践来设计内部逻辑。


更简洁的解决方案是基于泛型生成两个不同的时钟进程。这完全消除了讨厌的 delta-cycle。

终于!用于 SM 的 2 进程形式,其中保持时钟进程尽可能简单有好处......

没那么快……

您要重构的行为是时钟边沿检测,它已经从过时且复杂的(级别和事件)表达式重构为一个简单的函数。

为什么不进一步重构时钟边沿检测,例如...

function clock_edge(signal clk : in std_logic) return boolean is
begin
   if CLK_INVERT then
      return falling_edge(clk);
   else 
      return rising_edge(clk);
   end if;
end clock_edge;
...

process (my_clock) is
begin
   if clock_edge(my_clk) then ...

我尚未对此进行测试以查看综合工具是否可以根据需要实现此功能。有些可能会,有些可能不会(如果他们只是对系统提供的功能进行特殊处理而不是正确地完成工作)

在分层设计中,此功能将位于一个包中,供任何需要它的实体使用。这带来了泛型可见性的问题:一种建议是在包中使用常量,另一种建议是将泛型作为第二个参数传递给函数。正确的方法可能取决于综合工具施加的限制。

【讨论】:

  • 感谢您的回答。所涉及的数据信号实际上是在 FPGA 内部创建的,其方式与我在上面这个特定设计中更新的测试台中所示的方式相同,但所讨论的实体可以在两个信号都是外部输入的情况下使用。有趣的一点是,如果插入了实际的逆变器,这可能是一个现实世界的问题。
  • 另一种解决方案是基于泛型生成两个不同的时钟进程。最后!用于 SM 的 2 进程形式,其中保持时钟进程尽可能简单有好处!
  • 感谢您提供额外的想法,我没有想过将时钟边缘功能作为“本地”功能,这样就不需要每次都传递布尔值,这样就更干净了如果按照我的想法进行,那就会是这样。在这种情况下使用函数的主要问题是,该实体下面还有其他实体,它们也使用倒转(或不使用)时钟。
  • @scary_jeff 您可以将此限制添加到问题中。
  • @MartinZabel 我们指的是哪个“这个”?我已经有The clock in the real design [...] feeds several other entities below this one
【解决方案2】:

如何使用旧式时钟并将 CLK_INVERT 替换为 CLK_POLARITY。自己没有做过,但我很确定我在某个地方看到过。

entity Deltas is
    Generic (
        CLK_POLARITY : std_logic := '1'
    );
    Port (
        clk : in std_logic;
        data_in : in std_logic
    );
end Deltas;
architecture Behavioral of Deltas is
    signal data_reg : std_logic;
    signal data_out : std_logic;

begin

    process (clk)  
    begin
        if clk = CLK_POLARITY and clk'event then
            data_reg <= data_in;
        end if;
    end process;

这引发了一个更大的问题,一个用于不同设计的写入 IP 如何需要不同的时钟极性(如此处所做的)、不同的复位极性和不同的复位方式(异步复位与同步复位)。我认为我们需要一个实现触发器的程序包。除了专门构建的带有异步重置的 DFF 之外,我认为我们还可以拥有诸如 DFFR(带重置的 DFF)之类的东西,其实现由使用的包体确定。

-- In package body asynchronous resets
procedure DFFR (
  signal Clk   : std_logic ; 
  signal Reset : std_logic ; 
  signal DataIn : std_logic ;
  signal DataOut : std_logic ;
  constant ResetValue : std_logic 
) is
begin
  if Reset = RESET_POLARITY then 
    DataOut <= ResetValue ; 
  elsif Clk = CLOCK_POLARITY and Clk'event then 
    DataOut <= DataIn ; 
  end if ;
end procedure DFFR ; 

-- In package body synchronous resets
procedure DFFR (
  signal Clk   : std_logic ; 
  signal Reset : std_logic ; 
  signal DataIn : std_logic ;
  signal DataOut : std_logic ;
  constant ResetValue : std_logic 
) is
begin
  if Clk = CLOCK_POLARITY and Clk'event then 
    if Reset = RESET_POLARITY then 
      DataOut <= ResetValue ; 
    else
      DataOut <= DataIn ; 
    end if ;
  end if ; 
end procedure DFFR ; 

-- In package body power on reset by initialization
procedure DFFR (
  signal Clk   : std_logic ; 
  signal Reset : std_logic ; 
  signal DataIn : std_logic ;
  signal DataOut : std_logic ;
  constant ResetValue : std_logic 
) is
begin
  if Clk = CLOCK_POLARITY and Clk'event then 
    DataOut <= DataIn ; 
  end if ;
end procedure DFFR ; 

针对不同时钟域做出不同决策的设计需要为每个时钟域使用单独的库。

即将到来的 IEEE 1076-2018 正在为 IEEE 开源进行试点,以开源当前的 VHDL 包。也许我们可以在下一次修订中得到这个——但是,目前我的想法比我的时间还多,所以我们需要更多的参与者。

【讨论】:

  • 哇,开源 VHDL 包?!希望我们不会像 Linux 内核那样得到扩散。需要一些适当的审查机制。否则 VHDL 工具供应商只会有选择性地支持新标准(更糟糕的是,他们现在支持 VHDL-2008)
  • 顺便说一下,也许,关于第二个代码你不需要两个包。只需添加一个SYNCHR_RESET 布尔泛型,然后使用if not SYNCHR_RESET and reset=RESET_POLARITY then [...] elsif clk=CLOCK_POLARITY and clk'event then if SYNCHR_RESET and reset=RESET_POLARITY then [...etc.]。我认为综合工具会接受这一点。
  • @JHBonarius 开源的重要性在于分发,尤其是使用 GHDL 等开源工具进行分发。更进一步,WRT 供应商,我只希望他们关注 IEEE Open Source Group 发布的版本。流程仍待定,但我希望它融合了 IEEE 和开源方法的一些优点。
【解决方案3】:

我认为你有一个亚稳态发生的案例。您输入的数据,如果它来自外部世界可能不会同步。

通过两个级联寄存器对 data_in 进行时钟控制,并在两个寄存器之间进行比较,而不是将 data_reg 与输入信号进行比较。

我很确定我以前犯过这个错误

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-29
    • 2021-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多