变量是否也需要 VHDL 默认信号分配?
Is VHDL default signal assignment also necessary for variables?
我知道默认信号分配在 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;
标记为"is this line necessary?"的那一行是必需的吗?我知道如果 sequencer_count
是一个信号,它就必须在那里。当sequencer_count
是一个变量时有必要吗?
在同步过程中,与rising_edge(fpga_clock)
一样,既不需要默认变量赋值也不需要默认信号赋值,两者都会保持时钟上升前的值,如果没有新的赋值时由于时钟上升,进程触发(执行)。
关于推断锁存器,您可能想到的可能与组合过程有关,如果 if
语句的某些分支未分配给驱动信号,则可以推断出锁存器,喜欢:
process (d, en, q)
begin
if en = '1' then
q <= d;
end if;
end process;
在这种情况下 q
仅在 en
为 '1'
时更新,因此如果 en
则推断锁存器保存 q
的值是 '0'
.
作为相关评论,使用变量在顺序(时钟)进程中保存状态通常是糟糕的编码风格,最好为 sequencer_count
声明一个信号。原因是所有触发器的编码风格都是相同的,并且更容易调试,因为在典型的仿真波形中看不到变量。
None 这些行是必需的:
start_writing <= start_writing;
start_reading <= start_reading;
sequencer_count := sequencer_count; -- is this line necessary
None 他们什么都做。在完全赋值和锁存推理中,只有组合逻辑存在问题。这是(同步)时序逻辑。您永远不需要担心使用(同步)顺序逻辑完成分配。
这是为什么?好吧,在组合逻辑的情况下,如果您没有完整的赋值,将会有一些输入组合,这些组合会导致遵循流程的路径,流程的输出(由它驱动的信号)会执行没有被分配一个值。在分配新值之前,任何信号都将保持其值。因此,在分配不完整的情况下,生成的电路必须记住(存储)该输出的状态。因此,就会合成某种可以存储信息的电路。那不会是触发器,因为没有时钟,没有 rising_edge
函数调用等;相反,将合成锁存器来进行记忆。这是双重糟糕的,因为(a)锁存器从根本上说是坏的,(b)你想要组合逻辑但得到了顺序。这是一个错误。
但是,在(同步)时序电路中,您已经有了存储空间。触发器可以被认为是 1 位存储器。因此,您无需担心(同步)顺序过程中的完整分配。您期望生成的电路能够存储东西。
首先,默认分配仅在不存在时钟时才阻止锁存器——即:组合逻辑。
在 register/flip-flop 逻辑中("if rising_edge(clock)" 之后的代码),默认分配会阻止保持条件 - 这是寄存器逻辑的正常部分。 "if rising_edge(clock)".
下的代码永远不会产生锁存器
OTOH,我称之为身份分配:
start_writing <= start_writing;
start_reading <= start_reading;
sequencer_count := sequencer_count;
身份分配不是默认分配并明确指定反馈。这些不会阻止组合逻辑中的锁存器。
默认赋值分配文字值或组合逻辑信号输出的值:
start_writing <= '0';
sequencer_count := "0000";
LedState <= LedNext;
请注意,对于进程中的变量声明,初始化仅在 运行 详细说明进程时 - 这是在时间开始时,而不是每次进程 运行s :
variable sequencer_count : unsigned(3 downto 0) := (others => '0');
这与每次调用时创建并初始化变量的子程序执行不同。
我知道默认信号分配在 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;
标记为"is this line necessary?"的那一行是必需的吗?我知道如果 sequencer_count
是一个信号,它就必须在那里。当sequencer_count
是一个变量时有必要吗?
在同步过程中,与rising_edge(fpga_clock)
一样,既不需要默认变量赋值也不需要默认信号赋值,两者都会保持时钟上升前的值,如果没有新的赋值时由于时钟上升,进程触发(执行)。
关于推断锁存器,您可能想到的可能与组合过程有关,如果 if
语句的某些分支未分配给驱动信号,则可以推断出锁存器,喜欢:
process (d, en, q)
begin
if en = '1' then
q <= d;
end if;
end process;
在这种情况下 q
仅在 en
为 '1'
时更新,因此如果 en
则推断锁存器保存 q
的值是 '0'
.
作为相关评论,使用变量在顺序(时钟)进程中保存状态通常是糟糕的编码风格,最好为 sequencer_count
声明一个信号。原因是所有触发器的编码风格都是相同的,并且更容易调试,因为在典型的仿真波形中看不到变量。
None 这些行是必需的:
start_writing <= start_writing;
start_reading <= start_reading;
sequencer_count := sequencer_count; -- is this line necessary
None 他们什么都做。在完全赋值和锁存推理中,只有组合逻辑存在问题。这是(同步)时序逻辑。您永远不需要担心使用(同步)顺序逻辑完成分配。
这是为什么?好吧,在组合逻辑的情况下,如果您没有完整的赋值,将会有一些输入组合,这些组合会导致遵循流程的路径,流程的输出(由它驱动的信号)会执行没有被分配一个值。在分配新值之前,任何信号都将保持其值。因此,在分配不完整的情况下,生成的电路必须记住(存储)该输出的状态。因此,就会合成某种可以存储信息的电路。那不会是触发器,因为没有时钟,没有 rising_edge
函数调用等;相反,将合成锁存器来进行记忆。这是双重糟糕的,因为(a)锁存器从根本上说是坏的,(b)你想要组合逻辑但得到了顺序。这是一个错误。
但是,在(同步)时序电路中,您已经有了存储空间。触发器可以被认为是 1 位存储器。因此,您无需担心(同步)顺序过程中的完整分配。您期望生成的电路能够存储东西。
首先,默认分配仅在不存在时钟时才阻止锁存器——即:组合逻辑。
在 register/flip-flop 逻辑中("if rising_edge(clock)" 之后的代码),默认分配会阻止保持条件 - 这是寄存器逻辑的正常部分。 "if rising_edge(clock)".
下的代码永远不会产生锁存器OTOH,我称之为身份分配:
start_writing <= start_writing;
start_reading <= start_reading;
sequencer_count := sequencer_count;
身份分配不是默认分配并明确指定反馈。这些不会阻止组合逻辑中的锁存器。
默认赋值分配文字值或组合逻辑信号输出的值:
start_writing <= '0';
sequencer_count := "0000";
LedState <= LedNext;
请注意,对于进程中的变量声明,初始化仅在 运行 详细说明进程时 - 这是在时间开始时,而不是每次进程 运行s :
variable sequencer_count : unsigned(3 downto 0) := (others => '0');
这与每次调用时创建并初始化变量的子程序执行不同。