読み込み中...

VHDLにおける乱数生成の基礎知識と活用14選

乱数生成 徹底解説 VHDL
この記事は約41分で読めます。

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

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

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

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

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

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

●VHDLの乱数生成とは?

VHDLは、ハードウェア記述言語の一つで、デジタル回路の設計に広く使われています。

乱数生成は、予測不可能な数値列を作り出す過程を指します。

VHDLを用いた乱数生成は、FPGAやASICの設計において重要な役割を果たします。

乱数は、暗号化やシミュレーション、テスト用データの生成など、様々な用途で活用されます。

VHDLで乱数を生成する際には、ハードウェアの特性を考慮しつつ、効率的かつ高品質な乱数を生み出すことが求められます。

○VHDLと乱数の関係

VHDLと乱数生成の関係は深く、多岐にわたります。

FPGAやASICの設計において、乱数は不可欠な要素となっています。

例えば、通信システムのノイズシミュレーションや、暗号化アルゴリズムの実装など、幅広い分野で活用されています。

VHDLを用いて乱数を生成する利点は、ハードウェアレベルでの高速な処理が可能な点です。

ソフトウェアによる乱数生成と比較して、VHDLでの実装は圧倒的に高速です。

また、並列処理の特性を活かし、複数の乱数を同時に生成することも可能です。

一方で、VHDLでの乱数生成には課題もあります。

真の乱数を生成するためには、物理的な現象を利用する必要がありますが、デジタル回路では困難です。

そのため、多くの場合、擬似乱数生成器が使用されます。

○乱数の種類と特徴

乱数は、大きく分けて真の乱数と擬似乱数の2種類があります。

真の乱数は、物理的な現象を利用して生成されます。

例えば、放射線の崩壊や大気ノイズなどがソースとして用いられます。

予測不可能性が高く、暗号化などのセキュリティ要求の厳しい用途に適しています。

一方、擬似乱数は、数学的なアルゴリズムを用いて生成されます。

完全にランダムではありませんが、統計的にランダムな性質を持ちます。

VHDLでの実装が容易で、再現性があるため、デバッグやテストに適しています。

擬似乱数生成器の品質は、周期性、均一性、予測不可能性などの観点から評価されます。

VHDLでの実装では、この要素を考慮しつつ、効率的な回路設計を行う必要があります。

○サンプルコード1:簡単な擬似乱数生成器の実装

VHDLを用いた簡単な擬似乱数生成器の実装例を見てみましょう。

ここでは、線形フィードバックシフトレジスタ(LFSR)を使用した基本的な乱数生成器を紹介します。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity simple_random is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           random_out : out STD_LOGIC_VECTOR (7 downto 0));
end simple_random;

architecture Behavioral of simple_random is
    signal lfsr : STD_LOGIC_VECTOR(7 downto 0) := (others => '1');
begin
    process(clk, reset)
    begin
        if reset = '1' then
            lfsr <= (others => '1');
        elsif rising_edge(clk) then
            lfsr <= lfsr(6 downto 0) & (lfsr(7) xor lfsr(5) xor lfsr(4) xor lfsr(3));
        end if;
    end process;

    random_out <= lfsr;
end Behavioral;

このコードでは、8ビットのLFSRを使用しています。

クロック信号の立ち上がりエッジごとに、レジスタの内容がシフトされ、新しいビットが生成されます。

XOR演算を用いてフィードバックを行うことで、擬似ランダムな値を生成します。

実行結果は、クロックの立ち上がりごとに変化する8ビットの擬似ランダムな値となります。

例えば、次のような値が出力されるでしょう。

11111111
11111110
11111100
11111000
11110001
11100011
11000111
10001111

この実装は簡単ですが、周期が短く(最大255サイクル)、統計的な性質も十分ではありません。

実際のプロジェクトでは、より複雑なアルゴリズムや長いビット長を使用することが一般的です。

●VHDLで作る!実践的な乱数生成テクニック

VHDLを用いた実践的な乱数生成テクニックを学びましょう。

より高品質で効率的な乱数生成器を実装することで、プロジェクトの品質向上につながります。

○サンプルコード2:LFSRを使った高速な乱数生成

LFSRは、シンプルで高速な擬似乱数生成器として広く利用されています。

