如何避免VHDL中简单流程语句的输出延迟

how to avoid delay in the output of simple process statement in VHDL

我是 VHDL 的初学者。我想知道为什么在下面的code.and中有一个周期的延迟如何避免它..同时在verilog中语句总是@(posedge clk)没有任何延迟..如何做在 VHDL 中相同

library IEEE;
 use IEEE.std_logic_1164.all;

   -- entity
entity t_ff_s is
port ( T,S,CLK : in std_logic;
Q : out std_logic);
 end t_ff_s;
 -- entity
 architecture my_t_ff_s of t_ff_s is
signal t_tmp : std_logic; -- intermediate signal declaration
  begin
    tff: process (S,rising_edge(clk))
     begin
        if (S = '0') then
     t_tmp <= '1';
      --elsif (rising_edge(CLK)) then
     else 
     t_tmp <= T XOR t_tmp; -- temp output assignment
     end if;
    end process tff;
     Q <= t_tmp; -- final output assignment
     end my_t_ff_s;

VHDL 中的灵敏度列表不像 Verilog 中那样采用边缘规范。 VHDL 更灵活,因为您可以在进程中的任何位置自由使用 'event 信号属性来实现边沿触发行为。您可以混合电平和边缘敏感逻辑,而无需诉诸拆分 blocks/processes 或像 negedge 这样的 hack 来进行重置。敏感度列表中不允许像 rising_edge(clk)(实现 clk'event 的测试)这样的函数调用。它只包含信号名称。您的代码不会按原样编译。

如果您的代码的其他一些语法正确的版本可以干净地编译,您看到的延迟是模拟模型的伪影或敏感度列表损坏。如果你想要一个同步时钟驱动的过程,那么你只需要时钟信号,可能还需要灵敏度列表中的异步复位。

考虑以下过程:

tff: process(S, clk)
begin
  if S = '0' then -- Asynchronous reset (level sensitive)
    t_tmp <= '1';
  elsif rising_edge(clk) then -- Synchronous logic (edge sensitive)
    t_tmp <= T xor t_tmp;
  end if;
end process;

Q <= t_tmp;

此过程在 Sclk 上发生事件时执行。如果 S 为“0”,则重置条件优先于 elsif 子句执行(clk 无关)。对 t_tmp 的赋值在下一个 delta 周期生效,该周期仍与当前模拟时间相同。否则,如果 rising_edge(clk) 的计算结果为真,则 clk 上发生了一个事件,它的状态从“0”(或 'L')变为“1”(或 'H'),表明该事件是一个上升沿。同步赋值发生,新的 xored t_tmp 在下一个增量周期生效。 T 中的更改不会导致进程执行,因为它不在(也不应该)在敏感度列表中。

因为没有无条件的 else 子句,如果两个 if 条件都为假,t_tmp 信号将保留其最后分配的值。下次在 Sclk 上发生导致对 t_tmp 的新分配的事件时,它将更改。这将是下一个时钟边沿或重新应用异步复位。

Q 的分配是连续的,实际上与在其敏感列表中具有 t_tmp 的进程相同。因此,对 Q 的分配发生在 t_tmp 上的事件之后的增量周期,这是上升沿之后的两个增量周期。如果 Q 馈入更新早于边沿的第二个增量周期的逻辑,它似乎需要一个额外的时钟周期才能传播。

在检查波形时,围绕增量循环的行为有时会产生令人困惑的结果。您可能有一个上升沿,该上升沿应该捕获 出现 以在同一时间步长同时转换的数据输入,而实际上,数据正在稍后的增量周期中转换并且只会是在下一个时钟边沿捕获。

类似地,如果您构造一个没有任何时间延迟的简单门控时钟,它的边沿将同时出现,但比时钟的非门控版本更晚的增量周期。结果,从 "earlier" 非门控时钟驱动的数据将被门控逻辑捕获,比预期早一个时钟周期。驱动另一个方向的数据将出现一个时钟周期的意外延迟。

如果没有关于您如何驱动 STclk 信号的更多信息,则不清楚是什么导致了您看到的问题,但很可能以某种方式连接到模拟引擎的增量循环行为。

问题

