VHDLクロック載せ替え完全マスター10選

VHDLのクロック載せ替えを学ぶためのイラスト VHDL
この記事は約24分で読めます。

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

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

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

基本的な知識があればサンプルコードを活用して機能追加、目的を達成できるように作ってあります。

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

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

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

はじめに

VHDLのクロック載せ替えは、デジタル回路設計における重要なスキルの一つとなっています。

この記事では、初心者でも安心して学べるように、VHDLでのクロック載せ替えに関する基本から高度な技術まで、10のサンプルコードと共に徹底的に解説します。

●VHDLとは

VHDLは、VHSIC Hardware Description Languageの略で、高速集積回路のハードウェア記述言語として開発されました。

ハードウェアの振る舞いを記述するための言語として、半導体の設計やシミュレーションなど、広範な用途で利用されています。

○VHDLの基本概念

VHDLには、エンティティ、アーキテクチャ、プロセスなどの基本概念が存在します。

エンティティは回路の外部インターフェースを定義し、アーキテクチャはその振る舞いを記述します。

また、プロセスは連続的な動作を表現するためのブロックとして利用されます。

●クロックについて

クロックはデジタル回路において、動作のタイミングを決定する非常に重要な役割を果たします。

○クロックの役割

クロックは、回路の動作タイミングを制御するための基準として使用されます。

例えば、フリップフロップはクロックの立ち上がりエッジでデータをキャッチします。

○クロックの生成方法

多くのFPGAやASICデザインツールには、特定の周波数のクロックを生成するためのIP(Intellectual Property)が用意されています。

これを使用することで、必要なクロック信号を簡単に生成することができます。

●載せ替えの基本

○何故載せ替えが必要か

クロック載せ替えは、異なるクロックドメイン間でのデータ伝送や、パワーマネジメント、特定のタイミング要件を満たすために行われます。

○載せ替えの方法

基本的には、フリップフロップやラッチを使用して、一つのクロックドメインから別のクロックドメインへデータを安全に伝送する技術です。

●サンプルコードの紹介

ここでは、VHDLを使用してクロック載せ替えを行うためのサンプルコードを10個紹介します。

○サンプルコード1:基本的なクロック載せ替え

このコードでは、基本的なクロック載せ替えの方法を表しています。

この例では、フリップフロップを用いてデータを別のクロックドメインに転送しています。

entity clock_switch is
    Port ( clk1 : in  STD_LOGIC;
           clk2 : in  STD_LOGIC;
           data_in : in  STD_LOGIC_VECTOR(7 downto 0);
           data_out : out STD_LOGIC_VECTOR(7 downto 0));
end clock_switch;

architecture Behavioral of clock_switch is
signal temp_data: STD_LOGIC_VECTOR(7 downto 0);
begin
    process(clk1)
    begin
        if rising_edge(clk1) then
            temp_data <= data_in;
        end if;
    end process;

    process(clk2)
    begin
        if rising_edge(clk2) then
            data_out <= temp_data;
        end if;
    end process;
end Behavioral;

このサンプルコードでは、clk1というクロックドメインからclk2というクロックドメインへ8ビットのデータを転送しています。

data_inがclk1のタイミングでtemp_dataに格納され、その後、clk2のタイミングでdata_outに出力されます。

このコードを実行すると、data_inのデータがdata_outに伝送されるという動作を確認することができます。

○サンプルコード2:複数のクロックソース

VHDLの世界では、複数のクロックソースを使用する場面が数多くあります。

ここでは、複数のクロックソースを持つVHDLの設計方法について、詳細な説明とサンプルコードを交えてご紹介します。

複数のクロックソースを用いることで、特定のタスクや操作を独立して制御することが可能となります。

例えば、高速動作が求められる処理と、低速での省電力動作が求められる処理を同時に行いたい場合、異なるクロックソースを使用することで、効率的な動作が実現できます。

複数のクロックソースを持つVHDLのサンプルコードを紹介します。

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

entity multi_clock is
    Port ( clk1 : in STD_LOGIC;
           clk2 : in STD_LOGIC;
           reset : in STD_LOGIC;
           output1 : out STD_LOGIC;
           output2 : out STD_LOGIC);
end multi_clock;

architecture Behavioral of multi_clock is
signal tmp1 : STD_LOGIC := '0';
signal tmp2 : STD_LOGIC := '0';
begin
process(clk1, reset) -- クロック1のプロセス
begin
    if reset = '1' then
        tmp1 <= '0';
    elsif rising_edge(clk1) then
        tmp1 <= not tmp1; 
    end if;