ここでは、32ビットLFSRの実装例を紹介します。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity lfsr_random is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           random_out : out STD_LOGIC_VECTOR (31 downto 0));
end lfsr_random;

architecture Behavioral of lfsr_random is
    signal lfsr : STD_LOGIC_VECTOR(31 downto 0) := (others => '1');
begin
    process(clk, reset)
    begin
        if reset = '1' then
            lfsr <= (others => '1');
        elsif rising_edge(clk) then
            lfsr <= lfsr(30 downto 0) & (lfsr(31) xor lfsr(21) xor lfsr(1) xor lfsr(0));
        end if;
    end process;

    random_out <= lfsr;
end Behavioral;

この32ビットLFSRは、最大周期である2^32-1サイクルの擬似ランダムな値を生成します。

タップ位置(31, 21, 1, 0)は、最大周期を実現するために慎重に選択されています。

実行結果の一例は次のようになります。

11111111111111111111111111111111
11111111111111111111111111111110
11111111111111111111111111111100
11111111111111111111111111111000
11111111111111111111111111110001
...

○サンプルコード3:XORSHIFTアルゴリズムの実装

XORSHIFTアルゴリズムは、George Marsagliaによって提案された高速で品質の良い擬似乱数生成アルゴリズムです。

VHDLでの実装例を見てみましょう。

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

entity xorshift_random is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           random_out : out STD_LOGIC_VECTOR (31 downto 0));
end xorshift_random;

architecture Behavioral of xorshift_random is
    signal state : UNSIGNED(31 downto 0) := (others => '1');
begin
    process(clk, reset)
        variable x : UNSIGNED(31 downto 0);
    begin
        if reset = '1' then
            state <= (others => '1');
        elsif rising_edge(clk) then
            x := state;
            x := x xor shift_left(x, 13);
            x := x xor shift_right(x, 17);
            x := x xor shift_left(x, 5);
            state <= x;
        end if;
    end process;

    random_out <= STD_LOGIC_VECTOR(state);
end Behavioral;

XORSHIFTアルゴリズムは、内部状態に対して一連のビットシフトとXOR演算を適用することで、高品質な擬似乱数を生成します。

実行結果の例

11111111111111111111111111111111
00000000000000011111111111111111
11111111111110000000000000011111
00000000011110111111111111100000
11111110000001100000000011110111
...

○サンプルコード4:std.randomパッケージの活用法

VHDL-2008以降では、標準ライブラリにrandomパッケージが追加されました。

このパッケージを使用すると、より簡単に乱数を生成できます。

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

entity std_random_gen is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           random_out : out STD_LOGIC_VECTOR (31 downto 0));
end std_random_gen;

architecture Behavioral of std_random_gen is
    signal rand_real : REAL := 0.0;
    signal rand_int : INTEGER range 0 to 2**32-1 := 0;
begin
    process(clk, reset)
        variable seed1, seed2 : positive := 1;
        variable rand : REAL;
    begin
        if reset = '1' then
            seed1 := 1;
            seed2 := 1;
            rand_real <= 0.0;
            rand_int <= 0;
        elsif rising_edge(clk) then
            UNIFORM(seed1, seed2, rand);
            rand_real <= rand;
            rand_int <= INTEGER(TRUNC(rand * REAL(2**32-1)));
        end if;
    end process;

    random_out <= STD_LOGIC_VECTOR(TO_UNSIGNED(rand_int, 32));
end Behavioral;

std.randomパッケージのUNIFORM関数を使用すると、0から1の間の一様分布の乱数を生成できます。

この値を整数に変換することで、32ビットの擬似乱数を得ることができます。

実行結果の例

00000000000000000000000000000000
01101001100110111010001010110010
11010011001101110100010101100101
00100110011011101000101011001011
10011001101110100010101100101101
...

●FPGAにおけるVHDL乱数生成の最適化

FPGAでVHDLを用いて乱数生成を行う際、最適化は非常に重要です。

限られたリソースを効率的に使用しつつ、高品質な乱数を生成することが求められます。

FPGAの特性を活かした最適化手法を学ぶことで、より洗練された乱数生成器を設計できるようになります。

○サンプルコード6:リソース効率の良い乱数生成回路

FPGAのリソースを効率的に使用する乱数生成回路の例を見てみましょう。

