4ビット加算器を使った 10進カウンタを、ロジックICで作ります。アップカウンタ、ダウンカウンタの切り換え付きとしました。
前回は、4ビット加算器を使って 16進カウンタ (4ビットカウンタ) を作りました。
毎度のことですが、16進カウンタでは、出力を 7セグメントLEDに表示させるのがめんどうくさいです。なので、BCD で出力したい。BCD (Binary-Coded Decimal) とは、10進数を 2進数で表現する方式。要するに、10進カウンタの出力って BCD なんだな。
4ビット加算器による 10進カウンタ回路
ブロック図
4ビットカウンタを 10進カウンタにするには、フリップフロップの前に組み合わせ回路を付加する。これは分周器によるカウンタも同じ。加算器とストレージレジスタの間に、ゲート組み合わせ回路をはさみます。
ゲート組み合わせ回路は、4ビット加算器の出力 S[0-3] を入力し、D[0-3] を出力してストレージレジスタに渡します。
アップカウンタとダウンカウンタとでは、ゲート組み合わせ回路の動作が異なりますので、切換信号 UD も入れて、アップダウンを切り換えてやりましょう。UD は加算器のアップダウンの切り換えをしている A[3] の信号を使うことができます。
その他のブロックに変更はありません。
真理値表と論理式
ゲート組み合わせ回路の真理値表です。
できあがってしまえばなんでもない真理値表ですが、ここはちょっといろいろ、悩みました。
まず、UD=0、アップカウンタの場合です。
初期状態のストレージレジスタの出力 Q は 0x0 で、加算器の入力 B に入ります。入力 A は アップカウンタのとき 0x1。したがって、加算器の出力 S は 0x1 になります。このときのゲート出力 D は 0x1、ストレージレジスタでラッチされ、出力 Q が 0x1 にカウントアップします。
すると、加算器の出力 S は 0x2 になるので、Q=0x2 とさらにカウントアップする。
こうしてカウントアップしていき、Q=0x9 になると S=0xA になる。このときにゲート組み合わせ回路により D=0x0 にすることで、Q=0x0 に戻ります。
次に、UD=1、ダウンカウンタの場合。
加算器は A=0xF を加算しますので、Q=0x0 のとき S=0xF になります。このときに、ゲート組み合わせ回路により D=0x9 とします。すると、S=0x8 となるので D=0x8 とする。出力は Q=0x8 にカウントダウンします。
こうしてカウントダウンしていき、Q=0x0 になると、S=0xF、D=0x9 と戻っていきます。
通常は加算器の出力をそのままストレージレジスタへ送るけど、0xA のとき、または0xF のとき、値を変化させてやる、という動きです。
真理値表より求めた論理式は、
D3 = S3 ( S1 + UD ) D2 = S3 ⋅ S2 D1 = S3 ⋅ S1 D0 = S0
となりました。
ゲート組み合わせ回路
論理式を基に、回路を作りました。
各ブロック内の未使用ゲートで作れちゃう回路です。が、ブレッドボードで組むときは、できるだけブロックごとにしておきたいので、別途ゲート IC を用意しようと思います。
VerilogHDL によるシミュレーション
回路を組む前に、ちゃちゃっとシミュレーションで確認しておきましょう。
回路記述
- module COUNTER_BY_ADDER_MOD10(
- input wire CK, RST,
- output reg [3:0] Q
- );
- wire [3:0] S, D;
- parameter [3:0] A = 4'b0001;
- assign D[3] = S[3] & (~S[1] | A[3]);
- assign D[2] = ~S[3] & S[2];
- assign D[1] = ~S[3] & S[1];
- assign D[0] = S[0];
- always @(posedge CK or negedge RST) begin
- if(1'b0 == RST)
- Q <= 4'b0;
- else
- Q <= D;
- end
- assign S = A + Q;
- endmodule
ゲート組み合わせ回路は、論理式で記述しました。
テストベンチ
- module COUNTER_BY_ADDER_MOD10_TEST;
- reg CK, RST;
- wire [3:0] Q;
- parameter STEP = 10;
- COUNTER_BY_ADDER_MOD10 #(4'b0001) COUNTER_BY_ADDER_MOD10(CK, RST, Q);
- always begin
- CK = 0; #(STEP/2);
- CK = 1; #(STEP/2);
- end
- initial begin
- RST = 0; #STEP;
- RST = 1; #(STEP*25);
- RST = 0; #STEP;
- $finish;
- end
- initial begin
- $dumpfile("counter_by_adder_mod10.vcd");
- $dumpvars(0, COUNTER_BY_ADDER_MOD10_TEST);
- end
- endmodule
アップカウンタ、ダウンカウンタの切り換えは、パラメータ割り当てで行なっています。コンパイルしなおさないといけないので、テストベンチのなかで切り換えてもいいですね。
シミュレーション結果
アップカウンタのシミュレーション結果です。
S=0xA のときに、D=0x0 となっています。
ダウンカウンタのシミュレーション結果です。
こちらでは、S=0xF のときに D=0x9 となっています。
シミュレーションの結果は、設計通りになっているようです。
ブレッドボードのようす
ということで、実際にゲート組み合わせ回路を追加しました。いちばん下のブレッドボードの右側部分です。
動画じゃないんで LEDバーの表示の動きがわかりませんが、ちゃんとカウントしています。
左下の押しボタンスイッチ START/STOP を押すたびに、カウンタが動作・停止します。停止後の再開は、カウント値を継続します。
また、Up-Down 切り替えスイッチを押すと、アップカウンタ、ダウンカウンタが切り換わります。動作中でも、停止中でも切り換わります。
まぁ、ユーザーインターフェースは、用途に応じてどのようにでも。
製作後記
加算器のおさらいからの派生で、カウンタ回路を作ってみました。こんな方法もあるよってことで。
加算器に関しては、桁上げ先見加算器とか、演算速度をあげる方法もあるんで、そうしたものもやってみる価値はあるかもしれないです。でも、加算を含めた演算処理に関しては、Arduino などを使うほうが現実的だよねぇって気がする。
かといって、なにもかも Arduino に頼るってもの現実的じゃないと思うわけで、なにをどっちでやらせるか、まぁそのときの状況に応じてですわな。