比 Kevin 更简洁一点,rising_edge 是一个表达式而不是一个信号,敏感列表需要一个命名信号,一个事务,您可以在该事务上恢复执行挂起的进程。把elsif放回去,敏感列表里只有S和clk。

请注意,因为 t_tmp 不在敏感列表中,所以在下一个导致您注意到的延迟的时钟事件之前,您不会看到 Q 跟随 t_tmp。

固定语法流程:

     tff: process (S,clk) -- was (S, risingedge(CLK)), a syntax error)
     begin
        if (S = '0') then
     t_tmp <= '1';
      elsif (rising_edge(CLK)) then  -- put back
     -- else
     t_tmp <= T XOR t_tmp; -- temp output assignment
     end if;
      Q <= t_tmp; -- final output assignment
      end process tff;

显示t_tmp和Q:

之间的延迟

(可点击)

通过使 Q 成为并发信号分配来修复它

要消除半时钟延迟,您可以将对 Q 的赋值设为并发信号赋值语句(将其移出进程)。

tff:
    process (S, clk)
    begin
        if S = '0' then
            t_tmp <= '1';
        elsif  rising_edge(clk) then
            t_tmp <= T xor t_tmp;
        end if;
    end process;
    Q <= t_tmp;   -- concurrent signal assignment

给出:

(可点击)

你可以在上方看到 t_tmp 和 Q 现在处于同相。

通过将 t_tmp 设为变量来修复它

您还可以将 t_tmp 声明为进程 dff 中的变量而不是信号,并切换分配给它,因为变量分配也将解决 t_tmp 和 Q 之间的一个时钟延迟。

tff:
    process (S, clk)
        variable t_tmp:  std_logic;
    begin
        if S = '0' then
            t_tmp := '1';
        elsif  rising_edge(clk) then
            t_tmp := T xor t_tmp;
        end if;
        Q <= t_tmp;
    end process;

其中显示:

(可点击)

并且使用 gtkwave 的 ghdl 不输出变量或显示增量循环。可以看到Q发生在时钟的上升沿。

使 t_tmp 成为变量还具有消除 t_tmp 上的交易和 Q 上的交易之间的增量循环的效果。

消除增量循环使您的模型执行得更快(同时发生在当前模拟时间)。任何进程正在执行时信号分配不生效,变量分配立即生效。

通过将 t_tmp 添加到敏感度列表来修复它

或者您可以将 t_tmp 添加到敏感度列表(连同 S 和 clk)。

tff:
    process (S, clk, t_tmp)
    begin
        if S = '0' then
            t_tmp <= '1';
        elsif  rising_edge(clk) then
            t_tmp <= T xor t_tmp;
        end if;
        Q <= t_tmp;
    end process;

(可点击)

这比所有其他修复都慢,因为每次 t_tmp 有事件以及 S 或 CLK 时都会执行 if 语句。 rising_edge 是一个动态详细说明其接口列表的函数调用,这是一个显着的模拟器性能损失,特别是如果您使用大量这些原语。

这些是通过测试台完成的:

library IEEE;
 use IEEE.std_logic_1164.all;

   -- entity
entity t_ff_s is
port ( T,S,CLK : in std_logic;
Q : out std_logic);
 end entity t_ff_s;

architecture my_t_ff_s of t_ff_s is
signal t_tmp : std_logic; -- intermediate signal declaration
  begin
    tff: process (S,clk) -- was (S, risingedge(CLK)), a syntax error)
     begin
        if (S = '0') then
            t_tmp <= '1';
        elsif (rising_edge(CLK)) then  -- put back
        -- else
            t_tmp <= T XOR t_tmp; -- temp output assignment
        end if;
             Q <= t_tmp; -- final output assignment
    end process tff;
end my_t_ff_s;

architecture foe of t_ff_s is
    signal t_tmp: std_logic;
begin
tff:
    process (S, clk)
    begin
        if S = '0' then
            t_tmp <= '1';
        elsif  rising_edge(clk) then
            t_tmp <= T xor t_tmp;
        end if;
    end process;
    Q <= t_tmp;   -- concurrent signal assignment
end architecture;

