VHDLの組み合わせ回路

VHDLの組み合わせ回路

 デジタル回路設計において、順序回路の解説は多いのですが、組み合わせ回路の解説が少なくて、どのように記述すれば良いのか質問を受ける事があります。

 特にソフトウェアの経験がある方は、同時実行である組み合わせ回路のタイミングで悩む事が多い様です。

VHDLの回路はarchitectureの中で記述していますが、processは順序回路なので、それ以外のarchitectureの中が組み合わせ回路の記述部分です。

 その記述部分では、1つの処理が通常は1行の;までです。これらの処理が平行して処理されます。

 例として次のような回路があります。この回路のようにDFFが無い回路を組み合わせ回路と定義します。

 この回路をVHDLの組み合わせ回路で記載すると次のようになります。

WireA <= inputA and inputB;
OutputA <= WireA;
OutputC <= WireA or InputC;

 このようにVHDLで書くとなおのこと、WireAが決まってからOutputCが決まるように思えますが、上記のVHDL式はこのように変形できます。

OutputA <= inputA and inputB;
OutputC <= (inputA and inputB) or InputC;

 このように、式の書き方が違っても同じ動作をする場合があります。というより、その方が多くなります。実際に、動作する回路はコンパイラが論理圧縮してしまうので、最後はどのような組み合わせ回路に圧縮されたかを確認するには、レポートファイルなどで調査するしかありません。

 上記の組み合わせ回路はWireAが決まってからOutputBが決まるように見えます。実際、電気信号には流れる速度があるので、wireAが決まってからOutputBが決まります。詳しく信号で解析してみます。

 InputAとInputBのandであるWireAは青い時間分にあたるand素子の動作時間分遅れる事になります。

 厳密に考えればこのようになりますが、実際には先に記したように、論理圧縮が入るので、どのような組み合わせ回路になるのかはわかりません。したがって、遅延の時間もどうなるのかはコンパイル結果で判断することになります。

 FPGAの場合には同期設計しますので、上のように組み合わせ回路だけを使うという事は無く、DFFで同期をとりながら設計します。その様子を次に示します。

 上の図では組み合わせ回路の前・後にDFFで同期をとっています。通常はこのような構成になるので、組み合わせ回路は(前の)DFFと次の(後ろの)DFFの動作までの時間で結論が出ていればOK。という事になります。

 この様子を先ほどのタイミング図で示すと次のようになります。

 このように次のクロックまでに確定していれば良いので、赤矢印で示した時間は余裕という事になります。FPGAの場合はこのように、次のクロックまでに組み合わせ回路が終わるように設計します。最近のFPGAではクロックが早くなってきていますが、その分、内部回路も早くなっていますので、よほど高速で大規模な回路設計を行わない限りは組み合わせ回路の遅延時間を気にして設計する事はありません。むしろ、コンパイラが出力したレポートを読んで遅延が収まっているのかを確認する。という流れになります。

 以下にいろいろな組み合わせ回路の記述例を示します。各行の;までが一つの回路です。たとえば3行目は3行目だけで1つの組み合わせ回路です。6行目は28行目までで1つの組み合わせ回路です。

