読み込み中...

VHDLでのModelSimシミュレーション開始方法の基本と応用15選

ModelSim 徹底解説 VHDL
この記事は約32分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラム(回路記述)の基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

●VHDLとModelSimって何?

VHDLとModelSimは、デジタル回路設計の世界で欠かせない存在です。

VHDLはハードウェア記述言語の一種で、複雑な電子回路を効率的に設計できます。

一方、ModelSimは強力なシミュレーションツールで、VHDLで書かれた回路の動作を検証します。

初めてVHDLに触れる方は、プログラミング言語に似ているけれど少し違う感覚を覚えるでしょう。

VHDLは回路の構造や振る舞いを記述するため、ソフトウェア開発とは異なるアプローチが必要です。

ModelSimを使うと、設計した回路が意図通りに動作するか確認できます。

○ModelSimのインストール方法と初期設定のコツ

ModelSimをインストールするには、まずIntel FPGAのウェブサイトからダウンロードします。

無料版のModelSim-Altera Starter Editionがおすすめです。

ダウンロードしたインストーラーを実行し、画面の指示に従って進めます。

インストール完了後、環境変数の設定が重要です。

Windowsの場合、システム環境変数のPathにModelSimのbinフォルダを追加します。

こうすることで、コマンドプロンプトからModelSimを直接起動できるようになります。

初期設定では、プロジェクトの作業ディレクトリを適切に設定しましょう。

File > Change Directoryから、VHDLファイルを保存するフォルダを指定します。

また、プロジェクトごとにワークスペースを作成すると、ファイル管理が楽になります。

○VHDLプロジェクトの作成手順

VHDLプロジェクトを始めるには、まずModelSimを起動し、新しいプロジェクトを作成します。

File > New > Projectを選択し、プロジェクト名と保存場所を指定します。

次に、VHDLファイルを追加します。

Project > Add to Projectから新しいVHDLファイルを作成するか、既存のファイルを追加できます。

エンティティとアーキテクチャを含む基本的なVHDLファイルを作成しましょう。

プロジェクトに追加したファイルは、左側のプロジェクトウィンドウに表示されます。

ここから簡単にファイルを開いたり、コンパイルしたりできます。

○サンプルコード1:Hello, World! VHDLエディション

VHDLでの「Hello, World!」相当の回路を作ってみましょう。

LEDを点滅させる簡単な回路です。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity LED_Blinker is
    Port ( clk : in STD_LOGIC;
           reset : in STD_LOGIC;
           led : out STD_LOGIC);
end LED_Blinker;

architecture Behavioral of LED_Blinker is
    signal counter : unsigned(24 downto 0) := (others => '0');
begin
    process(clk, reset)
    begin
        if reset = '1' then
            counter <= (others => '0');
            led <= '0';
        elsif rising_edge(clk) then
            counter <= counter + 1;
            if counter = 25000000 then  -- 1秒間隔で点滅(50MHz クロック想定)
                led <= not led;
                counter <= (others => '0');
            end if;
        end if;
    end process;
end Behavioral;

このコードは、クロック信号を受け取り、約1秒間隔でLEDを点滅させます。

counterという信号を使ってタイミングを制御しています。

●VHDLコードのコンパイル術

VHDLコードをコンパイルする過程で、様々なエラーに遭遇するかもしれません。

コンパイルエラーは、構文の誤りや論理的な矛盾を指摘してくれる重要な情報源です。

ModelSimでコンパイルするには、プロジェクトウィンドウでファイルを右クリックし、「Compile > Compile Selected」を選択します。

エラーが発生した場合、下部のコンソールウィンドウに詳細が表示されます。

よくあるエラーとしては、セミコロンの忘れ、変数の型の不一致、未定義の信号の使用などがあります。

エラーメッセージをよく読み、指摘された行を確認しましょう。

○サンプルコード2:基本的なVHDLエンティティとアーキテクチャ