end process;

process(clk2, reset) -- クロック2のプロセス
begin
    if reset = '1' then
        tmp2 <= '0';
    elsif rising_edge(clk2) then
        tmp2 <= not tmp2;
    end if;
end process;

output1 <= tmp1;
output2 <= tmp2;
end Behavioral;

このコードでは、clk1clk2の2つのクロックソースを使って、2つの独立したプロセスを制御しています。

tmp1tmp2はそれぞれのクロックソースに従ってトグル動作をします。

リセット信号がアクティブになると、両方の信号はリセットされます。

この例では、clk1の周波数が高く、clk2の周波数が低い場合、output1は高速でトグルし、output2は低速でトグルします。

このように、異なるクロックソースを使用することで、異なる動作速度の制御が可能となります。

上記のコードを実行すると、2つの出力output1output2が得られます。

それぞれの出力は、与えられたクロックソースに基づいてトグル動作を行います。

リセット信号がアクティブになると、両方の出力が'0'にリセットされることが観測されます。

今回のサンプルコードではシンプルなトグル動作を示しましたが、複数のクロックソースを持つVHDL設計では、より複雑な操作や制御が可能となります。

適切なクロックソースの選択と設計により、効率的かつ柔軟なシステムの実現が可能となります。

○サンプルコード3:動的なクロック切り替え

動的なクロック切り替えとは、動作中のデバイスが異なるクロックソースに切り替えることを指します。

特定の条件や入力に基づいて、クロックソースを変更することで、柔軟なシステムの動作や消費電力の最適化が可能となります。

下記のコードは、2つのクロックソースの中から一つを選択し、そのクロックを使用する基本的な例を表しています。

この例では、入力として’select_signal’を使用してクロックソースを選択しています。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity DynamicClockSwitch is
    Port ( clk1 : in STD_LOGIC;
           clk2 : in STD_LOGIC;
           select_signal : in STD_LOGIC;
           clk_out : out STD_LOGIC);
end DynamicClockSwitch;

architecture Behavioral of DynamicClockSwitch is
begin
    process(clk1, clk2)
    begin
        -- クロックソースの選択
        if select_signal = '1' then
            clk_out <= clk1;
        else
            clk_out <= clk2;
        end if;
    end process;
end Behavioral;

このコードでは、’select_signal’が’1’の場合は’clk1’を、それ以外の場合は’clk2’を’clk_out’として出力します。

このようにして、実行中のシステムで動的にクロックソースを切り替えることができます。

この動的なクロック切り替えを使用する際の主な利点は次のとおりです。

  1. システムの動作を変更するために再プログラムすることなく、クロックの切り替えが可能。
  2. 異なる周波数のクロックを必要に応じて使用することで、消費電力の最適化やシステムの性能向上が図れる。

しかし、動的なクロック切り替えを行う際には、クロックの切り替え時の安定性や、クロックの切り替えによるタイミングの影響を考慮する必要があります。

このため、実際のシステムに導入する前に、十分なテストやシミュレーションが必要となります。

動的なクロック切り替えの技術を使用することで、VHDLで設計されたシステムの柔軟性と効率性を向上させることができます。

この技術を習得することで、様々な要件や環境に対応するための幅広い選択肢が得られるでしょう。

○サンプルコード4:クロックの位相シフト

クロックの位相シフトとは、クロックの立ち上がりや立ち下がりのタイミングを少し遅らせる、あるいは早める技術のことを指します。

この技術は、特定のタイミングでの動作を調整したい場合や、シグナル間のタイミングを揃えたい場合などに使用されます。

このコードでは、クロックの位相をシフトさせるための簡単な例を表しています。

この例では、元のクロックから位相を少し遅らせることで新しいクロックを生成しています。

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

entity PhaseShift is
    Port ( clk      : in STD_LOGIC;
           clk_shift : out STD_LOGIC);
end PhaseShift;

architecture Behavioral of PhaseShift is
    signal temp: STD_LOGIC := '0';
begin
    -- ここで位相シフトのロジックを実装
    process(clk)
    begin
        if rising_edge(clk) then
            temp <= not temp;
        end if;
    end process;

    clk_shift <= temp;
end Behavioral;

上記のコードでは、tempというシグナルを使用して、クロックの立ち上がりエッジのたびにその値を反転させることで、元のクロックとは異なる位相を持つ新しいクロックを生成しています。

