时钟边沿检测

短篇小说

如果 VHDL 2008 和时钟的类型是 bitbooleanieee.std_logic_1164.std_ulogicieee.std_logic_1164.std_logic,时钟边沿检测可以编码为上升沿

  • if rising_edge(clock) then
  • if clock'event and clock = '1' then -- type bit, std_ulogic or std_logic
  • if clock'event and clock then -- type boolean

并为下降边缘

  • if falling_edge(clock) then
  • if clock'event and clock = '0' then -- type bit, std_ulogic or std_logic
  • if clock'event and not clock then -- type boolean

无论是模拟还是合成,这都将按预期运行。

注意:std_ulogic 类型信号上升沿的定义比简单的 if clock'event and clock = '1' then 复杂一点。例如,标准的 rising_edge 函数具有不同的定义。即使它可能对合成没有任何影响,它也可以用于模拟。

强烈建议使用 rising_edgefalling_edge 标准功能。对于以前版本的 VHDL,使用这些函数可能需要明确声明使用标准软件包(例如,ieee.numeric_bit 用于类型 bit),甚至可以在自定义软件包中定义它们。

注意:请勿使用 rising_edgefalling_edge 标准功能来检测非时钟信号的边沿。一些合成器可以得出结论,信号是一个时钟。提示:通过对移位寄存器中的信号进行采样并比较移位寄存器的不同阶段的采样值,可以经常检测非时钟信号上的边沿。

长话故事

在建模 D-Flip-Flops(DFF) 时,正确描述时钟信号边沿的检测是必不可少的。根据定义,边缘是从一个特定值到另一个特定值的过渡。例如,我们可以定义 bit 信号的上升沿(标准 VHDL 枚举类型,它带有两个值:'0''1')作为从'0''1'的转换。对于 boolean 类型,我们可以将它定义为从 falsetrue 的过渡。

通常,使用更复杂的类型。例如,ieee.std_logic_1164.std_ulogic 类型也是枚举类型,就像 bitboolean,但它有 9 个值而不是 2:

含义
'U' 未初始化
'X' 强迫未知
'0' 强迫低水平
'1' 强迫高水平
'Z' 高阻抗
'W' 弱者未知
'L' 弱水平低
'H' 弱水平
'-' 不在乎

在这种类型上定义上升沿比 bitboolean 复杂一点。例如,我们可以决定它是从'0''1'的过渡。但我们也可以决定它是从'0''L''1''H'的过渡。

注意:这是第二个定义,标准用于 ieee.std_logic_1164 中定义的 rising_edge(signal s: std_ulogic) 函数。

在讨论检测边缘的各种方法时,重要的是要考虑信号的类型。考虑建模目标也很重要:仅模拟或逻辑综合?让我们举几个例子来说明这一点:

上升沿 DFF 与类型位

signal clock, d, q: bit;
...
P1: process(clock)
begin
  if clock = '1' then
    q <= d;
  end if;
end process P1;

从技术上讲,从纯模拟语义的角度来看,过程 P1 模拟了一个上升沿触发 DFF。实际上,当且仅当以下情况时才执行 q <= d 赋值:

  • clock 改变了(这是灵敏度列表表示的)
  • clock 的当前值是'1'

由于 clock 的类型为 bit,而 type 类型只有'0''1',这正是我们定义的 bit 类型信号的上升沿。任何模拟器都会按照我们的预期处理这个模型。

注意:对于逻辑合成器,事情有点复杂,我们稍后会看到。

上升沿 DFF 具有异步有效高电平复位和类型位

为了向我们的 DFF 添加异步有效高电平复位,可以尝试以下方法:

signal clock, reset, d, q: bit;
...
P2_BOGUS: process(clock, reset)
begin
  if reset = '1' then
    q <= '0';
  elsif clock = '1' then
    q <= d;
  end if;
end process P2_BOGUS;

但这不起作用。执行 q <= d 赋值的条件应该是: clock 的上升沿,而 reset = '0' 。但我们建模的是:

  • clockreset 或两者变化
  • reset = '0'
  • clock = '1'

这是不一样的:如果 reset'1'变为'0'clock = '1'分配将被执行,而它不是 clock 的上升边缘。

实际上,没有信号属性的帮助,无法在 VHDL 中对此进行建模:

P2_OK: process(clock, reset)
begin
  if reset = '1' then
    q <= '0';
  elsif clock = '1' and clock'event then
    q <= d;
  end if;
end process P2_OK;

clock'event 是应用于信号 clock 的信号属性 event。它评估为 boolean,当且仅当信号 clock 在当前执行阶段之前的信号更新阶段期间发生变化时,它才是 true。由于这一点,过程 P2_OK 现在完美地模拟了我们在模拟(和合成)中的需求。

综合语义

许多逻辑合成器基于语法模式识别信号边缘检测,而不是基于 VHDL 模型的语义。换句话说,他们考虑 VHDL 代码的外观,而不是它的模型行为。他们都认识到的一种模式是:

if clock = '1' and clock'event then

所以,即使在过程 P1 的例子中,如果我们希望我们的模型可以被所有逻辑合成器合成,我们也应该使用它:

signal clock, d, q: bit;
...
P1_OK: process(clock)
begin
  if clock = '1' and clock'event then
    q <= d;
  end if;
end process P1_OK;

and clock'event 部分条件与灵敏度列表完全冗余,但有些合成器需要它…

具有异步有效高电平复位和 std_ulogic 的上升沿 DFF

在这种情况下,表达时钟的上升沿和复位条件会变得复杂。如果我们保留上面提出的上升沿的定义,并且如果我们认为重置是活动的,如果它是'1''H',则模型变为:

library ieee;
use ieee.std_logic_1164.all;
...
signal clock, reset, d, q: std_ulogic;
...
P4: process(clock, reset)
begin
  if reset = '1' or reset = 'H' then
    q <= '0';
  elsif clock'event and
        (clock'last_value = '0' or clock'last_value = 'L') and
        (clock = '1' or clock = 'H') then
    q <= d;
  end if;
end process P4;

注意:'last_value 是另一个信号属性,它返回信号在最后一次值更改之前的值。

助手功能

VHDL 2008 标准提供了几个辅助函数来简化信号边缘的检测,特别是对于像 std_ulogic 这样的多值枚举类型。std.standard 包定义 rising_edgeboolean 类型的 rising_edgefalling_edge 函数,ieee.std_logic_1164 包定义 std_ulogicstd_logic 类型。

注意:对于以前版本的 VHDL,使用这些函数可能需要显式声明标准包的使用(例如 ieee.numeric_bit 用于类型位),甚至可以在用户包中定义它们。

让我们重新访问前面的示例并使用辅助函数:

signal clock, d, q: bit;
...
P1_OK_NEW: process(clock)
begin
  if rising_edge(clock) then
    q <= d;
  end if;
end process P1_OK_NEW;
signal clock, d, q: bit;
...
P2_OK_NEW: process(clock, reset)
begin
  if reset = '1' then
    q <= '0';
  elsif rising_edge(clock) then
    q <= d;
  end if;
end process P2_OK_NEW;
library ieee;
use ieee.std_logic_1164.all;
...
signal clock, reset, d, q: std_ulogic;
...
P4_NEW: process(clock, reset)
begin
  if reset = '1' then
    q <= '0';
  elsif rising_edge(clock) then
    q <= d;
  end if;
end process P4_NEW;

注意:在最后一个例子中,我们还简化了重置测试。浮动,高阻抗,复位非常罕见,在大多数情况下,这个简化版本适用于仿真和综合。