004a回
ちょっと複雑な論理回路

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を作る。

cnt_res <= Q(3) and Q(0);

そして、その値をセレクタに入れて、cnt_resが0だった場合はQ+1された値を出力。
1だった場合には0を出力するようにする。

z1 <= Q + "0001";

z2 <= "0000" when cnt_res = '1' else z1;

process (clock , reset) begin
 if (reset = '0') then
  Q <= "0000";
 elsif (clock'event and clock = '1') then
  Q <= z2;
 end if;
end process

この様にすれば、D-FF本体のリセットを使わずに値を0にする事が出来る。


尚、タイムチャートではcnt0_resとなっており、回路図ではcnt_resになっていますが、便宜上のものなんで気にしないでください。

考えその2 タイムチャート
桁上がり。同じのをもう1つ接続。取りあえず自分が9になった次の動作で上位の桁が動けばいい訳だから、cnt_resを次の段へ繋いでそれを利用すればいい。
outputでcnt_resを出力して次段へ接続するのでも良いし、接続する前にもう一度=9でHになるようにAND取って接続するのも良し。
今回は、出力QのANDを取ってから次段へ接続させてみる。
詳細 回路図
cnt_enってのが、条件を満たした際の信号である。
この信号が入力されないと、z3は常にQを出し続ける。


z1 <= Q + "0001";
z3 <= z1 when cnt_en = '1' else Q;
cnt_res <= Q(3) and Q(0);
z2 <= "0000" when cnt_res = '1' else z3;

process (clock , reset) begin
 if (reset = '0') then
  Q <= "0000";
 elsif (clock'event and clock = '1') then
  Q <= z2;
 end if;
end process



考えその3 タイムチャート
桁上がり後のリセット。
先ほどの回路で問題なく動く〜〜と思いきや、リセット時の動きがちょっと変です。⇒

見ての通り、出力が9になった次のクロックで0になる訳ですから、桁が上がったら・・・
詳細 タイムチャート
そういう訳で、右のタイムチャートの様に下位の桁の9になった際の信号も必要になる訳です。
Verilog 記述 回路

z1 <= Q + "0001";
z3 <= z1 when cnt_en = '1' else Q;
now_9 <= Q(3) and Q(0);
cnt_res <= now_9 and cnt_en;
z2 <= "0000" when cnt_res = '1' else z3;

process (clock , reset) begin
 if (reset = '0') then
  Q <= "0000";
 elsif (clock'event and clock = '1') then
  Q <= z2;
 end if;
end process


考えその4 タイムチャート
以上で、カウンタの桁上がりの際の動作は大丈夫になった。
よって次は、セット信号が入った際の動作について考える。
Verilog 記述 タイムチャート
セレクタの使い方さえ分かっていれば特に問題ないですかね。

z1 <= Q + "0001";
z3 <= z1 when cnt_en = '1' else Q;
now_9 <= Q(3) and Q(0);
cnt_res <= now_9 and cnt_en;
z2 <= "0000" when cnt_res = '1' else z3;
z4 <= data when data_set = '1' else z2;

process (clock , reset) begin
 if (reset = '0') then
  Q <= "0000";
 elsif (clock'event and clock = '1') then
  Q <= z4;
 end if;
end process


cnt_10.v cnt_th.v

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;

entity cnt_10 is
port(
  clock  : in std_logic;
  reset  : in std_logic;
  cnt_en  : in std_logic;
  data_set : in std_logic;
  data   : in std_logic_vector (3 downto 0);
  Q     : out std_logic_vector (3 downto 0)
  );
end cnt_10;

architecture RTL of cnt_10 is

signal Q_reg : std_logic_vector (3 downto 0);
signal z1   : std_logic_vector (3 downto 0);
signal z2   : std_logic_vector (3 downto 0);
signal z3   : std_logic_vector (3 downto 0);
signal z4   : std_logic_vector (3 downto 0);
signal now_9 : std_logic;
signal cnt_res: std_logic;

begin

Q <= Q_reg;

z1 <= Q_reg + "0001";
z3 <= z1 when cnt_en = '1' else Q_reg;
now_9 <= Q_reg(3) and Q_reg(0);
cnt_res <= now_9 and cnt_en;
z2 <= "0000" when cnt_res = '1' else z3;
z4 <= data when data_set = '1' else z2;

process (clock , reset) begin
 if (reset == 0) then
  Q_reg <= "0000";
 elsif (clock'event and clock = '1') then
  Q_reg <= z4;
 end if;
end process

end RTL;


library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;


entity ttcnt is
port(
  clock  : in std_logic;
  reset  : in std_logic;
  Q1    : out std_logic_vector (3 downto 0);
  Q2    : out std_logic_vector (3 downto 0);
  Q3    : out std_logic_vector (3 downto 0);
  data_set : in std_logic;
  data1  : in std_logic_vector (3 downto 0);
  data2  : in std_logic_vector (3 downto 0);
  data3  : in std_logic_vector (3 downto 0)
);
end ttcnt;


architecture RTL of ttcnt is

component cnt_10
port (
  clock  : in std_logic;
  reset  : in std_logic;
  cnt_en  : in std_logic;
  data_set : in std_logic;
  data   : in std_logic_vector (3 downto 0);
  Q     : out std_logic_vector (3 downto 0)
  );
end component;

signal Q1_reg : std_logic_vector (3 downto 0);
signal Q2_reg : std_logic_vector (3 downto 0);

signal cnt2_en : std_logic;
signal cnt2_9  : std_logic;
signal cnt3_en : std_logic;

signal hi : std_logic;






begin

Q1 <= Q1_reg;
Q2 <= Q2_reg;

hi <= '1';

zerocnt1:
cnt_10 port map
  (
  clock  => clock,
  reset  => reset,
  cnt_en  => hi,
  data_set => data_set,
  data   => data1,
  Q     => Q1_reg
  );

cnt2_en <= Q1_reg(3) and Q1(0)_reg;

zerocnt2:
cnt_10 port map
  (
  clock  => clock,
  reset  => reset,
  cnt_en  => cnt2_en,
  data_set => data_set,
  data   => data2,
  Q     => Q2_reg
  );

cnt2_9 <= Q2_reg(3) and Q2_reg(0);
cnt3_en <= cnt2_9 and cnt2_en;

zerocnt3:
cnt_10 port map
  (
  clock  => clock,
  reset  => reset,
  cnt_en  => cnt3_en,
  data_set => data_set,
  data   => data3,
  Q     => Q3
  );

end RTL;
↑10進カウンタ

↑10進カウンタを3個連結してみた。↑

接続する信号部分は、式や定数を記述することが出来ないため、hiの様な内部信号を宣言してアーキテクチャ内で1に固定して、それを接続する。


これで桁上がり時の動作も大丈夫な10進のカウンタが出来上がりました。


さて、ここでちょっくら頭の体操。
桁上がりの時の動作はちょっと無視してもらうけれど、上と下ではどう違うだろうか?



まず、上の回路は今まで説明してきた回路と同じであり、出力Qが9の際cnt_resが作られる。
下の回路は、出力Qが9の際それに値が加算され10になった際の信号でcnt_resが作られ、次のタイミングでQが0になるという 点では動作は同じであると考えられる。

↓↓違い↓↓

















違いは、ゲートの遅延にある。
動作周波数が低い場合は、どちらでも問題ないが、高速で動作する場合にはゲートの遅延分が問題となってくる。
上のQの9を利用する場合、"0000"を出力するセレクタにはAND回路1つ分のゲート遅延で済むが、
下のz1の10を利用する場合、セレクタに信号が到着するまでADDでの加算分の遅延+AND回路1つ分の遅延が発生する。


通常の回路でゲート数個分の遅延なんて気にしなくてももんだいないけれど・・・


[<<前へ | ↑Topへ戻る↑ | 次へ>>]