VHDLでクロックを10通り生成する方法

VHDLのロゴとクロック生成のイラストが描かれた画像VHDL
この記事は約26分で読めます。

 

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

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

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

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

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

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

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

はじめに

VHDLはデジタル回路の設計に使用される一般的なハードウェア記述言語であり、この記事ではその中でも特にクロック生成に焦点を当てて解説を行います。

10の異なる方法でクロックを生成する方法をサンプルコードと共に詳しく見ていきましょう。

●VHDLとは

VHDL(VHSIC Hardware Description Language)は、高度集積回路のためのハードウェア記述言語です。

VHDLを用いることで、複雑なデジタル回路やシステムを効率的に設計できます。

○VHDLの基本概念

VHDLの特徴として、複数の階層レベルでの設計が可能であり、その上でモジュール間の通信を容易に記述できる点が挙げられます。

また、設計者は回路の動作を詳細に記述することができるため、微細な動作までのシミュレーションが可能です。

●クロック生成の基本

○クロックの重要性

デジタル回路の設計において、クロックは中心的な役割を果たします。

多くの回路がクロックの立ち上がりや立ち下がりに同期して動作するため、安定したクロック信号は必須です。

○クロック生成の基本的な方法

クロックを生成する基本的な方法は、外部から供給されるクロック信号を使用するか、専用の発振回路を使用して内部で生成する2つが考えられます。

●クロック生成のサンプルコード

○サンプルコード1:基本的なクロック生成

このコードでは、外部からのクロック信号を受け取り、そのまま出力する簡単な例を表しています。

この例では、外部クロックをそのまま内部回路に供給しています。

entity clock_gen is
    Port ( ext_clock : in  STD_LOGIC;
           int_clock : out STD_LOGIC);
end clock_gen;

architecture Behavioral of clock_gen is
begin
    int_clock <= ext_clock;
end Behavioral;

このコードを実行すると、ext_clockからのクロック信号がそのままint_clockに出力される結果となります。

○サンプルコード2:クロックの分周

このコードでは、外部からのクロック信号を受け取り、特定の分周比で出力する例を表しています。

この例では、外部クロックを半分の周波数で出力しています。

entity clock_divider is
    Port ( ext_clock : in  STD_LOGIC;
           divided_clock : out STD_LOGIC);
end clock_divider;

architecture Behavioral of clock_divider is
signal temp : STD_LOGIC := '0';
begin
    process(ext_clock)
    begin
        if rising_edge(ext_clock) then
            temp <= not temp;
        end if;
    end process;

    divided_clock <= temp;
end Behavioral;

このコードを実行すると、ext_clockからのクロック信号が半分の周波数でdivided_clockに出力される結果となります。

○サンプルコード3:複数のクロック出力

VHDLの世界では、ある場面で同時に複数のクロック信号を生成することが求められることがあります。

ここでは、VHDLを使用して、複数の異なる周波数のクロック信号を同時に生成する方法を紹介します。

このコードではVHDLのprocessを使って、1つの入力クロック信号を元に複数のクロック出力を生成するコードを紹介しています。

この例では入力クロックを2分周し、2つの異なる周波数のクロック信号を出力しています。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity multiple_clocks is
    Port ( clk_in  : in  STD_LOGIC;
           clk_out1 : out STD_LOGIC;
           clk_out2 : out STD_LOGIC);
end multiple_clocks;

architecture Behavioral of multiple_clocks is
    signal tmp1, tmp2: STD_LOGIC := '0';
begin
    -- 入力クロックを2分周してclk_out1を生成
    process(clk_in)
    begin
        if rising_edge(clk_in) then
            tmp1 <= not tmp1;
        end if;
    end process;
    clk_out1 <= tmp1;

    -- 入力クロックを4分周してclk_out2を生成
    process(clk_in)
    begin
        if rising_edge(clk_in) then
            tmp2 <= not tmp2;
            if tmp2 = '1' then
                clk_out2 <= not clk_out2;
            end if;
        end if;
    end process;
end Behavioral;

上記のコードでは、clk_inが上昇エッジを検出するたびに、tmp1の状態を切り替え、これをclk_out1に直接出力しています。

これにより、clk_out1clk_inの半分の周波数で動作するクロックとなります。

また、tmp2を使用して、clk_inの4分の1の周波数で動作するclk_out2も生成しています。

tmp2が’1’のときにのみ、clk_out2の状態が切り替わるようにしているため、clk_inの4分の1の周波数で動作するクロックが出力されます。