architecture fie of t_ff_s is
begin
tff:
    process (S, clk)
        variable t_tmp:  std_logic;
    begin
        if S = '0' then
            t_tmp := '1';
        elsif  rising_edge(clk) then
            t_tmp := T xor t_tmp;
        end if;
        Q <= t_tmp;
    end process;
end architecture;

architecture fee of t_ff_s is
    signal t_tmp: std_logic;
begin
tff:
    process (S, clk, t_tmp)
    begin
        if S = '0' then
            t_tmp <= '1';
        elsif  rising_edge(clk) then
            t_tmp <= T xor t_tmp;
        end if;
        Q <= t_tmp;
    end process;
end architecture;

library ieee;
use ieee.std_logic_1164.all;

entity test_tff is
end entity;

architecture foo of test_tff is
    signal CLK: std_logic := '0';
    signal T:   std_logic := '0';
    signal S:   std_logic := '0';
    signal Q:   std_logic;

    component t_ff_s is
        port (
            signal CLK:     in  std_logic;
            signal T:       in  std_logic;
            signal S:       in  std_logic;
            signal Q:       out std_logic
        );
    end component;
begin

DUT: 
    t_ff_s
        port map (
            T => T,
            S => S,
            CLK => CLK,
            Q => Q
        );
CLOCK:
    process
    begin
        wait for 10 ns;
        CLK <= not CLK;
        if Now > 250 ns then
            wait;
        end if;
    end process;

SET:
    process
    begin
        S <= '0';
        wait for 20 ns;
        S <= '1';
        wait;
    end process;

TOGGLE:
    process
    begin
        wait for 20 ns;
        T <= '1';
        wait for 60 ns;
        T <= '0';
        wait for 40 ns;
        T <= '1';
        wait;
    end process;

end architecture;

configuration my_t_ff_s_config of test_tff is
    for foo 
        for  DUT: t_ff_s
            use entity work.t_ff_s(my_t_ff_s);
        end for;
    end for;
end configuration;

configuration concurrent_config of test_tff is
    for foo 
        for  DUT: t_ff_s
            use entity work.t_ff_s(foe);
        end for;
    end for;
end configuration;

configuration variable_config of test_tff is
    for foo 
        for  DUT: t_ff_s
            use entity work.t_ff_s(fie);
        end for;
    end for;
end configuration;

configuration sensitivity_config of test_tff is
    for foo 
        for  DUT: t_ff_s
            use entity work.t_ff_s(fee);
        end for;
    end for;
end configuration;

注意使用配置

使用 VHDL 的配置声明允许使用多种体系结构。 (my_t_ff_s - 原始,foe - 同时分配给 Q,fie - t_tmp 作为变量和 fee - t_tmp 在敏感列表中)。

令人惊讶的是,ghdl 的分析器对正确配置声明语法很有帮助。一旦你得到了第一个,其他的就很容易了。

我们倾向于使用配置生锈,综合工具历史上通常不支持它。不过话又说回来了,这是为了验证.

的模拟

对于那些使用 ghdl 和 gtkwave 的人来说,这是这样做的:

ghdl -a t_ff.vhdl
ghdl -e my_t_ff_s_config
ghdl -e concurrent_config
ghdl -e concurrent_config
ghdl -e sensitivity_config
ghdl -r my_t_ff_s_config --wave=test_tff_my_t_ff_s.ghw
ghdl -r concurrent_config --wave=test_tff_foe.ghw
ghdl -r variable_config --wave=test_tff_fie.ghw
ghdl -r sensitivity_config --wave=test_tff_fee.ghw

GHW is ghdl's native waveform dump file format, understood by gtkwave.

In gtkwave:

open t_ff_s.gtkw (reads in test_tff_my_t_ff_s.ghw)
(otherwise read in test_tff_my_t_ff_s.ghw and add signals to
waveform display, format the window, save save file to t_ff_s.gtkw)

new tab open test_tff_foe.ghw
read save file open t_ff_s.gtkw
new tab open test_tff_fie.ghw
read save file open t_ff_s.gtkw
new tab open test_tff_fee.ghw
read save file open t_ff_s.gtkw

Note ghdl doesn't save variable state or delta cycles, t_tmp won't show up in the waveform for test_ff_fie.ghw.