VHDLの基本構造であるエンティティとアーキテクチャを見てみましょう。

簡単な2入力ANDゲートを例に取ります。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity AND_Gate is
    Port ( A : in STD_LOGIC;
           B : in STD_LOGIC;
           Y : out STD_LOGIC);
end AND_Gate;

architecture Behavioral of AND_Gate is
begin
    Y <= A and B;
end Behavioral;

このコードでは、AND_Gateというエンティティを定義し、2つの入力ポート(AとB)と1つの出力ポート(Y)を持たせています。

アーキテクチャ部分では、YにAとBの論理積を代入しています。

○サンプルコード3:よく使うVHDLライブラリの指定方法

VHDLでは、標準ライブラリを使用して様々な機能を利用できます。

ライブラリの指定は、ファイルの先頭で行います。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;

entity Library_Example is
    Port ( clk : in STD_LOGIC;
           data_in : in STD_LOGIC_VECTOR(7 downto 0);
           data_out : out STD_LOGIC_VECTOR(7 downto 0));
end Library_Example;

architecture Behavioral of Library_Example is
    signal counter : unsigned(3 downto 0) := (others => '0');
begin
    process(clk)
    begin
        if rising_edge(clk) then
            counter <= counter + 1;
            if counter = 10 then
                data_out <= std_logic_vector(unsigned(data_in) + 1);
                counter <= (others => '0');
            end if;
        end if;
    end process;
end Behavioral;

このコードでは、IEEE.STD_LOGIC_1164.ALL、IEEE.NUMERIC_STD.ALL、IEEE.MATH_REAL.ALLを使用しています。

STD_LOGIC_1164は標準論理型を、NUMERIC_STDは算術演算を、MATH_REALは実数演算を提供します。

●テストベンチマスター

テストベンチは、VHDLデザインの動作を検証する上で欠かせない要素です。

回路の設計が完了しても、実際に動作するかどうかを確認する必要があります。

テストベンチを用いることで、実際のハードウェアを用意することなく、ソフトウェア上で回路の振る舞いを確認できます。

テストベンチの作成は、初めは難しく感じるかもしれません。

しかし、基本的な構造を理解し、少しずつ複雑なテストケースを追加していくことで、徐々にマスターできるようになります。

テストベンチを作成する際は、想定される全ての入力パターンを網羅することが重要です。

○サンプルコード5:基本的なテストベンチの構造

基本的なテストベンチの構造を見てみましょう。

先ほど作成したANDゲートのテストベンチを例にとります。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity AND_Gate_TB is
-- テストベンチには外部ポートは不要
end AND_Gate_TB;

architecture Behavioral of AND_Gate_TB is
    -- テスト対象のコンポーネント宣言
    component AND_Gate
        Port ( A : in STD_LOGIC;
               B : in STD_LOGIC;
               Y : out STD_LOGIC);
    end component;

    -- 信号の宣言
    signal A_tb, B_tb : STD_LOGIC := '0';
    signal Y_tb : STD_LOGIC;

begin
    -- テスト対象のインスタンス化
    UUT: AND_Gate port map (A => A_tb, B => B_tb, Y => Y_tb);

    -- テストプロセス
    stim_proc: process
    begin
        -- テストケース1
        A_tb <= '0'; B_tb <= '0';
        wait for 10 ns;
        assert (Y_tb = '0') report "Test case 1 failed" severity error;

        -- テストケース2
        A_tb <= '0'; B_tb <= '1';
        wait for 10 ns;
        assert (Y_tb = '0') report "Test case 2 failed" severity error;

        -- テストケース3
        A_tb <= '1'; B_tb <= '0';
        wait for 10 ns;
        assert (Y_tb = '0') report "Test case 3 failed" severity error;

        -- テストケース4
        A_tb <= '1'; B_tb <= '1';
        wait for 10 ns;
        assert (Y_tb = '1') report "Test case 4 failed" severity error;

        wait; -- シミュレーション終了
    end process;

