同步計數器的模擬環境
模擬環境
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
我們可以看到計數器按預期工作。
使用 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'
注意傳遞給 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
以及它宣告的 stop
和 finish
程式:
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