読み込み中...

VHDLで使うrising_edgeの基本事項と活用13選

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

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

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

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

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

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

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

●VHDLのrising_edgeとは?10倍効果的な回路設計の鍵

デジタル回路設計の分野で、VHDLは欠かせない存在。

その中でも、rising_edgeは特に重要な概念。

皆さんは、回路設計をする際にクロック信号の立ち上がりエッジを使ったことがありますか?

rising_edgeは、まさにそのクロック信号の立ち上がりエッジを検出するための機能。

回路設計者の皆さん、時間の正確な制御に悩んだ経験はありませんか?rising_edgeを使えば、そんな悩みから解放されます。

クロック信号の立ち上がりタイミングを正確に捉えることで、回路の同期を取ることができるのです。

○rising_edgeの基本概念と重要性

rising_edgeは、クロック信号が’0’から’1’に変化する瞬間を検出する関数。

この関数を使うことで、回路の動作タイミングを精密に制御できます。

例えば、データの読み取りや書き込み、状態の遷移などを、クロックの立ち上がりに合わせて行うことが可能になります。

回路設計において、タイミングは非常に重要。

rising_edgeを使用することで、回路全体の同期を取ることができ、信頼性の高い設計が可能になります。

また、複数の処理を同時に行う際にも、rising_edgeは大きな役割を果たします。

○クロック信号とタイミング制御の秘訣

クロック信号は、デジタル回路の心臓部分。クロック信号の周期に合わせて、回路内の各部分が動作します。

rising_edgeを使用することで、このクロック信号の立ち上がりのタイミングを正確に捉えることができます。

タイミング制御の秘訣は、クロック信号の周波数と位相を適切に設定すること。

高速な処理が必要な場合は高周波数のクロックを使用し、低消費電力が求められる場合は低周波数のクロックを選択します。

また、複数のクロックドメインがある場合は、それぞれのドメイン間の同期にも注意が必要です。

○サンプルコード1:基本的なrising_edge使用例

では、実際にrising_edgeを使用したVHDLコードを見てみましょう。

ここでは、単純なDフリップフロップの実装例を紹介します。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

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

architecture Behavioral of D_Flip_Flop is
begin
    process(CLK)
    begin
        if rising_edge(CLK) then
            Q <= D;
        end if;
    end process;
end Behavioral;

このコードでは、CLK信号の立ち上がりエッジでDの値をQに格納しています。

rising_edge関数を使用することで、クロックの立ち上がりのタイミングを正確に捉えることができます。

実行結果を見てみましょう。CLKが’0’から’1’に変化するたびに、Dの値がQに反映されます。

例えば、

CLK: 0 → 1 → 0 → 1 → 0 → 1
D : 0 → 1 → 1 → 0 → 0 → 1
Q : – → 0 → 0 → 1 → 1 → 0

このように、CLKが立ち上がるタイミングで、DのたQが更新されていることがわかります。

●rising_edgeの実践的活用法

rising_edgeの基本を理解したところで、より実践的な活用法を見ていきましょう。

rising_edgeは、様々な回路要素で使用されます。

フリップフロップ、シフトレジスタ、カウンタ、ステートマシンなど、多岐にわたります。

○サンプルコード2:フリップフロップでのrising_edge実装

フリップフロップは、デジタル回路の基本的な構成要素。

ここでは、JKフリップフロップの実装例を紹介します。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity JK_Flip_Flop is
    Port ( J : in  STD_LOGIC;
           K : in  STD_LOGIC;
           CLK : in  STD_LOGIC;
           Q : out  STD_LOGIC;
           Q_BAR : out  STD_LOGIC);
end JK_Flip_Flop;

architecture Behavioral of JK_Flip_Flop is
    signal state : STD_LOGIC := '0';
begin
    process(CLK)
    begin
        if rising_edge(CLK) then
            if (J = '0' and K = '0') then
                state <= state;
            elsif (J = '0' and K = '1') then
                state <= '0';
            elsif (J = '1' and K = '0') then
                state <= '1';
            else
                state <= not state;
            end if;
        end if;
    end process;

    Q <= state;
    Q_BAR <= not state;