end Behavioral;

テストベンチは、テスト対象の回路(UUT: Unit Under Test)をインスタンス化し、様々な入力パターンを与えて出力を確認します。

assert文を使用することで、期待される出力と実際の出力を比較し、エラーがあれば報告します。

○サンプルコード6:クロック生成と信号制御

多くのデジタル回路では、クロック信号が重要な役割を果たします。

テストベンチでクロック信号を生成する方法を見てみましょう。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity Clock_Gen_TB is
end Clock_Gen_TB;

architecture Behavioral of Clock_Gen_TB is
    signal clk : STD_LOGIC := '0';
    signal reset : STD_LOGIC := '0';
    signal counter : INTEGER := 0;

    constant CLK_PERIOD : TIME := 10 ns;

begin
    -- クロック生成プロセス
    clk_process: process
    begin
        clk <= '0';
        wait for CLK_PERIOD/2;
        clk <= '1';
        wait for CLK_PERIOD/2;
    end process;

    -- テストプロセス
    stim_proc: process
    begin
        reset <= '1';
        wait for 100 ns;
        reset <= '0';

        wait for 1000 ns;
        assert false report "Simulation finished" severity failure;
    end process;

    -- カウンタープロセス(テスト対象の代わり)
    counter_proc: process(clk, reset)
    begin
        if reset = '1' then
            counter <= 0;
        elsif rising_edge(clk) then
            counter <= counter + 1;
        end if;
    end process;

end Behavioral;

クロック信号は一定の周期で0と1を繰り返します。

CLK_PERIOD定数を使用することで、クロック周期を簡単に変更できます。

reset信号を使用してカウンターをリセットし、クロックの立ち上がりでカウントアップする簡単な回路をテストしています。

○サンプルコード7:アサーションを使った自動チェック

テストベンチでは、アサーションを使用して期待される動作を自動的にチェックできます。

先ほどのカウンター回路に、アサーションを追加してみましょう。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity Counter_TB is
end Counter_TB;

architecture Behavioral of Counter_TB is
    signal clk : STD_LOGIC := '0';
    signal reset : STD_LOGIC := '0';
    signal counter : INTEGER := 0;

    constant CLK_PERIOD : TIME := 10 ns;

begin
    -- クロック生成プロセス(前回と同じ)
    clk_process: process
    begin
        clk <= '0';
        wait for CLK_PERIOD/2;
        clk <= '1';
        wait for CLK_PERIOD/2;
    end process;

    -- テストプロセス
    stim_proc: process
    begin
        reset <= '1';
        wait for 100 ns;
        reset <= '0';

        wait for 1000 ns;
        assert false report "Simulation finished" severity failure;
    end process;

    -- カウンタープロセス
    counter_proc: process(clk, reset)
    begin
        if reset = '1' then
            counter <= 0;
        elsif rising_edge(clk) then
            counter <= counter + 1;
        end if;
    end process;

    -- アサーションプロセス
    assert_proc: process
    begin
        wait for CLK_PERIOD;
        assert (counter = 0) report "Counter not reset properly" severity error;

        wait for 10*CLK_PERIOD;
        assert (counter = 10) report "Counter not incrementing correctly" severity error;

        wait;
    end process;

end Behavioral;

アサーションプロセスを追加することで、カウンターが正しくリセットされ、適切にインクリメントされているかを自動的にチェックできます。

エラーが発生した場合、シミュレーション中にエラーメッセージが表示されます。

○サンプルコード8:高度なテストベンチテクニック

より複雑な回路をテストする場合、高度なテストベンチテクニックが必要になります。

例えば、ファイルからテストベクトルを読み込んだり、結果をファイルに出力したりする方法があります。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use STD.TEXTIO.ALL;
use IEEE.STD_LOGIC_TEXTIO.ALL;

entity Advanced_TB is
end Advanced_TB;

