同步計數器的模擬環境

模擬環境

VHDL 設計(被測設計或 DUT)的模擬環境是另一種 VHDL 設計,至少:

  • 宣告對應於 DUT 的輸入和輸出埠的訊號。
  • 例項化 DUT 並將其埠連線到宣告的訊號。
  • 例項化驅動連線到 DUT 輸入埠的訊號的過程。

可選地,模擬環境可以例項化除 DUT 之外的其他設計,例如,介面上的流量生成器,用於檢查通訊協議的監視器,DUT 輸出的自動驗證器……

對模擬環境進行分析,闡述和執行。大多數模擬器提供了選擇一組訊號進行觀察,繪製圖形波形,在原始碼中放置斷點,步入原始碼的可能性……

理想情況下,模擬環境應該可用作穩健的非迴歸測試,也就是說,它應該自動檢測違反 DUT 規範的情況,報告有用的錯誤訊息並保證 DUT 功能的合理覆蓋。當這樣的模擬環境可用時,可以在 DUT 的每次更改時重新執行它們以檢查它是否仍然在功能上是正確的,而不需要對模擬跡線進行繁瑣且容易出錯的視覺檢查。

在實踐中,設計理想的甚至是好的模擬環境是一項挑戰。它經常比設計 DUT 本身更難,甚至更難。

在此示例中,我們為同步計數器示例提供了一個模擬環境。我們將展示如何使用 GHDL 和 ModelSim 以及如何觀察使用圖形的波形來執行它 GTKWave與 GHDL 並採用 ModelSim 內建的波形顯示器。然後我們討論模擬的一個有趣方面:如何阻止它們?

同步計數器的第一個模擬環境

同步計數器有兩個輸入埠和一個輸出埠。一個非常簡單的模擬環境可能是:

-- File counter_sim.vhd
-- Entities of simulation environments are frequently black boxes without
-- ports.
entity counter_sim is
end entity counter_sim;

architecture sim of counter_sim is

  -- One signal per port of the DUT. Signals can have the same name as
  -- the corresponding port but they do not need to.
  signal clk:  bit;
  signal rst:  bit;
  signal data: natural;

begin

  -- Instantiation of the DUT
  u0: entity work.counter(sync)
  port map(
    clock => clk,
    reset => rst,
    data  => data
  );

  -- A clock generating process with a 2ns clock period. The process
  -- being an infinite loop, the clock will never stop toggling.
  process
  begin
    clk <= '0';
    wait for 1 ns;
    clk <= '1';
    wait for 1 ns;
  end process;

  -- The process that handles the reset: active from beginning of
  -- simulation until the 5th rising edge of the clock.
  process
  begin
    rst  <= '1';
    for i in 1 to 5 loop
      wait until rising_edge(clk);
    end loop;
    rst  <= '0';
    wait; -- Eternal wait. Stops the process forever.
  end process;

end architecture sim;

用 GHDL 模擬

讓我們用 GHDL 編譯和模擬這個:

$ mkdir gh_work
$ ghdl -a --workdir=gh_work counter_sim.vhd
counter_sim.vhd:27:24: unit "counter" not found in 'library "work"'
counter_sim.vhd:50:35: no declaration for "rising_edge"

然後錯誤訊息告訴我們兩個重要的事情:

  • GHDL 分析器發現我們的設計例項化了一個名為 counter 的實體,但是這個實體在庫 work 中找不到。這是因為我們沒有在 counter_sim 之前編譯 counter。在編譯例項化實體的 VHDL 設計時,必須始終在最高階別之前編譯底層(層次結構設計也可以自上而下編譯,但前提是它們例項化 component,而不是實體)。
  • 我們的設計使用的 rising_edge 函式沒有定義。這是因為這個函式是在 VHDL 2008 中引入的,我們沒有告訴 GHDL 使用這個版本的語言(預設情況下它使用 VHDL 1993,容忍 VHDL 1987 語法)。

讓我們修復這兩個錯誤並啟動模擬:

$ ghdl -a --workdir=gh_work --std=08 counter.vhd counter_sim.vhd
$ ghdl -r --workdir=gh_work --std=08 counter_sim sim
^C

請注意,分析模擬需要 --std=08 選項。另請注意,我們在實體 counter_sim,架構 sim 上啟動了模擬,而不是在原始檔上。

由於我們的模擬環境具有永無止境的過程(生成時鐘的過程),因此模擬不會停止,我們必須手動中斷它。相反,我們可以使用 --stop-time 選項指定停止時間:

$ ghdl -r --workdir=gh_work --std=08 counter_sim sim --stop-time=60ns
ghdl:info: simulation stopped by --stop-time

