2006/9/8 【ソフトウエア編TOPに戻る】
今回はワンショットパルスを発生させるためのVHDLをご紹介しましょう。ワンショットパルスというのは、信号の入力をうけて任意の幅のパルスを一発だけ発生させることを言います。ほんの短いパルスや長いパルス信号を受けて任意の幅のパルス信号に整形してあげる場合に利用します。パルスを発生させるトリガとして、用途によって入力信号の立ち上がりタイミングや立下りタイミングなどを利用します。ワンショットパルスを発生させるには、カウンタ回路を走らせる、止める、カウント値によって信号を変化させる、などカウンタ回路を上手に使ってあげることが必要になります。カウンタ回路の応用事例として参考になるのではないかと思います。
今回は入力信号のチャタリングなどはないものとし、基本的な動作を抑えることにしましょう。
416−1.ワンショットパルス動作

上図はワンショットパルス発生の動作事例の波形です。INPUTというパルス波形が入力された場合、OUT1とOUT2が入力の立ち上がりタイミングをトリガとしたパルス発生で、OUT3が立下りタイミングをトリガとしたパルス発生です。また、OUT1は入力信号よりも短いパルス幅の事例で、OUT2は入力信号よりも長いパルス幅の事例です。それぞれの出力のパルス幅はVHDL上で任意に設定します。
この動作は周期的に繰り返す動作ではなく、単発で完結する動作です。入力信号のトリガを検出してパルス発生を行い、任意の設定時間が経過したらパルスを終了させて、再び入力信号を待ち構えた安定状態に戻さなければなりません。
416−2.立ち上がりタイミングでのパルス発生
それでは、先ずは立ち上がりタイミングでのワンショットパルス発生を見てみましょう。

