時鐘邊沿檢測

短篇小說

如果 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;

注意:在最後一個例子中,我們還簡化了重置測試。浮動,高阻抗,復位非常罕見,在大多數情況下,這個簡化版本適用於模擬和綜合。