VHDLについて

− プロセス文とタイミング −


井澤 裕司

(H19.5.23)


1. 同時処理文と順次処理文.

VHDLには,「 同時処理文 」と,「 順次処理文 」があります.

「 architecture 」本体には,「 同時処理文 」を並列に記述します.
実際の論理回路は,常に「 activeな状態 」にあるため,本来「 同時処理文 」で表現するのが自然です.
「 同時処理文 」に対して,「 順次処理文 」があります.

「 順次処理文 」は,ソフトウェアの「 逐次的な構文 」を用いて,「 ハードウェアのモデリング 」を行うものであり,
同時処理文に比べ,「 より抽象的な動作 」を直観的に記述することが可能です.
後で説明する「 プロセス文 」の内部は,この「 順次処理文 」が使用されます.
例えば,「 if文 」や「 case文 」,「 loop文 」などが,その代表的なものです.

「 同時処理文 」,「 順次処理文 」というのは,あくまでVHDLの「 構文を解析して,処理する手順 」が,
「 同時並行 」, あるいは「 順次 」を表しているのであり,
  「 信号が同時に変化するか,しないか 」
を表しているのではないことに,注意する必要があります.

下の 組合せ回路の例を用いて,具体的に説明しましょう.
entity inverter is
port (
A : in std_logic;
B : out std_logic;
C : out std_logic
);
end inverter;

architecture Dataflow of inverter is
begin
B <= A; 同時処理文
C <= B; 同時処理文
end Dataflow;

「 architecture 」の2つの代入文,
  B <= A;
   と,
  C <= B;
は,ともに「 同時処理文 」として解析されます.
「 信号Bへの代入 」は,「 信号Aの変化 」から「 1δ時間後 」に行われます.
一方,「 信号Cへの代入 」は,「 信号Bの変化 」から「 1δ時間後 」,すなわち,
「 信号Aの変化 」から「 2δ時間後 」に行われます.
このように,「 信号への代入 」は,必ずしも同時に行われるとは限りません.
なお,「 遅延時間δ 」の値は,「 0 」であり,実質的な 遅延はありません.

すなわち,上記VHDLでは回路の機能を記述しているだけであり,
具体的な回路の「 遅延時間δ 」の値については, 使用する「 ライブラリ 」 や,
「 FPGAの種類 」 など,「 論理合成 」の結果により決まります.
「 テストベンチ 」などで「 0以外の遅延量 」を設定するには,以下のように記述します.
B <= A after 5ns;
C <= B after 10ns;
これは,「 信号A 」が変化して「 5ns後 」に「 信号B 」が変化することを表しています.
同様に,「 信号B 」が変化して「 10ns後 」に「 信号C 」が変化します.
なお,2つの代入文の「 位置(前・後) 」を入れ換えても,「 等価 」であることは明らかです.

2. プロセス文について.

先に述べたように,「 architecture 」本体には,「 同時処理文 」を並列に記述します.
下の例に示すように,プロセス文が含まれる場合も,
  process(CLK) から end process;
までの記述を,1つの「 同時処理文 」とみなすことができます.