これにより、clk_shiftという出力クロックは、元のclkよりも半周期遅れたクロックとして動作します。

このように、VHDLを用いることで、クロックの位相を簡単にシフトさせることができます。

特に高速な回路設計においては、タイミングの細かな調整が求められることが多いので、このような位相シフトの技術は非常に役立ちます。

このコードをFPGAやASICに実装すると、clk_shiftという名前の新しいクロックが生成され、元のclkに比べて半周期遅れて動作することが確認できます。

この遅れたクロックは、特定の動作タイミングを持つ部分の回路などに使用することができます。

○サンプルコード5:載せ替えにおけるトラブルシューティング

VHDLのクロック載せ替えは、その利便性から多くのプロジェクトで用いられていますが、その実装の際には多くのトラブルも生じることが知られています。

ここでは、載せ替えの際によくあるトラブルと、それを解決するためのシンプルなサンプルコードをご紹介いたします。

このコードでは、クロックの載せ替え時に発生する一般的なトラブルを表しています。

この例では、クロックの載せ替え時に誤ったクロックが選択されてしまう問題を扱っています。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity clock_switch_debug is
    Port ( clk1 : in STD_LOGIC;
           clk2 : in STD_LOGIC;
           sel : in STD_LOGIC;
           clk_out : out STD_LOGIC);
end clock_switch_debug;

architecture Behavioral of clock_switch_debug is
begin
    process(clk1, clk2)
    begin
        -- セレクタによってクロックを載せ替え
        if sel = '1' then
            clk_out <= clk1;
        else
            clk_out <= clk2;
        end if;
    end process;
end Behavioral;

しかし、上記のコードには一つの問題点があります。

クロックの入力ピンがプロセスの感度リストに記載されているため、クロックのエッジが検出されるたびにクロック載せ替えの処理が実行されてしまいます。

これにより、クロックの載せ替え時に不安定な動作を引き起こす可能性が高まります。

解決策として、クロックの選択処理を単一のクロックイベント内で行う必要があります。

その修正を行ったサンプルコードを紹介します。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity clock_switch_fixed is
    Port ( clk1 : in STD_LOGIC;
           clk2 : in STD_LOGIC;
           sel : in STD_LOGIC;
           clk_out : out STD_LOGIC);
end clock_switch_fixed;

architecture Behavioral of clock_switch_fixed is
begin
    process(clk1)
    begin
        if rising_edge(clk1) then
            if sel = '1' then
                clk_out <= clk1;
            else
                clk_out <= clk2;
            end if;
        end if;
    end process;
end Behavioral;

修正後のコードでは、rising_edge(clk1)の条件下でのみクロックの載せ替えを行っているため、不安定な動作のリスクを大きく軽減することができます。

この修正後のコードを実行すると、sel1の場合はclk1が出力され、sel0の場合はclk2が出力されることを確認することができます。

これにより、適切にクロック載せ替えを行うことができます。

○サンプルコード6:クロックソースの選択

クロックソースの選択は、VHDL設計において重要な決定の1つです。適切なクロックソースを選択することで、システムの動作や効率が大きく変わります。

ここでは、クロックソースの選択方法についての基本的な考え方と、そのためのサンプルコードを紹介します。

このコードでは、VHDLを使ってクロックソースを選択する方法を表しています。

この例では、2つの異なるクロックソースから1つを選択して出力する方法を表しています。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity clock_source_select is
    Port ( clk1 : in STD_LOGIC;
           clk2 : in STD_LOGIC;
           select : in STD_LOGIC;
           clk_out : out STD_LOGIC);
end clock_source_select;

architecture Behavioral of clock_source_select is
begin
process (clk1, clk2)
begin
    -- クロックソースの選択
    if select = '0' then
        clk_out <= clk1; -- selectが0の場合、clk1を出力
    else
        clk_out <= clk2; -- selectが1の場合、clk2を出力
    end if;
end process;
end Behavioral;

この例では、入力として2つのクロックソースclk1clk2を持っており、select信号を使って、どちらのクロックソースをclk_outとして出力するかを選択します。

selectが’0’の場合はclk1を、’1’の場合はclk2を出力します。

このようにして、システムの動作条件や特定の動作モードに応じて、最適なクロックソースを動的に選択することができます。

たとえば、低消費電力の動作モードでは低周波数のクロックを、高性能な動作モードでは高周波数のクロックを選択する、といった使い方が考えられます。

注意点としては、select信号の変更タイミングに注意が必要です。