end Behavioral;

このコードでは、rising_edge(CLK)を使用して、クロックの立ち上がりエッジでJとKの入力に応じて状態を更新しています。

実行結果の例

CLK : 0 → 1 → 0 → 1 → 0 → 1 → 0 → 1
J : 0 → 1 → 1 → 0 → 0 → 1 → 1 → 1
K : 0 → 0 → 1 → 0 → 1 → 0 → 1 → 1
state : 0 → 0 → 1 → 1 → 1 → 0 → 0 → 1
Q : 0 → 0 → 1 → 1 → 1 → 0 → 0 → 1
Q_BAR : 1 → 1 → 0 → 0 → 0 → 1 → 1 → 0

CLKの立ち上がりごとに、JとKの値に応じてstateが更新されていることがわかります。

○サンプルコード3:シフトレジスタにおけるrising_edgeの使用

シフトレジスタは、データを順次シフトさせる回路。

rising_edgeを使用することで、正確なタイミングでデータをシフトさせることができます。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity Shift_Register is
    Port ( DATA_IN : in  STD_LOGIC;
           CLK : in  STD_LOGIC;
           RESET : in  STD_LOGIC;
           DATA_OUT : out  STD_LOGIC_VECTOR(3 downto 0));
end Shift_Register;

architecture Behavioral of Shift_Register is
    signal shift_reg : STD_LOGIC_VECTOR(3 downto 0) := (others => '0');
begin
    process(CLK, RESET)
    begin
        if RESET = '1' then
            shift_reg <= (others => '0');
        elsif rising_edge(CLK) then
            shift_reg <= shift_reg(2 downto 0) & DATA_IN;
        end if;
    end process;

    DATA_OUT <= shift_reg;
end Behavioral;

このコードでは、rising_edge(CLK)を使用して、クロックの立ち上がりエッジでデータを1ビットずつシフトしています。

実行結果の例

CLK : 0 → 1 → 0 → 1 → 0 → 1 → 0 → 1
RESET : 0 → 0 → 0 → 0 → 0 → 0 → 0 → 0
DATA_IN : 1 → 0 → 1 → 1 → 0 → 1 → 0 → 1
shift_reg: 0000 → 0001 → 0001 → 0010 → 0010 → 0101 → 0101 → 1010

CLKの立ち上がりごとに、DATA_INの値がshift_regの最下位ビットに入り、他のビットが左にシフトしていることがわかります。

○サンプルコード4:カウンタ回路でのrising_edge活用

カウンタは、デジタル回路で頻繁に使用される要素。

rising_edgeを使用することで、正確なタイミングでカウントを増加させることができます。

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

entity Counter is
    Port ( CLK : in  STD_LOGIC;
           RESET : in  STD_LOGIC;
           ENABLE : in  STD_LOGIC;
           COUNT : out STD_LOGIC_VECTOR(3 downto 0));
end Counter;

architecture Behavioral of Counter is
    signal count_value : unsigned(3 downto 0) := (others => '0');
begin
    process(CLK, RESET)
    begin
        if RESET = '1' then
            count_value <= (others => '0');
        elsif rising_edge(CLK) then
            if ENABLE = '1' then
                if count_value = "1111" then
                    count_value <= (others => '0');
                else
                    count_value <= count_value + 1;
                end if;
            end if;
        end if;
    end process;

    COUNT <= std_logic_vector(count_value);
end Behavioral;

このコードでは、rising_edge(CLK)を使用して、クロックの立ち上がりエッジでカウント値を増加させています。

実行結果の例

CLK : 0 → 1 → 0 → 1 → 0 → 1 → 0 → 1
RESET : 0 → 0 → 0 → 0 → 0 → 0 → 0 → 0
ENABLE : 1 → 1 → 1 → 1 → 1 → 1 → 1 → 1
COUNT : 0000 → 0001 → 0001 → 0010 → 0010 → 0011 → 0011 → 0100