上述のサンプルコードをシミュレーションすると、入力クロックに対して、2つの異なる周波数で動作するクロック信号が得られます。

clk_out1は入力クロックの半分の周波数、clk_out2は入力クロックの4分の1の周波数で動作していることが確認できます。

このように、VHDLを使用して、1つの入力クロックから複数の異なる周波数のクロックを簡単に生成することができます。

特に、FPGAの設計などで複数のクロックドメインを持つ場合にこの手法が役立ちます。

○サンプルコード4:条件付きクロック生成

VHDLを利用したクロック生成にはさまざまな方法があります。

その中でも、特定の条件下でクロックを生成する方法に焦点を当てて解説を進めていきます。

この方法は、システムの動作条件に応じて、クロックを生成する場面で非常に有効です。

このコードでは、入力として与えられた条件信号に基づいてクロックを生成する方法を紹介しています。

この例では、条件信号がハイレベルのときのみクロックを生成し、ローレベルのときはクロックを生成しない方法を表しています。

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

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

architecture Behavioral of condition_clock_gen is
begin
process(clk_in)
-- 入力クロックに同期して動作するプロセス
begin
    if rising_edge(clk_in) then
        if condition = '1' then
            -- 条件がハイレベルの場合、出力クロックを生成
            clk_out <= not clk_out;
        else
            -- 条件がローレベルの場合、出力クロックは変化しない
            clk_out <= clk_out;
        end if;
    end if;
end process;
end Behavioral;

上記のコードにおいて、conditionという入力ポートが追加され、このポートに’1’が入力された場合のみ、clk_outがトグルします。

つまり、conditionが’1’でない限り、clk_outは変化しません。

このように、特定の条件下でのみクロックを生成する機能は、例えばデバッグモードや特定のオペレーションモード時にクロックを制御する際などに非常に役立ちます。

このコードを実際にFPGAやシミュレータ上で動かすと、condition信号がハイレベルのときのみ、clk_outがトグルすることが確認できるでしょう。

逆に、conditionがローレベルの時は、clk_outはその前の状態を保持し続けることになります。

○サンプルコード5:外部入力を用いたクロック生成

VHDLでクロックを生成する際、外部からの入力信号を参考にしてクロックを生成する方法もあります。

外部入力を利用することで、柔軟にシステム全体の動作を制御したり、特定の条件や状況に合わせてクロックを生成することが可能になります。

この手法は、例えば外部のセンサー情報に基づいてクロックを生成する場合などに有用です。

下記のサンプルコードでは、外部からの入力信号を参考にしてクロックを生成する方法を紹介しています。

この例では、外部入力信号を使ってクロックをオン/オフ切り替える方法を表しています。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

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

architecture Behavior of ExternalInputClockGen is
begin
    process(clk_in)
    begin
        -- 外部信号ext_signalが'1'の時にクロックを出力
        if ext_signal = '1' then
            clk_out <= clk_in;
        else
            clk_out <= '0';
        end if;
    end process;
end Behavior;

このコードでは、外部からの信号ext_signalを使って、クロック信号clk_inの出力を制御しています。

具体的には、ext_signalが’1’のときのみclk_inをそのままclk_outとして出力し、それ以外のときはclk_outを’0’にします。

このような制御を行うことで、外部の条件や状況に合わせてクロック信号の出力をオン/オフすることができます。

例えば、特定のセンサーからの入力がある場合にのみクロックを出力する、といった利用シーンが考えられます。

このコードを実際に実行すると、外部入力に応じてclk_outが動作することが確認できます。

外部信号が’1’の場合、clk_inのクロックがそのまま出力されますが、’0’の場合は出力が停止します。

○サンプルコード6:クロックの位相を調整

デジタル回路におけるクロックの位相調整は非常に重要です。

特定のタイミングで動作する部分のみを微調整することで、全体の動作を最適化することが可能です。

VHDLを使用して、クロックの位相を簡単に調整する方法を紹介します。

このコードでは、位相遅延要素を使って、クロックの位相を調整するコードを表しています。

この例では、与えられたクロック信号に対して、指定した位相で遅延させたクロックを生成しています。

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

entity phase_shift is
    Port ( clk_in   : in STD_LOGIC;
           phase    : in STD_LOGIC_VECTOR(2 downto 0);
           clk_out  : out STD_LOGIC);
end phase_shift;

architecture Behavioral of phase_shift is
signal phase_clk : STD_LOGIC_VECTOR(2 downto 0) := "000";
begin
    process(clk_in)
    begin
        if rising_edge(clk_in) then
            phase_clk <= phase_clk + 1;
        end if;
    end process;

    clk_out <= '1' when phase_clk = phase else '0';