クロックソースを切り替える際に、クロックの不安定な期間が生じる可能性があるため、クロックソースの切り替えはシステムが安定している状態で行うことが推奨されます。

このサンプルコードを実行すると、select信号に基づいて選択されたクロックソースがclk_outとして出力されることになります。

つまり、select信号の値に応じて、clk1またはclk2の信号がそのまま出力として得られます。

このクロックソースの選択方法は、特に複数のクロックソースを持つ大規模なシステムや、動的にクロック周波数を変更する必要があるシステムで有効です。

また、外部からのクロック供給と、内部で生成したクロックの間で切り替える際にも利用できます。

○サンプルコード7:外部クロックの利用

VHDLの設計において、外部からのクロックを利用する場面は多々あります。

特にFPGAやASICの設計では、外部オシレータやPLLからのクロックを受け取り、システム内でそのクロックを配布するケースが一般的です。

このコードでは、外部からのクロック信号を入力として受け取り、それを内部のモジュールに渡す方法を紹介しています。

この例では、外部クロックをそのまま内部のモジュールに渡す基本的な方法を採用しています。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity external_clock is
    Port ( ext_clk : in STD_LOGIC;
           internal_clk : out STD_LOGIC);
end external_clock;

architecture Behavioral of external_clock is
begin
    -- 外部クロックを内部クロックとしてそのまま出力
    process(ext_clk)
    begin
        if rising_edge(ext_clk) then
            internal_clk <= ext_clk;
        end if;
    end process;
end Behavioral;

このコードでは、ext_clkという名前のポートを通じて外部からクロック信号を受け取り、internal_clkという名前のポートを通じて内部にクロック信号を送出しています。

process内では、外部クロックext_clkの立ち上がりエッジを検出して、そのままinternal_clkに転送しています。

このコードを実行すると、外部から供給されるクロックがそのまま内部モジュールに渡されることとなります。

したがって、外部のオシレータや他のクロック源からの信号を受け取り、VHDLのシステム内で利用したい場合にこのようなコードを利用することが考えられます。

○サンプルコード8:複雑なクロックツリーの設計

クロックツリーはデジタル回路設計における重要な部分です。

VHDLを使用して、複数のクロックソースが絡み合った複雑なクロックツリーを効果的に設計する方法を学びましょう。

このコードでは、VHDLを用いて複雑なクロックツリーを設計する方法を表しています。

この例では、複数のクロックソースを組み合わせて一つの複雑なクロックツリーを構築しています。

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

entity ComplexClockTree is
Port ( clk1 : in STD_LOGIC;
       clk2 : in STD_LOGIC;
       clk3 : in STD_LOGIC;
       out_clk : out STD_LOGIC);
end ComplexClockTree;

architecture Behavioral of ComplexClockTree is
signal temp_clk : STD_LOGIC;
begin
temp_clk <= clk1 and clk2;
out_clk <= temp_clk or clk3;
end Behavioral;

上記のサンプルコードでは、3つのクロックソースclk1clk2clk3を使用して、temp_clkという中間クロックを生成し、最終的な出力クロックout_clkを得ています。

具体的には、clk1clk2がANDされてtemp_clkを生成し、その後、temp_clkclk3がORされてout_clkを生成します。

このような設計をすることで、特定の条件下で異なるクロックソースを使用することが可能となり、より柔軟なクロック設計が可能となります。

○サンプルコード9:クロックのスキュー対策

クロックスキューは、回路全体のタイミングを乱す可能性があり、これを解消するための手法が必要です。

こちらでは、VHDLでクロックのスキューを対策する一例を紹介します。

この例では、クロックの遅延回路を追加してスキューを調整しています。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity ClockSkewAdjustment is
Port ( clk_in : in STD_LOGIC;
       clk_out : out STD_LOGIC);
end ClockSkewAdjustment;

architecture Behavioral of ClockSkewAdjustment is
signal delayed_clk : STD_LOGIC;
begin
-- ここでは遅延回路の一例として単純なバッファを使用
delayed_clk <= clk_in after 5 ns;
clk_out <= delayed_clk;
end Behavioral;

上記のコードでは、入力されたclk_inを5ns遅延させてclk_outとして出力しています。

このようにして、他の回路とのタイミング差、すなわちスキューを調整することができます。

○サンプルコード10:テストベンチでのクロック載せ替えテスト

最後に、VHDLのテストベンチを使ってクロックの載せ替えをテストする方法を見ていきます。