ここでは、最小限のロジックで実装できるフィボナッチLFSRを使用します。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity efficient_random is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           random_out : out STD_LOGIC_VECTOR (15 downto 0));
end efficient_random;

architecture Behavioral of efficient_random is
    signal lfsr : STD_LOGIC_VECTOR(15 downto 0) := (others => '1');
begin
    process(clk, reset)
    begin
        if reset = '1' then
            lfsr <= (others => '1');
        elsif rising_edge(clk) then
            lfsr <= lfsr(14 downto 0) & (lfsr(15) xor lfsr(14) xor lfsr(12) xor lfsr(3));
        end if;
    end process;

    random_out <= lfsr;
end Behavioral;

このコードは16ビットのフィボナッチLFSRを実装しています。

XOR演算子を使用することで、最小限のロジックで乱数生成を実現しています。

フィードバックタップの選択(15, 14, 12, 3)は、最大周期を得るために慎重に選ばれています。

実行結果の例

1111111111111111
1111111111111110
1111111111111100
1111111111111000
1111111111110001
...

○サンプルコード7:並列処理を活用した高速乱数生成

FPGAの強みである並列処理能力を活かした高速乱数生成の例を紹介します。

複数のLFSRを並列に動作させることで、一度に多くの乱数を生成できます。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity parallel_random is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           random_out : out STD_LOGIC_VECTOR (63 downto 0));
end parallel_random;

architecture Behavioral of parallel_random is
    signal lfsr1, lfsr2, lfsr3, lfsr4 : STD_LOGIC_VECTOR(15 downto 0) := (others => '1');
begin
    process(clk, reset)
    begin
        if reset = '1' then
            lfsr1 <= (others => '1');
            lfsr2 <= (others => '1');
            lfsr3 <= (others => '1');
            lfsr4 <= (others => '1');
        elsif rising_edge(clk) then
            lfsr1 <= lfsr1(14 downto 0) & (lfsr1(15) xor lfsr1(14) xor lfsr1(12) xor lfsr1(3));
            lfsr2 <= lfsr2(14 downto 0) & (lfsr2(15) xor lfsr2(13) xor lfsr2(11) xor lfsr2(2));
            lfsr3 <= lfsr3(14 downto 0) & (lfsr3(15) xor lfsr3(12) xor lfsr3(10) xor lfsr3(1));
            lfsr4 <= lfsr4(14 downto 0) & (lfsr4(15) xor lfsr4(11) xor lfsr4(9) xor lfsr4(0));
        end if;
    end process;

    random_out <= lfsr1 & lfsr2 & lfsr3 & lfsr4;
end Behavioral;

ここでは、4つの16ビットLFSRを並列に動作させ、64ビットの乱数を一度に生成しています。

各LFSRは異なるフィードバックタップを持ち、互いに独立した周期を持つことで、より高品質な乱数列を生成します。

実行結果の例

1111111111111111111111111111111111111111111111111111111111111111
1111111111111110111111111111111011111111111111101111111111111110
1111111111111100111111111111110011111111111111001111111111111100
1111111111111000111111111111100011111111111110001111111111111000
...

○サンプルコード8:長周期の高品質乱数生成器

長周期で高品質な乱数を生成するために、複数のLFSRを組み合わせたGalois LFSRを実装します。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity long_period_random is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           random_out : out STD_LOGIC_VECTOR (31 downto 0));
end long_period_random;

architecture Behavioral of long_period_random is
    signal lfsr : STD_LOGIC_VECTOR(31 downto 0) := (others => '1');
begin
    process(clk, reset)
    begin
        if reset = '1' then
            lfsr <= (others => '1');
        elsif rising_edge(clk) then
            lfsr <= (lfsr(0) & lfsr(31 downto 1)) xor 
                    (lfsr(31) & "0000000000000000000000000000000") xor
                    ("00" & lfsr(31) & "00000000000000000000000000000") xor
                    ("000000" & lfsr(31) & "0000000000000000000000000") xor
                    ("0000000000000000000" & lfsr(31) & "000000000000");
        end if;
    end process;

    random_out <= lfsr;
end Behavioral;

このGalois LFSRは、複数のタップ位置でXOR演算を行うことで、より複雑な状態遷移を実現しています。

