デジタル回路のカウンタ

デジタル回路のカウンタ

 カウンタはデジタル回路の中でも一番よく使う回路です。数を数える。という回路ですが、FPGAでは同期カウンタを使います。デジタル回路で数を数える。というカウンタには様々な種類があって「バイナリカウンタ」「ジョンソンカウンタ」「グレイコードカウンタ」などがあります。FPGAでは特別な事が無い限りバイナリカウンタを使います。

 バイナリカウンタは2進数の数え方をハードウェアで行うものです。一番単純で馴染みやすいと思います。数を+1していくだけのカウンタですから。ただし、バイナリカウンタの欠点は知っておくべきでしょう

 4ビットのバイナリカウンタで数を数えてみましょう。

0000,0001,0010,0011,0100,0101,0110,0111,1000,1001,1010,1011,1100,1101,1110,1111…..

 このように数える動作になります。数えているちょうど真ん中を見てください。数でいうと、0111から1000になる所です。このときのカウンタは下3桁が1111です。その3ビットが一気に000になって、最上位ビットが1になりますね。当たり前の動作なのですが、これがバイナリカウンタの欠点です。

 バイナリカウンタは数えるちょうど真ん中でカウンタビット数のDFFが一気に反転するのです。一気に反転動作する。というのはハードウェアにとってあまり好ましい事ではありません。それは、消費電流が増えるからです。一般的にFPGAはビットが反転する瞬間に消費電流が増えます。なので、バイナリカウンタのビットが同じ瞬間に反転すると多くの電流を消費するのです。

 ハードウェアにおいて、電流を消費するというのは省エネの観点から攻められる事ではありますが、それ以上に問題なのがノイズです。消費電流とノイズには非常に密接な関係があり、消費電流が大きいという事はエネルギーが大きくなるので、必然的にノイズも多くなるのです。しかも、消費電流が変化するとその変化した周波数に見合ったノイズが生じます。

 カウンタのように頻繁に動作している回路が一気にビット反転を起こすという症状はノイズの発生源になります。それでも、数を数えるのがデジタル回路なので仕方が無いのですが、この欠点は理解しておきましょう。

 どうしても、ノイズで困って、それがカウンタである。という事が起こった時、その時にはグレイカウンタを設計してください。

 グレイカウンタは全部のビットが一気に動くことがありません。ただし、非常に動作が分かりにくいです。いつか解説する時が来るかも知れませんが、今の時代余程の事が無ければ使うことは無いでしょう。

 さて、カウンタですが、FPGAで扱うときに注意することがあります。それは初期値を入れる。という事です。人は自然に0から数えるかも知れませんし、1から数えるかも知れませんが、HDLでは何も指示をしなければ不定です。

 不定という事は何から始まるのかわからない訳ですから、正常な動作は望めません。FPGAのトラブルで一番多いのは電源投入時の動作がおかしい。という物で、DFFの初期値を入れていない為に起こるトラブルです。

 初期値はリセット信号が入っていればリセットで初期値に設定しても良いですし、リセットが無い場合(お勧めできませんが)には、宣言時に初期値を入れておく事が出来ます。

 本ブログではリセットが入力される前提でFPGAの設計を行っていますので、すべてのDFFを含むカウンタ初期値はリセットで宣言しています。

 ソースコードを見ながらカウンタについて解説いたします。

------------------------------------------
-- カウンタ
------------------------------------------
process (clock,nreset) begin
	if nreset = '0' then									-- リセット初期化
		rcounter <= (others => '0');
		rPls_out <= '0';
	elsif clock' event and clock = '1' then					-- clock 立ち上がり指示
		if (wtim1msec = '1') then
			if (rcounter = c10MSEC) then
				rPls_out <= '1';
				rcounter <= (others => '0');				-- reset counter 
			else
				rPls_out <= '0';
				rcounter <= rcounter +'1';
			end if;
		else
			rPls_out <= '0';
		end if;
	end if;
end process;

 これはスイッチ入力の回路で10msecを作った時のカウンタ回路です。Verilogを学ばれている方も本ブログにありますので、そちらを参照してください。

 5行目のif nreset = ‘0’ thenがリセット信号がLowの時の処理。つまり初期値を入れている所です。上のソースコードでは6行目のようにrcounter <= (others => ‘0’);という書き方で全部のビットに0を入れる。という事になります。

 初心者の方が最初理解できないのが、「全部のビット」という所ではないでしょうか? この場合、rcounterの全部のビットなので、rcounterの宣言を確認してみてください。この場合、上のスースコードには載っていませんが、全ソースコードを乗せたこちらに記載していますので参照してください。

 これによると、signal rcounter: std_logic_vector(3 downto 0);
となっていますので、rcounterは4ビットです。

 この4ビット全部に’0’を入れる。という事です。つまり”0000″になります。これが(others => ‘0’)で置き換えられる。という事になります。逆に、rcounter <=”0000″;でも同じ意味になります。いちいち4個数えるのを省ける便利な予約語ですね。

 さて、初期値を入れたので、8行目のelsif clock’ event and clock = ‘1’ thenでclockがエッジで’1’になるとき(つまり立ち上がりエッジ)に9行目が実施されます。

 この9行目でカウンタをON/OFFしています。動作的には一時停止。という感じになります。9行目のif (wtim1msec = ‘1’) thenはwtim1msecが’1’の時にカウンタを回す。という事を示しています。このようにカウンタやシフトレジスタなどをON/OFFする信号の事をイネーブル信号といいます。

 イネーブル信号は同期設計において非常に重要な考え方です。クロックは止めてはいけません。クロックを止めないでイネーブル信号でカウンタをON/OFFするのです。それによって、回路はクロックに同期して動きます。この様子を以下に示します。

 クロックは止まっていません。wtim1msecの信号は1msecに1回10MHz幅のパルスが来ます。このwtim1msecパルス幅とクロックの関係をよく見てください。10MHzの1波長分のパルス幅です。つまり、立ち上がりが1回分という事になります。このクロックの立ち上がりでなおかつwtim1msecが1の時にrcounterは+1されます。

 逆の見方をするとwtim1msec=0の時にはrcounterはそのまま。という事になります。

 10行目にif (rcounter = c10MSEC) thenがありますが、これはデコーダといいます。カウンタとデコーダは対で使います。デコーダはカウンタで数えた数から所定の瞬間を切り取る回路を示します。この場合、カウンタの数がc10MSECの数になった時。という意味になります。この時12行目でカウンタはゼロに戻っています。このようにカウンタをゼロに戻すことを「ゼロリセットする」といいます。上の波形でも9(Hex)の次に0(hex)になっているのがわかると思います。

 15行目でカウンタをアップ(+1)しています。rcounter = c10MSEC以外はカウンタをアップする。という事になるので、動作としては、0~c10MSECまでは+1していって、c10MSECになったら0に戻る。という事を繰り返すカウンタになります。

 このようにカウンタは数を数えるのですが、だいたいデコーダと一緒に使って、ある数まで行ったならxxxをする。というような使い方、またはある数まで行ったら停止する。よいう動作をします。

 カウンタ回路はデジタル回路で重要な考え方ですから、必ずマスターしてください。