テストベンチは、実際の動作をシミュレートして確認するためのものです。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity ClockSwitchTestbench is
end ClockSwitchTestbench;

architecture sim of ClockSwitchTestbench is
signal clk1, clk2, test_clk : STD_LOGIC := '0';
begin
-- クロック生成
clk1 <= not clk1 after 10 ns;
clk2 <= not clk2 after 20 ns;

process
begin
    wait for 50 ns;
    test_clk <= clk1;  -- クロック載せ替え
    wait for 50 ns;
    test_clk <= clk2;  -- クロック載せ替え
    wait;
end process;
end sim;

このテストベンチでは、2つの異なるクロックclk1clk2を生成し、50nsごとにtest_clkに載せ替えています。

このようにして、載せ替えの影響をシミュレーションで確認することができます。

●注意点と対処法

○クロック載せ替え時のよくあるトラブル

VHDLを用いてクロックを載せ替える際に、一般的に遭遇するトラブルをいくつか解説します。

さらに、それらのトラブルへの対処法も紹介します。

❶クロックの不安定さ

複数のクロックソースを載せ替える際、タイミングのズレやクロック信号の衝突が原因で、クロックが不安定になることがあります。

このコードでは、2つのクロックソースを切り替える例を紹介しています。

クロックの衝突を避けるため、クロックソースを切り替える前に、一時停止する機能を追加しています。

signal clk_source1, clk_source2, final_clk, temp : STD_LOGIC;
...
process
begin
    if switching_condition then
        temp <= '0';
        wait for 10 ns;
        final_clk <= clk_source2;
        temp <= '1';
    else
        final_clk <= clk_source1;
    end if;
end process;

❷クロックスキュー

クロック信号が異なる部分の回路に異なるタイミングで到達することをクロックスキューと呼びます。

これは、データのタイミングミスやセットアップ・ホールド違反を引き起こす原因となります。

クロックスキューを調整するための一例を紹介します。

このコードでは、クロック信号に遅延を加えることで、クロックのタイミングを調整しています。

signal clk_input, clk_adjusted : STD_LOGIC;
...
clk_adjusted <= clk_input after 5 ns;

適切な遅延を設定することで、クロックのスキューを調整し、全体のタイミングを改善することができます。

○最適なクロック設計のコツ

クロック設計はデジタル回路設計の中核をなす部分です。

次のポイントを考慮することで、最適なクロック設計を実現できます。

❶クロックの分布を最小限に

クロックの分岐を多くすると、クロックスキューやジッターの原因となりやすいです。

必要最小限の分岐に留めることが重要です。

❷クロックバッファの使用

クロック信号はバッファを通じて伝播させることで、スキューを減少させることができます。

適切な場所にクロックバッファを配置することで、クロックの伝播を最適化しましょう。

❸適切なクロック周波数の選択

VHDLでの設計時に、適切なクロック周波数を選択することで、消費電力を節約し、回路の性能を向上させることができます。

●応用例

○高速なデータ伝送のための載せ替え

高速なデータ伝送を実現するためには、クロックの載せ替えを効果的に使用する必要があります。

例として、データを高速に送信するためのサンプルコードを紹介します。

signal fast_clk, slow_clk, data_clk : STD_LOGIC;
...
process
begin
    if data_rate = "high" then
        data_clk <= fast_clk;
    else
        data_clk <= slow_clk;
    end if;
end process;

このコードでは、データの伝送レートが高い場合にはfast_clkを使用し、低い場合にはslow_clkを使用しています。

これにより、データの伝送速度に応じて適切なクロックを選択することができます。

○低消費電力デザインのためのクロック載せ替え

低消費電力を実現するためには、必要に応じてクロックの周波数を下げることが効果的です。

下記のコードでは、低電力モード時にクロックの周波数を下げる例を表しています。

signal operation_clk, low_power_clk, sys_clk : STD_LOGIC;
...
process
begin
    if power_mode = "low_power" then
        sys_clk <= low_power_clk;
    else
        sys_clk <= operation_clk;
    end if;
end process;

このコードでは、低電力モード時にlow_power_clkを使用し、通常の動作時にはoperation_clkを使用しています。

これにより、電力の消費を効果的に節約することができます。

まとめ

VHDLのクロック載せ替えに関する様々な技術やポイントを紹介しました。

この情報を参考に、効果的なクロック設計を進めていきましょう。

VHDLでのクロック載せ替えは、デジタル回路設計における重要な技術の一つです。

上手に活用することで、高性能で低消費電力の回路設計が可能となります。