end Behavioral;

このVHDLコードは、3ビットの入力phaseを持ち、この値に基づいてclk_inの位相を変更したクロックclk_outを生成します。

phaseの値が増加すると、出力クロックclk_outの遅延も増加します。

このようにして、位相の調整が可能となります。

例えば、phaseに”010″を入力すると、clk_inの位相が2クロック遅れたclk_outが得られます。

この仕組みを利用して、さまざまな位相のクロックを簡単に生成できます。

このコードを使用すると、デバイス上でclk_outがどのように動作するかを観察することができます。

特に、phaseの値を変更することで、clk_outの位相遅延が変わることを実感できるでしょう。

これは、実際の回路設計やシミュレーション時に、特定のタイミング要件を満たすための位相調整として利用されます。

○サンプルコード7:クロックの遅延生成

デジタルシステムの設計において、クロック信号の遅延は様々な理由で必要とされることがあります。

例えば、特定のロジックがクロックの立ち上がりと立ち下がりの間の特定の時間だけ動作する必要がある場合などです。

VHDLを使って、指定された時間だけクロックを遅延させる方法を考えてみましょう。

このコードでは、指定された時間だけクロックを遅延させるコードを表しています。

この例では、カウンタを使用してクロックの遅延を制御しています。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity clk_delay is
    Port ( clk_in   : in STD_LOGIC;
           delay_amount : in integer range 0 to 255;
           clk_out  : out STD_LOGIC);
end clk_delay;

architecture Behavioral of clk_delay is
signal counter : integer range 0 to 255 := 0;
signal delayed_clk : STD_LOGIC := '0';
begin
    process(clk_in)
    begin
        if rising_edge(clk_in) then
            if counter < delay_amount then
                counter <= counter + 1;
            else
                delayed_clk <= not delayed_clk;
                counter <= 0;
            end if;
        end if;
    end process;

    clk_out <= delayed_clk;

end Behavioral;

このコードを使用することで、delay_amountに指定した回数だけクロックを遅延させたclk_outが生成されます。

たとえば、delay_amountを10に設定すると、clk_inの10クロック後にclk_outがトグルすることになります。

これを使用して、必要な遅延を持つクロックを簡単に生成することができます。

この方法でクロック信号を遅延させることは、一部のアプリケーションでは非常に役立つことがあります。

例えば、異なるクロックドメイン間の信号転送や、特定のタイミング条件を満たす必要がある場合などです。

このコードを使用して生成された遅延クロックを実際のデバイスで観察すると、delay_amountの値に応じてclk_outの遅延が変わることが確認できます。

これにより、必要な遅延を持つクロックを簡単に生成し、それを基にシステムの動作を調整することができます。

○サンプルコード8:クロックの一時停止

システムの動作を一時的に停止させるために、クロックを一時停止することが必要な場面があります。

これは、例えばデバッグ時に特定の条件下でシステムの動作を確認したい場合や、特定の条件下でのみシステムを動作させたい場合などです。

VHDLを使用して、クロックを一時的に停止させる方法を学びましょう。

このコードでは、指定された条件下でクロックを一時停止するコードを表しています。

この例では、外部からの信号を使ってクロックを一時停止します。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

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

architecture Behavioral of clk_pause is
signal last_clk_state : STD_LOGIC := '0';
begin
    process(clk_in, pause_signal)
    begin
        if pause_signal = '1' then
            clk_out <= last_clk_state;
        else
            if rising_edge(clk_in) then
                last_clk_state <= not last_clk_state;
                clk_out <= last_clk_state;
            end if;
        end if;
    end process;

end Behavioral;

このコードでは、pause_signalが’1’のとき、clk_outは前回の状態を保持し続け、クロックは一時停止します。

pause_signalが’0’の場合、clk_outは通常のクロックとして動作します。

実際にこのコードを使用してデバイス上で動作を確認すると、pause_signalが’1’のとき、clk_outが停止し、’0’のときに再開することが確認できます。

この機能は、デバッグやテスト時に特定のタイミングでシステムの動作を一時停止させるために使用することができます。

○サンプルコード9:クロックの再同期

VHDLにおけるクロック信号の再同期は、異なるクロックドメイン間でデータを安全に転送するための重要な手法です。

再同期は、データが異なるクロックドメインを越えるときに、そのデータが正確に伝わるようにするために使用されます。