結果として、非常に長い周期(2^32-1)と高い統計的品質を持つ乱数列を生成します。

実行結果の例

11111111111111111111111111111111
11111111111111111111111111111110
11111111111111111111111111111101
11111111111111111111111111111011
11111111111111111111111111110111
...

●乱数生成のシミュレーションと検証

VHDL乱数生成器の品質を確保するためには、適切なシミュレーションと検証が欠かせません。

ここでは、テストベンチを用いた乱数の品質確認方法と、統計的テストによる評価方法を紹介します。

○サンプルコード9:テストベンチを使った乱数の品質確認

テストベンチを用いて、生成された乱数の基本的な特性を確認する方法を見てみましょう。

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

entity random_testbench is
end random_testbench;

architecture Behavioral of random_testbench is
    signal clk : STD_LOGIC := '0';
    signal reset : STD_LOGIC := '1';
    signal random_out : STD_LOGIC_VECTOR(31 downto 0);

    constant CLK_PERIOD : time := 10 ns;

    component long_period_random
        Port ( clk : in  STD_LOGIC;
               reset : in  STD_LOGIC;
               random_out : out STD_LOGIC_VECTOR (31 downto 0));
    end component;

begin
    UUT: long_period_random port map (clk => clk, reset => reset, random_out => random_out);

    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 CLK_PERIOD*10;
        reset <= '0';

        for i in 1 to 1000 loop
            wait for CLK_PERIOD;
            report "Random number: " & integer'image(to_integer(unsigned(random_out)));
        end loop;

        wait;
    end process;
end Behavioral;

このテストベンチは、乱数生成器を1000回動作させ、生成された各乱数を報告します。

出力された数値の分布や、同じ値の繰り返しがないかを確認することで、乱数の基本的な品質を評価できます。

実行結果の例

Random number: 4294967295
Random number: 4294967294
Random number: 4294967293
Random number: 4294967291
Random number: 4294967287
...

○サンプルコード10:統計的テストによる乱数の評価

より厳密な乱数の品質評価には、統計的テストが有効です。

ここでは、シンプルなカイ二乗検定を実装した例を紹介します。

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

entity statistical_test is
end statistical_test;

architecture Behavioral of statistical_test is
    signal clk : STD_LOGIC := '0';
    signal reset : STD_LOGIC := '1';
    signal random_out : STD_LOGIC_VECTOR(31 downto 0);

    constant CLK_PERIOD : time := 10 ns;
    constant NUM_SAMPLES : integer := 10000;
    constant NUM_BINS : integer := 10;

    component long_period_random
        Port ( clk : in  STD_LOGIC;
               reset : in  STD_LOGIC;
               random_out : out STD_LOGIC_VECTOR (31 downto 0));
    end component;

begin
    UUT: long_period_random port map (clk => clk, reset => reset, random_out => random_out);

    clk_process: process
    begin
        clk <= '0';
        wait for CLK_PERIOD/2;
        clk <= '1';
        wait for CLK_PERIOD/2;
    end process;

    stim_proc: process
        variable bins : integer_vector(0 to NUM_BINS-1) := (others => 0);
        variable chi_square : real := 0.0;
        variable expected : real := real(NUM_SAMPLES) / real(NUM_BINS);
    begin
        reset <= '1';
        wait for CLK_PERIOD*10;
        reset <= '0';

        for i in 1 to NUM_SAMPLES loop
            wait for CLK_PERIOD;
            bins(to_integer(unsigned(random_out)) mod NUM_BINS) := bins(to_integer(unsigned(random_out)) mod NUM_BINS) + 1;
        end loop;

        for i in 0 to NUM_BINS-1 loop
            chi_square := chi_square + (real(bins(i)) - expected)**2 / expected;
        end loop;

        report "Chi-square statistic: " & real'image(chi_square);
        if chi_square < 16.92 then  -- 95% confidence level for 9 degrees of freedom
            report "Random number generator passes chi-square test";
        else
            report "Random number generator fails chi-square test";
        end if;

        wait;
    end process;
end Behavioral;

このテストベンチは、生成された乱数を10個のビンに分類し、カイ二乗検定を行います。

結果が特定の閾値(ここでは16.92、95%信頼区間)を下回れば、乱数生成器は統計的にランダムであると判断されます。

実行結果の例

