它不是按顺序“执行”的,而是合成器按顺序解释代码,并创建适合这种解释的硬件设计。
例如,如果您signal
在时钟进程中为 a 赋值两次,第一个赋值将被忽略,而第二个赋值生效(请记住 a仅在语句signal
末尾赋值,而不是立即赋值):process
signal a : UNSIGNED(3 downto 0) := (others => '0');
(...)
process(clk)
begin
if(rising_edge(clk)) then
a <= a - 1;
a <= a + 1;
end if;
end process;
上述过程将始终递增a
1。类似地,如果您在if
语句中有第二个赋值,则合成器将简单地创建两条路径a
- 当语句未满时递减,当if
语句未满时递增。
如果你使用变量,想法是一样的——尽管使用了中间值,因为变量会立即采用它们的新值。
但这一切都归结为合成器完成process
了以顺序方式解释您的所有“魔术”,然后生成执行您所描述的硬件。
您的示例基本上描述了一个 d 触发器(Xilinx FPGA 工具 iirc 区分锁存器和触发器,因为触发器是边沿敏感的,而锁存器是电平敏感的),尽管方式与通常推荐的方式不同。
您基本上可以编写与以下相同的代码:
process(clk)
begin
if(rising_edge(clk)) then
q <= d;
end if;
end process;
在其他情况下,它将自动保持其值。这将作为 FPGA 内部的触发器实现。大多数 FPGA 由查找表和触发器块组成,可以映射很多不同的硬件。上面的代码将简单地绕过查找表,并且只使用其中一个块的触发器。
您可以通过查看特定 FPGA 的数据表来了解有关内部工作原理的更多信息。例如,对于 Spartan3 系列 FPGA,请查看Xilinx Spartan3 FPGA 系列数据表的第 24 页