下記のサンプルコードは、クロックの再同期を行うための基本的な手法を表しています。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity clk_resync is
    Port ( clk_in1   : in  STD_LOGIC;
           clk_in2   : in  STD_LOGIC;
           data_in   : in  STD_LOGIC;
           data_out  : out STD_LOGIC);
end clk_resync;

architecture Behavioral of clk_resync is
signal temp1 : STD_LOGIC;
signal temp2 : STD_LOGIC;
begin
    -- 第一段階のフリップフロップ
    process(clk_in1)
    begin
        if rising_edge(clk_in1) then
            temp1 <= data_in;
        end if;
    end process;

    -- 第二段階のフリップフロップ
    process(clk_in2)
    begin
        if rising_edge(clk_in2) then
            temp2 <= temp1;
            data_out <= temp2;
        end if;
    end process;
end Behavioral;

このコードでは、2つの異なるクロックドメインclk_in1clk_in2を用いて、データの再同期を行っています。

データはdata_inから入力され、2段階のフリップフロップを通過してdata_outに出力されます。

この2段階のフリップフロップは、異なるクロックドメイン間のデータの転送を安定化するために用いられます。

このコードを実行した場合、clk_in1のクロックドメインで動作するデータが、clk_in2のクロックドメインに安全に転送されることが期待されます。

しかし、クロックの再同期を行う際は、転送元と転送先のクロックの関係や、実際のハードウェア環境に応じて、最適な再同期の方法を選択する必要があります。

○サンプルコード10:特定の条件下でのクロック出力

場合によっては、特定の条件下でのみクロックを出力したいことがあります。

下記のサンプルコードでは、指定された条件下でクロックを出力する方法を表しています。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

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

architecture Behavioral of conditional_clk is
signal last_clk_state : STD_LOGIC := '0';
begin
    process(clk_in, condition)
    begin
        if condition = '1' then
            if rising_edge(clk_in) then
                last_clk_state <= not last_clk_state;
                clk_out <= last_clk_state;
            end if;
        else
            clk_out <= '0';
        end if;
    end process;
end Behavioral;

このコードでは、conditionが’1’のとき、clk_outclk_inのクロックを出力します。

conditionが’0’の場合、clk_outは常に’0’を出力します。

このコードを実行すると、指定された条件が満たされたときにのみ、クロックが出力されることが確認できます。

このような方法は、特定の操作を条件付きで制御したい場合などに使用することができます。

●クロック生成の応用例

VHDLでのクロック生成は、単に周期的な信号を生成するだけでなく、様々な応用が考えられます。

ここでは、クロックを中心とした同期回路と非同期回路の基本的な概念とサンプルコードを通じて、その実装方法を学びます。

○クロックを用いた同期回路

同期回路は、クロック信号に同期して動作する回路を指します。

VHDLでは、クロックエッジ(立ち上がりや立ち下がり)を検出して、データの処理や保存を行います。

このコードでは、クロックの立ち上がりエッジでデータを保存する単純なDフリップフロップを紹介しています。

この例では、D入力をクロックの立ち上がりでQ出力に転送しています。

entity d_flipflop is
    Port ( D : in  STD_LOGIC;
           CLK : in  STD_LOGIC;
           Q : out  STD_LOGIC);
end d_flipflop;

architecture Behavioral of d_flipflop is
begin
process(CLK)
begin
    -- クロックの立ち上がりエッジで動作
    if rising_edge(CLK) then
        Q <= D;
    end if;
end process;
end Behavioral;

上記のサンプルコードを実行すると、D入力のデータが、クロックの立ち上がりエッジでQに転送される動作を観察できます。

○クロックを用いた非同期回路

非同期回路は、クロック信号に依存せずに動作する回路を指します。しかし、実際にはクロックを使用して非同期動作を模倣することが多いです。

非同期リセットを持つDフリップフロップの例を紹介します。

このコードでは、非同期リセット信号RESETを使ってDフリップフロップをリセットするコードを表しています。

この例では、RESETが’1’のときにQ出力を強制的に’0’にしています。

entity async_d_flipflop is
    Port ( D : in  STD_LOGIC;
           CLK : in  STD_LOGIC;
           RESET : in STD_LOGIC;
           Q : out  STD_LOGIC);
end async_d_flipflop;

architecture Behavioral of async_d_flipflop is
begin
process(CLK, RESET)
begin
    -- RESETが'1'の時、非同期にリセット
    if RESET = '1' then
        Q <= '0';
    elsif rising_edge(CLK) then
        Q <= D;
    end if;
end process;
end Behavioral;