入力端子はトリガとなるパルス信号を入力する[INPUT]と、クロックの[CLK]、そしてリセット信号の[REST]です。出力はワンショットパルスを出力する[OUTPUT]のみとしています。動作仕様としては以下の通りとしましょう。
1.入力信号[INPUT]の立ち上がりタイミングでワンショットパルスを出力する。
2.入力クロック[CLK]の周波数は1MHzとする。
3.出力するワンショットパルスのパルス幅は100mSとする。
動作のブロック図は上図の通りです。分かりづらい絵ですみません...
内部にCNT1とCNT2という2個のカウンタ回路を持ちます。CNT1は1MHzの入力クロックが速すぎるため、1000カウントに一回だけ内部のN_FLAG端子からパルスを出力させ、それをもとにCNT2を動かすためのものです。CNT2はワンショットパルスを出力させるためのカウンタで、出力パルスの幅も決定しています。
CNT2の中味がゴチャゴチャと書かれていますが、主な動作は自身のカウンタ値が100(十進)より小さければクロック(N_FLAG)に同期してカウントアップを行い、100になったらカウント値を102にプリセットしてカウントを止めるというものです。そして、入力INPUTの信号がLOWだったらカウント値を101にプリセットし、カウント値が101の時に入力INPUTがHIGHだったらカウント値を0にして、カウントをスタートさせます。さて肝心のパルス出力OUTPUTですが、このカウント値を監視していて、もし100以下だったら信号を出力するという動作を行わせます。そうすると、100カウント(100mS)だけのパルスが出力されるという動作になります。
ちょっとややこしいので、以下に流れを整理します。
1.回路に電源が投入されてRESET信号がLOWになると、CNT2のカウント値は101にプリセットされる。
2.カウント値が101で、入力INPUTがLOWのままなら、カウンタは動かず静止状態のまま。
3.ここで入力INPUTがHIGHになると、現在のカウント値は101なので、あらためてカウント値が0にプリセットされる。
4.カウント値が0になると100以下という条件に合致するため、N_FLAGをクロックとしてカウンタが動き出す。
5.カウント値が100までアップすると、カウント値は102にプリセットされ、カウンタ動作が停止する。
6.入力INPUTがHIGHのままだと、カウント値は102のまま保持される。
7.入力INPUTがLOWになると、カウント値が101にプリセットされ、次の入力INPUTの立ち上がりを待つ。
ここで、カウント値を101にするというのは、カウンタを停止させるためだというのはお分かり頂けると思いますが、100まで数えた後にわざわざ102という値にプリセットするのはナゼか?と思うことでしょう。ここを102ではなく101としてしまうと、100まで数えてカウントを停止した直後、入力INPUTがまだHIGHのままだったら、再びカウントを再開してしまいます。なので入力信号の立ち上がりタイミングではないのにカウントを再開してしまうことを防止するために、カウント終了後に入力INPUTがLOWに戻るまでの間、102にしておくのです。
416−3.立ち上がりタイミングによるワンショットパルス発生のVHDL記述
では、前項でご紹介したブロック図のVHDLをご紹介します。中味は全くブロック図のまんまです。
ここをクリックするとダウンロードできます。ONESHOT_FRONT1.VHD
| --------------------------------------------------- -- ONESHOT_FRONT1 2006.07.31 RIKIYA -- -- CLOCK DOWN 1MHz -> 1KHz -- INPUT PULSE FRONT EDGE TRIGGER -- OUTPUT ONESHOT PULSE WIDTH -> (WIDTH)mS -- N_FLAG : CLOCK DOWN FLAG --------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity ONESHOT_FRONT1 is port( CLK : in std_logic; RESET : in std_logic; INPUT : in std_logic; OUTPUT : out std_logic); end ONESHOT_FRONT1; architecture RTL of ONESHOT_FRONT1 is signal CNT1 : std_logic_vector(9 downto 0); -- CLOCK DOWN COUNTER signal CNT2 : std_logic_vector(6 downto 0); -- OUTPUT PULSE COUNTER signal N_FLAG : std_logic; -- CLOCK DOWN FLAG -- CLOCK DOWN INTREVAL 3E8h (1000) constant INTVL : std_logic_vector(9 downto 0) := "1111101000"; -- OUTPUT PULSE WIDTH 64h (100 mS) constant WIDTH : std_logic_vector(6 downto 0) := "1100100"; begin ----------------------------------- CLK DOWN 1/1000 INTERVAL process(RESET,CLK) begin if (RESET = '0') then CNT1 <= (others => '0'); elsif (CLK 'event and CLK = '1') then if (CNT1 = INTVL) then CNT1 <= (others => '0'); N_FLAG <= '1'; else CNT1 <= CNT1 + '1'; N_FLAG <= '0'; end if; end if; end process; ----------------------------------- PULSE WIDTH COUNTER process(RESET,N_FLAG) begin if (RESET = '0') then CNT2 <= WIDTH + '1'; elsif (N_FLAG 'event and N_FLAG = '1') then if (CNT2 = WIDTH) then CNT2 <= WIDTH + "10"; -- OUTPUT PULSE COUNT END elsif (CNT2 < WIDTH) then CNT2 <= CNT2 + '1'; -- OUTPUT PULSE COUNTING elsif (INPUT = '0') then CNT2 <= WIDTH + '1'; -- INPUT WITHOUT DETECT elsif (INPUT = '1' and CNT2 = WIDTH + '1') then CNT2 <= "0000000"; -- OUTPUT PULSE COUNT START end if; end if; end process; ----------------------------------- ONESHOT PULSE OUTPUT process(RESET,N_FLAG) begin if (RESET = '0') then OUTPUT <= '0'; elsif (N_FLAG 'event and N_FLAG = '1') then if (CNT2 <= WIDTH) then OUTPUT <= '1'; -- OUTPUT ONESHOT PULSE else OUTPUT <= '0'; -- WITHOUT ONESHOT PULSE end if; end if; end process; end RTL; |
■それでは、ポイントだけご説明します。
-- CLOCK DOWN INTREVAL 3E8h (1000)
constant INTVL : std_logic_vector(9 downto 0) := "1111101000";
-- OUTPUT PULSE WIDTH 64h (100 mS)
constant WIDTH : std_logic_vector(6 downto 0) := "1100100";
事前の解説では、ワンショットパルス発生の動作仕様として「100mSのパルス幅」と定義しました。1MHzのクロックから100mSのパルスを作るのですから、まず1MHzから1/1000のタイミングを作り(N_FLAG)、そのタイミングで100までカウントアップすれば100mSになります。ここでは、後から色々なパルス幅の生成に対応しやすくするため、1/1000の1000という数字と、100mSの100という数字を、それぞれINTVL(インターバル) とWIDTH(パルス幅)という変数にしてみました。 1000は2進数で3E8hなので、2進数なら1111101000となり、100は2進数なので64hなので、2進数なら1100100です。
----------------------------------- CLK DOWN 1/1000 INTERVAL
process(RESET,CLK)
begin
if (RESET = '0') then
CNT1 <= (others => '0');
elsif (CLK 'event and CLK = '1') then
if (CNT1 = INTVL) then
CNT1 <= (others => '0');
N_FLAG <= '1';
else
CNT1 <= CNT1 + '1';
N_FLAG <= '0';
end if;
end if;
end process;
このprocess文では、1MHzのクロックから1/INTVLのタイミングごとにパルスを発生させます。CNT1のカウント値がINTVLの値と一致したときだけN_FLAG値を1にセットし、すぐにCNT1を0にリセットするので、N_FLAGもすぐ0にリセットされます。
----------------------------------- PULSE WIDTH COUNTER
process(RESET,N_FLAG)
begin
if (RESET = '0') then
CNT2 <= WIDTH + '1';
elsif (N_FLAG 'event and N_FLAG = '1') then
if (CNT2 = WIDTH) then
CNT2 <= WIDTH + "10";
-- OUTPUT PULSE COUNT END
elsif (CNT2 < WIDTH) then
CNT2 <= CNT2 + '1';
-- OUTPUT PULSE COUNTING
elsif (INPUT = '0') then
CNT2 <= WIDTH + '1';
-- INPUT WITHOUT DETECT
elsif (INPUT = '1' and CNT2 = WIDTH + '1')
then
CNT2 <= "0000000";
-- OUTPUT PULSE COUNT START
end if;
end if;
end process;
このprocess文では、ワンショットパルスの動作を決定するカウンタCNT2の動作を記述しています。if文なので、ifの上位から優先的に動作します。なのでifやelsifの順番が変わると正常に動作しなくなります。ここの動作は前項でちょっとご説明した以下の流れのまんまです。上のprocess文と見比べて見て下さい。
1.回路に電源が投入されてRESET信号がLOWになると、CNT2のカウント値は101にプリセットされる。
2.カウント値が101で、入力INPUTがLOWのままなら、カウンタは動かず静止状態のまま。
3.ここで入力INPUTがHIGHになると、現在のカウント値は101なので、あらためてカウント値が0にプリセットされる。
4.カウント値が0になると100以下という条件に合致するため、N_FLAGをクロックとしてカウンタが動き出す。
5.カウント値が100までアップすると、カウント値は102にプリセットされ、カウンタ動作が停止する。
6.入力INPUTがHIGHのままだと、カウント値は102のまま保持される。
7.入力INPUTがLOWになると、カウント値が101にプリセットされ、次の入力INPUTの立ち上がりを待つ。
----------------------------------- ONESHOT PULSE OUTPUT
process(RESET,N_FLAG)
begin
if (RESET = '0') then
OUTPUT <= '0';
elsif (N_FLAG 'event and N_FLAG = '1') then
if (CNT2 <= WIDTH) then
OUTPUT <= '1'; -- OUTPUT
ONESHOT PULSE
else
OUTPUT <= '0'; -- WITHOUT
ONESHOT PULSE
end if;
end if;
end process;
このprocess文では、CNT2のカウント値を横で見ながら、カウント値に合わせて出力OUTPUTを1にしたり0にしたりしています。このOUTPUTが最終的にほしいワンショットパルス出力になりますので、役目は重大です。ここではCNT2のカウント値がWIDTH値(100)以下の間だけOUTPUTを1にセットし、それ以外は0にセットしています。これでほしいワンショットパルスが得られますね。
■以下はHEW4でのシミュレーション結果です。

一番上のCLKの部分は1MHzのクロックで、シミュレーションレンジに対して周波数が高過ぎるため真っ黒になっています。一発目の短めのINPUTパルスを受けて、OUTPUTからパルスが出力されますが、RESET入力によって出力もリセットされています。その後、今度はINPUTに長めのINPUTパルス(100mS以上)が入力されると、OUTPUTから100mSのパルスが出力されている様子がわかります。
416−4.立ち下がりタイミングでのパルス発生
次に、立ち下がりタイミングでのワンショットパルス発生を見てみましょう。

416−2.項でご紹介したブロック図と似ていますが、CNT2をカウントさせる部分に若干の違いがあります。
今度は動作仕様を以下の通りとしてみましょう。
1.入力信号[INPUT]の立ち下がりタイミングでワンショットパルスを出力する。
2.入力クロック[CLK]の周波数は1MHzとする。
3.出力するワンショットパルスのパルス幅は100mSとする。
ここでも前出と同じように、CNT2の部分だけ流れを整理します。
1.回路に電源が投入されてRESET信号がLOWになると、CNT2のカウント値は102にプリセットされる。
2.カウント値が102で、入力INPUTがLOWのままなら、カウンタは動かず静止状態のまま。
3.ここで入力INPUTがHIGHになると、現在のカウント値は101になり、入力INPUTがLOWになるのを待つ。
4.次に入力INPUTがLOWになると、条件に合致してカウント値が0になる。
5.カウント値が0になると100以下という条件に合致するため、N_FLAGをクロックとしてカウンタが動き出す。
6.カウント値が100までアップすると、カウント値は102にプリセットされ、カウンタ動作が停止し、次の入力INPUTの立ち上がりを待つ。
ここでは、INPUTにパルスが入力されたことを検知し、その立下りタイミングを待つためのステータスとして、カウント値101を準備しています。
416−5.立ち下がりタイミングによるワンショットパルス発生のVHDL記述
では、前項でご紹介したブロック図のVHDLをご紹介します。ここでも中味は全くブロック図のまんまですが、立ち上がりタイミングのプログラミングと違う部分だけを青文字としておきます。
ここをクリックするとダウンロードできます。ONESHOT_REAR1.VHD
| --------------------------------------------------- -- ONESHOT_REAR1 2006.07.31 RIKIYA -- -- CLOCK DOWN 1MHz -> 1KHz -- INPUT PULSE REAR EDGE TRIGGER -- OUTPUT ONESHOT PULSE WIDTH -> (WIDTH)mS -- N_FLAG : CLOCK DOWN FLAG --------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity ONESHOT_REAR1 is port( CLK : in std_logic; RESET : in std_logic; INPUT : in std_logic; OUTPUT : out std_logic); end ONESHOT_REAR1; architecture RTL of ONESHOT_REAR1 is signal CNT1 : std_logic_vector(9 downto 0); -- CLOCK DOWN COUNTER signal CNT2 : std_logic_vector(6 downto 0); -- OUTPUT PULSE COUNTER signal N_FLAG : std_logic; -- CLOCK DOWN FLAG -- CLOCK DOWN INTREVAL 3E8h (1000) constant INTVL : std_logic_vector(9 downto 0) := "1111101000"; -- OUTPUT PULSE WIDTH 64h (100 mS) constant WIDTH : std_logic_vector(6 downto 0) := "1100100"; begin ----------------------------------- CLK DOWN 1/1000 INTERVAL process(RESET,CLK) begin if (RESET = '0') then CNT1 <= (others => '0'); elsif (CLK 'event and CLK = '1') then if (CNT1 = INTVL) then CNT1 <= (others => '0'); N_FLAG <= '1'; else CNT1 <= CNT1 + '1'; N_FLAG <= '0'; end if; end if; end process; ----------------------------------- PULSE WIDTH COUNTER process(RESET,N_FLAG) begin if (RESET = '0') then CNT2 <= WIDTH + "10"; elsif (N_FLAG 'event and N_FLAG = '1') then if (CNT2 = WIDTH) then CNT2 <= WIDTH + "10"; -- OUTPUT PULSE COUNT END elsif (CNT2 < WIDTH) then CNT2 <= CNT2 + '1'; -- OUTPUT PULSE COUNTING elsif (INPUT = '1') then CNT2 <= WIDTH + '1'; -- INPUT PULSE DETECT elsif (INPUT = '0' and CNT2 = WIDTH + "1") then CNT2 <= "0000000"; -- OUTPUT PULSE COUNT START end if; end if; end process; ----------------------------------- ONESHOT PULSE OUTPUT process(RESET,N_FLAG) begin if (RESET = '0') then OUTPUT <= '0'; elsif (N_FLAG 'event and N_FLAG = '1') then if (CNT2 <= WIDTH) then OUTPUT <= '1'; -- OUTPUT ONESHOT PULSE else OUTPUT <= '0'; -- WITHOUT ONESHOT PULSE end if; end if; end process; end RTL; |
■基本的には立ち上がりタイミングの場合と同じなので説明は省略します。立ち上がりタイミングとの違いについては、前記流れの説明を元に追いかけて見て下さい。シミュレーション結果だけ以下にご紹介しましょう。

一発目の短めのINPUTパルスの立下りタイミングに合わせて、OUTPUTからパルスが出力されますが、RESET入力によって出力もリセットされています。その後、今度再びINPUTにパルスが入力されて立ち下がるタイミングで、OUTPUTから100mSのパルスが出力されている様子がわかります。
今回はカウンタ動作の応用例としてワンショットパルス発生についてご紹介しました。記事の中で「立ち上がりをトリガとして」とか、「立ち上がりタイミングで」などと書いていますが、厳密に言うと入力パルスの立ち上がりや立ち下りのエッジを捉えて動作させているわけではなく、あくまでクロックの立ち上がりタイミングの時の入力の状態を調べて動作させています。'event 文でINPUTのパルスのエッジを捉えられれば、もっと分かりやすい記述ができるのかもしれませんが、クロックに同期させて動作を行うことを前提とすると、こんな記述でしか実現できませんでした。 始める前はもっと簡単にできるかなー、などと思っていましたが、意外と難しかったというのが正直なところです。まだまだ勉強不足ですみません。m(_ _)m もっと分かりやすく、効率のよいワンショット発生のVHDL記述があれば、是非ご教授下さい。