architecture Behavioral of Advanced_TB is
    signal clk : STD_LOGIC := '0';
    signal reset : STD_LOGIC := '0';
    signal input : STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
    signal output : STD_LOGIC_VECTOR(7 downto 0);

    constant CLK_PERIOD : TIME := 10 ns;

    -- テスト対象のコンポーネント宣言
    component DUT
        Port ( clk : in STD_LOGIC;
               reset : in STD_LOGIC;
               input : in STD_LOGIC_VECTOR(7 downto 0);
               output : out STD_LOGIC_VECTOR(7 downto 0));
    end component;

begin
    -- テスト対象のインスタンス化
    UUT: DUT port map (clk => clk, reset => reset, input => input, output => output);

    -- クロック生成プロセス(前回と同じ)
    clk_process: process
    begin
        clk <= '0';
        wait for CLK_PERIOD/2;
        clk <= '1';
        wait for CLK_PERIOD/2;
    end process;

    -- テストプロセス
    stim_proc: process
        file input_file : text open read_mode is "input_vectors.txt";
        file output_file : text open write_mode is "output_results.txt";
        variable input_line, output_line : line;
        variable input_vector : STD_LOGIC_VECTOR(7 downto 0);
    begin
        reset <= '1';
        wait for 100 ns;
        reset <= '0';

        while not endfile(input_file) loop
            readline(input_file, input_line);
            read(input_line, input_vector);
            input <= input_vector;

            wait for CLK_PERIOD;

            write(output_line, output);
            writeline(output_file, output_line);
        end loop;

        assert false report "Simulation finished" severity failure;
    end process;

end Behavioral;

ファイルからテストベクトルを読み込み、結果をファイルに出力するテストベンチです。大量のテストケースを効率的に処理できます。

input_vectors.txtファイルにテストデータを用意し、output_results.txtファイルに結果が書き込まれます。

●ModelSimコマンドの使いこなし

ModelSimを効率的に使用するには、コマンドラインインターフェースの活用が欠かせません。

GUIで操作するよりも、コマンドを使うことで作業を自動化し、生産性を向上させることができます。

○サンプルコード9:vsimコマンドの応用

vsimコマンドは、ModelSimでシミュレーションを開始する際に使用する重要なコマンドです。

様々なオプションを組み合わせることで、シミュレーションの挙動をカスタマイズできます。

# 基本的なvsimコマンド
vsim work.testbench

# 最適化なしでシミュレーションを実行
vsim -novopt work.testbench

# 特定の時間までシミュレーションを実行
vsim -do "run 1000ns" work.testbench

# 波形ファイルを自動的に保存
vsim -do "log -r /*" work.testbench

# デバッグモードでシミュレーションを実行
vsim -debugDB work.testbench

# 特定の変数に注目
vsim -do "add wave /testbench/uut/clk" work.testbench

vsimコマンドを使いこなすことで、シミュレーションの実行方法を細かく制御できます。

-doオプションを使用すると、シミュレーション開始時に特定のコマンドを実行できます。

○サンプルコード10:シミュレーション制御スクリプト

TCLスクリプトを使用すると、複雑なシミュレーション制御を自動化できます。

ここでは、複数のテストケースを連続して実行し、結果をログファイルに出力するスクリプトの例を紹介します。

# シミュレーション制御スクリプト

# プロジェクトのコンパイル
vcom -work work *.vhd

