变量是不是也需要 VHDL 默认信号分配?
Posted
技术标签:
【中文标题】变量是不是也需要 VHDL 默认信号分配?【英文标题】:Is VHDL default signal assignment also necessary for variables?变量是否也需要 VHDL 默认信号分配? 【发布时间】:2018-09-26 14:05:35 【问题描述】:我了解默认信号分配在 VHDL 中很有用(甚至是必要的),以避免不完整的分配和推断的锁存器。我无法发现变量是否也需要。
考虑一下这段代码:
test_sequencer : process(fpga_clock)
variable sequencer_count : unsigned(3 downto 0) := (others => '0');
begin
if rising_edge(fpga_clock) then
start_writing <= start_writing;
start_reading <= start_reading;
sequencer_count := sequencer_count; -- is this line necessary
if not_every_fpga_clock = '1' then
case sequencer_count is
when x"4" =>
start_writing <= '1';
when x"12" =>
start_reading <= '1';
when others =>
null;
end case;
if sequencer_count /= 15 then
sequencer_count := sequencer_count + 1;
end if;
end if;
end if;
end process;
是否标有“这条线是必要的吗?”必需的?我知道如果sequencer_count
是一个信号,它必须在那里。 sequencer_count
是变量时是否需要?
【问题讨论】:
您将获得多个不同问题的某些方面的答案。在没有值变化的模拟信号标识分配中不会产生事件(IEEE Std 1076-2008, 14.7.3.4 信号更新),变量具有单个当前值(6.4.2.4 变量声明),标识分配在综合(IEEE Std 1076.6-2004,RTL 综合,撤回,6.2.1.1 具有敏感性列表的过程中的电平敏感存储“默认情况下,信号(或变量)的身份分配的效果应如同分配没有现在。”)。 您的评论意味着什么?身份分配没有价值是否是进一步的论点?或者您是说在某些情况下有理由使用身份分配? 【参考方案1】:首先,默认分配仅在不存在时钟的情况下防止锁存 - 即:组合逻辑。
在寄存器/触发器逻辑中(“ifrising_edge(clock)”之后的代码),默认分配防止保持条件 - 这是寄存器逻辑的正常部分。 “如果上升沿(时钟)”下的代码永远不会产生锁存器。
OTOH,我称之为身份分配:
start_writing <= start_writing;
start_reading <= start_reading;
sequencer_count := sequencer_count;
身份分配不是默认分配并明确指定反馈。这些不会阻止组合逻辑中的锁存器。
默认赋值分配一个字面值或一个组合逻辑信号输出的值:
start_writing <= '0';
sequencer_count := "0000";
LedState <= LedNext;
请注意,对于进程中的变量声明,初始化仅在进程详细说明时运行 - 这是在开始时间而不是每次进程运行时:
variable sequencer_count : unsigned(3 downto 0) := (others => '0');
这与每次调用时创建和初始化变量的子程序执行不同。
【讨论】:
您的回答帮助了我的理解。身份分配是否有任何价值?如果在if rising_edge(clock)
子句中设置了一个信号或变量,它无论如何都会是一个锁存器,这足以定义行为并且标识分配是多余的。这是我从其他两个答案中理解的。
身份分配唯一有价值的时候是它覆盖了以前的设置(在同一过程中完成)。我从来没有任何机会使用它。
是否有充分的理由来定义明确的反馈(例如您所说的身份分配,或 if rising_edge(clock) then signal <= … else signal <= signal;
中的 else
子句)?这会改变内在功能,还是会以任何方式影响合成?
我发现使用身份分配没有任何好处。作为else
到if rising_edge(Clk)
,综合工具不太可能支持它。同样从模拟的角度来看,这是一个额外的、不必要的动作。【参考方案2】:
这些行都不是必需的:
start_writing <= start_writing;
start_reading <= start_reading;
sequencer_count := sequencer_count; -- is this line necessary
他们都没有做任何事情。在完全分配和锁存推理中,只是组合逻辑的问题。这是(同步)顺序逻辑。您永远不需要担心(同步)顺序逻辑的完整分配。
这是为什么?好吧,在组合逻辑的情况下,如果您没有完整的分配,将会有一些输入组合导致通过过程的路径,其中过程的输出(由它驱动的信号)确实没有被赋值。在分配新值之前,任何信号都将保持其值。因此,在分配不完整的情况下,结果电路有必要记住(存储)该输出的状态。因此,将合成某种可以存储信息的电路。那不会是触发器,因为没有时钟,没有rising_edge
函数调用等;相反,将合成锁存器来进行记忆。这是双重糟糕的,因为 (a) 锁存器从根本上来说是坏的,并且 (b) 你想要组合逻辑但得到了顺序。这是一个错误。
但是,在(同步)时序电路中,您已经有了存储空间。触发器可以被认为是 1 位存储器。因此,您无需担心(同步)顺序过程中的完整分配。您期望生成的电路能够存储东西。
【讨论】:
【参考方案3】:在同步进程中,使用rising_edge(fpga_clock)
,既不需要默认变量赋值也不需要默认信号赋值,两者都会保持上升时钟之前的值,如果进程触发时没有新的赋值(执行) 作为上升时钟的结果。
关于推断锁存器,您可能想到的可能与组合过程有关,如果if
语句的某些分支未分配给驱动信号,则可以推断锁存器,例如:
process (d, en, q)
begin
if en = '1' then
q <= d;
end if;
end process;
在这种情况下,q
仅在 en
是 '1'
时更新,因此如果 en
是 '0'
,则推断一个锁存器来保存 q
的值。
作为相关评论,在顺序(时钟)过程中使用变量来保持状态通常是不好的编码风格,最好为sequencer_count
声明一个信号。原因是所有触发器的编码风格都是相同的,并且更容易调试,因为在典型的仿真波形中看不到变量。
【讨论】:
以上是关于变量是不是也需要 VHDL 默认信号分配?的主要内容,如果未能解决你的问题,请参考以下文章