CLKの立ち上がりごとに、ENABLEが’1’の場合にカウント値が増加していることがわかります。

○サンプルコード5:ステートマシンにおけるrising_edge

ステートマシンは、複雑な制御ロジックを実装する際に非常に有用。

rising_edgeを使用することで、状態遷移を正確に制御できます。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity Traffic_Light is
    Port ( CLK : in  STD_LOGIC;
           RESET : in  STD_LOGIC;
           RED : out  STD_LOGIC;
           YELLOW : out  STD_LOGIC;
           GREEN : out  STD_LOGIC);
end Traffic_Light;

architecture Behavioral of Traffic_Light is
    type state_type is (S_RED, S_GREEN, S_YELLOW);
    signal state : state_type := S_RED;
    signal counter : integer range 0 to 50 := 0;
begin
    process(CLK, RESET)
    begin
        if RESET = '1' then
            state <= S_RED;
            counter <= 0;
        elsif rising_edge(CLK) then
            case state is
                when S_RED =>
                    if counter = 30 then
                        state <= S_GREEN;
                        counter <= 0;
                    else
                        counter <= counter + 1;
                    end if;
                when S_GREEN =>
                    if counter = 20 then
                        state <= S_YELLOW;
                        counter <= 0;
                    else
                        counter <= counter + 1;
                    end if;
                when S_YELLOW =>
                    if counter = 5 then
                        state <= S_RED;
                        counter <= 0;
                    else
                        counter <= counter + 1;
                    end if;
            end case;
        end if;
    end process;

    RED <= '1' when state = S_RED else '0';
    YELLOW <= '1' when state = S_YELLOW else '0';
    GREEN <= '1' when state = S_GREEN else '0';
end Behavioral;

このコードでは、rising_edge(CLK)を使用して、クロックの立ち上がりエッジで状態遷移とカウンタの更新を行っています。

実行結果の例(クロック50回分)

CLK : 0101010101…(省略)…0101
RESET : 0000000000…(省略)…0000
state : RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRGGGGGGGGGGGGGGGGGGGGYYYYY
RED : 1111111111111111111111111111110000000000000000000000000
YELLOW : 0000000000000000000000000000000000000000000000000011111
GREEN : 0000000000000000000000000000001111111111111111111100000

CLKの立ち上がりごとに、カウンタが増加し、設定した時間(赤30、緑20、黄5クロック)で状態が遷移していることがわかります。

●VHDLシミュレーションとrising_edge

VHDLで設計した回路が正しく動作するか確認するために、シミュレーションは欠かせません。

rising_edgeを使用した回路のシミュレーションには、特別な注意が必要です。

クロックの立ち上がりエッジを正確に捉えることが、シミュレーションの成功の鍵となります。

○テストベンチの作成方法

テストベンチは、設計した回路の動作を検証するための重要なツールです。

rising_edgeを含む回路のテストベンチを作成する際は、クロック信号の生成に注意が必要です。

適切な周期でクロック信号を生成し、rising_edgeが正しく検出されるようにしなければなりません。

テストベンチの作成手順は以下のとおりです。まず、テスト対象の回路を entity として宣言します。

次に、クロック信号を生成するプロセスを記述します。

さらに、テスト用の入力信号を生成するプロセスを追加します。

最後に、出力信号を観察し、期待される動作と一致するか確認します。

○サンプルコード6:rising_edgeを含むテストベンチ

ここでは、先ほど紹介したカウンタ回路のテストベンチの例を紹介します。

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
    -- テスト対象の回路を宣言
    component Counter
        Port ( CLK : in  STD_LOGIC;
               RESET : in  STD_LOGIC;
               ENABLE : in  STD_LOGIC;
               COUNT : out STD_LOGIC_VECTOR(3 downto 0));
    end component;

    -- 入力信号の宣言
    signal CLK : STD_LOGIC := '0';
    signal RESET : STD_LOGIC := '0';
    signal ENABLE : STD_LOGIC := '0';

    -- 出力信号の宣言
    signal COUNT : STD_LOGIC_VECTOR(3 downto 0);

    -- クロック周期の定義
    constant CLK_PERIOD : time := 10 ns;