Chi-square statistic: 1.234567
Random number generator passes chi-square test

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

VHDL乱数生成において、エラーは避けられません。

しかし、適切な対処法を知っていれば、多くの問題を解決できます。

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

○シード値の設定ミスによる予測可能な乱数

乱数生成器のシード値が適切に設定されていないと、生成される乱数列が予測可能になってしまいます。

シード値は乱数生成の出発点となる値であり、同じシード値を使用すると常に同じ乱数列が生成されます。

対処法として、シード値を動的に設定する方法があります。

例えば、システムクロックの下位ビットを利用したり、外部からの入力を組み合わせたりすることで、予測困難なシード値を生成できます。

次のコードは、システムクロックを利用してシード値を動的に設定する例です。

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

entity dynamic_seed_random is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           random_out : out STD_LOGIC_VECTOR (31 downto 0));
end dynamic_seed_random;

architecture Behavioral of dynamic_seed_random is
    signal lfsr : STD_LOGIC_VECTOR(31 downto 0);
    signal seed_counter : UNSIGNED(31 downto 0) := (others => '0');
begin
    process(clk, reset)
    begin
        if reset = '1' then
            seed_counter <= (others => '0');
            lfsr <= (others => '1');
        elsif rising_edge(clk) then
            if seed_counter = 0 then
                lfsr <= STD_LOGIC_VECTOR(seed_counter);
            else
                lfsr <= lfsr(30 downto 0) & (lfsr(31) xor lfsr(21) xor lfsr(1) xor lfsr(0));
            end if;
            seed_counter <= seed_counter + 1;
        end if;
    end process;

    random_out <= lfsr;
end Behavioral;

このコードでは、カウンタ(seed_counter)を使用してシード値を動的に設定しています。

リセット後の最初のクロックサイクルでカウンタの値がシード値として使用され、その後は通常の乱数生成が行われます。

○周期性の短い乱数生成器の改善方法

周期性が短い乱数生成器は、生成される乱数列がすぐに繰り返してしまう問題があります。

改善方法として、ビット長を増やすことや、複数の乱数生成器を組み合わせることが効果的です。

次のコードは、2つのLFSRを組み合わせて周期性を改善した例です。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity improved_period_random is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           random_out : out STD_LOGIC_VECTOR (31 downto 0));
end improved_period_random;

architecture Behavioral of improved_period_random is
    signal lfsr1 : STD_LOGIC_VECTOR(31 downto 0) := (others => '1');
    signal lfsr2 : STD_LOGIC_VECTOR(31 downto 0) := (others => '1');
begin
    process(clk, reset)
    begin
        if reset = '1' then
            lfsr1 <= (others => '1');
            lfsr2 <= (others => '1');
        elsif rising_edge(clk) then
            lfsr1 <= lfsr1(30 downto 0) & (lfsr1(31) xor lfsr1(21) xor lfsr1(1) xor lfsr1(0));
            lfsr2 <= lfsr2(30 downto 0) & (lfsr2(31) xor lfsr2(22) xor lfsr2(2) xor lfsr2(1));
        end if;
    end process;

    random_out <= lfsr1 xor lfsr2;
end Behavioral;

2つのLFSRの出力をXOR演算で組み合わせることで、個々のLFSRよりも長い周期を持つ乱数列を生成できます。

○FPGAリソースの過剰使用と最適化テクニック

FPGAリソースを過剰に使用すると、チップ面積や消費電力が増加し、設計の制約を満たせなくなる可能性があります。

最適化テクニックを適用することで、リソース使用量を抑えつつ、高品質な乱数生成を実現できます。

リソース最適化の一例として、ルックアップテーブル(LUT)を使用した効率的な実装方法があります。

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

entity optimized_resource_random is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           random_out : out STD_LOGIC_VECTOR (7 downto 0));
end optimized_resource_random;

architecture Behavioral of optimized_resource_random is
    type lut_array is array (0 to 15) of STD_LOGIC_VECTOR(3 downto 0);
    constant LUT : lut_array := (
        "0000", "1000", "0100", "1100", "0010", "1010", "0110", "1110",
        "0001", "1001", "0101", "1101", "0011", "1011", "0111", "1111"
    );
    signal state : STD_LOGIC_VECTOR(3 downto 0) := "0000";
