为什么在 VHDL for-loop 中不起作用?

Why in VHDL for-loop is not working?

我尝试在 ModelSim 中模拟 for 循环,但它无法正常工作。我知道它不能合成,但我不知道为什么它不能正确模拟。

模拟时,没有显示任何错误。但最终结果是“00000001”(即只增加一次)而不是“00000011”的预期输出。

library ieee;
use ieee.std_logic_1164.all;
use IEEE.STD_LOGIC_unsigned.ALL;

entity forloop is 
port(   enable      : in std_logic;         
     data_out       : out std_logic_vector(7 downto 0)); 
end forloop;

architecture behaviour of forloop is

signal temp     : std_logic_vector(7 downto 0) := "00000000";

begin

process(enable)         
begin   

    for i in 0 to 3 loop            
        temp <= temp +1;
    end loop;
end process;        
data_out <= temp;       
end behaviour;

模拟输出正确。 VHDL 中的 for 循环扩展为并行赋值,而不是您在 C 中得到的(顺序赋值)。这意味着

for i in 0 to 3 loop            
    temp <= temp +1;
end loop;

会变成

    temp <= temp +1;
    temp <= temp +1;
    temp <= temp +1;
    temp <= temp +1;

请注意,temp 是一个信号,将在下一个周期更新,因此 +1 被分配给所有 4 行代码的相同 temp 值。除此之外,您不使用敏感列表中的启用信号。您确定要使用异步过程吗?

IEEE 标准 1076-2008,10.10 循环声明:

A loop statement includes a sequence of statements that is to be executed repeatedly, zero or more times.

这具有将临时分配复制为 dieli 在他的回答中指出。

    process (enable)
    begin
        for i in 0 to 3 loop
            temp <= temp + 1;
        end loop;
    end process;

其实有两个缺陷。你已经注意到第一个了。

第一个缺陷

首先要注意的是,您正在对位于同一进程中的同一信号执行连续的顺序信号分配。每个进程只有一组驱动程序,进程执行一直发生到在同一模拟周期中暂停为止。作为等待敏感列表的最后一条语句被隐含的等待语句暂停。

这些分配作为循环的连续迭代发生,并且因为对于任何特定模拟时间的任何驱动器只有一个投影波形,所以只有最后一个信号分配才会实际发生。本质上,您是在为每个信号分配安排一次信号更新,并且它们的预计值会被下一个信号覆盖,直到只剩下最后一个信号分配。

当任何进程仍在当前模拟周期中执行或挂起时,没有信号分配更新值。

具有没有延迟的元素的波形的信号分配将导致增量循环,并且下一个排队模拟时间的所有未决信号分配将在当前模拟循环完成之后和增量模拟循环执行之前更新开始。另请参阅此答案 - The VHDL Simulation Cycle 以全面了解模拟周期中发生的情况。

在 IEEE 标准的先前修订版中更容易阅读深入的描述,请参阅 IEEE 标准 1076-2002/-2000/-1993,12.6 模型的执行。 14.7 中的 -2008 标准描述的可读性受到了 VHPI 和强制分配标准的补充。

有几种方法可以克服在同一进程中连续写入信号并在同一模拟周期执行期间发生的影响。

在此之前,让我们解决第二个缺陷。

第二个缺陷

您的敏感度列表包含一个元素,enable,它会触发循环语句的执行。 enable 上的任何交易都会发生这种情况,包括 std_ulogic 表示的所有四个二进制值及其元值。这包括从“1”到“0”、从“0”到“1”的转换或与 'H' 和 'L' 的组合,它们在综合中被相同地对待。

如果你想要一个基于锁存器的计数器,你可以用一个特定的值来限定它:

    process (enable)
    begin
        if enable = '1' then
            for i in 0 to 3 loop
                temp <= temp + 1;
            end loop;
        end if;
    end process;

这将导致循环语句仅在 enable 转换为“1”时执行。

我们通常不使用基于锁存器的计数器,因为正如您可能注意到的那样,这是边沿触发的,敏感度列表中只有 'enable',如果您要扩展敏感度列表中未使用的信号循环,但在该过程的其他地方,您的 temp 累加器将因其他事件而增加。

请注意,您无法在执行该过程时为 temp 获取新值,因此即使使用循环语句,它也只会按原样为每个启用事务递增一次。您在 i 循环的第 3 次迭代期间有效地分配了 temp + 1 的新值。当进程执行完成时(以及当前活动的任何其他进程)temp 得到更新。

那么我们怎样才能让temp递增四次呢?

我们可以简单地加 4。这并没有给我们一个通用的解决方案。

我们可以声明一个流程变量,在流程执行的时候给它赋值temp,在执行循环语句后赋值给temp

    process (enable)
        variable ltemp: std_logic_vector(7 downto 0);
    begin
        ltemp := temp;
        if enable = '1' then
            for i in 0 to 3 loop
                ltemp := ltemp + 1;
            end loop;
            temp <= ltemp;
        end if;
    end process;

这可能不符合综合条件,具体取决于综合工具,因为正如 dieli 所说,综合希望将变量赋值视为并行操作,而我们指望它相当于:

            ltemp := ltemp + 1 + 1 + 1 + 1;

这需要一种能够对添加进行排序的更智能的合成工具。

确保合成资格

我们可以通过使用一组 ltemp 值来解决这个问题,如下所示:

    process (enable, temp)
        type ltemp_array is array (0 to 3) of std_logic_vector(7 downto 0);
        variable ltemp: ltemp_array;
    begin
        if enable = '1' then
            for i in 0 to 3 loop
                if i = 0 then
                    ltemp(i) := temp + 1;
                else
                    ltemp(i) := ltemp(i-1) + 1;
                end if;
            end loop;
            temp <= ltemp(3);
        end if;
    end process;

注意 temp 已添加到敏感列表中,因为锁存器在启用为 TRUE 时是透明的。

条件 enable = '1' 不是现已撤销的 IEEE 标准 1076.6-2004 中给出的 \ 形式,用于 RTL 合成作为边沿敏感事件(所有这些都需要条件中的第二项) .综合工具基本上忽略了敏感列表,这意味着我们依赖于规则 c) of 1076.6 6.2.1.1 Level-sensitive storage from process with sensitivity list:

There are executions of the process that do not execute an explicit assignment (via an assignment statement) to the signal (or variable).

以及以下内容:

The process sensitivity list shall contain all signals read within the process statement.

这告诉我们 temp 应该一直在敏感列表中,以使过程综合符合条件。顺便说一句,也使这个答案综合中显示的过程的前两种形式不合格。

这也揭示了 第三个缺陷 当打算合成过程时,它有一个反馈循环通过 temp .您实际上会注意到将 temp 添加到敏感度列表并在模拟上述过程期间引起反馈的效果。如果你的模拟工具中存在,你要么 运行 进入增量循环限制,要么模拟器挂起或者可能依赖于其他一些依赖于实现的机制来检测无效模拟。

摆脱反馈回路的方法是使用边沿敏感时钟。

这也提出了使用单独进程的想法(当我们不能在一个时钟内执行所有加法时,可以通过将单独的并发条件信号分配给提升为信号的 ltemp 元素来推断循环.