key :
10進カウンタ 設計フロー
前回までで、基本的&よく使うであろう回路らの記述を簡単に説明したので、それらを組み合わせてみる。
で、組み合わせるもなにも、どんな回路を作るかが決まってなければ作りようが無いってことで、
時計化を見越した上で10進のカウンタ+αの3段組を作ってみることにする。
仕様
・システム周波数と同期
・10進カウンタ
・ある信号を入れると、任意の値を出力するようにする。(時刻調整)
以上が、必須な条件にする。
注意する点としては、桁上がり時の動
作を上手く調整すること
非同期の信号を使わないこと。
<悪い例>
例えば出力値が9になった際の信号をリセットに入れたり、次の信号のクロック部に入れたり。↓
解答?
考えその1 | タイムチャート |
タイムチャートを書いてみると、D-FFの出力カウンタの値が9になった際にリセット用の信号が発生し、
その信号が入力された次のクロックでD-FFのカウンタ値が0になる様にしたい。 一番最初に書いたようにD-FFのリセット部にその信号を入れたら楽なのだけれど、それではリセット部に ゲート信号を入れてしまうことになる・・・ |
![]() |
詳細 | 回路 |
そこで、前回説明したセレクタを用いてこの問題を解決します。 まず出力が9になった際、つまり、Qの3bit目と1bit目が1の時にHとなるANDゲートを1つ用いて信号cnt_resを作る。 assign cnt_res = Q[3] & Q[0]; そして、その値をセレクタに入れて、cnt_resが0だった場合はQ+1された値を出力。 1だった場合には0を出力するようにする。 assign z1 = Q + 4'h1; assign z2 = (cnt_res == 1)? 4'h0 : z1; always @(posedge clock or negedge reset) begin if (reset == 0) Q <= 4'h0; else Q <= z2; end この様にすれば、D-FF本体のリセットを使わずに値を0にする事が出来る。 尚、タイムチャートではcnt0_resとなっており、回路図ではcnt_resになっていますが、便宜上のものなんで気にしないでください。 |
![]() |
考えその2 | タイムチャート |
桁上がり。同じのをもう1つ接続。取りあえず自分が9になった次の動作で上位の桁が動けばいい訳だから、cnt_resを次の段へ繋いでそれを利用すればいい。 outputでcnt_resを出力して次段へ接続するのでも良いし、接続する前にもう一度=9でHになるようにAND取って接続するのも良し。 今回は、出力QのANDを取ってから次段へ接続させてみる。 |
![]() |
詳細 | 回路図 |
cnt_enってのが、条件を満たした際の信号である。 この信号が入力されないと、z3は常にQを出し続ける。 assign z1 = Q + 4'h1; assign z3 = (cnt_en == 1)? z1 : Q; assign cnt_res = Q[3] & Q[0]; assign z2 = (cnt_res == 1)? 4'h0 : z3; always @(posedge clock or negedge reset) begin if (reset == 0) Q <= 4'h0; else Q <= z2; end |
![]() |
考えその3 | タイムチャート |
桁上がり後のリセット。 先ほどの回路で問題なく動く〜〜と思いきや、リセット時の動きがちょっと変です。⇒ 見ての通り、出力が9になった次のクロックで0になる訳ですから、桁が上がったら・・・ |
![]() |
詳細 | タイムチャート |
そういう訳で、右のタイムチャートの様に下位の桁の9になった際の信号も必要になる訳です。 |
![]() |
Verilog 記述 | 回路 |
assign z1 = Q + 4'h1; assign z3 = (cnt_en == 1)? z1 : Q; assign now_9 = Q[3] & Q[0]; assign cnt_res = now_9 & cnt_en; assign z2 = (cnt_res == 1)? 4'h0 : z3; always @(posedge clock or negedge reset) begin if (reset == 0) Q <= 4'h0; else Q <= z2; end |
![]() |
考えその4 | タイムチャート |
以上で、カウンタの桁上がりの際の動作は大丈夫になった。 よって次は、セット信号が入った際の動作について考える。 |
![]() |
Verilog 記述 | タイムチャート |
セレクタの使い方さえ分かっていれば特に問題ないですかね。 assign z1 = Q + 4'h1; assign z3 = (cnt_en == 1)? z1 : Q; assign now_9 = Q[3] & Q[0]; assign cnt_res = now_9 & cnt_en; assign z2 = (cnt_res == 1)? 4'h0 : z3; assign z4 = (data_set == 1)? data : z2; always @(posedge clock or negedge reset) begin if (reset == 0) Q <= 4'h0; else Q <= z4; end |
![]() |
cnt_10.v | cnt_th.v | |
module cnt_10(clock,reset,cnt_en,data_set,data,Q); input clock,reset,cnt_en,data_set; input [3:0] data; output [3:0] Q; wire [3:0] z1,z2,z3,z4; wire now_9,cnt_res; reg [3:0] Q; assign z1 = Q + 4'h1; assign z3 = (cnt_en == 1)? z1 : Q; assign now_9 = Q[3] & Q[0]; assign cnt_res = now_9 & cnt_en; assign z2 = (cnt_res == 1)? 4'h0 : z3; assign z4 = (data_set == 1)? data : z2; always @(posedge clock or negedge reset) begin if (reset == 0) Q <= 4'h0; else Q <= z4; end endmodule |
module cnt_th(clock,reset,Q0,Q1,Q2,data_set,data0,data1,data2); input clock,reset,cnt_set; input [3:0] data0,data1,data2; output [3:0] Q0,Q1,Q2; wire cnt1_en,cnt1_9,cnt2_en; cnt_10 con_0 ( .clock(clock), .reset(reset), .cnt_en(1'b1), .data_set(data_set), .data(data0), .Q(Q0) ); assign cnt1_en = Q0[3] & Q0[1]; |
cnt_10 con_1 ( .clock(clock), .reset(reset), .cnt_en(cnt1_en), .data_set(data_set), .data(data1), .Q(Q1) ); assign cnt1_9 = Q1[3] & Q1[0]; assign cnt2_en = cnt1_9 & cnt1_en; cnt_10 con_2 ( .clock(clock), .reset(reset), .cnt_en(cnt2_en), .data_set(data_set), .data(data2), .Q(Q2) ); endmodule |
↑10進カウンタ | ↑10進カウンタを3個連結してみた。↑ |
これで桁上がり時の動作も大丈夫な10進のカウンタが出来上がりました。
さて、ここでちょっくら頭の体操。
桁上がりの時の動作はちょっと無視してもらうけれど、上と下ではどう違うだろうか?
![]() |
まず、上の回路は今まで説明してきた回路と同じであり、出力Qが9の際cnt_resが作られる。
下の回路は、出力Qが9の際それに値が加算され10になった際の信号でcnt_resが作られ、次のタイミングでQが0になるという
点では動作は同じであると考えられる。
↓↓違い↓↓
違いは、ゲートの遅延にある。
動作周波数が低い場合は、どちらでも問題ないが、高速で動作する場合にはゲートの遅延分が問題となってくる。
上のQの9を利用する場合、"0000"を出力するセレクタにはAND回路1つ分のゲート遅延で済むが、
下のz1の10を利用する場合、セレクタに信号が到着するまでADDでの加算分の遅延+AND回路1つ分の遅延が発生する。
通常の回路でゲート数個分の遅延なんて気にしなくてももんだいないけれど・・・