初心者向け!VHDLサブプログラムの使い方10選

VHDLのサブプログラムを図解して詳細に解説するイメージVHDL
この記事は約15分で読めます。

 

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

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

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

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

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

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

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

はじめに

VHDLはハードウェア記述言語として広く利用されており、サブプログラムはその中での重要な要素となっています。

この記事では、初心者向けにVHDLのサブプログラムの使い方を詳しく解説していきます。

実際のサンプルコードを交えながら、その活用方法や注意点、カスタマイズの方法までを網羅的に学べる内容となっています。

●VHDLのサブプログラムとは

VHDLのサブプログラムは、一連の処理をまとめて再利用可能な形にしたものです。

これにより、コードの冗長性を減らし、モジュール性を高めることができます。

○サブプログラムの種類と基本概念

主に関数と手続き(procedure)の2つのタイプが存在します。

関数は何らかの値を返すことを目的としたもので、手続きは特定のタスクを実行するためのものです。

●VHDLサブプログラムの使い方

○サンプルコード1:基本的なサブプログラムの記述

このコードでは基本的なサブプログラムを定義し、それを呼び出す方法を表しています。

この例では、関数を定義して整数の加算を行っています。

-- 関数の定義
function add(a: integer; b: integer) return integer is
begin
    return a + b;
end function add;

-- 関数の利用
variable result: integer;
begin
    result := add(5, 3);  -- 8がresultに格納される
end;

上のコードを実行すると、変数resultには8が格納されることになります。

○サンプルコード2:関数を使用する例

このコードでは、関数を使って二つの数の乗算を行う例を表しています。

-- 関数の定義
function multiply(a: integer; b: integer) return integer is
begin
    return a * b;
end function multiply;

-- 関数の利用
variable result: integer;
begin
    result := multiply(4, 7);  -- 28がresultに格納される
end;

この例では、resultに28という値が格納される結果となります。

○サンプルコード3:手続き(procedure)の活用

手続きは、関数と異なり値を返すことを主目的としていません。

このコードでは、二つの変数の値を交換する手続きを表しています。

-- 手続きの定義
procedure swap(variable a: in out integer; variable b: in out integer) is
    variable temp: integer;
begin
    temp := a;
    a := b;
    b := temp;
end procedure swap;

-- 手続きの利用
variable x: integer := 10;
variable y: integer := 20;
begin
    swap(x, y);  -- xとyの値が交換される
end;

この例を実行すると、xには20、yには10という値が格納されます。

○サンプルコード4:サブプログラムの引数と戻り値

サブプログラムには引数を取ることができます。

このコードでは、関数に引数を渡して、戻り値を受け取る方法を表しています。

-- 関数の定義
function subtract(a: integer; b: integer) return integer is
begin
    return a - b;
end function subtract;

-- 関数の利用
variable result: integer;
begin
    result := subtract(15, 5);  -- 10がresultに格納される
end;

この例を実行すると、resultには10が格納されます。

●VHDLサブプログラムの応用例

VHDLを使用した際のサブプログラムの応用例を学ぶことで、より柔軟で強力なコードの設計が可能となります。

ここでは、様々な応用例をサンプルコードとともに詳細に解説していきます。

○サンプルコード5:複数のサブプログラムを組み合わせる

このコードでは複数のサブプログラムを組み合わせて一つのタスクを完成させる方法を紹介しています。

この例では、2つの関数を作成し、それらを組み合わせて1つの結果を出力する方法を示しています。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- 関数1: 2つの数字を足す
function add(a: integer; b: integer) return integer is
begin
  return a + b;
end function add;

-- 関数2: 結果を2で割る
function divide_by_two(num: integer) return integer is
begin
  return num / 2;
end function divide_by_two;

-- メインプロセス
begin
  variable result: integer;
  result := divide_by_two(add(10, 20));  -- 10と20を足して2で割る
  assert false report "結果は " & integer'image(result) severity note;  -- 結果の表示
end;

このコードを実行すると、10と20を足した後、その結果を2で割った値が出力されます。

したがって、出力される結果は15となります。

○サンプルコード6:外部ファイルからサブプログラムを呼び出す

このコードでは外部のVHDLファイルに定義されたサブプログラムを呼び出す方法を紹介しています。

この例では、別のVHDLファイルに定義された関数を利用して計算を行います。

外部ファイル external_functions.vhdl として次の内容が記述されていると仮定します。

function multiply(a: integer; b: integer) return integer is
begin
  return a * b;
end function multiply;

メインのVHDLファイルではこの関数を利用します。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use work.external_functions.ALL;  -- 外部ファイルの関数を利用