signal TMP : std_logic_vector(3 downto 0);
process (CLK) |
begin |
if (CLK'event and CLK = '1') then |
if TMP = "1001" then |
  TMP <= "0000" ; | 同時処理文
else |
TMP <= TMP + 1; |
end if ; |
end if ; |
end process ; |
 
COUNT <= TMP;   同時処理文
end Behavior;
したがって,信号「 COUNT 」への代入文の位置はどこでもよく,
下の図のように,「 プロセス文 」とその位置を入れ換えても等価です.
signal TMP : std_logic_vector(3 downto 0);
COUNT <= TMP;   同時処理文
 
process (CLK) |
begin |
if (CLK'event and CLK = '1') then |
if TMP = "1001" then |
  TMP <= "0000" ; | 同時処理文
else |
TMP <= TMP + 1; |
end if ; |
end if ; |
end process; |
end Behavior;

次に下の図を用いて, 順次処理文 を記述する プロセス文 と タイミング の関係について,
整理してみましょう.

この例は,
 process(CLK) から end process;
までの1つの プロセス文 と,
 COUNT <= TMP;
という1つの代入文で構成されています.
すなわち,2つの「 同時処理文 」が含まれています.

「 プロセス文 」直後のカッコ内の「 信号 」を「 センシティビティ・リスト 」と呼び,
この値が変化したとき,このプロセスが「 アクティブ 」となり,�A〜�Dの処理を行います.

�Aで,CLKの変化が「 立ち上り 」である場合は,�B以降の処理を行います.
一方,「 立ち下り 」である場合は,�Dに移行し,プロセスは「 スタンバイ状態 」となります.

�Bでは,「 信号TMP 」の状態を調べ,その値が「 "1001" 」のときは,「 δ時間後 」,
「 信号TMP 」に 「 "0000" 」を代入するよう,スケジューリングを行います.

一方,「 信号TMP 」の値が「 "1001" 」以外のときは, 「 信号TMP 」の値に,
「 1を加算した値 」を「 δ時間後 」に代入するよう,スケジューリングします.
「 スケジューリング 」が終わった後,�Dに移行し,プロセスは「 スタンバイ状態 」となります.
ここで「 δの値 」は「 0 」で,「 遅延はない 」のですが,「 プロセスが終了 」した後に,
代入が行われることに注意して下さい.






3. Behavior レベルと Register Transfer レベル.

前節のVHDLの「 architectureの仕様 」は,「 Behavior 」となっています.

トップダウン設計では,まず「 Behaviorレベルの記述 」を行い,システムの全般的な機能を確認します.
「 Behaviorレベル 」のシミュレーションでは, いわゆる「 イベント・ドリブン 」という手法により,
「 信号代入のタイミング 」を効率的に計算します.
例えば,信号の「 CLK 」が変化したとき,「 end process; 」までの処理を,「 時間0 」で順次に実行します.

しかし,このような処理は「 ソフトウェアによるシミュレーション 」上で成立するものです.
例えば10進カウンタで,「 信号のTMP 」の値を呼び出し,これに「 1を加算 」するためには,
4bitと1bitの加算に伴う演算時間が必要です.
ゲート遅延を伴う実際の「 ハードウェア 」で実現できるものではありません.
すなわち,いわゆるテストベンチ上で,「 動作仕様 」に基づく論理や基本的なタイミングに関する
シミュレーションを行うのが目的となります.

「 Behaviorレベル 」の検証を終えると, 「 VHDLのライブラリ 」等を用いて,
「 Register Transferレベル 」の具体的な回路構成に変換し,それらのタイミングを厳密に検証します.
下の図を用いて,「 Register Transfer レベル 」の回路の動作について説明しましょう.

「 Behaviorレベル 」では,クロックの立ち上りが「 すべての起点 」となっていました.
「 信号TMPへの代入 」は,クロックCLKの立ち上りから「 δ時間後 」に行われます.
ということは,「 信号TMPの値 」は,ほぼ1クロック前に,すでに確定していることになります.

「 RTレベル 」では,「 信号TMPの値 」が確定した時点を「 起点 」とします.
すなわち,「 TMPの値をチェック 」したり,「 TMPの値に1を加算 」する処理を,
ほぼ「 1クロック前 」から開始し, その結果を,「 フリップフロップ 」の「 D-FF 」を用いて,
クロック「 CLK 」の立ち上りで確定します.
「 D-FF 」の応答は比較的速いので,「 Behaviorレベル 」に近いシミュレーション結果が得られます.

「 RTレベル 」の回路設計では,「 TMPの値をチェック 」したり,「 TMPの値に1を加算 」する処理は,
すべて「 組合せ回路 」で実現します.

すなわち,下の図に示すように「 1クロック前のTMP 」の値を「 組合せ回路 」に入力し,
その出力を「 D-FF 」を用いて,波形整形します.
このような回路構成により,「 D-FF 」の出力は,「 Behaviorレベル 」の設計と同様,
「 CLKの立ち上り 」直後に変化する信号となります.



4. プロセス文による組合せ回路.

すべての「 組合せ回路 」は,「 同時処理文 」で表現できますが,
しばしば「 順次処理文 」が使える 「 プロセス文 」を用いて記述されることがあります.
「 if-else文 」や 「 case文 」等を用いて,回路の機能を判りやすく表現することができるためです.

このとき,以下の2点に注意する必要があります.

�@ 「 センシティビティ・リスト 」 に「 すべての入力信号 」 を, もれなく記述すること.
�A 「 入力のすべての状態 」に対する出力の値を,もれなく記述すること.

「 この条件が満たされないとき 」,入力の変化に出力が応答しないことがあり,
結果的に「 ラッチ機能 」が埋め込まれてしまうことがあるので,注意が必要です.

プロセス文の「 センシティビティ・リスト 」は,その信号が「 変化 」したとき,
それ以下の「 順次処理文 」が実行されます.
したがって,上記�@と�Aの条件が満たされていれば,「 入力信号の変化 」に対し,
必ず「 出力信号への代入 」が行われるため,実質的に「 遅延回路 」が構成されます.

ところが,「 入力信号が変化 」しても,「 出力信号への代入 」が行われない場合は,
「 プロセス 」終了後,自動的に「 スタンバイ状態 」となってしまうので,入力信号に追随できません.
このため,その「 出力の値 」を保持するための「 ラッチ回路 」 が生成されてしまうわけです.

「 プロセス文 」 を用いて「 組合せ回路 」 を記述する場合は,上記「2条件 」 を満たしているか,
必ず確認するようして下さい.

5. 信号と変数について.

ここでは,「 信号 signal 」と,「 変数 variable 」の違いについて説明します.

それらを整理した結果を,「 下の表 」に示します.
VHDL
時間的要素
代入表現
意 味
信号
signal
あり
<=
ハードウェア的
変数
variable
なし(中間値)
:=
ソフトウェア的
「 信号 」は,「 配線 」や「 D-FF 」で表される「 ハードウェア 」です.
「 代入処理 」による「 スケジューリング 」が行われ, 一定の遅延(0も含む)の後に, その値は変化します.

一方の「 変数 」は,「 コンパイル等のソフトウェア処理 」で利用される一時的な変数であり,
信号のような「 実体 」を持たないことに注意して下さい.

6. まとめ.

本資料では,「 VHDL 」で使用される「 プロセス文 」とその「 タイミング 」の関係を中心に解説しました.

「 VHDL 」は,「 ソフトウェア 」的な要素と,「 ハードウェア 」的な要素を兼ね備えた言語であり,
その利用価値は極めて高いのですが,理解し難い面があると言われています.
本資料が,理解の一助となることを願っています.

なお,「 誤り 」や「 誤解されやすい記述 」がありましたら,メール等でご連絡いただければ幸いです.