begin
    -- テスト対象の回路をインスタンス化
    uut: Counter port map (
        CLK => CLK,
        RESET => RESET,
        ENABLE => ENABLE,
        COUNT => COUNT
    );

    -- クロック信号生成プロセス
    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*2;
        RESET <= '0';

        -- カウンタの有効化
        ENABLE <= '1';

        -- 20クロックサイクル待機
        wait for CLK_PERIOD*20;

        -- カウンタの無効化
        ENABLE <= '0';

        wait;
    end process;
end Behavioral;

このテストベンチでは、クロック信号を10nsの周期で生成しています。

リセット信号を2クロックサイクル分アサートした後、ENABLEを高にして20クロックサイクル待機し、再びENABLEを低にしています。

テストベンチの実行結果は次のようになります。

CLK : 0101010101…(省略)…0101
RESET : 1100000000…(省略)…0000
ENABLE : 0011111111…(省略)…1100
COUNT : 0000000001…(省略)…1110

最初の2クロックサイクルでRESETが高になっているため、COUNTは0000を保持します。

その後、ENABLEが高になると、COUNTが順次増加していきます。

20クロックサイクル後、ENABLEが低になるとCOUNTの値が固定されます。

○シミュレーション結果の解析テクニック

シミュレーション結果を効果的に解析するために、いくつかのテクニックがあります。

波形ビューアを使用して、各信号の変化を視覚的に確認することが重要です。

特に、クロックの立ち上がりエッジでCOUNTの値が正しく更新されているかを注意深く観察します。

また、テストベンチにアサーション(assertion)を追加することで、期待される動作を自動的にチェックすることができます。

例えば、リセット後にCOUNTが0になっているか、ENABLEが高の間にCOUNTが正しく増加しているかなどを確認するアサーションを記述できます。

シミュレーション結果の解析では、タイミング図を細かく観察することが重要です。

クロックの立ち上がりエッジでCOUNTの値が変化しているか、RESETやENABLEの変化に対してCOUNTが適切に反応しているかを確認します。

また、予期せぬ動作や異常な値の変化がないかも注意深くチェックしましょう。

●Quartusを使ったrising_edgeの実装

Quartusは、Intelが提供するFPGA開発ツールです。

rising_edgeを使用した回路をQuartusで実装する際には、いくつかの重要なステップがあります。

○サンプルコード7:Quartusでのプロジェクト設定

Quartusでプロジェクトを作成し、VHDLファイルを追加する手順は次のとおりです。

  1. Quartusを起動し、”File” -> “New Project Wizard”を選択します。
  2. プロジェクト名と保存場所を指定します。
  3. 使用するデバイスファミリーとデバイスを選択します。
  4. VHDLファイルを追加します。

ここでは、Quartusでプロジェクトを設定するためのTcl scriptの例を紹介します。

# プロジェクトを作成
project_new rising_edge_example -overwrite

# デバイスを設定
set_global_assignment -name FAMILY "Cyclone V"
set_global_assignment -name DEVICE 5CSEMA5F31C6

# VHDLファイルを追加
set_global_assignment -name VHDL_FILE counter.vhd

# ピン割り当てを設定
set_location_assignment PIN_AF14 -to CLK
set_location_assignment PIN_AA14 -to RESET
set_location_assignment PIN_AA15 -to ENABLE
set_location_assignment PIN_V16 -to COUNT[0]
set_location_assignment PIN_W16 -to COUNT[1]
set_location_assignment PIN_V17 -to COUNT[2]
set_location_assignment PIN_V18 -to COUNT[3]

# プロジェクトをコンパイル
execute_flow -compile

project_close

このスクリプトは、”rising_edge_example”というプロジェクトを作成し、Cyclone Vデバイスを指定しています。