begin
  variable result: integer;
  result := multiply(5, 6);  -- 5と6を掛ける
  assert false report "結果は " & integer'image(result) severity note;  -- 結果の表示
end;

このコードを実行すると、5と6を掛けた結果が出力されます。

したがって、出力される結果は30となります。

○サンプルコード7:再帰的なサブプログラム

再帰とは、ある関数から自身を呼び出すことを指します。

再帰的な関数は、その機能を実現するために自身を再度呼び出す必要がある場合に使用されます。

例えば、階乗やフィボナッチ数列の計算などが代表的です。

このコードでは、VHDLで再帰的なサブプログラムを使って、整数の階乗を計算するコードを表しています。

この例では、関数を定義して整数の階乗を計算しています。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity factorial is
    Port ( n : in  integer range 0 to 10;
           result : out integer);
end factorial;

architecture Behavioral of factorial is
    function factorial_func(n : integer) return integer is
    begin
        if n <= 1 then
            return 1; --基本ケース
        else
            return n * factorial_func(n-1); --再帰的な呼び出し
        end if;
    end function factorial_func;

begin
    process(n)
    begin
        result <= factorial_func(n);
    end process;
end Behavioral;

このコードでは、factorial_funcという関数を定義しています。

関数の中で、nが1以下の場合は1を返すようにしています。

それ以外の場合は、nfactorial_func(n-1)を掛け算することで、階乗の計算を実現しています。

例として、入力nが5の場合、出力resultは5! = 5 × 4 × 3 × 2 × 1 = 120となります。

このように、再帰的なサブプログラムを使うことで、コードがシンプルかつ分かりやすくなります。

このサンプルコードを利用すれば、再帰的な計算が必要な他の問題にも応用が効きます。

VHDLでの再帰的なサブプログラムの記述方法を理解することは、より複雑な問題を効率的に解決するための強力なツールとなります。

○サンプルコード8:サブプログラム内でのシグナルの扱い

VHDLにおいてサブプログラム内でのシグナルの扱いは、非常に重要な概念となります。

シグナルは回路内の情報の流れを表現するための要素であり、これを適切にサブプログラム内で扱うことで、より高度なロジックを効率的に記述することが可能となります。

このコードではシグナルを使って値の変更を行い、それをサブプログラムで処理する例を表しています。

この例ではシグナルに値を代入し、それをサブプログラムに渡して処理を行っています。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity sample_signal is
end sample_signal;

architecture Behavioral of sample_signal is
    signal sample_value: std_logic_vector(7 downto 0);

    procedure Signal_Processing(signal data: inout std_logic_vector) is
    begin
        -- サンプルとしてデータを1ビット右シフト
        data <= data srl 1;
    end procedure;

begin
    sample_value <= "10011010"; -- 初期値の設定
    process
    begin
        Signal_Processing(sample_value); -- サブプログラムの呼び出し
        wait for 10 ns;
    end process;
end Behavioral;

この例のサブプログラム「Signal_Processing」は、シグナルsample_valueを受け取り、1ビット右シフトの処理を行います。

このようにサブプログラムを使うことで、シグナルの処理を簡潔に記述することができます。

このコードを実行すると、sample_valueが”10011010″から”01001101″に変更されることが確認できます。

これにより、シグナルの値をサブプログラム内で直接変更して反映することができるのがわかります。

応用例として、複数のサブプログラムを組み合わせてシグナルの変換や計算を行う場合などが考えられます。

例えば、異なるビット操作を行う複数のサブプログラムを作成し、それらを組み合わせて複雑なデータ変換や演算を行うことができます。

また、注意点として、シグナルをサブプログラム内で扱う際は、そのシグナルがどのような範囲の値を取り得るのか、またその値の変更が他の部分にどのような影響を与えるのかを常に意識する必要があります。

シグナルの扱いに関するエラーやバグは、システムの動作に大きな影響を与える可能性があるため、十分な注意が求められます。

○サンプルコード9:ジェネリックを用いたサブプログラム

VHDLにおいて、ジェネリックは非常に便利な機能の一つとして知られています。

ジェネリックを利用すると、一つのサブプログラムをさまざまなデータ型やビット幅で再利用することが可能となります。

これにより、似たような動作をするサブプログラムを何度も記述する手間が省けるだけでなく、コードの可読性や保守性も向上します。

このコードでは、ジェネリックを使ってビット幅を指定するサブプログラムを表しています。

この例では、ビット幅を変更して同じサブプログラムを再利用しています。