RAM_nG <= Inn_nG and Inn_A(1);
RAM_nUB <= not Inn_A(0);
RAM_nLB <= Inn_A(0);
RAM_DQU <= Inn_wdat when (Inn_nE = '0' and Inn_nG = '1' and Inn_A(0) = '1') else (others => 'Z');
Inn_rdat <= MRAM_DQL when (Inn_A(0) = '0') else MRAM_DQU;
Number <=	'1' when ( ASCIIin = X"30" ) else		-- "0"
			'1' when ( ASCIIin = X"31" ) else		-- "1"
			'1' when ( ASCIIin = X"32" ) else		-- "2"
			'1' when ( ASCIIin = X"33" ) else		-- "3"
			'1' when ( ASCIIin = X"34" ) else		-- "4"
			'1' when ( ASCIIin = X"35" ) else		-- "5"
			'1' when ( ASCIIin = X"36" ) else		-- "6"
			'1' when ( ASCIIin = X"37" ) else		-- "7"
			'1' when ( ASCIIin = X"38" ) else		-- "8"
			'1' when ( ASCIIin = X"39" ) else		-- "9"
			'1' when ( ASCIIin = X"41" ) else		-- "A"
			'1' when ( ASCIIin = X"42" ) else		-- "B"
			'1' when ( ASCIIin = X"43" ) else		-- "C"
			'1' when ( ASCIIin = X"44" ) else		-- "D"
			'1' when ( ASCIIin = X"45" ) else		-- "E"
			'1' when ( ASCIIin = X"46" ) else		-- "F"
			'1' when ( ASCIIin = X"61" ) else		-- "a"
			'1' when ( ASCIIin = X"62" ) else		-- "b"
			'1' when ( ASCIIin = X"63" ) else		-- "c"
			'1' when ( ASCIIin = X"64" ) else		-- "d"
			'1' when ( ASCIIin = X"65" ) else		-- "e"
			'1' when ( ASCIIin = X"66" ) else		-- "f"
			'0' ;
ASCIIOutL	<=	X"30" when ( HexIn(3 downto 0) = X"0" ) else	--	0
                X"31" when ( HexIn(3 downto 0) = X"1" ) else	--	1
                X"32" when ( HexIn(3 downto 0) = X"2" ) else	--	2
                X"33" when ( HexIn(3 downto 0) = X"3" ) else	--	3
                X"34" when ( HexIn(3 downto 0) = X"4" ) else	--	4
                X"35" when ( HexIn(3 downto 0) = X"5" ) else	--	5
                X"36" when ( HexIn(3 downto 0) = X"6" ) else	--	6
                X"37" when ( HexIn(3 downto 0) = X"7" ) else	--	7
                X"38" when ( HexIn(3 downto 0) = X"8" ) else	--	8
                X"39" when ( HexIn(3 downto 0) = X"9" ) else	--	9
                X"41" when ( HexIn(3 downto 0) = X"A" ) else	--	A
                X"42" when ( HexIn(3 downto 0) = X"B" ) else	--	B
                X"43" when ( HexIn(3 downto 0) = X"C" ) else	--	C
                X"44" when ( HexIn(3 downto 0) = X"D" ) else	--	D
                X"45" when ( HexIn(3 downto 0) = X"E" ) else	--	E
		        X"46" when ( HexIn(3 downto 0) = X"F" ) else	--	F
		     	X"30" ;

 6行目は良く使うセレクタの回路になります。NumberをASCIIinの内容によって、’1’か’0’かを決めています。when~elseの回路はこのようによく使うので覚えておくと便利です。

 29行目はwhenでASCIIOutLの内容を切り替えていますが、ASCIIOutLそのものがバスです。さらに、whenの中身はHexIn(3 downto 0) = X”0″のようにバスの一部で判断する。という式になっています。このようにバスにも使えますし、バスの一部を判断に使う事でロジック回路を簡素化することもできます。29行目程度に複雑になると、どのように論理圧縮されるのかは、想像できないので、コンパイラにお任せしてしまいます。

 上記の例の他にfunctionやprocedureでサブプログラムを書く事ができるので、もっと複雑な組み合わせ回路を作ることができますが、慣れないとかえって見通しが悪くなりますので、上達してからチャレンジするのが良いでしょう。

 組み合せ回路を設計するコツは、自分が把握できるところで一旦切ってしまう。という事です。冒頭のandとorの回路図に記載されたWireAのように、どこでも良いので、自分で見通せるところで一旦ワイヤーとして回路を切る事です。

 そして、改めてそのワイヤーから次の組み合わせ回路を考えていけば、どんなに大規模な回路でも少しづつ記述することができます。

 どの程度の組み合わせ回路になれば、遅延がどの程度になるのか。というのは、レポートファイルを読んでいけば、そのうちなんとなく理解できるようになるでしょう。その意味でも、コンパイルが成功した時点で、エラーが無くてもレポートファイルを読む習慣をつけるようにします。