因此,模擬並沒有告訴我們很多關於 DUT 的行為。讓我們轉儲檔案中訊號的值變化:

$ ghdl -r --workdir=gh_work --std=08 counter_sim sim --stop-time=60ns --vcd=counter_sim.vcd
Vcd.Avhpi_Error!
ghdl:info: simulation stopped by --stop-time

(忽略錯誤訊息,這是需要在 GHDL 中修復的東西,並且沒有任何後果)。已建立 counter_sim.vcd 檔案。它包含 VCD(ASCII) 格式模擬期間的所有訊號變化。GTKWave 可以向我們展示相應的圖形波形:

$ gtkwave counter_sim.vcd

我們可以看到計數器按預期工作。

StackOverflow 文件

使用 Modelsim 進行模擬

與 Modelsim 的原理完全相同:

$ vlib ms_work
...
$ vmap work ms_work
...
$ vcom -nologo -quiet -2008 counter.vhd counter_sim.vhd
$ vsim -voptargs="+acc" 'counter_sim(sim)' -do 'add wave /*; run 60ns'

StackOverflow 文件

注意傳遞給 vsim-voptargs="+acc" 選項:它可以防止模擬器優化 data 訊號,並允許我們在波形上看到它。

優雅地結束模擬

使用兩個模擬器,我們必須中斷永無止境的模擬或使用專用選項指定停止時間。這不是很方便。在許多情況下,很難預測模擬的結束時間。當達到特定條件時,例如,當計數器的當前值達到 20 時,從模擬環境的 VHDL 程式碼內部停止模擬會更好。這可以通過在處理重置的程序:

  process
  begin
    rst <= '1';
    for i in 1 to 5 loop
      wait until rising_edge(clk);
    end loop;
    rst <= '0';
    loop
      wait until rising_edge(clk);
      assert data /= 20 report "End of simulation" severity failure;
    end loop;
  end process;

只要 data 與 20 不同,模擬就會繼續。當 data 達到 20 時,模擬崩潰並顯示錯誤訊息:

$ ghdl -a --workdir=gh_work --std=08 counter_sim.vhd
$ ghdl -r --workdir=gh_work --std=08 counter_sim sim
counter_sim.vhd:90:24:@51ns:(assertion failure): End of simulation
ghdl:error: assertion failed
  from: process work.counter_sim(sim2).P1 at counter_sim.vhd:90
ghdl:error: simulation failed

請注意,我們僅重新編譯了模擬環境:它是唯一改變的設計,它是頂級的。如果我們只修改了 counter.vhd,我們將不得不重新編譯兩個:counter.vhd 因為它改變而 counter_sim.vhd 因為它取決於 counter.vhd

使用錯誤訊息破壞模擬並不是很優雅。在自動解析模擬訊息以確定是否通過自動非迴歸測試時,它甚至可能是一個問題。更好,更優雅的解決方案是在達到條件時停止所有程序。例如,這可以通過新增 boolean 模擬結束(eof)訊號來完成。預設情況下,它在模擬開始時初始化為 false。當時間結束模擬時,我們的一個過程將把它設定為 true。所有其他過程將監視此訊號,並在它將成為 tihuan 時停止一個永恆的 true

  signal eos:  boolean;
...
  process
  begin
    clk <= '0';
    wait for 1 ns;
    clk <= '1';
    wait for 1 ns;
    if eos then
      report "End of simulation";
      wait;
    end if;
  end process;

  process
  begin
    rst <= '1';
    for i in 1 to 5 loop
      wait until rising_edge(clk);
    end loop;
    rst <= '0';
    for i in 1 to 20 loop
      wait until rising_edge(clk);
    end loop;
    eos <= true;
    wait;
  end process;
$ ghdl -a --workdir=gh_work --std=08 counter_sim.vhd
$ ghdl -r --workdir=gh_work --std=08 counter_sim sim
counter_sim.vhd:120:24:@50ns:(report note): End of simulation

最後但並非最不重要的是,VHDL 2008 中引入了更好的解決方案,標準包 env 以及它宣告的 stopfinish 程式:

use std.env.all;
...
  process
  begin
    rst    <= '1';
    for i in 1 to 5 loop
      wait until rising_edge(clk);
    end loop;
    rst    <= '0';
    for i in 1 to 20 loop
      wait until rising_edge(clk);
    end loop;
    finish;
  end process;
$ ghdl -a --workdir=gh_work --std=08 counter_sim.vhd
$ ghdl -r --workdir=gh_work --std=08 counter_sim sim
simulation finished @49ns