entity my_generic_module is
    generic (
        DATA_WIDTH : integer := 8
    );
    port (
        a : in std_logic_vector(DATA_WIDTH-1 downto 0);
        b : in std_logic_vector(DATA_WIDTH-1 downto 0);
        c : out std_logic_vector(DATA_WIDTH-1 downto 0)
    );
end entity my_generic_module;

architecture behavioral of my_generic_module is
begin
    process(a, b)
    begin
        -- ここではビット幅がジェネリックによって決定される
        c <= a + b;
    end process;
end architecture behavioral;

上記のサブプログラムでは、DATA_WIDTHという名前のジェネリックを定義しています。

このジェネリックは、入出力ポートa, b, cのビット幅を決定します。

例えば、DATA_WIDTHを16に設定すれば、16ビット幅のデータを処理するサブプログラムとして動作します。

このように、ジェネリックを活用することで、変更が容易で再利用性の高いサブプログラムを設計することができます。

こちらのサブプログラムを実際にVHDLで実行すると、指定したビット幅に応じた加算結果が出力ポートcに表示されます。

たとえば、8ビットの入力データabが与えられ、それぞれ0000111100000001だった場合、出力c00010000として表示されます。

応用例として、このサブプログラムを異なるビット幅での加算器として使用することが考えられます。

例えば、12ビットや24ビットの加算器を必要とする場合にも、ジェネリックの値を変更するだけで対応が可能です。

これにより、一つのサブプログラムで複数の用途に対応することができ、効率的なコード設計が実現できます。

また、注意点として、ジェネリックの値を変更する際は、そのビット幅に合わせた適切なデータを入力するように注意が必要です。

不適切なビット幅のデータを入力すると、予期しない動作やエラーが発生する可能性があります。

○サンプルコード10:オーバーロードされたサブプログラムの活用

VHDLでは、同じ名前を持つ複数のサブプログラムを定義することが可能です。

これは「オーバーロード」と呼ばれる技術で、関数や手続きの引数の数や型が異なる場合に、同じ名前で複数のサブプログラムを使用することができます。

この技術は、コードの整理や可読性を向上させるために非常に有効です。

一つの機能に対して、異なるデータ型やパラメータで動作する複数のサブプログラムを定義し、それらを同じ名前で呼び出すことができるのです。

このコードではオーバーロードを使って、整数と実数の両方を引数として受け取る2つの関数を紹介しています。

この例では整数を受け取る関数と実数を受け取る関数を、同じ名前「calculate」で定義しています。

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

entity OverloadExample is
end OverloadExample;

architecture Behavior of OverloadExample is
    function calculate(x : integer) return integer is
    begin
        return x * 2;
    end function;

    function calculate(x : real) return real is
    begin
        return x * 2.5;
    end function;

begin
    -- これらの関数を呼び出すプロセスなどのコードをこちらに記述
end Behavior;

このように、VHDLのオーバーロード機能を使用することで、データ型に応じて適切な関数を自動的に呼び出すことが可能になります。

例えば、整数5を引数としてcalculate関数を呼び出すと、結果として10が返されます。

一方、実数2.0を引数として同じ関数を呼び出すと、結果として5.0が返されます。

●VHDLサブプログラムの注意点と対処法

VHDLのサブプログラムを使用する際には、いくつかの注意点が存在します。

特に、オーバーロードされたサブプログラムを使用する際には、関数や手続きの定義が正確に行われているか、また、期待通りの関数が呼び出されているかを確認する必要があります。

誤ったサブプログラムが呼び出されると、予期しない結果やエラーが発生することが考えられます。

また、サブプログラム内で使用する変数や定数のスコープに注意することも重要です。

局所変数や定数はそのサブプログラム内でのみ有効であり、外部からアクセスすることはできません。

●カスタマイズ方法と拡張性

VHDLのサブプログラムは、その機能性や拡張性を活かすことで、さまざまなカスタマイズや改良が可能です。

例えば、オーバーロードされたサブプログラムの活用をさらに進めることで、3つ以上の異なるデータ型やパラメータを持つ関数を同じ名前で定義することも考えられます。

また、サブプログラムを外部のライブラリとして定義し、複数のVHDLファイルで共有することも可能です。

これにより、再利用性を高め、開発効率を向上させることができます。

まとめ

VHDLのサブプログラムは、デザインの再利用性や可読性を高めるための強力なツールとなります。

特に、オーバーロードされたサブプログラムは、同じ名前で異なるデータ型やパラメータを持つ関数を定義することができるため、コードの整理や整合性を保つ上で非常に有効です。