counter.vhdファイルを追加し、ピン割り当てを行っています。最後に、プロジェクトをコンパイルします。

○サンプルコード8:Quartusでのrising_edge回路合成

Quartusでrising_edge回路を合成する際は、タイミング制約を適切に設定することが重要です。

ここでは、Quartusでタイミング制約を設定するためのSDCファイルの例を紹介します。

# クロック制約を設定
create_clock -period 10.000 -name CLK [get_ports CLK]

# 入力遅延を設定
set_input_delay -clock CLK -max 2 [get_ports {RESET ENABLE}]
set_input_delay -clock CLK -min 1 [get_ports {RESET ENABLE}]

# 出力遅延を設定
set_output_delay -clock CLK -max 2 [get_ports COUNT*]
set_output_delay -clock CLK -min 1 [get_ports COUNT*]

# False pathを設定
set_false_path -from [get_ports RESET] -to [get_registers *]

このSDCファイルでは、クロック周期を10nsに設定し、入力と出力の遅延を指定しています。

また、RESETからレジスタへのパスをfalse pathとして設定しています。

Quartusでの回路合成結果は、通常Resource Utilization Summaryとして表示されます。

例えば、次のようなレポートが生成されることがあります。

Logic utilization: 15 / 32,070 ( < 1 % )
Total registers: 4
Total pins: 7 / 457 ( 2 % )
Total block memory bits: 0 / 4,065,280 ( 0 % )
Total PLLs: 0 / 6 ( 0 % )

この結果から、カウンタ回路が非常に小さなリソース使用量で実装されていることがわかります。

○サンプルコード9:タイミング解析とコンストレイント設定

Quartusでのタイミング解析は非常に重要です。

特に、rising_edgeを使用する回路では、セットアップ時間とホールド時間の違反がないかを慎重に確認する必要があります。

ここでは、より詳細なタイミング制約を設定するSDCファイルの例を紹介します。

# クロック制約を設定
create_clock -period 10.000 -name CLK [get_ports CLK]

# 入力遅延を設定
set_input_delay -clock CLK -max 2 [get_ports {RESET ENABLE}]
set_input_delay -clock CLK -min 1 [get_ports {RESET ENABLE}]

# 出力遅延を設定
set_output_delay -clock CLK -max 2 [get_ports COUNT*]
set_output_delay -clock CLK -min 1 [get_ports COUNT*]

# False pathを設定
set_false_path -from [get_ports RESET] -to [get_registers *]

# 最大遅延制約を設定
set_max_delay -from [get_registers *] -to [get_registers *] 8

# 最小遅延制約を設定
set_min_delay -from [get_registers *] -to [get_registers *] 0.5

# マルチサイクルパスを設定
set_multicycle_path -setup -end -from [get_registers *] -to [get_registers *] 2
set_multicycle_path -hold -end -from [get_registers *] -to [get_registers *] 1

このSDCファイルでは、前述の制約に加えて、最大遅延と最小遅延の制約、およびマルチサイクルパスの設定を追加しています。

Quartusでのタイミング解析の結果は、通常Timing Analyzerで確認できます。

例えば、次のようなレポートが生成されることがあります。

Setup slack: 5.234 ns
Hold slack: 0.321 ns
Recovery slack: N/A
Removal slack: N/A

この結果から、設計した回路がタイミング制約を満たしていることがわかります。

セットアップスラックとホールドスラックが両方とも正の値であることが重要です。

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

VHDLでrising_edgeを使用する際、様々なエラーに遭遇することがあります。

エラーを迅速に解決できるスキルは、効率的な回路設計に不可欠です。

初心者からベテランまで、誰もが直面する可能性がある問題と、その解決策を探っていきましょう。

○タイミング違反の解決策

タイミング違反は、rising_edgeを使用する際に最も頻繁に発生するエラーの一つです。

セットアップタイムやホールドタイム違反が起こると、回路が正しく動作しない可能性があります。

解決策として、まずクロック周波数を下げることを検討します。