# テストベンチの実行
vsim -do {
    # ログファイルの設定
    log -r /*

    # 波形の設定
    add wave -r /*

    # シミュレーションの実行
    run 1000ns

    # 結果の確認
    if {[examine /testbench/test_passed] == 1} {
        echo "Test case 1 passed"
    } else {
        echo "Test case 1 failed"
    }

    # 次のテストケースの準備
    restart -f

    # 2つ目のシミュレーション実行
    run 2000ns

    # 結果の確認
    if {[examine /testbench/test_passed] == 1} {
        echo "Test case 2 passed"
    } else {
        echo "Test case 2 failed"
    }

    # シミュレーションの終了
    quit -sim
} work.testbench

# ログファイルの保存
log -flush

TCLスクリプトを使用することで、複数のテストケースを自動的に実行し、結果を簡単に確認できます。

スクリプトの中でvsimコマンドを使用し、シミュレーションの実行と結果の確認を自動化しています。

○サンプルコード11:バッチモードでの自動化

大規模なプロジェクトでは、多数のテストケースを自動的に実行する必要があります。

バッチモードを使用すると、ModelSimをコマンドラインから起動し、一連のシミュレーションを自動的に実行できます。

#!/bin/bash

# ModelSimのパスを設定
MODELSIM_PATH="/path/to/modelsim/bin"

# プロジェクトディレクトリに移動
cd /path/to/project

# コンパイルとシミュレーションを実行
$MODELSIM_PATH/vsim -c -do "
# プロジェクトのコンパイル
vcom -work work *.vhd

# テストベンチの実行
vsim -do {
    # ログファイルの設定
    log -r /*

    # すべてのテストケースを実行
    foreach testcase {testcase1 testcase2 testcase3} {
        # テストケースの設定
        force -freeze /testbench/test_case $testcase

        # シミュレーションの実行
        run 1000ns

        # 結果の確認
        if {[examine /testbench/test_passed] == 1} {
            echo \"$testcase passed\"
        } else {
            echo \"$testcase failed\"
        }

        # 次のテストケースの準備
        restart -f
    }

    # シミュレーションの終了
    quit -sim
} work.testbench

# ログファイルの保存
log -flush

exit
"

# 結果の解析
grep "failed" vsim.log && echo "Some tests failed" || echo "All tests passed"

バッチモードスクリプトは、シェルスクリプトとTCLスクリプトを組み合わせて作成します。

シェルスクリプトでModelSimを起動し、TCLスクリプトでシミュレーションを制御します。

複数のテストケースを連続して実行し、結果をログファイルに出力します。最後に、グレップコマンドを使用してログファイルを解析し、テスト結果を表示します。

バッチモードを活用することで、大規模なテストスイートを夜間や週末に自動実行できます。

継続的インテグレーション(CI)システムと組み合わせることで、コード変更の度に自動的にテストを実行することも可能です。

ModelSimのコマンドを使いこなすことで、VHDLプロジェクトの効率と品質を大幅に向上させることができます。

コマンドラインインターフェースを活用し、テスト自動化スクリプトを作成することで、人為的ミスを減らし、テストカバレッジを向上させることができるでしょう。

●よくあるエラーと対処法

VHDLプログラミングとModelSimシミュレーションの過程で、様々なエラーに遭遇することがあります。

初心者にとっては、エラーメッセージを理解し、適切に対処することが難しい場合があります。

しかし、経験を積むにつれて、エラーの原因を素早く特定し、効率的に解決できるようになります。

ここでは、頻繁に発生するエラーとその対処法について詳しく解説します。

エラーに遭遇したときに慌てずに対応できるよう、各エラーの特徴と解決のアプローチを学びましょう。

○「エンティティが見つかりません」の解決策

「エンティティが見つかりません」というエラーは、コンパイル時によく発生します。

通常、ファイル名とエンティティ名の不一致や、ライブラリの指定ミスが原因です。

解決策として、まずファイル名とエンティティ名が一致しているか確認しましょう。

VHDLでは、ファイル名とエンティティ名を同じにすることが推奨されています。

例えば、「my_entity.vhd」というファイルなら、エンティティ名も「my_entity」にします。

次に、ライブラリの指定を確認します。

正しいライブラリ名を使用しているか、library文とuse文が適切に記述されているか確認しましょう。

最後に、コンパイル順序を見直します。

依存関係のあるエンティティは、使用される前にコンパイルされている必要があります。

ModelSimのプロジェクトウィンドウで、ファイルの順序を適切に設定しましょう。

○無限ループに陥ったシミュレーションの止め方

シミュレーション中に無限ループに陥ると、プログラムが応答しなくなることがあります。

原因としては、終了条件の設定ミスや、プロセス内でのループ制御の問題が考えられます。

対処法として、まずModelSimのGUIからシミュレーションを強制終了します。

「Simulation」メニューから「Break」を選択するか、ツールバーの赤い停止ボタンをクリックします。

次に、コードを見直し、ループの終了条件が適切に設定されているか確認します。

while文やfor文の条件式を点検し、必要に応じて修正します。

プロセス内でのループには特に注意が必要です。

無限ループを避けるため、wait文を適切に配置しているか確認しましょう。

例えば、クロック同期のプロセスでは、必ず「wait until rising_edge(clk);」のような文を入れます。

○メモリ不足エラーの対処法

大規模なシミュレーションを実行する際、メモリ不足エラーに遭遇することがあります。

シミュレーション時間が長すぎたり、信号の数が多すぎたりすると発生しやすくなります。

解決策として、まずModelSimの設定を調整します。

「Tools」メニューから「Options」を選択し、「VSim」タブでメモリ使用量の上限を増やします。

次に、シミュレーション時間を適切に設定します。

必要以上に長い時間シミュレーションを実行していないか確認し、適切な時間に調整します。

また、波形表示の設定を見直します。

すべての信号を表示する必要はありません。

必要な信号のみを選択して表示することで、メモリ使用量を削減できます。

最後に、コードの最適化を検討します。

不要な信号や変数を削除し、ループ構造を見直すことで、メモリ使用量を抑えられる場合があります。

●ModelSimの応用例

ModelSimは、FPGAデザインプロセスにおいて重要な役割を果たします。

FPGAベンダーのツールと連携することで、より効率的な設計フローを実現できます。

ここでは、特にQuartusとModelSimの連携について詳しく解説します。

○サンプルコード12:QuartusプロジェクトとModelSimの連携設定

QuartusプロジェクトとModelSimを連携させるには、適切な設定が必要です。

次のTCLスクリプトは、QuartusプロジェクトファイルからModelSimプロジェクトを生成する例です。

# Quartusプロジェクトファイルのパスを指定
set quartus_project_file "C:/path/to/your/project.qpf"

# ModelSimプロジェクトの名前を指定
set modelsim_project_name "my_modelsim_project"

# Quartusプロジェクトを開く
project_open $quartus_project_file

# ModelSimプロジェクトを生成
generate_simulation_script -tool modelsim -simulation_dir ./$modelsim_project_name

# Quartusプロジェクトを閉じる
project_close

puts "ModelSimプロジェクトが生成されました: $modelsim_project_name"

このスクリプトを実行すると、QuartusプロジェクトからModelSimで使用可能なシミュレーションスクリプトが生成されます。

生成されたスクリプトを使用することで、QuartusとModelSimの設定の整合性を保つことができます。

○サンプルコード13:FPGAタイミング制約のシミュレーション

FPGAデザインでは、タイミング制約を満たすことが重要です。

ModelSimを使用して、タイミング制約をシミュレーションで検証できます。

ここでは、セットアップ時間とホールド時間を確認するためのVHDLテストベンチの例を紹介します。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity Timing_Constraint_TB is
end Timing_Constraint_TB;

architecture Behavioral of Timing_Constraint_TB is
    signal clk : STD_LOGIC := '0';
    signal data_in : STD_LOGIC := '0';
    signal data_out : STD_LOGIC;

    constant CLK_PERIOD : TIME := 10 ns;
    constant SETUP_TIME : TIME := 2 ns;
    constant HOLD_TIME : TIME := 1 ns;

begin
    -- クロック生成
    clk_process: process
    begin
        clk <= '0';
        wait for CLK_PERIOD/2;
        clk <= '1';
        wait for CLK_PERIOD/2;
    end process;

    -- テスト対象のコンポーネント
    DUT: entity work.MyComponent
        port map (
            clk => clk,
            data_in => data_in,
            data_out => data_out
        );

    -- タイミング制約テスト
    stim_proc: process
    begin
        -- セットアップ時間違反のテスト
        wait for CLK_PERIOD - SETUP_TIME + 100 ps;
        data_in <= '1';
        wait for CLK_PERIOD;
        assert false report "セットアップ時間違反をチェック" severity note;

        -- ホールド時間違反のテスト
        wait for CLK_PERIOD - HOLD_TIME + 100 ps;
        data_in <= '0';
        wait for CLK_PERIOD;
        assert false report "ホールド時間違反をチェック" severity note;

        wait;
    end process;

end Behavioral;

このテストベンチでは、意図的にセットアップ時間とホールド時間の違反を引き起こし、タイミング制約がどのように影響するかを確認します。

実際のFPGAデザインでは、これらの違反を避けるようにコードを最適化する必要があります。

○サンプルコード14:高度な波形解析テクニック

ModelSimの波形ビューアは、デバッグに非常に役立ちます。

次のTCLスクリプトは、波形ビューアをカスタマイズし、特定の条件で信号をハイライトする例です。

# 波形ウィンドウを開く
view wave

# すべての信号を追加
add wave *

# クロック信号の表示をカスタマイズ
configure wave -namecolwidth 150
configure wave -valuecolwidth 100
configure wave -justifyvalue left
configure wave -signalnamewidth 1
configure wave -snapdistance 10
configure wave -datasetprefix 0
configure wave -rowmargin 4
configure wave -childrowmargin 2

# 特定の条件で信号をハイライト
when {/testbench/data_out == 8'hFF} {
    set_wave_color -color red -item /testbench/data_out
}

# カーソルを使用して時間を測定
set cursor1 [wave cursor new]
set cursor2 [wave cursor new]
wave cursor time $cursor1 100ns
wave cursor time $cursor2 200ns
set time_diff [expr {[wave cursor time $cursor2] - [wave cursor time $cursor1]}]
echo "カーソル間の時間差: $time_diff ns"

# 波形を更新
update

このスクリプトを使用すると、波形ビューアをより効果的に利用できます。

信号の状態に応じた色分けや、カーソルを使用した時間測定など、高度な分析が可能になります。

○サンプルコード15:ModelSimとFPGAの協調デバッグ

FPGAボード上で実行されているデザインとModelSimシミュレーションを連携させることで、より効果的なデバッグが可能になります。

JTAGを使用してFPGAとModelSimを接続するTCLスクリプトの例を見てみましょう。

# JTAGチェーンに接続
jtag connect

# デバイスを選択
jtag select device 1

# FPGAからデータを読み取る
set fpga_data [jtag read -ir 6 -dr 32]

# ModelSimのシミュレーション変数を更新
force -freeze /testbench/fpga_input $fpga_data

# シミュレーションを実行
run 100 ns

# シミュレーション結果をFPGAに書き戻す
set sim_result [examine /testbench/sim_output]
jtag write -ir 7 -dr $sim_result

# JTAGチェーンから切断
jtag close

このスクリプトを使用することで、FPGAの実際の動作とModelSimのシミュレーション結果を比較しながらデバッグを進めることができます。

ハードウェアとソフトウェアの境界で発生する問題の解決に特に有効です。

まとめ

VHDLとModelSimを使用したFPGAデザインの流れを詳しく見てきました。

基本的な概念から高度なテクニックまで、幅広いトピックをカバーしました。

VHDLとModelSimのスキルを磨くことは、FPGAエンジニアとしてのキャリアにおいて大きな強みとなります。

学んだ知識を実践し、継続的に技術を磨いていくことで、高度なFPGA設計者への道が開けるでしょう。