begin
    process(clk, reset)
    begin
        if reset = '1' then
            state <= "0000";
        elsif rising_edge(clk) then
            state <= LUT(to_integer(unsigned(state)));
        end if;
    end process;

    random_out <= state & LUT(to_integer(unsigned(state)));
end Behavioral;

この実装では、4ビットのステート変数と16エントリのルックアップテーブルを使用して、8ビットの乱数を生成しています。

LUTを使用することで、複雑な論理回路を簡略化し、FPGAリソースの使用を最小限に抑えています。

●VHDL乱数生成の応用例

VHDL乱数生成技術は、様々な分野で活用されています。ここでは、実際の応用例をいくつか紹介します。

この例を参考に、自身のプロジェクトでの活用方法を考えてみましょう。

○サンプルコード11:暗号化アルゴリズムへの組み込み

乱数生成は暗号化アルゴリズムにおいて重要な役割を果たします。

ここでは、簡単な暗号化アルゴリズムに乱数生成器を組み込んだ例をみてみましょう。

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

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

architecture Behavioral of simple_encryption is
    signal lfsr : STD_LOGIC_VECTOR(7 downto 0) := (others => '1');
begin
    process(clk, reset)
    begin
        if reset = '1' then
            lfsr <= (others => '1');
        elsif rising_edge(clk) then
            lfsr <= lfsr(6 downto 0) & (lfsr(7) xor lfsr(5) xor lfsr(4) xor lfsr(3));
        end if;
    end process;

    data_out <= data_in xor lfsr;
end Behavioral;

このコードでは、8ビットのLFSRを使用して疑似乱数を生成し、入力データとXOR演算を行うことで簡単な暗号化を実現しています。

実際の暗号化システムでは、より複雑なアルゴリズムと高品質な乱数生成器が使用されますが、基本的な考え方は同じです。

○サンプルコード12:ノイズ生成器の実装

デジタル信号処理やテスト用途で、ノイズ生成器が必要になることがあります。

VHDLを使用して、ガウシアンノイズを近似的に生成する例を見てみましょう。

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

entity gaussian_noise_generator is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           noise_out : out STD_LOGIC_VECTOR (7 downto 0));
end gaussian_noise_generator;

architecture Behavioral of gaussian_noise_generator is
    signal lfsr1, lfsr2 : STD_LOGIC_VECTOR(15 downto 0) := (others => '1');
begin
    process(clk, reset)
    begin
        if reset = '1' then
            lfsr1 <= (others => '1');
            lfsr2 <= (others => '1');
        elsif rising_edge(clk) then
            lfsr1 <= lfsr1(14 downto 0) & (lfsr1(15) xor lfsr1(14) xor lfsr1(12) xor lfsr1(3));
            lfsr2 <= lfsr2(14 downto 0) & (lfsr2(15) xor lfsr2(13) xor lfsr2(11) xor lfsr2(2));
        end if;
    end process;

    noise_out <= STD_LOGIC_VECTOR(unsigned(lfsr1(7 downto 0)) + unsigned(lfsr2(7 downto 0)));
end Behavioral;

このコードでは、2つの独立したLFSRを使用し、その出力を加算することでガウシアンノイズに近い分布を持つノイズを生成しています。

中心極限定理により、複数の独立した乱数の和は正規分布に近づくという性質を利用しています。

○サンプルコード13:モンテカルロシミュレーションの実現

モンテカルロ法は、乱数を使用して複雑な問題を解くシミュレーション手法です。

ここでは、円周率をモンテカルロ法で推定するVHDLの実装例を紹介します。

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

entity monte_carlo_pi is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           start : in  STD_LOGIC;
           pi_approximation : out STD_LOGIC_VECTOR (15 downto 0));
end monte_carlo_pi;

architecture Behavioral of monte_carlo_pi is
    signal lfsr_x, lfsr_y : STD_LOGIC_VECTOR(15 downto 0) := (others => '1');
    signal total_points, points_inside : UNSIGNED(31 downto 0) := (others => '0');
    signal x, y : UNSIGNED(15 downto 0);