クロック周波数を下げると、信号が安定するまでの時間的余裕が増えます。

例えば、100MHzから80MHzに下げることで、タイミング違反が解消されることがあります。

また、パイプライン化も効果的な手法です。

長い組み合わせ論理をいくつかのステージに分割し、各ステージ間にレジスタを挿入します。

-- パイプライン化前
process(CLK)
begin
    if rising_edge(CLK) then
        Result <= A + B + C + D + E;
    end if;
end process;

-- パイプライン化後
process(CLK)
begin
    if rising_edge(CLK) then
        Stage1 <= A + B;
        Stage2 <= Stage1 + C;
        Stage3 <= Stage2 + D;
        Result <= Stage3 + E;
    end if;
end process;

パイプライン化により、各クロックサイクルでの処理量が減少し、タイミング違反を解消できる可能性が高まります。

○メタステーブル状態の回避方法

メタステーブル状態は、非同期信号をrising_edgeで同期化する際に発生することがあります。

メタステーブル状態では、回路の出力が予測不可能になり、システム全体の動作不良を引き起こす可能性があります。

避けるための一般的な方法は、複数段のフリップフロップを使用することです。

二段のフリップフロップを用いる方法を「ダブルフリップフロップ同期化」と呼びます。

entity MetastablePrevention is
    Port ( CLK : in STD_LOGIC;
           AsyncInput : in STD_LOGIC;
           SyncOutput : out STD_LOGIC);
end MetastablePrevention;

architecture Behavioral of MetastablePrevention is
    signal FF1, FF2 : STD_LOGIC := '0';
begin
    process(CLK)
    begin
        if rising_edge(CLK) then
            FF1 <= AsyncInput;
            FF2 <= FF1;
        end if;
    end process;

    SyncOutput <= FF2;
end Behavioral;

この方法により、最初のフリップフロップでメタステーブル状態が発生しても、二段目のフリップフロップで安定した値を出力できる確率が格段に高まります。

○合成エラーのトラブルシューティング

合成エラーは、VHDLコードが正しく合成できない場合に発生します。

rising_edgeを使用する際、よく見られる合成エラーとして、複数のクロックドメインの混在があります。

問題解決のアプローチとして、クロックドメインを明確に分離することが挙げられます。

各クロックドメインを別々のプロセスで記述し、クロックドメイン間の信号の受け渡しには非同期FIFOなどを使用します。

-- クロックドメイン1
process(CLK1)
begin
    if rising_edge(CLK1) then
        -- クロックドメイン1の処理
    end if;
end process;

-- クロックドメイン2
process(CLK2)
begin
    if rising_edge(CLK2) then
        -- クロックドメイン2の処理
    end if;
end process;

-- クロックドメイン間の信号受け渡し
AsyncFIFO : entity work.AsyncFIFO
    port map (
        WrClk => CLK1,
        RdClk => CLK2,
        -- その他の接続
    );

クロックドメインを適切に分離することで、合成ツールがコードを正しく解釈し、エラーを回避できる可能性が高まります。

●rising_edgeの高度な応用例

rising_edgeの基本を押さえたら、より高度な応用へと歩を進めましょう。

複雑な回路設計においては、単純なrising_edgeの使用だけでは不十分な場合があります。

ここでは、実践的な応用例を通じて、rising_edgeの真価を発揮する方法を学びます。

○サンプルコード10:非同期リセット付きフリップフロップ

非同期リセットは、システムの初期化や緊急停止などで重要な役割を果たします。

rising_edgeと組み合わせることで、より柔軟な制御が可能になります。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

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

architecture Behavioral of AsyncResetFF is
begin
    process(CLK, RESET)
    begin
        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’の場合、通常のrising_edge動作を行います。

実行結果の例

CLK : 0 → 1 → 0 → 1 → 0 → 1 → 0 → 1
RESET : 1 → 1 → 0 → 0 → 0 → 0 → 1 → 0
D : 1 → 0 → 1 → 1 → 0 → 1 → 1 → 0
Q : 0 → 0 → 0 → 1 → 1 → 1 → 0 → 0