このサンプルコードを実行すると、RESETが’1’の間はQが’0’になり、RESETが’0’の時はD入力のデータがクロックの立ち上がりエッジでQに転送される動作が確認できます。

これらの基本的な回路は、より複雑なデジタルシステムの基盤となります。

VHDLを使って、これらの基本的な概念を理解し、さらに発展させることができます。

●注意点と対処法

クロック設計は、デジタルシステムの基盤となる重要な部分です。

しかし、クロックに関連する様々な問題やトラブルも存在します。

ここでは、そのような問題の代表例として、クロックスキューとジッタについて解説します。

○クロックスキューとは

クロックスキューは、同じクロック信号が異なる回路要素に異なる時間で到達する現象を指します。

これは、回路のレイアウトや配線の長さの違い、ロード容量の違いなどが原因で生じることがあります。

この問題を解決するためには、回路設計の段階で配線の長さやロード容量を考慮し、クロックの分布を最適化する必要があります。

また、特定の場所でのクロック遅延を意図的に追加することで、スキューを補正するテクニックもあります。

○ジッタとは

ジッタは、クロックの周期や位相に生じる不規則な変動を指します。

これは、外部ノイズや電源ノイズ、温度変動などが原因で生じることが多いです。

ジッタの問題を解決するためには、高品質のクロック発振器の使用や、ノイズ対策として適切な基板設計、電源回路の最適化などが考えられます。

○クロックの安定性の確保

クロックの安定性を確保するためには、上記のような問題を事前に検出し、適切な対策を講じることが重要です。

特に、大規模なデジタルシステムでは、これらの問題が複雑に絡み合うことがありますので、シミュレーションや実験を繰り返し、確実な動作を確認することが求められます。

●カスタマイズ方法

VHDLを使うと、自身のニーズに合わせてクロック生成ロジックをカスタマイズすることが可能です。

特定の目的や要件に合わせてクロック動作を変更したいとき、あるいは、既存のクロックジェネレータが提供する機能だけでは不足しているときに、カスタマイズされたクロック生成ロジックが役立ちます。

例えば、特定のタスクが完了するまでクロックを一時停止したり、特定の条件を満たしたときだけクロックを動作させたりすることができます。

また、複数のクロックを異なる位相や周波数で生成することも可能です。

これらは、異なる動作スピードの周辺機器との接続や、時間的な動作を精密に制御するために必要となる場面が多いです。

ここでは、特定の条件を満たしたときだけクロックを動作させるロジックを構築する方法について説明します。

○独自のクロック生成ロジックの構築

このコードでは、外部からのスタート信号STARTを受けてクロックCLKを生成するロジックを作成しています。

この例では、START信号が’1’になった時だけCLKが動作し、それ以外のときはCLKは’0’になります。

entity conditional_clock_generator is
    Port ( START : in  STD_LOGIC;
           CLK : out  STD_LOGIC);
end conditional_clock_generator;

architecture Behavioral of conditional_clock_generator is
    signal internal_clock : STD_LOGIC := '0';
    constant T : time := 10 ns; -- クロック周期
begin
    clock_process : process
    begin
        wait for T/2;
        internal_clock <= not internal_clock;
    end process;

    CLK_process : process (START, internal_clock)
    begin
        if START = '1' then
            CLK <= internal_clock;
        else
            CLK <= '0';
        end if;
    end process;
end Behavioral;

このサンプルコードを実行すると、STARTが’1’の間は、internal_clockの値がCLKに出力され、周期的に’0’と’1’が切り替わることを観察できます。

一方、STARTが’0’の間は、CLKは常に’0’となります。

このようなカスタマイズされたクロック生成ロジックは、例えば特定のタスクが終了するまで次のタスクを待つような制御を行う場面や、特定のイベントをトリガーとしてクロックを動作させる必要がある場合などに有用です。

クロック生成ロジックは非常に柔軟で、VHDLのようなハードウェア記述言語を用いれば、具体的な要件や目的に合わせて自由に設計することが可能です。

基本的な概念と手法を理解した上で、自身のニーズに応じてロジックをカスタマイズしてみてください。

まとめ

この記事では、VHDLでのクロック生成について詳しく解説しました。

クロックはデジタルシステムの心臓部とも言えるもので、その生成方法や応用例、注意点と対処法を理解することは、VHDLを使用したシステム設計において極めて重要です。

また、独自のクロック生成ロジックの構築方法についても触れました。

これらの知識を活かして、自身の要件に合わせたクロック設計を行い、より高度で効率的なシステムを構築してください。