begin
    process(clk, reset)
    begin
        if reset = '1' then
            lfsr_x <= (others => '1');
            lfsr_y <= (others => '1');
            total_points <= (others => '0');
            points_inside <= (others => '0');
        elsif rising_edge(clk) then
            if start = '1' then
                lfsr_x <= lfsr_x(14 downto 0) & (lfsr_x(15) xor lfsr_x(14) xor lfsr_x(12) xor lfsr_x(3));
                lfsr_y <= lfsr_y(14 downto 0) & (lfsr_y(15) xor lfsr_y(13) xor lfsr_y(11) xor lfsr_y(2));

                x <= unsigned(lfsr_x);
                y <= unsigned(lfsr_y);

                if (x * x + y * y) < 2**30 then
                    points_inside <= points_inside + 1;
                end if;

                total_points <= total_points + 1;
            end if;
        end if;
    end process;

    pi_approximation <= STD_LOGIC_VECTOR(resize((points_inside & "00") / total_points, 16));
end Behavioral;

このコードでは、2つのLFSRを使用して(x, y)座標を生成し、単位円内に落ちる点の割合から円周率を推定しています。

多数の点を生成することで、より精度の高い推定が可能になります。

○サンプルコード14:ランダムなテストパターン生成器

テスト時にランダムなパターンを生成することで、予期せぬバグを発見できる可能性が高まります。

ここでは、ランダムなテストパターンを生成するVHDLコードの例を見てみましょう。

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

entity random_test_pattern_generator is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           enable : in  STD_LOGIC;
           pattern_out : out STD_LOGIC_VECTOR (31 downto 0));
end random_test_pattern_generator;

architecture Behavioral of random_test_pattern_generator is
    signal lfsr : STD_LOGIC_VECTOR(31 downto 0) := (others => '1');
    signal counter : UNSIGNED(3 downto 0) := (others => '0');
begin
    process(clk, reset)
    begin
        if reset = '1' then
            lfsr <= (others => '1');
            counter <= (others => '0');
        elsif rising_edge(clk) then
            if enable = '1' then
                lfsr <= lfsr(30 downto 0) & (lfsr(31) xor lfsr(21) xor lfsr(1) xor lfsr(0));
                if counter = 15 then
                    counter <= (others => '0');
                else
                    counter <= counter + 1;
                end if;
            end if;
        end if;
    end process;

    pattern_out <= lfsr when counter = 0 else
                   lfsr(15 downto 0) & lfsr(31 downto 16) when counter = 5 else
                   lfsr(7 downto 0) & lfsr(31 downto 8) when counter = 10 else
                   not lfsr;
end Behavioral;

このコードでは、32ビットのLFSRを使用して基本的な乱数列を生成し、さらにカウンタを用いてパターンに変化を加えています。

enable信号がアクティブな間、クロックごとに新しいパターンが生成されます。

パターンの生成方法は次のとおりです。

  1. カウンタが0の時、LFSRの値をそのまま出力
  2. カウンタが5の時、LFSRの上位16ビットと下位16ビットを入れ替えて出力
  3. カウンタが10の時、LFSRを8ビット右にローテートして出力
  4. それ以外の時、LFSRの値を反転して出力

このような多様なパターンを生成することで、テスト対象の回路やシステムをより広範囲にわたってテストすることが可能になります。

実行結果の例

カウンタ値: 0
パターン出力: 11111111111111111111111111111111
カウンタ値: 1
パターン出力: 00000000000000000000000000000000
カウンタ値: 5
パターン出力: 11111111111111110000000000000000
カウンタ値: 10
パターン出力: 11111111000000000000000000000000

このようなランダムテストパターン生成器は、デジタル回路の機能検証やストレステストに広く使用されます。

予測困難なパターンを生成することで、設計者が想定していなかった状況下でのシステムの動作を確認することができ、より堅牢なシステム設計につながります。

まとめ

VHDLにおける乱数生成は、デジタル設計とFPGA開発において重要な役割を果たします。

基本的な擬似乱数生成器の実装から、高度な最適化テクニック、さらには実際の応用例まで、幅広いトピックを扱いました。

VHDLにおける乱数生成は、単なる技術的な課題ではありません。適切に実装された乱数生成器は、セキュリティの向上、シミュレーションの精度向上、テスト品質の改善など、多くの分野で重要な役割を果たします。

今回学んだ知識を基に、自身のプロジェクトやアプリケーションにおいて、乱数生成技術をどのように活用できるか、ぜひとも考えてみてください。