RESETが’1’の間はQが’0’に保たれ、RESETが’0’になった後はクロックの立ち上がりでDの値がQに反映されています。

○サンプルコード11:クロックドメイン crossing

異なるクロックドメイン間でデータを安全に転送することは、複雑な回路設計において重要な課題です。

rising_edgeを使用して、安全なクロックドメインクロッシングを実現できます。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity ClockDomainCrossing is
    Port ( CLK_A : in STD_LOGIC;
           CLK_B : in STD_LOGIC;
           DATA_A : in STD_LOGIC;
           DATA_B : out STD_LOGIC);
end ClockDomainCrossing;

architecture Behavioral of ClockDomainCrossing is
    signal data_a_reg : STD_LOGIC := '0';
    signal sync_reg1, sync_reg2 : STD_LOGIC := '0';
begin
    -- クロックドメインA
    process(CLK_A)
    begin
        if rising_edge(CLK_A) then
            data_a_reg <= DATA_A;
        end if;
    end process;

    -- クロックドメインB
    process(CLK_B)
    begin
        if rising_edge(CLK_B) then
            sync_reg1 <= data_a_reg;
            sync_reg2 <= sync_reg1;
        end if;
    end process;

    DATA_B <= sync_reg2;
end Behavioral;

このコードでは、CLK_Aドメインからのデータを一旦レジスタに格納し、CLK_Bドメインで二段のフリップフロップを使用して同期化しています。

実行結果の例

CLK_A : 0 → 1 → 0 → 1 → 0 → 1 → 0 → 1
CLK_B : 0 → 0 → 1 → 0 → 1 → 0 → 1 → 0
DATA_A : 0 → 1 → 1 → 0 → 0 → 1 → 1 → 0
data_a_reg : 0 → 0 → 1 → 1 → 0 → 0 → 1 → 1
sync_reg1 : 0 → 0 → 0 → 0 → 1 → 1 → 0 → 0
sync_reg2 : 0 → 0 → 0 → 0 → 0 → 0 → 1 → 1
DATA_B : 0 → 0 → 0 → 0 → 0 → 0 → 1 → 1

データがCLK_Aドメインから安全にCLK_Bドメインに転送され、メタステーブル状態のリスクを最小限に抑えていることがわかります。

○サンプルコード12:パイプライン処理でのrising_edge活用

パイプライン処理は、高速な回路設計において欠かせない技術です。

rising_edgeを使用してパイプラインステージを制御することで、処理速度と効率を向上させることができます。

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

entity PipelinedMultiplier is
    Port ( CLK : in STD_LOGIC;
           A, B : in STD_LOGIC_VECTOR(7 downto 0);
           RESULT : out STD_LOGIC_VECTOR(15 downto 0));
end PipelinedMultiplier;

architecture Behavioral of PipelinedMultiplier is
    signal A_reg, B_reg : STD_LOGIC_VECTOR(7 downto 0);
    signal Mult_result : STD_LOGIC_VECTOR(15 downto 0);
    signal Result_reg : STD_LOGIC_VECTOR(15 downto 0);
begin
    process(CLK)
    begin
        if rising_edge(CLK) then
            -- Stage 1: 入力レジスタ
            A_reg <= A;
            B_reg <= B;

            -- Stage 2: 乗算
            Mult_result <= std_logic_vector(unsigned(A_reg) * unsigned(B_reg));

            -- Stage 3: 出力レジスタ
            Result_reg <= Mult_result;
        end if;
    end process;

    RESULT <= Result_reg;
end Behavioral;

このコードでは、8ビットの乗算器を3ステージのパイプラインで実装しています。

各ステージはrising_edgeで制御されています。

実行結果の例

