如何解决此增量周期时钟延迟问题

时间:2016-03-22 11:18:59

标签: vhdl simulation

我有以下代码的简化示例,其中可以模拟DeltasTest实体以显示问题。实际设计中的时钟是基于通用的反转或不反转,并且在此之下提供其他几个实体。

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

到目前为止,我最好的解决方案是将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;

3 个答案:

答案 0 :(得分:3)

我认为data_in上额外增量周期的“解决方案”可能是最简洁的解决方案。

从语义上讲,对clk_inverted的分配可能会转换为硬件作为时钟信号中的反相器,因此仿真中的增量循环延迟表示在实际硬件中引入的(可能(*)实际)竞争条件。 / p>

因此,模拟行为不仅仅是一种烦恼,而且是一种有用的属性,警告您可能会偏离必须遵守的良好同步设计实践。

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

当额外的delta周期隐藏在别人的IP中时,这可能是一个问题,可能是外部组件(如内存)的模型。然后,IP不再受您的控制,并且板级仿真可能会在错误的时钟周期内读取或写入数据。

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

更清洁的解决方案是基于通用生成两个不同的时钟进程。这完全消除了讨厌的delta周期。

最后!用于2过程形式的SM,其中保持时钟过程尽可能简单有益...

没那么快......

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

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

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 ...

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

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

答案 1 :(得分:1)

如何使用旧式时钟并将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包。也许我们可以在下一次修订中得到这个 - 但是,目前我有更多的想法,而不是我有时间,所以我们需要更多的参与者。

答案 2 :(得分:0)

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

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

我很确定我在

之前犯了这个错误