我尝试在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;
答案 0 :(得分:2)
IEEE Std 1076-2008,10.10 Loop声明:
循环语句包含一系列语句 反复执行,零次或多次。
这具有将临时赋值复制为的效果 dieli在他的回答中表示。
process (enable)
begin
for i in 0 to 3 loop
temp <= temp + 1;
end loop;
end process;
实际上有两个缺陷。你注意到了第一个。
第一个缺陷
首先要注意的是,您正在对同一信号执行连续的顺序信号分配,并且分配位于同一过程中。每个进程只有一组驱动程序,并且在相同的模拟循环中暂停执行进程。由隐含的等待语句作为等待敏感性列表的最后一个语句暂停。
这些分配作为循环的连续迭代发生,并且因为对于任何特定模拟时间的任何驱动器,只有一个投影波形,实际上只会发生最后一次信号分配。基本上,您为每个信号分配安排信号更新,并且它们的预测值被下一个覆盖,直到只剩下最后一个信号分配。
当前模拟周期中任何进程仍在执行或挂起时,没有信号分配更新值。
具有无延迟元素的波形的信号分配将导致增量循环,并且在当前模拟循环完成之后且增量模拟循环执行开始之前,下一个排队模拟时间的所有待定信号分配将更新。另请参阅此答案 - The VHDL Simulation Cycle,了解模拟周期中发生的情况。
在IEEE标准的先前版本中更容易阅读深入描述,参见IEEE Std 1076-2002 / -2000 / -1993,12.6模型的执行。 14.7中的-2008标准描述使其可读性受到VHPI和强制分配标准的增加的阻碍。
有几种方法可以克服在同一过程中连续写入信号并在同一模拟周期执行期间发生的影响。
在此之前让我们解决第二个缺陷。
第二个缺陷
您的敏感列表包含一个元素enable
,它触发循环语句的执行。对enable
上的任何事务都会发生这种情况,包括由std_ulogic表示的所有四个二进制值及其元值。这包括从&#39; 1&#39;到&#39; 0&#39;,&#39; 0&#39;到&#39; 1&#39;或者与&#39; H&#39;和&#39; L&#39;在合成中处理相同。
如果你想要一个基于闩锁的计数器,你可以使用特定值来限定它:
process (enable)
begin
if enable = '1' then
for i in 0 to 3 loop
temp <= temp + 1;
end loop;
end if;
end process;
这会导致循环语句仅在enable
转换为&#39; 1时执行。
我们通常不会使用基于锁存器的计数器,因为您可能会注意到这只是边缘触发,只有&#39;启用&#39;在灵敏度列表中,如果您要扩展循环中未使用的信号的灵敏度列表,但在此过程中的其他位置,您的temp
累加器将会被其他事件递增。
请注意,在执行进程时,您无法为temp
获取新值,因此即使使用循环语句,它也只会按每个启用事务递增一次。您在temp + 1
循环的第3次迭代期间有效地分配了i
的新值。并且当完成流程的执行时(以及当前处于活动状态的任何其他流程),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 Std 1076.6-2004中用于RTL合成的形式的\ {作为边缘敏感事件(所有这些都需要第二个条件) 。综合工具基本上忽略了敏感性列表,这意味着我们依赖于1076.6的规则c)6.2.1.1具有敏感性列表的过程中的级别敏感存储:
执行过程中没有对信号(或变量)执行显式赋值(通过赋值语句)。
以及以下内容:
过程敏感性列表应包含在过程语句中读取的所有信号。
这告诉我们temp
应该一直在敏感度列表中,以使流程综合符合条件。顺便提一下,这个答案合成中显示的前两种形式的过程也不合格。
当打算合成流程时,它还会显示 第三个缺陷 ,它通过temp
进行反馈循环。您实际上已经注意到将temp
添加到灵敏度列表并在模拟上述过程期间引起反馈的效果。如果您的模拟工具中存在增量循环限制,或者模拟器将挂起或者可能依赖于某些其他实现相关机制来检测无效模拟,那么您将遇到增量循环限制。
摆脱反馈循环的方法是使用边缘敏感时钟。
这也提出了使用单独进程的想法(当我们无法在一个时钟内执行所有添加时,可以通过对ltemp
元素的单独并发条件信号分配来推断信号的推断。周期。
答案 1 :(得分:1)
模拟输出正确。 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行代码的相同临时值。除此之外,您不必在灵敏度列表中使用启用信号。你确定要使用异步过程吗?