CLK : 0 → 1 → 0 → 1 → 0 → 1 → 0 → 1
A : 05 → 03 → 07 → 02 → 04 → 06 → 01 → 08
B : 02 → 04 → 03 → 05 → 01 → 07 → 06 → 02
A_reg : 00 → 05 → 05 → 03 → 07 → 02 → 04 → 06
B_reg : 00 → 02 → 02 → 04 → 03 → 05 → 01 → 07
Mult_result : 0000 → 0000 → 000A → 000A → 000C → 0015 → 000A → 0004
Result_reg : 0000 → 0000 → 0000 → 000A → 000A → 000C → 0015 → 000A
RESULT : 0000 → 0000 → 0000 → 000A → 000A → 000C → 0015 → 000A

各クロックサイクルで、データがパイプラインステージを通過していく様子が見て取れます。

○サンプルコード13:CDCを考慮したrising_edge設計

Clock Domain Crossing (CDC)は、複数のクロックドメインが存在する大規模な設計で重要な考慮事項です。

rising_edgeを適切に使用することで、安全なCDC設計が可能になります。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity CDCAwareDesign is
    Port ( CLK_SLOW : in STD_LOGIC;
           CLK_FAST : in STD_LOGIC;
           DATA_IN : in STD_LOGIC_VECTOR(7 downto 0);
           DATA_OUT : out STD_LOGIC_VECTOR(7 downto 0));
end CDCAwareDesign;

architecture Behavioral of CDCAwareDesign is
    signal data_slow : STD_LOGIC_VECTOR(7 downto 0);
    signal data_fast_1, data_fast_2 : STD_LOGIC_VECTOR(7 downto 0);
    signal handshake_slow, handshake_fast_1, handshake_fast_2 : STD_LOGIC := '0';
begin
    -- スロークロックドメイン
    process(CLK_SLOW)
    begin
        if rising_edge(CLK_SLOW) then
            if handshake_slow = handshake_fast_2 then
                data_slow <= DATA_IN;
                handshake_slow <= not handshake_slow;
            end if;
        end if;
    end process;

    -- ファストクロックドメイン
    process(CLK_FAST)
    begin
        if rising_edge(CLK_FAST) then
            handshake_fast_1 <= handshake_slow;
            handshake_fast_2 <= handshake_fast_1;

            if handshake_fast_2 /= handshake_fast_1 then
                data_fast_1 <= data_slow;
                data_fast_2 <= data_fast_1;
            end if;
        end if;
    end process;

    DATA_OUT <= data_fast_2;
end Behavioral;

このコードでは、スロークロックドメインからファストクロックドメインへのデータ転送を安全に行うためのCDC設計を実装しています。

handshake信号を使用して、データの同期を取っています。

実行結果の例

CLK_SLOW : 0 → 1 → 0 → 1 → 0 → 1 → 0 → 1
CLK_FAST : 0101010101010101
DATA_IN : AA → BB → CC → DD
data_slow : 00 → AA → AA → BB → BB → CC → CC → DD
handshake_slow : 0 → 1 → 1 → 0 → 0 → 1 → 1 → 0
handshake_fast_1 : 0 → 0 → 1 → 1 → 0 → 0 → 1 → 1
handshake_fast_2 : 0 → 0 → 0 → 1 → 1 → 0 → 0 → 1
data_fast_1 : 00 → 00 → AA → AA → AA → BB → BB → CC
data_fast_2 : 00 → 00 → 00 → AA → AA → AA → BB → BB
DATA_OUT : 00 → 00 → 00 → AA → AA → AA → BB → BB

スロークロックドメインで取り込まれたデータが、handshake信号を介してファストクロックドメインに安全に転送されている様子が確認できます。

異なるクロックドメイン間でのデータ転送が、メタステーブル状態のリスクを最小限に抑えつつ実現されています。

まとめ

VHDLにおけるrising_edgeの使用は、デジタル回路設計の基礎となる重要な概念です。

本記事では、rising_edgeの基本から高度な応用まで、幅広いトピックを網羅しました。

rising_edgeの適切な使用は、高性能で信頼性の高いデジタル回路の設計に不可欠です。

学んだ知識とテクニックを活かし、より洗練された回路設計にチャレンジしてみてください。